diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/README.md b/README.md new file mode 100644 index 00000000..ebfb3665 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# documentation diff --git a/docs/.buildinfo b/docs/.buildinfo new file mode 100644 index 00000000..d0420a2d --- /dev/null +++ b/docs/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 183f66e75532697ec6d3cd0a8682cfdf +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/.doctrees/cli/index.doctree b/docs/.doctrees/cli/index.doctree new file mode 100644 index 00000000..0cd2fb7c Binary files /dev/null and b/docs/.doctrees/cli/index.doctree differ diff --git a/docs/.doctrees/cli/install.doctree b/docs/.doctrees/cli/install.doctree new file mode 100644 index 00000000..824c4b28 Binary files /dev/null and b/docs/.doctrees/cli/install.doctree differ diff --git a/docs/.doctrees/cli/simulate.doctree b/docs/.doctrees/cli/simulate.doctree new file mode 100644 index 00000000..2ee68986 Binary files /dev/null and b/docs/.doctrees/cli/simulate.doctree differ diff --git a/docs/.doctrees/cli/translate.doctree b/docs/.doctrees/cli/translate.doctree new file mode 100644 index 00000000..70e1365e Binary files /dev/null and b/docs/.doctrees/cli/translate.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.cli.doctree b/docs/.doctrees/dragonfly_energy.cli.doctree new file mode 100644 index 00000000..30e1bbd6 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.cli.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.cli.install.doctree b/docs/.doctrees/dragonfly_energy.cli.install.doctree new file mode 100644 index 00000000..5403d6de Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.cli.install.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.cli.simulate.doctree b/docs/.doctrees/dragonfly_energy.cli.simulate.doctree new file mode 100644 index 00000000..b2b6e80a Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.cli.simulate.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.cli.translate.doctree b/docs/.doctrees/dragonfly_energy.cli.translate.doctree new file mode 100644 index 00000000..2d4f7ad0 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.cli.translate.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.config.doctree b/docs/.doctrees/dragonfly_energy.config.doctree new file mode 100644 index 00000000..1d07f4f4 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.config.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.des.connector.doctree b/docs/.doctrees/dragonfly_energy.des.connector.doctree new file mode 100644 index 00000000..26c74891 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.des.connector.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.des.doctree b/docs/.doctrees/dragonfly_energy.des.doctree new file mode 100644 index 00000000..cce4b39e Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.des.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.des.ghe.doctree b/docs/.doctrees/dragonfly_energy.des.ghe.doctree new file mode 100644 index 00000000..c91cc555 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.des.ghe.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.des.junction.doctree b/docs/.doctrees/dragonfly_energy.des.junction.doctree new file mode 100644 index 00000000..25898326 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.des.junction.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.des.loop.doctree b/docs/.doctrees/dragonfly_energy.des.loop.doctree new file mode 100644 index 00000000..a33e9dc3 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.des.loop.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.doctree b/docs/.doctrees/dragonfly_energy.doctree new file mode 100644 index 00000000..73405bb4 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.measure.doctree b/docs/.doctrees/dragonfly_energy.measure.doctree new file mode 100644 index 00000000..a5a07246 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.measure.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.colorobj.doctree b/docs/.doctrees/dragonfly_energy.opendss.colorobj.doctree new file mode 100644 index 00000000..d627b3cd Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.colorobj.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.connector.doctree b/docs/.doctrees/dragonfly_energy.opendss.connector.doctree new file mode 100644 index 00000000..efbe4f6f Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.connector.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.doctree b/docs/.doctrees/dragonfly_energy.opendss.doctree new file mode 100644 index 00000000..4a56437b Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.junction.doctree b/docs/.doctrees/dragonfly_energy.opendss.junction.doctree new file mode 100644 index 00000000..11c2b73f Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.junction.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.lib.doctree b/docs/.doctrees/dragonfly_energy.opendss.lib.doctree new file mode 100644 index 00000000..ee8769e8 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.lib.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.lib.powerlines.doctree b/docs/.doctrees/dragonfly_energy.opendss.lib.powerlines.doctree new file mode 100644 index 00000000..5cb2ad73 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.lib.powerlines.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.lib.transformers.doctree b/docs/.doctrees/dragonfly_energy.opendss.lib.transformers.doctree new file mode 100644 index 00000000..7bfced42 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.lib.transformers.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.lib.wires.doctree b/docs/.doctrees/dragonfly_energy.opendss.lib.wires.doctree new file mode 100644 index 00000000..e1483eb3 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.lib.wires.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.network.doctree b/docs/.doctrees/dragonfly_energy.opendss.network.doctree new file mode 100644 index 00000000..122a30c1 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.network.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.powerline.doctree b/docs/.doctrees/dragonfly_energy.opendss.powerline.doctree new file mode 100644 index 00000000..05b368fc Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.powerline.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.result.doctree b/docs/.doctrees/dragonfly_energy.opendss.result.doctree new file mode 100644 index 00000000..29966284 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.result.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.road.doctree b/docs/.doctrees/dragonfly_energy.opendss.road.doctree new file mode 100644 index 00000000..9b31bddf Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.road.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.substation.doctree b/docs/.doctrees/dragonfly_energy.opendss.substation.doctree new file mode 100644 index 00000000..266259c9 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.substation.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.transformer.doctree b/docs/.doctrees/dragonfly_energy.opendss.transformer.doctree new file mode 100644 index 00000000..8a25a4d3 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.transformer.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.transformerprop.doctree b/docs/.doctrees/dragonfly_energy.opendss.transformerprop.doctree new file mode 100644 index 00000000..9d572904 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.transformerprop.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.opendss.wire.doctree b/docs/.doctrees/dragonfly_energy.opendss.wire.doctree new file mode 100644 index 00000000..634d0c4f Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.opendss.wire.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.properties.building.doctree b/docs/.doctrees/dragonfly_energy.properties.building.doctree new file mode 100644 index 00000000..812f4970 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.properties.building.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.properties.context.doctree b/docs/.doctrees/dragonfly_energy.properties.context.doctree new file mode 100644 index 00000000..401077d5 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.properties.context.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.properties.doctree b/docs/.doctrees/dragonfly_energy.properties.doctree new file mode 100644 index 00000000..b0b38e0d Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.properties.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.properties.model.doctree b/docs/.doctrees/dragonfly_energy.properties.model.doctree new file mode 100644 index 00000000..b1767887 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.properties.model.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.properties.room2d.doctree b/docs/.doctrees/dragonfly_energy.properties.room2d.doctree new file mode 100644 index 00000000..9fbd7dec Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.properties.room2d.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.properties.story.doctree b/docs/.doctrees/dragonfly_energy.properties.story.doctree new file mode 100644 index 00000000..ee7bfb94 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.properties.story.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.reopt.doctree b/docs/.doctrees/dragonfly_energy.reopt.doctree new file mode 100644 index 00000000..3a61f465 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.reopt.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.run.doctree b/docs/.doctrees/dragonfly_energy.run.doctree new file mode 100644 index 00000000..489d7ad2 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.run.doctree differ diff --git a/docs/.doctrees/dragonfly_energy.writer.doctree b/docs/.doctrees/dragonfly_energy.writer.doctree new file mode 100644 index 00000000..e39f9a92 Binary files /dev/null and b/docs/.doctrees/dragonfly_energy.writer.doctree differ diff --git a/docs/.doctrees/environment.pickle b/docs/.doctrees/environment.pickle new file mode 100644 index 00000000..6580192b Binary files /dev/null and b/docs/.doctrees/environment.pickle differ diff --git a/docs/.doctrees/index.doctree b/docs/.doctrees/index.doctree new file mode 100644 index 00000000..35bd7a29 Binary files /dev/null and b/docs/.doctrees/index.doctree differ diff --git a/docs/.doctrees/modules.doctree b/docs/.doctrees/modules.doctree new file mode 100644 index 00000000..582d8f13 Binary files /dev/null and b/docs/.doctrees/modules.doctree differ diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..ebfb3665 --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +# documentation diff --git a/docs/_modules/dragonfly_energy/config.html b/docs/_modules/dragonfly_energy/config.html new file mode 100644 index 00000000..0bd00a68 --- /dev/null +++ b/docs/_modules/dragonfly_energy/config.html @@ -0,0 +1,1379 @@ + + + + + + + dragonfly_energy.config — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.config

+"""dragonfly_energy configurations.
+
+Import this into every module where access configurations are needed.
+
+Usage:
+
+.. code-block:: python
+
+    from dragonfly_energy.config import folders
+    print(folders.mapper_path)
+    folders.mapper_path = "C:/Urbanopt_test/Honeybee.rb"
+"""
+from ladybug.futil import write_to_file
+import honeybee_energy.config as hb_energy_config
+
+import os
+import platform
+import json
+import subprocess
+
+
+
+[docs] +class Folders(object): + """Dragonfly_energy folders. + + Args: + config_file: The path to the config.json file from which folders are loaded. + If None, the config.json module included in this package will be used. + Default: None. + mute: If False, the paths to the various folders will be printed as they + are found. If True, no printing will occur upon initialization of this + class. Default: True. + + Properties: + * mapper_path + * urbanopt_gemfile_path + * urbanopt_cli_path + * urbanopt_env_path + * urbanopt_version + * urbanopt_version_str + * reopt_assumptions_path + * docker_version + * docker_version_str + * config_file + * mute + """ + URBANOPT_VERSION = (0, 12, 0) + COMPATIBILITY_URL = 'https://github.com/ladybug-tools/lbt-grasshopper/wiki/' \ + '1.4-Compatibility-Matrix' + + def __init__(self, config_file=None, mute=True): + self.mute = bool(mute) # set the mute value + self.config_file = config_file # load paths from the config JSON file + + @property + def mapper_path(self): + """Get or set the path to the Ruby mapper used in URBANopt workflows. + + This is the Ruby file that is used to map URBANopt geoJSON features to + honeybee model JSONs. + """ + return self._mapper_path + + @mapper_path.setter + def mapper_path(self, path): + if not path: # check the default installation location + path = self._find_mapper_path() + if path: # check that the mapper file exists in the path + assert os.path.isfile(path) and path.endswith('.rb'), \ + '{} is not a valid path to a Ruby mapper file.'.format(path) + self._mapper_path = path # set the mapper_path + if path and not self.mute: + print("Path to Mapper is set to: %s" % path) + + @property + def urbanopt_gemfile_path(self): + """Get or set the path to the Gemfile used in URBANopt workflows. + + Setting this can be used to test newer versions of URBANopt with upgraded + dependencies in the Gemfile. + """ + return self._urbanopt_gemfile_path + + @urbanopt_gemfile_path.setter + def urbanopt_gemfile_path(self, path): + if not path: # check the default installation location + path = self._find_urbanopt_gemfile_path() + if path: # check that the Gemfile exists at the path + assert os.path.isfile(path), \ + '{} is not a valid path to an URBANopt Gemfile.'.format(path) + self._urbanopt_gemfile_path = path # set the urbanopt_gemfile_path + if path and not self.mute: + print("Path to URBANopt Gemfile is set to: %s" % path) + + @property + def urbanopt_cli_path(self): + """Get or set the path to the path where URBANopt is installed. + + Setting this can be used to test newer versions of URBANopt. + """ + return self._urbanopt_cli_path + + @urbanopt_cli_path.setter + def urbanopt_cli_path(self, path): + if not path: # check the default installation location + path = self._find_urbanopt_cli_path() + if path: # check that the installation exists at the path + assert os.path.isdir(path), \ + '{} is not a valid path to an URBANopt installation.'.format(path) + self._urbanopt_cli_path = path # set the urbanopt_cli_path + self._urbanopt_env_path = None + self._urbanopt_version = None + self._urbanopt_version_str = None + self._docker_version = None + self._docker_version_str = None + if path and not self.mute: + print("Path to URBANopt CLI is set to: %s" % path) + + @property + def urbanopt_env_path(self): + """Get or set the path to the executable used to set the URBANopt environment. + """ + return self._urbanopt_env_path + + @urbanopt_env_path.setter + def urbanopt_env_path(self, path): + if path: # check that the file exists at the path + assert os.path.isfile(path), \ + '{} is not a valid path to an URBANopt env executable.'.format(path) + self._urbanopt_env_path = path # set the urbanopt_env_path + if path and not self.mute: + print("Path to URBANopt Environment executable is set to: %s" % path) + + @property + def urbanopt_version(self): + """Get a tuple for the version of URBANopt (eg. (0, 7, 1)). + + This will be None if the version could not be sensed or if no URBANopt + installation was found. + """ + if self._urbanopt_cli_path and self._urbanopt_version_str is None: + self._urbanopt_version_from_cli() + return self._urbanopt_version + + @property + def urbanopt_version_str(self): + """Get text for the full version of URBANopt (eg. "0.7.1"). + + This will be None if the version could not be sensed or if no URBANopt + installation was found. + """ + if self._urbanopt_cli_path and self._urbanopt_version_str is None: + self._urbanopt_version_from_cli() + return self._urbanopt_version_str + + @property + def reopt_assumptions_path(self): + """Get or set the path to the JSON file that contains base REopt assumptions. + """ + return self._reopt_assumptions_path + + @reopt_assumptions_path.setter + def reopt_assumptions_path(self, path): + if not path: # check the default installation location + path = self._find_reopt_assumptions_path() + if path: # check that the file exists at the path + assert os.path.isfile(path), \ + '{} is not a valid path to a REopt assumptions JSON.'.format(path) + self._reopt_assumptions_path = path # set the reopt_assumptions_path + if path and not self.mute: + print("Path to REopt assumptions is set to: %s" % path) + + @property + def docker_version(self): + """Get a tuple for the version of Docker installed (eg. (24, 0, 7)). + + This will be None if the version could not be sensed or if no Docker + installation was found. + """ + if self._docker_version_str is None: + self._docker_version_from_cli() + return self._docker_version + + @property + def docker_version_str(self): + """Get text for the full version of Docker (eg. "24.0.7"). + + This will be None if the version could not be sensed or if no Docker + installation was found. + """ + if self._docker_version_str is None: + self._docker_version_from_cli() + return self._docker_version_str + + @property + def config_file(self): + """Get or set the path to the config.json file from which folders are loaded. + + Setting this to None will result in using the config.json module included + in this package. + """ + return self._config_file + + @config_file.setter + def config_file(self, cfg): + if cfg is None: + cfg = os.path.join(os.path.dirname(__file__), 'config.json') + self._load_from_file(cfg) + self._config_file = cfg + +
+[docs] + def generate_urbanopt_env_path(self): + """Run the URBANopt setup-env file to set this object's urbanopt_env_path.""" + # search for the file in its default location + home_folder = os.getenv('HOME') or os.path.expanduser('~') + env_file = os.path.join(home_folder, '.env_uo.bat') if os.name == 'nt' else \ + os.path.join(home_folder, '.env_uo.sh') + + if self.urbanopt_cli_path: # try to generate the env file + env_setup = os.path.join(self.urbanopt_cli_path, 'setup-env.bat') \ + if os.name == 'nt' else \ + os.path.join(self.urbanopt_cli_path, 'setup-env.sh') + if os.path.isfile(env_setup): + if os.name == 'nt': # run the batch file on Windows + os.system(env_setup) + else: # run the sell file on Mac or Linux + subprocess.check_call(['chmod', 'u+x', env_setup]) + subprocess.call(env_setup) + if os.path.isfile(env_file): + self._urbanopt_env_path = env_file # the file was successfully generated
+ + +
+[docs] + def check_urbanopt_version(self): + """Check if the installed version of URBANopt is the acceptable one.""" + in_msg = 'Get a compatible version of URBANopt by downloading and installing\n' \ + 'the version of URBANopt listed in the Ladybug Tools compatibility ' \ + 'matrix\n{}.'.format(self.COMPATIBILITY_URL) + assert self.urbanopt_cli_path is not None, \ + 'No URBANopt installation was found on this machine.\n{}'.format(in_msg) + uo_version = self.urbanopt_version + if uo_version is None: + if self.urbanopt_env_path is not None: + ext = '.bat' if os.name == 'nt' else '.sh' + ver_file = os.path.join( + os.path.dirname(self.urbanopt_env_path), + '.check_uo_version{}'.format(ext)) + process = subprocess.Popen(ver_file, stderr=subprocess.PIPE, shell=True) + _, stderr = process.communicate() + else: + stderr = 'Unable to set up the URBANopt environment.' + msg = 'An URBANopt installation was found at {}\n' \ + 'but the URBANopt executable is not accessible.\n{}'.format( + self.urbanopt_cli_path, stderr) + raise ValueError(msg) + assert uo_version[0] == self.URBANOPT_VERSION[0] and \ + uo_version[1] == self.URBANOPT_VERSION[1], \ + 'The installed URBANopt is version {}.\nMust be version {} to work ' \ + 'with dragonfly.\n{}'.format( + '.'.join(str(v) for v in uo_version), + '.'.join(str(v) for v in self.URBANOPT_VERSION), in_msg)
+ + + def _load_from_file(self, file_path): + """Set all of the the properties of this object from a config JSON file. + + Args: + file_path: Path to a JSON file containing the file paths. A sample of this + JSON is the config.json file within this package. + """ + # check the default file path + assert os.path.isfile(str(file_path)), \ + ValueError('No file found at {}'.format(file_path)) + + # set the default paths to be all blank + default_path = { + "mapper_path": r'', + "urbanopt_gemfile_path": r'', + "urbanopt_cli_path": r'', + "urbanopt_env_path": r'', + "reopt_assumptions_path": r'' + } + + with open(file_path, 'r') as cfg: + try: + paths = json.load(cfg) + except Exception as e: + print('Failed to load paths from {}.\n{}'.format(file_path, e)) + else: + for key, p in paths.items(): + if isinstance(key, list) or not key.startswith('__'): + try: + default_path[key] = p.strip() + except AttributeError: + default_path[key] = p + + # set paths for the configuration + self.mapper_path = default_path["mapper_path"] + self.urbanopt_gemfile_path = default_path["urbanopt_gemfile_path"] + self.urbanopt_cli_path = default_path["urbanopt_cli_path"] + self.urbanopt_env_path = default_path["urbanopt_env_path"] + self.reopt_assumptions_path = default_path["reopt_assumptions_path"] + + def _urbanopt_version_from_cli(self): + """Set this object's URBANopt version by making a call to URBANopt CLI.""" + if not self.urbanopt_env_path: + self.generate_urbanopt_env_path() + assert self.urbanopt_env_path is not None, 'Unable to set up the URBANopt ' \ + 'environment. Make sure it is installed correctly.' + if os.name == 'nt': + working_drive = self.urbanopt_env_path[:2] + batch = '{}\ncd {}\ncall {}\nuo --version'.format( + working_drive, working_drive, self.urbanopt_env_path) + batch_file = os.path.join( + os.path.dirname(self.urbanopt_env_path), '.check_uo_version.bat') + write_to_file(batch_file, batch, True) + process = subprocess.Popen(batch_file, stdout=subprocess.PIPE, shell=True) + stdout = process.communicate() + else: + shell = '#!/usr/bin/env bash\nsource {}\nuo --version'.format( + self.urbanopt_env_path) + shell_file = os.path.join( + os.path.dirname(self.urbanopt_env_path), '.check_uo_version.sh') + write_to_file(shell_file, shell, True) + # make the shell script executable using subprocess.check_call + subprocess.check_call(['chmod', 'u+x', shell_file]) + # run the shell script + process = subprocess.Popen(shell_file, stdout=subprocess.PIPE, shell=True) + stdout = process.communicate() + base_str = str(stdout[0]).split('--version')[-1] + base_str = base_str.replace(r"\r", '').replace(r"\n", '').replace(r"'", '') + base_str = base_str.strip() + try: + ver_nums = base_str.split('.') + self._urbanopt_version = tuple(int(i) for i in ver_nums) + self._urbanopt_version_str = base_str + except Exception: + pass # failed to parse the version into integers + + def _docker_version_from_cli(self): + """Set this object's Docker version by making a call to Docker CLI.""" + cmds = ['docker', '--version'] + use_shell = True if os.name == 'nt' else False + process = subprocess.Popen(cmds, stdout=subprocess.PIPE, shell=use_shell) + stdout = process.communicate() + base_str = str(stdout[0]).replace("b'", '').replace(r"\n'", '') + try: + self._docker_version_str = base_str.split(',')[0].split(' ')[-1] + ver_nums = self._docker_version_str.split('.') + self._docker_version = tuple(int(i) for i in ver_nums) + except Exception: + pass # failed to parse the version into integers + + @staticmethod + def _find_mapper_path(): + """Find the mapper that is distributed with the honeybee-openstudio-gem.""" + measure_install = hb_energy_config.folders.honeybee_openstudio_gem_path + if measure_install: + mapper_file = os.path.join(measure_install, 'files', 'Honeybee.rb') + if os.path.isfile(mapper_file): + return mapper_file + return None + + @staticmethod + def _find_urbanopt_gemfile_path(): + """Find the URBANopt Gemfile that's distributed with honeybee-openstudio-gem.""" + measure_install = hb_energy_config.folders.honeybee_openstudio_gem_path + if measure_install: + gem_file = os.path.join(measure_install, 'files', 'urbanopt_Gemfile') + if os.path.isfile(gem_file): + return gem_file + return None + + @staticmethod + def _find_reopt_assumptions_path(): + """Find the REopt assumptions that's distributed with honeybee-openstudio-gem.""" + measure_install = hb_energy_config.folders.honeybee_openstudio_gem_path + if measure_install: + reopt_file = os.path.join(measure_install, 'files', 'reopt_assumptions.json') + if os.path.isfile(reopt_file): + return reopt_file + return None + + @staticmethod + def _find_urbanopt_cli_path(): + """Find the most recent URBANopt CLI in its default location.""" + def getversion(urbanopt_path): + """Get digits for the version of OpenStudio.""" + try: + ver = ''.join(s for s in urbanopt_path if (s.isdigit() or s == '.')) + return sum(int(d) * (10 ** i) + for i, d in enumerate(reversed(ver.split('.')))) + except ValueError: # folder starting with 'openstudio' and no version + return 0 + + if os.name == 'nt': # search the C:/ drive on Windows + uo_folders = ['C:\\{}'.format(f) for f in os.listdir('C:\\') + if (f.lower().startswith('urbanopt') and + os.path.isdir('C:\\{}'.format(f)))] + elif platform.system() == 'Darwin': # search the Applications folder on Mac + uo_folders = \ + ['/Applications/{}'.format(f) for f in os.listdir('/Applications/') + if (f.lower().startswith('urbanopt') and + os.path.isdir('/Applications/{}'.format(f)))] + elif platform.system() == 'Linux': # search the usr/local folder + uo_folders = ['/usr/local/{}'.format(f) for f in os.listdir('/usr/local/') + if (f.lower().startswith('urbanopt') and + os.path.isdir('/usr/local/{}'.format(f)))] + else: # unknown operating system + uo_folders = None + + if not uo_folders: # No Openstudio installations were found + return None + + # get the most recent version of OpenStudio that was found + uo_path = sorted(uo_folders, key=getversion, reverse=True)[0] + return uo_path
+ + + +"""Object possesing all key folders within the configuration.""" +folders = Folders(mute=True) +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/des/connector.html b/docs/_modules/dragonfly_energy/des/connector.html new file mode 100644 index 00000000..5796bd56 --- /dev/null +++ b/docs/_modules/dragonfly_energy/des/connector.html @@ -0,0 +1,1138 @@ + + + + + + + dragonfly_energy.des.connector — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.des.connector

+# coding=utf-8
+"""Thermal connector in a District Energy System."""
+from __future__ import division
+
+from .._base import _GeometryBase
+
+from ladybug_geometry.geometry2d.line import LineSegment2D
+from ladybug_geometry.geometry2d.polyline import Polyline2D
+from dragonfly.projection import polygon_to_lon_lat
+
+
+
+[docs] +class ThermalConnector(_GeometryBase): + """Represents a thermal connector carrying hot, chilled or ambient water in a DES. + + Args: + identifier: Text string for a unique thermal connector ID. Must contain only + characters that are acceptable in a DES. This will be used to + identify the object across the exported geoJSON and DES files. + geometry: A LineSegment2D or Polyline2D representing the geometry of the + thermal connector. + + Properties: + * identifier + * display_name + * geometry + """ + __slots__ = () + + def __init__(self, identifier, geometry): + """Initialize ThermalConnector.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, (LineSegment2D, Polyline2D)), 'Expected ' \ + 'ladybug_geometry LineSegment2D or Polyline2D. Got {}'.format(type(geometry)) + self._geometry = geometry + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an ThermalConnector from a dictionary. + + Args: + data: A dictionary representation of an ThermalConnector object. + """ + # check the type of dictionary + assert data['type'] == 'ThermalConnector', 'Expected ThermalConnector ' \ + 'dictionary. Got {}.'.format(data['type']) + geo = LineSegment2D.from_dict(data['geometry']) \ + if data['geometry']['type'] == 'LineSegment2D' \ + else Polyline2D.from_dict(data['geometry']) + con = cls(data['identifier'], geo) + if 'display_name' in data and data['display_name'] is not None: + con.display_name = data['display_name'] + return con
+ + +
+[docs] + @classmethod + def from_dict_abridged(cls, data): + """Initialize an ThermalConnector from an abridged dictionary. + + Args: + data: A ThermalConnector dictionary. + """ + return cls.from_dict(data)
+ + +
+[docs] + @classmethod + def from_geojson_dict( + cls, data, origin_lon_lat, conversion_factors): + """Get a ThermalConnector from a dictionary as it appears in a GeoJSON. + + Args: + data: A GeoJSON dictionary representation of an ThermalConnector feature. + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + geo = cls._geojson_coordinates_to_line2d( + data['geometry']['coordinates'], origin_lon_lat, conversion_factors) + return cls(data['properties']['id'], geo)
+ + + @property + def geometry(self): + """Get a LineSegment2D or Polyline2D representing the thermal connector.""" + return self._geometry + +
+[docs] + def reverse(self): + """Reverse the direction of this object's geometry. + + This is useful when trying to orient the connector to the direction + of flow within a larger loop. + """ + self._geometry = self._geometry.flip() \ + if isinstance(self._geometry, LineSegment2D) else self._geometry.reverse()
+ + +
+[docs] + def to_dict(self): + """ThermalConnector dictionary representation.""" + base = {'type': 'ThermalConnector'} + base['identifier'] = self.identifier + base['geometry'] = self.geometry.to_dict() + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, start_id, end_id, origin_lon_lat, conversion_factors, + start_feature_id=None, end_feature_id=None): + """Get ThermalConnector dictionary as it appears in an URBANopt geoJSON. + + Args: + start_id: Identifier of the junction at the start of the pipe. + end_id: Identifier of the junction at the end of the pipe. + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + start_feature_id: Optional identifier for a feature (Building or GHE field) + at the start of the pipe. + end_feature_id: Optional identifier for a feature (Building or GHE field) + at the end of the pipe. + """ + # translate the geometry coordinates to latitude and longitude + if isinstance(self.geometry, LineSegment2D): + pts = [(pt.x, pt.y) for pt in (self.geometry.p1, self.geometry.p2)] + else: # it's a polyline + pts = [(pt.x, pt.y) for pt in self.geometry.vertices] + coords = polygon_to_lon_lat(pts, origin_lon_lat, conversion_factors) + # assign all of the properties to the connector + conn_props = { + 'id': self.identifier, + 'type': 'ThermalConnector', + 'name': self.display_name, + 'startJunctionId': start_id, + 'endJunctionId': end_id, + 'total_length': round(self.geometry.length, 2), + 'connector_type': 'OnePipe', + 'fluid_temperature_type': 'Ambient', + 'flow_direction': 'Unspecified' + + } + if start_feature_id is not None: + conn_props['startFeatureId'] = start_feature_id + if end_feature_id is not None: + conn_props['endFeatureId'] = end_feature_id + # return the full dictionary + return { + 'type': 'Feature', + 'properties': conn_props, + 'geometry': { + 'type': 'LineString', + 'coordinates': coords + } + }
+ + + def __copy__(self): + new_con = ThermalConnector(self.identifier, self.geometry) + new_con._display_name = self._display_name + return new_con + + def __repr__(self): + return 'ThermalConnector: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/des/ghe.html b/docs/_modules/dragonfly_energy/des/ghe.html new file mode 100644 index 00000000..f53946c3 --- /dev/null +++ b/docs/_modules/dragonfly_energy/des/ghe.html @@ -0,0 +1,1972 @@ + + + + + + + dragonfly_energy.des.ghe — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.des.ghe

+# coding=utf-8
+"""Ground Heat Exchanger (GHE) in a district thermal system."""
+from .._base import _GeometryBase
+
+from ladybug_geometry.geometry2d.polygon import Polygon2D
+from honeybee.typing import valid_string, float_positive, float_in_range, int_in_range
+from honeybee.altnumber import autocalculate
+from dragonfly.projection import polygon_to_lon_lat
+
+
+
+[docs] +class GroundHeatExchanger(_GeometryBase): + """Represents a Ground Heat Exchanger in a district thermal system. + + Args: + identifier: Text string for a unique heat exchanger ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + geometry: A Polygon2D representing the geometry of the heat exchanger. + + Properties: + * identifier + * display_name + * geometry + """ + __slots__ = () + + def __init__(self, identifier, geometry): + """Initialize GroundHeatExchanger.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, Polygon2D), 'Expected ladybug_geometry ' \ + 'Polygon2D for GroundHeatExchanger geometry. Got {}'.format(type(geometry)) + self._geometry = geometry + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize a GroundHeatExchanger from a dictionary. + + Args: + data: A dictionary representation of an GroundHeatExchanger object. + """ + # check the type of dictionary + assert data['type'] == 'GroundHeatExchanger', 'Expected GroundHeatExchanger ' \ + 'dictionary. Got {}.'.format(data['type']) + geo = Polygon2D.from_dict(data['geometry']) + trans = cls(data['identifier'], geo) + if 'display_name' in data and data['display_name'] is not None: + trans.display_name = data['display_name'] + return trans
+ + +
+[docs] + @classmethod + def from_geojson_dict( + cls, data, origin_lon_lat, conversion_factors): + """Get a GroundHeatExchanger from a dictionary as it appears in a GeoJSON. + + Args: + data: A GeoJSON dictionary representation of an GroundHeatExchanger feature. + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + geo = cls._geojson_coordinates_to_polygon2d( + data['geometry']['coordinates'], origin_lon_lat, conversion_factors) + return cls(data['properties']['id'], geo)
+ + + @property + def geometry(self): + """Get a Polygon2D representing the ground heat exchanger.""" + return self._geometry + +
+[docs] + def to_dict(self): + """GroundHeatExchanger dictionary representation.""" + base = {'type': 'GroundHeatExchanger'} + base['identifier'] = self.identifier + base['geometry'] = self.geometry.to_dict() + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, origin_lon_lat, conversion_factors): + """Get GroundHeatExchanger dictionary as it appears in an URBANopt geoJSON. + + Args: + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + pts = [(pt.x, pt.y) for pt in self.geometry.vertices] + coords = [polygon_to_lon_lat(pts, origin_lon_lat, conversion_factors)] + coords[0].append(coords[0][0]) + return { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'geometryType': 'Rectangle', + 'name': self.display_name, + 'type': 'District System', + 'footprint_area': round(self.geometry.area, 1), + 'footprint_perimeter': round(self.geometry.perimeter, 1), + 'floor_area': round(self.geometry.area, 1), + 'district_system_type': 'Ground Heat Exchanger', + }, + 'geometry': { + 'type': 'Polygon', + 'coordinates': coords + } + }
+ + + def __copy__(self): + new_ghe = GroundHeatExchanger(self.identifier, self.geometry) + new_ghe._display_name = self._display_name + return new_ghe + + def __repr__(self): + return 'GroundHeatExchanger: {}'.format(self.display_name)
+ + + +
+[docs] +class SoilParameter(object): + """Represents the soil properties within a ground heat exchanger field. + + Args: + conductivity: A number for the soil conductivity in W/m-K. (Default: 2.3). + heat_capacity: A number for the volumetric heat capacity of the soil + in J/m3-K. (Default: 2,343,500). + undisturbed_temperature: A number for the undisturbed annual average soil + temperature in degrees Celsius. If autocalculate, this value will + automatically be replaced with the average EPW temperature before + simulation. (Default: Autocalculate). + grout_conductivity: A number for the grout conductivity in W/m-K. (Default: 1.0). + grout_heat_capacity: A number for the volumetric heat capacity of the grout + in J/m3-K. (Default: 3,901,000). + + Properties: + * conductivity + * heat_capacity + * undisturbed_temperature + * grout_conductivity + * grout_heat_capacity + """ + + __slots__ = ('_conductivity', '_heat_capacity', '_undisturbed_temperature', + '_grout_conductivity', '_grout_heat_capacity') + + def __init__(self, conductivity=2.3, heat_capacity=2343500, + undisturbed_temperature=autocalculate, + grout_conductivity=1.0, grout_heat_capacity=3901000): + """Initialize SoilParameter.""" + self.conductivity = conductivity + self.heat_capacity = heat_capacity + self.undisturbed_temperature = undisturbed_temperature + self.grout_conductivity = grout_conductivity + self.grout_heat_capacity = grout_heat_capacity + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a SoilParameter object from a dictionary + + Args: + data: A dictionary representation of an SoilParameter object + in the format below. + + .. code-block:: python + + { + 'type': 'SoilParameter', + 'conductivity': 1.8, # float in W/m2-K + 'heat_capacity': 2100000, # float in J/m3-K + 'undisturbed_temperature': 18, # float in C or autocalculate + 'grout_conductivity': 1.0, # float in W/m2-K + 'grout_heat_capacity': 3901000 + } + """ + cond = data['conductivity'] if 'conductivity' in data else 2.3 + cap = data['heat_capacity'] if 'heat_capacity' in data else 2343500 + u_temp = autocalculate if 'undisturbed_temperature' not in data or \ + data['undisturbed_temperature'] == autocalculate.to_dict() \ + else data['undisturbed_temperature'] + g_cond = data['grout_conductivity'] if 'grout_conductivity' in data else 1.0 + g_cap = data['grout_heat_capacity'] if 'grout_heat_capacity' in data else 3901000 + return cls(cond, cap, u_temp, g_cond, g_cap)
+ + + @property + def conductivity(self): + """Get or set a number for the soil conductivity in W/m-K.""" + return self._conductivity + + @conductivity.setter + def conductivity(self, value): + self._conductivity = float_positive(value, 'soil conductivity') + + @property + def heat_capacity(self): + """Get or set a number for the volumetric heat capacity of the soil in J/m3-K.""" + return self._heat_capacity + + @heat_capacity.setter + def heat_capacity(self, value): + self._heat_capacity = float_positive(value, 'soil heat_capacity') + + @property + def undisturbed_temperature(self): + """Get or set an integer (or Autocalculate) for the vegetation end month.""" + return self._undisturbed_temperature if self._undisturbed_temperature \ + is not None else autocalculate + + @undisturbed_temperature.setter + def undisturbed_temperature(self, value): + if value == autocalculate: + self._undisturbed_temperature = None + else: + self._undisturbed_temperature = \ + float_in_range(value, -273, 200, 'undisturbed_temperature') + + @property + def grout_conductivity(self): + """Get or set a number for the grout conductivity in W/m-K.""" + return self._grout_conductivity + + @grout_conductivity.setter + def grout_conductivity(self, value): + self._grout_conductivity = float_positive(value, 'grout conductivity') + + @property + def grout_heat_capacity(self): + """Get or set a number for the volumetric heat capacity of the grout in J/m3-K. + """ + return self._grout_heat_capacity + + @grout_heat_capacity.setter + def grout_heat_capacity(self, value): + self._grout_heat_capacity = float_positive(value, 'grout heat_capacity') + +
+[docs] + def to_dict(self): + """Get SoilParameter dictionary.""" + base = {'type': 'SoilParameter'} + base['conductivity'] = self.conductivity + base['heat_capacity'] = self.heat_capacity + if self._undisturbed_temperature is not None: + base['undisturbed_temperature'] = self._undisturbed_temperature + base['grout_conductivity'] = self.grout_conductivity + base['grout_heat_capacity'] = self.grout_heat_capacity + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_obj = SoilParameter(self._conductivity, self._heat_capacity) + new_obj._undisturbed_temperature = self._undisturbed_temperature + new_obj._grout_conductivity = self._grout_conductivity + new_obj._grout_heat_capacity = self._grout_heat_capacity + return new_obj + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent SoilParameter.""" + return 'SoilParameter: [conductivity: {} W/m2-K] ' \ + '[heat capacity: {} J/m3-K]'.format(self._conductivity, self._heat_capacity)
+ + + +
+[docs] +class FluidParameter(object): + """Represents the fluid properties within a ground heat exchanger field. + + Args: + fluid_type: Text to indicate the type of fluid circulating through the + ground heat exchanger loop. Choose from the options + below. (Default: Water). + + * Water + * EthylAlcohol + * EthyleneGlycol + * MethylAlcohol + * PropyleneGlycol + + concentration: A number between 0 and 60 for the concentration of the + fluid_type in water in percent. Note that this variable has no effect + when the fluid_type is Water. (Default: 35). + temperature: A number for the average design fluid temperature at peak + conditions in Celsius. (Default: 20). + + Properties: + * fluid_type + * concentration + * temperature + """ + + __slots__ = ('_fluid_type', '_concentration', '_temperature') + FLUID_TYPES = ( + 'Water', 'EthylAlcohol', 'EthyleneGlycol', 'MethylAlcohol', 'PropyleneGlycol') + + def __init__(self, fluid_type='Water', concentration=35, temperature=20): + """Initialize FluidParameter.""" + self.fluid_type = fluid_type + self.concentration = concentration + self.temperature = temperature + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a FluidParameter object from a dictionary + + Args: + data: A dictionary representation of an FluidParameter object + in the format below. + + .. code-block:: python + + { + 'type': 'FluidParameter', + 'fluid_type': 'PropyleneGlycol', # text for fluid_type + 'concentration': 33, # float for percentage concentration + 'temperature': 22 # float in C + } + """ + ft = data['fluid_type'] if 'fluid_type' in data else 'Water' + con = data['concentration'] if 'concentration' in data else 35 + temp = 20 if 'temperature' not in data else data['temperature'] + return cls(ft, con, temp)
+ + + @property + def fluid_type(self): + """Get or set text to indicate the type of fluid.""" + return self._fluid_type + + @fluid_type.setter + def fluid_type(self, value): + clean_input = valid_string(value).lower() + for key in self.FLUID_TYPES: + if key.lower() == clean_input: + value = key + break + else: + raise ValueError( + 'fluid_type {} is not recognized.\nChoose from the ' + 'following:\n{}'.format(value, self.FLUID_TYPES)) + self._fluid_type = value + + @property + def concentration(self): + """Get or set a number for the concentration of the fluid_type in water [%].""" + return self._concentration + + @concentration.setter + def concentration(self, value): + self._concentration = float_in_range(value, 0, 60, 'fluid concentration') + + @property + def temperature(self): + """Get or set a number for the average design fluid temperature in Celsius.""" + return self._temperature + + @temperature.setter + def temperature(self, value): + self._temperature = float_positive(value, 'fluid temperature') + +
+[docs] + def to_dict(self): + """Get FluidParameter dictionary.""" + base = {'type': 'FluidParameter'} + base['fluid_type'] = self.fluid_type + base['concentration'] = self.concentration + base['temperature'] = self.temperature + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + return FluidParameter(self._fluid_type, self._concentration, self._temperature) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent SoilParameter.""" + return 'FluidParameter: [fluid_type: {}] ' \ + '[concentration: {}%]'.format(self._fluid_type, self._concentration)
+ + + +
+[docs] +class PipeParameter(object): + """Represents the pipe properties within a ground heat exchanger field. + + Args: + inner_diameter: A number for the diameter of the inner pipe surface in + meters. (Default: 0.0216). + outer_diameter: A number for the diameter of the outer pipe surface in + meters. (Default: 0.0266). + shank_spacing: A number for the spacing between the U-tube legs, as + referenced from outer surface of the pipes in meters. (NOT referenced + from each pipe's respective centerline). (Default: 0.0323). + roughness: A number for the linear dimension of bumps on the pipe surface + in meters. (Default: 1e-06) + conductivity: A number for the conductivity of the pipe material in + W/m-K. (Default: 0.4). + heat_capacity: A number for the volumetric heat capacity of the pipe + material in J/m3-K. (Default: 1,542,000). + arrangement: Text for the specified pipe arrangement. Choose from the + following options. (Default: SingleUTube). + + * SingleUTube + * DoubleUTubeSeries + * DoubleUTubeParallel + + Properties: + * inner_diameter + * outer_diameter + * shank_spacing + * roughness + * conductivity + * heat_capacity + * arrangement + """ + __slots__ = ('_inner_diameter', '_outer_diameter', '_shank_spacing', + '_roughness', '_conductivity', '_heat_capacity', '_arrangement') + ARRANGEMENT_TYPES = ('SingleUTube', 'DoubleUTubeSeries', 'DoubleUTubeParallel') + + def __init__( + self, inner_diameter=0.0216, outer_diameter=0.0266, shank_spacing=0.0323, + roughness=1e-06, conductivity=0.4, heat_capacity=1542000, + arrangement='SingleUTube'): + """Initialize PipeParameter.""" + self._inner_diameter = float_positive(inner_diameter, 'pipe inner_diameter') + self.outer_diameter = outer_diameter + self.shank_spacing = shank_spacing + self.roughness = roughness + self.conductivity = conductivity + self.heat_capacity = heat_capacity + self.arrangement = arrangement + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a PipeParameter object from a dictionary + + Args: + data: A dictionary representation of an PipeParameter object + in the format below. + + .. code-block:: python + + { + 'type': 'PipeParameter', + 'inner_diameter': 0.0216, # float for inner diameter in meters + 'outer_diameter': 0.0266 # float for outer diameter in meters + 'shank_spacing': 0.0323, # float for spacing between outer pipes in meters + 'roughness': 1e-06, # float for the dimension of the surface bumps + 'conductivity': 0.6, # float in W/m2-K + 'heat_capacity': 1542000, # float in J/m3-K + 'arrangement': 'SingleUTube' # text for arrangement type + } + """ + in_d = data['inner_diameter'] if 'inner_diameter' in data else 0.0216 + out_d = data['outer_diameter'] if 'outer_diameter' in data else 0.0266 + s_spc = data['shank_spacing'] if 'shank_spacing' in data else 0.0323 + rough = data['roughness'] if 'roughness' in data else 1e-06 + cond = data['conductivity'] if 'conductivity' in data else 2.3 + cap = data['heat_capacity'] if 'heat_capacity' in data else 2343500 + arr = data['arrangement'] if 'arrangement' in data else 'SingleUTube' + return cls(in_d, out_d, s_spc, rough, cond, cap, arr)
+ + + @property + def inner_diameter(self): + """Get or set a number for the inner diameter of the pipe in meters.""" + return self._inner_diameter + + @inner_diameter.setter + def inner_diameter(self, value): + self._inner_diameter = float_positive(value, 'pipe inner diameter') + self._diameter_check() + + @property + def outer_diameter(self): + """Get or set a number for the outer diameter of the pipe in meters.""" + return self._outer_diameter + + @outer_diameter.setter + def outer_diameter(self, value): + self._outer_diameter = float_positive(value, 'pipe outer diameter') + self._diameter_check() + + @property + def shank_spacing(self): + """Get or set a number for the shank spacing between the pipes in meters.""" + return self._shank_spacing + + @shank_spacing.setter + def shank_spacing(self, value): + self._shank_spacing = float_positive(value, 'pipe shank spacing') + + @property + def roughness(self): + """Get or set a number for the dimension of the pipe surface bumps in meters.""" + return self._roughness + + @roughness.setter + def roughness(self, value): + self._roughness = float_positive(value, 'pipe roughness') + + @property + def conductivity(self): + """Get or set a number for the conductivity of the pipe material in W/m-K.""" + return self._conductivity + + @conductivity.setter + def conductivity(self, value): + self._conductivity = float_positive(value, 'pipe conductivity') + + @property + def heat_capacity(self): + """Get or set a number for the volumetric heat capacity of the pipe in J/m3-K.""" + return self._heat_capacity + + @heat_capacity.setter + def heat_capacity(self, value): + self._heat_capacity = float_positive(value, 'pipe heat_capacity') + + @property + def arrangement(self): + """Get or set text for the pipe arrangement. + + Choose from the following options: + + * SingleUTube + * DoubleUTubeSeries + * DoubleUTubeParallel + """ + return self._arrangement + + @arrangement.setter + def arrangement(self, value): + clean_input = valid_string(value).lower() + for key in self.ARRANGEMENT_TYPES: + if key.lower() == clean_input: + value = key + break + else: + raise ValueError( + 'arrangement {} is not recognized.\nChoose from the ' + 'following:\n{}'.format(value, self.ARRANGEMENT_TYPES)) + self._arrangement = value + +
+[docs] + def to_dict(self): + """Get PipeParameter dictionary.""" + base = {'type': 'PipeParameter'} + base['inner_diameter'] = self.inner_diameter + base['outer_diameter'] = self.outer_diameter + base['shank_spacing'] = self.shank_spacing + base['roughness'] = self.roughness + base['conductivity'] = self.conductivity + base['heat_capacity'] = self.heat_capacity + base['arrangement'] = self.arrangement + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + return PipeParameter( + self.inner_diameter, self.outer_diameter, self.shank_spacing, + self.roughness, self.conductivity, self.heat_capacity, self.arrangement) + + def _diameter_check(self): + """Check that outer_diameter is greater than or equal to the inner_diameter.""" + assert self._outer_diameter > self._inner_diameter, \ + 'Pipe outer_diameter must be greater than or equal to inner_diameter.' + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent PipeParameter.""" + return 'PipeParameter: [diameter: {}m - {}m]'.format( + self.inner_diameter, self.outer_diameter)
+ + + +
+[docs] +class BoreholeParameter(object): + """Represents the borehole properties within a ground heat exchanger field. + + Args: + min_depth: A number for the minimum depth of the heat-exchanging part + of the boreholes in meters. All boreholes will have a depth of + at least this value. So this typically represents the depth at which + borehole-drilling is most economical or the point at which it becomes + more cost effective to start a new borehole instead of making a given + borehole deeper. (Default: 60). + max_depth: A number for the maximum depth of the heat-exchanging part + of the boreholes in meters. When the system demand cannot be met + using boreholes with the min_depth, the boreholes will be extended + until either the loads or met or they reach this depth. So this + typically represents the depth of bedrock or the point at which + drilling deeper ceases to be practical. (Default: 135). + min_spacing: A number for the minimum spacing between boreholes in meters. + When the system demand cannot be met using boreholes with the max_spacing, + the borehole spacing will be reduced until either the loads or met + or they reach this spacing. So this typically represents the spacing + at which each borehole will interfere with neighboring ones so much + that it is not worthwhile to decrease the spacing further. (Default: 3). + max_spacing: A number for the maximum spacing between boreholes in meters. + All boreholes will have a spacing of at most this value. So this + typically represents the spacing at which the performance effects of + one borehole on a neighboring one are negligible. (Default: 25). + buried_depth: A number for the depth below the ground surface at which + the top of the heat exchanging part of the borehole sits in + meters. (Default: 2). + diameter: A number for the diameter of the borehole in meters. (Default: 0.15). + + Properties: + * min_depth + * max_depth + * min_spacing + * max_spacing + * buried_depth + * diameter + """ + __slots__ = ('_min_depth', '_max_depth', '_min_spacing', '_max_spacing', + '_buried_depth', '_diameter') + + def __init__(self, min_depth=60, max_depth=135, min_spacing=3, max_spacing=25, + buried_depth=2, diameter=0.15): + """Initialize BoreholeParameter.""" + self._min_depth = float_positive(min_depth, 'borehole min_depth') + self.max_depth = max_depth + self._min_spacing = float_positive(min_spacing, 'borehole min_spacing') + self.max_spacing = max_spacing + self.buried_depth = buried_depth + self.diameter = diameter + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a BoreholeParameter object from a dictionary + + Args: + data: A dictionary representation of an BoreholeParameter object + in the format below. + + .. code-block:: python + + { + 'type': 'BoreholeParameter', + 'min_depth': 30, # float in meters + 'max_depth': 90, # float in meters + 'min_spacing': 2.5, # float in meters + 'max_spacing': 8, # float in meters + 'buried_depth': 4, # float in meters + 'diameter': 0.2 # float in meters + } + """ + min_depth = data['min_depth'] if 'min_depth' in data else 60 + max_depth = data['max_depth'] if 'max_depth' in data else 135 + min_spacing = data['min_spacing'] if 'min_spacing' in data else 3 + max_spacing = data['max_spacing'] if 'max_spacing' in data else 10 + dth = data['buried_depth'] if 'buried_depth' in data else 2 + dia = data['diameter'] if 'diameter' in data else 0.15 + return cls(min_depth, max_depth, min_spacing, max_spacing, dth, dia)
+ + + @property + def min_depth(self): + """Get or set a number for the minimum depth of the borehole in meters.""" + return self._min_depth + + @min_depth.setter + def min_depth(self, value): + self._min_depth = float_positive(value, 'borehole min_depth') + self._depth_check() + + @property + def max_depth(self): + """Get or set a number for the maximum depth of the borehole in meters.""" + return self._max_depth + + @max_depth.setter + def max_depth(self, value): + self._max_depth = float_positive(value, 'borehole max_depth') + self._depth_check() + + @property + def min_spacing(self): + """Get or set a number for the minimum spacing between boreholes in m. + """ + return self._min_spacing + + @min_spacing.setter + def min_spacing(self, value): + self._min_spacing = float_positive(value, 'borehole min_spacing') + self._spacing_check() + + @property + def max_spacing(self): + """Get or set a number for the maximum spacing between boreholes in m. + """ + return self._max_spacing + + @max_spacing.setter + def max_spacing(self, value): + self._max_spacing = float_positive(value, 'borehole max_spacing') + self._spacing_check() + + @property + def buried_depth(self): + """Get or set a number for the depth of the top of the borehole in meters.""" + return self._buried_depth + + @buried_depth.setter + def buried_depth(self, value): + self._buried_depth = float_positive(value, 'borehole buried_depth') + + @property + def diameter(self): + """Get or set a number for the diameter of the borehole in meters.""" + return self._diameter + + @diameter.setter + def diameter(self, value): + self._diameter = float_positive(value, 'borehole diameter') + +
+[docs] + def to_dict(self): + """Get BoreholeParameter dictionary.""" + base = {'type': 'BoreholeParameter'} + base['min_depth'] = self.min_depth + base['max_depth'] = self.max_depth + base['min_spacing'] = self.min_spacing + base['max_spacing'] = self.max_spacing + base['buried_depth'] = self.buried_depth + base['diameter'] = self.diameter + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + return BoreholeParameter( + self.min_depth, self.max_depth, self.min_spacing, self.max_spacing, + self.buried_depth, self.diameter) + + def _depth_check(self): + """Check that max_depth is greater than or equal to min_depth.""" + assert self._max_depth >= self._min_depth, \ + 'Borehole max_depth must be greater than or equal to min_depth.' + + def _spacing_check(self): + """Check that max_spacing is greater than or equal to min_spacing.""" + assert self._max_spacing >= self._min_spacing, \ + 'Borehole max_spacing must be greater than or equal to min_spacing.' + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent BoreholeParameter.""" + return 'BoreholeParameter: [depth: {}m - {}m] [spacing: {}m - {}m]'.format( + self.min_depth, self.max_depth, self.min_spacing, self.max_spacing)
+ + + +
+[docs] +class GHEDesignParameter(object): + """Represents criteria used to design a ground heat exchanger. + + Args: + flow_rate: A number for the volumetric design flow rate through the + ground heat exchanger in L/s. The value specified will be either for + the entire system system or per-borehole flow rate depending on + the flow_type set. (Default: 0.2 L/s). + flow_type: Text to indicate whether the design volumetric flow rate set on + a per-borehole or system basis. Choose from the following + options. (Default: Borehole). + + * Borehole + * System + + max_eft: A number for the maximum heat pump entering fluid temperature + in Celsius. (Default: 35C). + min_eft: A number for the minimum heat pump entering fluid temperature + in Celsius. (Default: 5C). + month_count: An integer for the number of months over which the simulation + will be run in order to ensure stable ground temperature + conditions. (Default: 240). + + Properties: + * flow_rate + * flow_type + * max_eft + * min_eft + * month_count + """ + __slots__ = ('_flow_rate', '_flow_type', '_max_eft', '_min_eft', '_month_count') + FLOW_TYPES = ('Borehole', 'System') + + def __init__(self, flow_rate=0.2, flow_type='Borehole', max_eft=35, min_eft=5, + month_count=240): + """Initialize BoreholeParameter.""" + self.flow_rate = flow_rate + self.flow_type = flow_type + self._min_eft = float_positive(min_eft, 'GHE min entering fluid temperature') + self.max_eft = max_eft + self.month_count = month_count + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a GHEDesignParameter object from a dictionary + + Args: + data: A dictionary representation of an GHEDesignParameter object + in the format below. + + .. code-block:: python + + { + 'type': 'GHEDesignParameter', + 'flow_rate': 30, # float in L/s + 'flow_type': 'Borehole', # text for the type of object flow_rate references + 'max_eft': 35, # float for max entering fluid temperature in C + 'min_eft': 5, # float for min entering fluid temperature in C + 'month_count': 240 # int for the number of months to run the simulation + } + """ + flow_rate = data['flow_rate'] if 'flow_rate' in data else 0.2 + flow_type = data['flow_type'] if 'flow_type' in data else 'Borehole' + max_eft = data['max_eft'] if 'max_eft' in data else 35 + min_eft = data['min_eft'] if 'min_eft' in data else 5 + month_count = data['month_count'] if 'month_count' in data else 240 + return cls(flow_rate, flow_type, max_eft, min_eft, month_count)
+ + + @property + def flow_rate(self): + """Get or set a number the volumetric design flow rate in L/s.""" + return self._flow_rate + + @flow_rate.setter + def flow_rate(self, value): + self._flow_rate = float_positive(value, 'ground heat exchanger flow_rate') + + @property + def flow_type(self): + """Get or set text for the type of object flow_rate references. + + Choose from the following options: + + * Borehole + * System + """ + return self._flow_type + + @flow_type.setter + def flow_type(self, value): + clean_input = valid_string(value).lower() + for key in self.FLOW_TYPES: + if key.lower() == clean_input: + value = key + break + else: + raise ValueError( + 'Flow type {} is not recognized.\nChoose from the ' + 'following:\n{}'.format(value, self.FLOW_TYPES)) + self._flow_type = value + + @property + def min_eft(self): + """Get or set a number for the minimum entering fluid temperature in Celsius.""" + return self._min_eft + + @min_eft.setter + def min_eft(self, value): + self._min_eft = float_positive(value, 'GHE min entering fluid temperature') + self._eft_check() + + @property + def max_eft(self): + """Get or set a number for the maximum entering fluid temperature in Celsius.""" + return self._max_eft + + @max_eft.setter + def max_eft(self, value): + self._max_eft = float_positive(value, 'GHE max entering fluid temperature') + self._eft_check() + + @property + def month_count(self): + """Get or set a number for the maximum entering fluid temperature in Celsius.""" + return self._month_count + + @month_count.setter + def month_count(self, value): + self._month_count = int_in_range(value, 12, input_name='GHE month count') + +
+[docs] + def to_dict(self): + """Get GHEDesignParameter dictionary.""" + base = {'type': 'GHEDesignParameter'} + base['flow_rate'] = self.flow_rate + base['flow_type'] = self.flow_type + base['min_eft'] = self.min_eft + base['max_eft'] = self.max_eft + base['month_count'] = self.month_count + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + return GHEDesignParameter( + self.flow_rate, self.flow_type, self.min_eft, self.max_eft, self.month_count) + + def _eft_check(self): + """Check that max_eft is greater than or equal to min_eft.""" + assert self._max_eft >= self._min_eft, \ + 'GHE max_eft must be greater than or equal to min_eft.' + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent GHEDesignParameter.""" + return 'GHEDesignParameter: [flow: {}L/s] [eft: {}C - {}C]'.format( + self.flow_rate, self.min_eft, self.max_eft)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/des/junction.html b/docs/_modules/dragonfly_energy/des/junction.html new file mode 100644 index 00000000..048a2b86 --- /dev/null +++ b/docs/_modules/dragonfly_energy/des/junction.html @@ -0,0 +1,1072 @@ + + + + + + + dragonfly_energy.des.junction — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.des.junction

+# coding=utf-8
+"""Thermal junction in a District Energy System (DES)."""
+from __future__ import division
+
+from .._base import _GeometryBase
+
+from ladybug_geometry.geometry2d.pointvector import Point2D
+from honeybee.typing import valid_ep_string
+from dragonfly.projection import polygon_to_lon_lat
+
+
+
+[docs] +class ThermalJunction(_GeometryBase): + """Represents an thermal junction connecting two objects in a DES. + + Args: + identifier: Text string for a unique thermal junction ID. Must contain only + characters that are acceptable in a DED. This will be used to + identify the object across the exported geoJSON and DES files. + geometry: A LineSegment2D or Polyline2D representing the geometry of the + thermal junction. + system_identifier: An optional text string for the identifier of a district + system object associated with the junction. District system objects + include Ground Heat Exchangers. (Default: None). + building_identifier: An optional text string for the identifier of a Building + object associated with the junction. (Default: None). + + Properties: + * identifier + * display_name + * geometry + * system_identifier + * building_identifier + """ + __slots__ = ('_system_identifier', '_building_identifier') + + def __init__(self, identifier, geometry, system_identifier=None, + building_identifier=None): + """Initialize ThermalJunction.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, Point2D), 'Expected ladybug_geometry ' \ + 'Point2D for ThermalJunction. Got {}'.format(type(geometry)) + self._geometry = geometry + self.system_identifier = system_identifier + self.building_identifier = building_identifier + + @property + def geometry(self): + """Get a Point2D representing the ThermalJunction.""" + return self._geometry + + @property + def system_identifier(self): + """Get or set a text string for the ID of a Transformer or Substation.""" + return self._system_identifier + + @system_identifier.setter + def system_identifier(self, value): + self._system_identifier = valid_ep_string(value, 'system_identifier') \ + if value is not None else None + + @property + def building_identifier(self): + """Get or set a text string for the ID of a dragonfly Building.""" + return self._building_identifier + + @building_identifier.setter + def building_identifier(self, value): + self._building_identifier = valid_ep_string(value, 'building_identifier') \ + if value is not None else None + +
+[docs] + def to_geojson_dict(self, origin_lon_lat, conversion_factors): + """Get an ThermalJunction dictionary as it appears in an URBANopt geoJSON. + + Args: + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + pt = (self.geometry.x, self.geometry.y) + coord = polygon_to_lon_lat([pt], origin_lon_lat, conversion_factors)[0] + geo_dict = { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'type': 'ThermalJunction', + 'junction_type': 'DES', + 'connection_type': 'Series' + }, + 'geometry': { + 'type': 'Point', + 'coordinates': coord + } + } + if self._system_identifier is not None: + geo_dict['properties']['DSId'] = self._system_identifier + if self._building_identifier is not None: + geo_dict['properties']['buildingId'] = self._building_identifier + return geo_dict
+ + + def __copy__(self): + new_jct = ThermalJunction( + self.identifier, self.geometry, self._system_identifier, + self._building_identifier) + new_jct._display_name = self._display_name + return new_jct + + def __repr__(self): + return 'ThermalJunction: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/des/loop.html b/docs/_modules/dragonfly_energy/des/loop.html new file mode 100644 index 00000000..bf1105ec --- /dev/null +++ b/docs/_modules/dragonfly_energy/des/loop.html @@ -0,0 +1,2298 @@ + + + + + + + dragonfly_energy.des.loop — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.des.loop

+# coding=utf-8
+"""Thermal Loop of a District Energy System (DES)."""
+import os
+import uuid
+import json
+
+from ladybug_geometry.geometry2d import Point2D, LineSegment2D, Polyline2D, Polygon2D
+from ladybug.location import Location
+from ladybug.datacollection import HourlyContinuousCollection
+from honeybee.typing import valid_ep_string, float_in_range
+from honeybee.units import conversion_factor_to_meters
+from dragonfly.projection import meters_to_long_lat_factors, \
+    origin_long_lat_from_location
+
+from .ghe import GroundHeatExchanger, SoilParameter, FluidParameter, \
+    PipeParameter, BoreholeParameter, GHEDesignParameter
+from .connector import ThermalConnector
+from .junction import ThermalJunction
+
+
+
+[docs] +class GHEThermalLoop(object): + """Represents an Ground Heat Exchanger Thermal Loop in a DES. + + This includes a GroundHeatExchanger and all thermal connectors needed + to connect these objects to Dragonfly Buildings in a loop. + + Args: + identifier: Text string for a unique thermal loop ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + ground_heat_exchangers: An array of GroundHeatExchanger objects representing + the fields of boreholes that supply the loop with thermal capacity. + connectors: An array of ThermalConnector objects that are included + within the thermal loop. In order for a given connector to be + valid within the loop, each end of the connector must touch either + another connector, a building footprint, or the ground_heat_exchangers. In + order for the loop as a whole to be valid, the connectors must form a + single continuous loop when passed through the buildings and the heat + exchanger field. + clockwise_flow: A boolean to note whether the direction of flow through the + loop is clockwise (True) when viewed from above in the GeoJSON or it + is counterclockwise (False). (Default: False). + soil_parameters: Optional SoilParameter object to specify the properties + of the soil in which the loop is operating. If None, default + values will be used. (Default: None). + fluid_parameters: Optional FluidParameter object to specify the properties + of the fluid that is circulating through the loop. If None, default + values will be used. (Default: None). + pipe_parameters: Optional PipeParameter object to specify the properties + of the ground-heat-exchanging pipes used across the loop. If None, + default values will be used. (Default: None). + borehole_parameters: Optional BoreholeParameter object to specify the + properties of the boreholes used across the loop. If None, + default values will be used. (Default: None). + design_parameters: Optional GHEDesignParameter object to specify the + design constraints across the loop. If None, default values + will be used. (Default: None). + + Properties: + * identifier + * display_name + * ground_heat_exchangers + * connectors + * clockwise_flow + * soil_parameters + * fluid_parameters + * pipe_parameters + * borehole_parameters + * design_parameters + """ + __slots__ = ( + '_identifier', '_display_name', '_ground_heat_exchangers', '_connectors', + '_clockwise_flow', '_soil_parameters', '_fluid_parameters', '_pipe_parameters', + '_borehole_parameters', '_design_parameters') + + def __init__(self, identifier, ground_heat_exchangers, connectors, + clockwise_flow=False, soil_parameters=None, fluid_parameters=None, + pipe_parameters=None, borehole_parameters=None, design_parameters=None): + """Initialize GHEThermalLoop.""" + self.identifier = identifier + self._display_name = None + self.ground_heat_exchangers = ground_heat_exchangers + self.connectors = connectors + self.clockwise_flow = clockwise_flow + self.soil_parameters = soil_parameters + self.fluid_parameters = fluid_parameters + self.pipe_parameters = pipe_parameters + self.borehole_parameters = borehole_parameters + self.design_parameters = design_parameters + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an GHEThermalLoop from a dictionary. + + Args: + data: A dictionary representation of an GHEThermalLoop object. + """ + # check the type of dictionary + assert data['type'] == 'GHEThermalLoop', 'Expected GHEThermalLoop ' \ + 'dictionary. Got {}.'.format(data['type']) + # re-serialize geometry objects + ghe = [GroundHeatExchanger.from_dict(g) for g in data['ground_heat_exchangers']] + conns = [ThermalConnector.from_dict(c) for c in data['connectors']] + clock = data['clockwise_flow'] if 'clockwise_flow' in data else False + soil = SoilParameter.from_dict(data['soil_parameters']) \ + if 'soil_parameters' in data else None + fluid = FluidParameter.from_dict(data['fluid_parameters']) \ + if 'fluid_parameters' in data else None + pipe = PipeParameter.from_dict(data['pipe_parameters']) \ + if 'pipe_parameters' in data else None + bore = BoreholeParameter.from_dict(data['borehole_parameters']) \ + if 'borehole_parameters' in data else None + des = GHEDesignParameter.from_dict(data['design_parameters']) \ + if 'design_parameters' in data else None + loop = cls(data['identifier'], ghe, conns, clock, soil, fluid, pipe, bore, des) + if 'display_name' in data and data['display_name'] is not None: + loop.display_name = data['display_name'] + return loop
+ + +
+[docs] + @classmethod + def from_geojson( + cls, geojson_file_path, location=None, point=None, units='Meters', + clockwise_flow=False): + """Get an GHEThermalLoop from a dictionary as it appears in a GeoJSON. + + Args: + geojson_file_path: Text for the full path to the geojson file to load + as GHEThermalLoop. + location: An optional ladybug location object with longitude and + latitude data defining the origin of the geojson file. If None, + an attempt will be made to sense the location from the project + point in the GeoJSON (if it exists). If nothing is found, the + origin is autocalcualted as the bottom-left corner of the bounding + box of all building footprints in the geojson file. (Default: None). + point: A ladybug_geometry Point2D for where the location object exists + within the space of a scene. The coordinates of this point are + expected to be in the units input. If None, an attempt will be + made to sense the CAD coordinates from the GeoJSON if they + exist. If not found, they will default to (0, 0). + units: Text for the units system in which the model geometry + exists. Default: 'Meters'. Choose from the following: + + * Meters + * Millimeters + * Feet + * Inches + * Centimeters + + Note that this method assumes the point coordinates are in the + same units. + clockwise_flow: A boolean to note whether the direction of flow through the + loop is clockwise (True) when viewed from above in the GeoJSON or it + is counterclockwise (False). (Default: False). + """ + # parse the geoJSON into a dictionary + with open(geojson_file_path, 'r') as fp: + data = json.load(fp) + + # extract the CAD coordinates and location from the GeoJSON if they exist + if 'project' in data: + prd = data['project'] + if 'latitude' in prd and 'longitude' in prd and location is None: + location = Location(latitude=prd['latitude'], longitude=prd['longitude']) + if 'cad_coordinates' in prd and point is None: + point = Point2D(*prd['cad_coordinates']) + if point is None: # just use the world origin if no point was found + point = Point2D(0, 0) + + # Get the list of thermal connector and GHE data + connector_data, ghe_data = [], [] + for obj_data in data['features']: + if 'type' in obj_data['properties']: + if obj_data['properties']['type'] == 'ThermalConnector': + connector_data.append(obj_data) + elif obj_data['properties']['type'] == 'District System' and \ + obj_data['properties']['district_system_type'] == \ + 'Ground Heat Exchanger': + ghe_data.append(obj_data) + + # if model units is not Meters, convert non-meter user inputs to meters + scale_to_meters = conversion_factor_to_meters(units) + if units != 'Meters': + point = point.scale(scale_to_meters) + + # Get long and lat in the geojson that correspond to the model origin (point). + # If location is None, derive coordinates from the geojson geometry. + if location is None: + point_lon_lat = cls._bottom_left_coordinate_from_geojson(connector_data) + location = Location(longitude=point_lon_lat[0], latitude=point_lon_lat[1]) + + # The model point may not be at (0, 0), so shift the longitude and latitude to + # get the equivalent point in longitude and latitude for (0, 0) in the model. + origin_lon_lat = origin_long_lat_from_location(location, point) + _convert_facs = meters_to_long_lat_factors(origin_lon_lat) + convert_facs = 1 / _convert_facs[0], 1 / _convert_facs[1] + + # extract the connectors + connectors = [] + for con_data in connector_data: + con_obj = ThermalConnector.from_geojson_dict( + con_data, origin_lon_lat, convert_facs) + connectors.append(con_obj) + # extract the substation + ghe_field = GroundHeatExchanger.from_geojson_dict( + ghe_data, origin_lon_lat, convert_facs) + + # create the loop and adjust for the units + base_name = os.path.basename(geojson_file_path) + loop_id = base_name.replace('.json', '').replace('.geojson', '') + loop = cls(loop_id, ghe_field, connectors, clockwise_flow) + if units != 'Meters': + loop.convert_to_units(units) + return loop
+ + + @staticmethod + def _bottom_left_coordinate_from_geojson(connector_data): + """Calculate the bottom-left bounding box coordinate from geojson coordinates. + + Args: + connector_data: a list of dictionaries containing geojson geometries that + represent thermal connectors. + + Returns: + The bottom-left most corner of the bounding box around the coordinates. + """ + xs, ys = [], [] + for conn in connector_data: + conn_coords = conn['geometry']['coordinates'] + if conn['geometry']['type'] == 'LineString': + for pt in conn_coords: + xs.append(pt[0]) + ys.append(pt[1]) + return min(xs), min(ys) + + @property + def identifier(self): + """Get or set the text string for unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, identifier): + self._identifier = valid_ep_string(identifier, 'identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def ground_heat_exchangers(self): + """Get or set a tuple of GroundHeatExchanger objects for the loop's GHEs. + """ + return self._ground_heat_exchangers + + @ground_heat_exchangers.setter + def ground_heat_exchangers(self, values): + try: + if not isinstance(values, tuple): + values = tuple(values) + except TypeError: + raise TypeError( + 'Expected list or tuple for thermal loop ground_heat_exchangers. ' + 'Got {}'.format(type(values))) + for g in values: + assert isinstance(g, GroundHeatExchanger), 'Expected GroundHeatExchanger ' \ + 'object for thermal loop ground_heat_exchangers. Got {}.'.format(type(g)) + assert len(values) > 0, 'ThermalLoop must possess at least one GHE.' + self._ground_heat_exchangers = values + + @property + def connectors(self): + """Get or set the list of ThermalConnector objects within the loop.""" + return self._connectors + + @connectors.setter + def connectors(self, values): + try: + if not isinstance(values, tuple): + values = tuple(values) + except TypeError: + raise TypeError('Expected list or tuple for thermal loop connectors. ' + 'Got {}'.format(type(values))) + for c in values: + assert isinstance(c, ThermalConnector), 'Expected ThermalConnector ' \ + 'object for thermal loop connectors. Got {}.'.format(type(c)) + assert len(values) > 0, 'ThermalLoop must possess at least one connector.' + self._connectors = values + + @property + def clockwise_flow(self): + """Get or set a boolean for whether the flow through the loop is clockwise.""" + return self._clockwise_flow + + @clockwise_flow.setter + def clockwise_flow(self, value): + self._clockwise_flow = bool(value) + + @property + def soil_parameters(self): + """Get or set a SoilParameter object for the heat exchanger field.""" + return self._soil_parameters + + @soil_parameters.setter + def soil_parameters(self, value): + if value is None: + value = SoilParameter() + assert isinstance(value, SoilParameter), \ + 'Expected SoilParameter object' \ + ' for GroundHeatExchanger. Got {}.'.format(type(value)) + self._soil_parameters = value + + @property + def fluid_parameters(self): + """Get or set a FluidParameter object for the heat exchanger field.""" + return self._fluid_parameters + + @fluid_parameters.setter + def fluid_parameters(self, value): + if value is None: + value = FluidParameter() + assert isinstance(value, FluidParameter), \ + 'Expected FluidParameter object' \ + ' for GroundHeatExchanger. Got {}.'.format(type(value)) + self._fluid_parameters = value + + @property + def pipe_parameters(self): + """Get or set a PipeParameter object for the heat exchanger field.""" + return self._pipe_parameters + + @pipe_parameters.setter + def pipe_parameters(self, value): + if value is None: + value = PipeParameter() + assert isinstance(value, PipeParameter), \ + 'Expected PipeParameter object' \ + ' for GroundHeatExchanger. Got {}.'.format(type(value)) + self._pipe_parameters = value + + @property + def borehole_parameters(self): + """Get or set a BoreholeParameter object for the heat exchanger field.""" + return self._borehole_parameters + + @borehole_parameters.setter + def borehole_parameters(self, value): + if value is None: + value = BoreholeParameter() + assert isinstance(value, BoreholeParameter), \ + 'Expected BoreholeParameter object' \ + ' for GroundHeatExchanger. Got {}.'.format(type(value)) + self._borehole_parameters = value + + @property + def design_parameters(self): + """Get or set a GHEDesignParameter object for the heat exchanger field.""" + return self._design_parameters + + @design_parameters.setter + def design_parameters(self, value): + if value is None: + value = GHEDesignParameter() + assert isinstance(value, GHEDesignParameter), \ + 'Expected GHEDesignParameter object' \ + ' for GroundHeatExchanger. Got {}.'.format(type(value)) + self._design_parameters = value + +
+[docs] + def junctions(self, tolerance=0.01): + """Get a list of ThermalJunction objects for the unique thermal loop junctions. + + The resulting ThermalJunction objects will be associated with the loop's + GroundHeatExchanger if they are in contact with it (within the tolerance). + However, they won't have any building_identifier associated with them. + The assign_junction_buildings method on this object can be used + to associate the junctions with an array of Dragonfly Buildings. + + Args: + tolerance: The minimum difference between the coordinate values of two + faces at which they can be considered centered adjacent. (Default: 0.01, + suitable for objects in meters). + + Returns: + A tuple with two items. + + - junctions - A list of lists of the unique ThermalJunction objects + that exist across the loop. + + - connector_junction_ids - A list of lists that align with the connectors + in the loop. Each sub-list contains two string values for the junction + IDs for each of the start and end of each of the connectors. + """ + return self._junctions_from_connectors(self.connectors, tolerance)
+ + +
+[docs] + def loop_polygon(self, buildings, tolerance=0.01): + """Get a Polygon2D for the single continuous loop formed by connectors. + + This method will raise an exception if the ThermalConnectors do not form + a single continuous loop through the Buildings and the ground_heat_exchangers. + The Polygon2D will have the correct clockwise ordering according to this + object's clockwise_flow property. + + Args: + buildings: An array of Dragonfly Building objects in the same units + system as the GHEThermalLoop geometry. + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # get the footprints of the Buildings in 2D space and the GHE field + footprint_2d, bldg_ids = GHEThermalLoop._building_footprints( + buildings, tolerance) + for ghe in self.ground_heat_exchangers: + footprint_2d.append(ghe.geometry) + bldg_ids.append(ghe.identifier) + + # determine which ThermalConnectors are linked to the buildings + feat_dict = {} + for bldg_poly, bldg_id in zip(footprint_2d, bldg_ids): + for conn in self.connectors: + c_p1, c_p2 = conn.geometry.p1, conn.geometry.p2 + p1_con = bldg_poly.is_point_on_edge(c_p1, tolerance) + p2_con = bldg_poly.is_point_on_edge(c_p2, tolerance) + if p1_con or p2_con: + rel_pt = c_p1 if p1_con else c_p2 + try: # assume that the first connection has been found + feat_dict[bldg_id].append(rel_pt) + except KeyError: # this is the first connection + feat_dict[bldg_id] = [rel_pt] + + # create a list with all line segment geometry in the loop + loop_segs = [] + for conn in self.connectors: + if isinstance(conn.geometry, LineSegment2D): + loop_segs.append(conn.geometry) + else: # assume that it is a PolyLine2D + loop_segs.extend(conn.geometry.segments) + for feat_id, f_pts in feat_dict.items(): + if len(f_pts) == 2: # valid connection with clear supply and return + loop_segs.append(LineSegment2D.from_end_points(f_pts[0], f_pts[1])) + elif len(f_pts) < 2: # only one connection; raise an error + msg = 'Feature "{}" contains only a single connection to a ' \ + 'ThermalConnector and cannot be integrated into a valid ' \ + 'loop.'.format(feat_id) + raise ValueError(msg) + else: # multiple connections; raise an error + msg = 'Feature "{}" contains {} connections to ThermalConnectors and ' \ + 'cannot be integrated into a valid loop.'.format(feat_id, len(f_pts)) + raise ValueError(msg) + + # join all of the segments together into a single polygon and set the order + loop_geos = Polyline2D.join_segments(loop_segs, tolerance) + assert len(loop_geos) == 1, 'A total of {} different loops were found across ' \ + 'all ThermalConnectors.\nOnly one loop is allowed.'.format(len(loop_geos)) + loop_geo = loop_geos[0] + assert loop_geo.is_closed(tolerance), 'The ThermalConnectors form an ' \ + 'open loop.\nThis loop must be closed in order to be valid.' + loop_poly = loop_geo.to_polygon(tolerance) + if loop_poly.is_clockwise is not self.clockwise_flow: + loop_poly = loop_poly.reverse() + return loop_poly
+ + +
+[docs] + def ordered_connectors(self, buildings, tolerance=0.01): + """Get the ThermalConnectors of this GHEThermalLoop correctly ordered in a loop. + + The resulting connectors will not only be ordered correctly along the loop + but the orientation of the connector geometries will be property coordinated + with the clockwise_flow property on this object. + + This method will raise an exception if the ThermalConnectors do not form + a single continuous loop through the Buildings and the ground_heat_exchangers. + + Args: + buildings: An array of Dragonfly Building objects in the same units + system as the GHEThermalLoop geometry. + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # first get a Polygon2D for the continuous loop + loop_poly = self.loop_polygon(buildings, tolerance) + + # loop through the polygon segments and find each matching thermal connector + ord_conns, skip_count, tol = [], 0, tolerance + for loop_seg in loop_poly.segments: + if skip_count != 0: + skip_count -= 1 + continue + for conn in self.connectors: + if isinstance(conn.geometry, LineSegment2D) and \ + conn.geometry.is_equivalent(loop_seg, tol): + if not conn.geometry.p1.is_equivalent(loop_seg.p1, tol): + conn.reverse() + ord_conns.append(conn) + break + elif isinstance(conn.geometry, Polyline2D) and \ + (conn.geometry.p1.is_equivalent(loop_seg.p1, tol) or + conn.geometry.p2.is_equivalent(loop_seg.p1, tol)): + if not conn.geometry.p1.is_equivalent(loop_seg.p1, tol): + conn.reverse() + ord_conns.append(conn) + skip_count = len(conn.geometry.vertices) - 1 + break + return ord_conns
+ + +
+[docs] + def move(self, moving_vec): + """Move this object along a vector. + + Args: + moving_vec: A ladybug_geometry Vector3D with the direction and distance + to move the object. + """ + for ghe in self.ground_heat_exchangers: + ghe.move(moving_vec) + for connector in self.connectors: + connector.move(moving_vec)
+ + +
+[docs] + def rotate_xy(self, angle, origin): + """Rotate this object counterclockwise in the XY plane by a certain angle. + + Args: + angle: An angle in degrees. + origin: A ladybug_geometry Point3D for the origin around which the + object will be rotated. + """ + for ghe in self.ground_heat_exchangers: + ghe.rotate_xy(angle, origin) + for connector in self.connectors: + connector.rotate_xy(angle, origin)
+ + +
+[docs] + def reflect(self, plane): + """Reflect this object across a plane. + + Args: + plane: A ladybug_geometry Plane across which the object will be reflected. + """ + for ghe in self.ground_heat_exchangers: + ghe.reflect(plane) + for connector in self.connectors: + connector.reflect(plane)
+ + +
+[docs] + def scale(self, factor, origin=None): + """Scale this object by a factor from an origin point. + + Args: + factor: A number representing how much the object should be scaled. + origin: A ladybug_geometry Point3D representing the origin from which + to scale. If None, it will be scaled from the World origin (0, 0, 0). + """ + for ghe in self.ground_heat_exchangers: + ghe.scale(factor, origin) + for connector in self.connectors: + connector.scale(factor, origin)
+ + +
+[docs] + def convert_to_units(self, units='Meters', starting_units='Meters'): + """Convert all of the geometry in this ThermalLoop to certain units. + + Args: + units: Text for the units to which the Model geometry should be + converted. (Default: Meters). Choose from the following: + + * Meters + * Millimeters + * Feet + * Inches + * Centimeters + + starting_units: The starting units system of the loop. (Default: Meters). + """ + if starting_units != units: + scale_fac1 = conversion_factor_to_meters(starting_units) + scale_fac2 = conversion_factor_to_meters(units) + scale_fac = scale_fac1 / scale_fac2 + self.scale(scale_fac)
+ + +
+[docs] + def to_dict(self): + """GHEThermalLoop dictionary representation.""" + base = {'type': 'GHEThermalLoop'} + base['identifier'] = self.identifier + base['ground_heat_exchangers'] = \ + [g.to_dict() for g in self.ground_heat_exchangers] + base['connectors'] = [c.to_dict() for c in self.connectors] + base['clockwise_flow'] = self.clockwise_flow + base['soil_parameters'] = self.soil_parameters.to_dict() + base['fluid_parameters'] = self.fluid_parameters.to_dict() + base['pipe_parameters'] = self.pipe_parameters.to_dict() + base['borehole_parameters'] = self.borehole_parameters.to_dict() + base['design_parameters'] = self.design_parameters.to_dict() + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, buildings, location, point=Point2D(0, 0), tolerance=0.01): + """Get GHEThermalLoop dictionary as it appears in an URBANopt geoJSON. + + The resulting dictionary array can be directly appended to the "features" + key of a base GeoJSON dict in order to represent the loop in the + GeoJSON. Note that, in order to successfully simulate the DES, you will also + have to write a system_parameter.json from this GHEThermalLoop using + the to_des_param_dict method. + + Args: + buildings: An array of Dragonfly Building objects that are along + the GHEThermalLoop. Buildings that do not have their footprint + touching the loop's ThermalConnectors are automatically excluded + in the result. + location: A ladybug Location object possessing longitude and latitude data. + point: A ladybug_geometry Point2D for where the location object exists + within the space of a scene. The coordinates of this point are + expected to be in the units of this Model. (Default: (0, 0)). + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # get the conversion factors over to (longitude, latitude) + origin_lon_lat = origin_long_lat_from_location(location, point) + convert_facs = meters_to_long_lat_factors(origin_lon_lat) + + # translate ground heat exchangers into the GeoJSON features list + features_list = [] + for ghe in self.ground_heat_exchangers: + features_list.append(ghe.to_geojson_dict(origin_lon_lat, convert_facs)) + + # get the footprints of the Buildings in 2D space + footprint_2d, bldg_ids = GHEThermalLoop._building_footprints( + buildings, tolerance) + all_feat = footprint_2d + [ghe.geometry for ghe in self.ground_heat_exchangers] + feat_ids = bldg_ids + [ghe.identifier for ghe in self.ground_heat_exchangers] + + # order the connectors correctly on the loop and translate them to features + ordered_conns = self.ordered_connectors(buildings, tolerance) + junctions, connector_jct_ids = self._junctions_from_connectors( + ordered_conns, tolerance) + for conn, jct_ids in zip(self.connectors, connector_jct_ids): + st_feat, end_feat, cp1, cp2 = None, None, conn.geometry.p1, conn.geometry.p2 + for f_poly, f_id in zip(all_feat, feat_ids): + if f_poly.is_point_on_edge(cp1, tolerance): + st_feat = f_id + elif f_poly.is_point_on_edge(cp2, tolerance): + end_feat = f_id + conn_dict = conn.to_geojson_dict( + jct_ids[0], jct_ids[1], origin_lon_lat, convert_facs, st_feat, end_feat) + features_list.append(conn_dict) + + # translate junctions into the GeoJSON features list + for jct in junctions: + for bldg_poly, bldg_id in zip(footprint_2d, bldg_ids): + if bldg_poly.is_point_on_edge(jct.geometry, tolerance): + jct.building_identifier = bldg_id + break + for i, jct in enumerate(junctions): + jct_dict = jct.to_geojson_dict(origin_lon_lat, convert_facs) + if i == 0: + jct_dict['properties']['is_ghe_start_loop'] = True + features_list.append(jct_dict) + return features_list
+ + +
+[docs] + def to_des_param_dict(self, buildings, tolerance=0.01): + """Get the DES System Parameter dictionary for the ThermalLoop. + + Args: + buildings: An array of Dragonfly Building objects that are along + the GHEThermalLoop. Buildings that do not have their footprint + touching the loop's ThermalConnectors are automatically excluded + in the result. + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # set up a dictionary to be updated with the params + des_dict = {} + + # add the relevant buildings to the DES parameter dictionary + footprint_2d, bldg_ids = GHEThermalLoop._building_footprints( + buildings, tolerance) + rel_bldg_ids = set() + junctions, _ = self.junctions(tolerance) + for jct in junctions: + for bldg_poly, bldg_id in zip(footprint_2d, bldg_ids): + if bldg_poly.is_point_on_edge(jct.geometry, tolerance): + rel_bldg_ids.add(bldg_id) + bldg_array = [] + for bldg_id in rel_bldg_ids: + b_dict = { + 'geojson_id': bldg_id, + 'load_model': 'time_series', + 'load_model_parameters': { + 'time_series': { + 'filepath': 'To be populated', + 'delta_temp_air_cooling': 10, + 'delta_temp_air_heating': 18, + 'has_liquid_cooling': True, + 'has_liquid_heating': True, + 'has_electric_cooling': False, + 'has_electric_heating': False, + 'max_electrical_load': 0, + 'temp_chw_return': 12, + 'temp_chw_supply': 7, + 'temp_hw_return': 35, + 'temp_hw_supply': 40, + 'temp_setpoint_cooling': 24, + 'temp_setpoint_heating': 20 + } + }, + 'ets_model': 'Fifth Gen Heat Pump', + 'fifth_gen_ets_parameters': { + 'supply_water_temperature_building': 15, + 'chilled_water_supply_temp': 5, + 'hot_water_supply_temp': 50, + 'cop_heat_pump_heating': 2.5, + 'cop_heat_pump_cooling': 3.5, + 'pump_flow_rate': 0.01, + 'pump_design_head': 150000, + 'ets_pump_flow_rate': 0.0005, + 'ets_pump_head': 10000, + 'fan_design_flow_rate': 0.25, + 'fan_design_head': 150 + } + } + bldg_array.append(b_dict) + des_dict['buildings'] = bldg_array + + # add the ground loop parameters + des_param = { + 'fifth_generation': { + 'ghe_parameters': self.to_ghe_param_dict(tolerance) + } + } + des_dict['district_system'] = des_param + return des_dict
+ + +
+[docs] + def to_ghe_param_dict(self, tolerance=0.01): + """Get the GroundHeatExchanger as it appears in a System Parameter dictionary. + + Args: + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # compute the geometric constraints of the borehole fields + geo_pars = [] + for ghe in self.ground_heat_exchangers: + ghe_geo = ghe.geometry + max_dim = max((ghe_geo.max.x - ghe_geo.min.x, ghe_geo.max.y - ghe_geo.min.y)) + ang_tol = tolerance / max_dim + if ghe_geo.is_rectangle(ang_tol): + ghe_dims = (ghe_geo.segments[0].length, ghe_geo.segments[1].length) + else: + rect_geo = ghe_geo.rectangular_approximation() + ghe_dims = (rect_geo.segments[0].length, rect_geo.segments[1].length) + geo_par = { + 'ghe_id': ghe.identifier, + 'ghe_geometric_params': { + 'length_of_ghe': max(ghe_dims), + 'width_of_ghe': min(ghe_dims) + }, + 'borehole': { + 'buried_depth': self.borehole_parameters.buried_depth, + 'diameter': self.borehole_parameters.diameter + }, + 'ground_loads': [] + } + geo_pars.append(geo_par) + # handle autocalculated soil temperatures + u_temp = self.soil_parameters.undisturbed_temperature \ + if self.soil_parameters._undisturbed_temperature is not None \ + else 'Autocalculate' + # return a dictionary with all of the information + return { + 'fluid': { + 'fluid_name': self.fluid_parameters.fluid_type, + 'concentration_percent': self.fluid_parameters.concentration, + 'temperature': self.fluid_parameters.temperature + }, + 'grout': { + 'conductivity': self.soil_parameters.grout_conductivity, + 'rho_cp': self.soil_parameters.grout_heat_capacity, + }, + 'soil': { + 'conductivity': self.soil_parameters.conductivity, + 'rho_cp': self.soil_parameters.heat_capacity, + 'undisturbed_temp': u_temp + }, + 'pipe': { + 'inner_diameter': self.pipe_parameters.inner_diameter, + 'outer_diameter': self.pipe_parameters.outer_diameter, + 'shank_spacing': self.pipe_parameters.shank_spacing, + 'roughness': self.pipe_parameters.roughness, + 'conductivity': self.pipe_parameters.conductivity, + 'rho_cp': self.pipe_parameters.heat_capacity, + 'arrangement': self.pipe_parameters.arrangement.lower() + }, + 'simulation': { + 'num_months': self.design_parameters.month_count + }, + 'geometric_constraints': { + 'b_min': self.borehole_parameters.min_spacing, + 'b_max': self.borehole_parameters.max_spacing, + 'max_height': self.borehole_parameters.max_depth, + 'min_height': self.borehole_parameters.min_depth, + 'method': 'rectangle' + }, + 'design': { + 'method': 'AREAPROPORTIONAL', + 'flow_rate': self.design_parameters.flow_rate, + 'flow_type': self.design_parameters.flow_type.lower(), + 'max_eft': self.design_parameters.max_eft, + 'min_eft': self.design_parameters.min_eft + }, + 'ghe_specific_params': geo_pars + }
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + +
+[docs] + @staticmethod + def ghe_designer_dict( + thermal_load, site_geometry, soil_parameters=None, fluid_parameters=None, + pipe_parameters=None, borehole_parameters=None, design_parameters=None, + tolerance=0.01): + """Get a dictionary following the schema of the input JSON for GHEDesigner. + + This includes many of the same parameters that are used to size ground + heat exchangers in an URBANopt DES system but it requires the input of + hourly thermal loads. + + The dictionary returned by this method can be written to a JSON and + passed directly to the GHEDesigner CLI in order to receive sizing + information for the GHE and a G-function that can be used to meet + the input load in a building energy simulation. + + Args: + thermal_load: An annual data collection of hourly thermal loads on + the ground in Watts. These are the heat extraction and heat rejection + loads directly on the ground heat exchanger and should already + account for factors like additional heat added or removed by the + heat pump compressors. Positive values indicate heat extraction + from the ground and negative values indicate heat rejection to + the ground. + site_geometry: A list of horizontal Face3D representing the footprint + of the site to be populated with boreholes. These Face3D can + have holes in them and these holes will be excluded from + borehole placement. Note that it is expected that this geometry's + dimensions are in meters and, if they are not, then it should + be scaled before input to this method. + soil_parameters: Optional SoilParameter object to specify the properties + of the soil in which the loop is operating. If None, default + values will be used. (Default: None). + fluid_parameters: Optional FluidParameter object to specify the properties + of the fluid that is circulating through the loop. If None, default + values will be used. (Default: None). + pipe_parameters: Optional PipeParameter object to specify the properties + of the ground-heat-exchanging pipes used across the loop. If None, + default values will be used. (Default: None). + borehole_parameters: Optional BoreholeParameter object to specify the + properties of the boreholes used across the loop. If None, + default values will be used. (Default: None). + design_parameters: Optional GHEDesignParameter object to specify the + design constraints across the loop. If None, default values + will be used. (Default: None). + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # check that the inputs are what we expect + assert isinstance(thermal_load, HourlyContinuousCollection), \ + 'Expected hourly continuous data collection for thermal_load. ' \ + 'Got {}'.format(type(thermal_load)) + period = thermal_load.header.analysis_period + assert period.is_annual and period.timestep == 1, 'Hourly thermal load ' \ + 'is not annual. Analysis period is: {}.'.format(period) + assert thermal_load.header.unit == 'W', 'Expected load data collection to be in Watts. ' \ + 'Got {}.'.format(thermal_load.header.unit) + + # process the input geometry into the format needed for GHEDesigner + site_boundaries, site_holes = [], [] + for face in site_geometry: + bnd_poly = Polygon2D([Point2D(pt.x, pt.y) for pt in face.boundary]) + bnd_poly = bnd_poly.remove_colinear_vertices(tolerance) + if bnd_poly.is_clockwise: + bnd_poly = bnd_poly.reverse() + site_boundaries.append([[pt.x, pt.y] for pt in bnd_poly]) + if face.has_holes: + for hole in face.holes: + hole_poly = Polygon2D([Point2D(pt.x, pt.y) for pt in hole]) + hole_poly = hole_poly.remove_colinear_vertices(tolerance) + if hole_poly.is_clockwise: + hole_poly = hole_poly.reverse() + site_holes.append([[pt.x, pt.y] for pt in hole_poly]) + + # set defaults if any of the inputs are unspecified + soil = soil_parameters if soil_parameters is not None else SoilParameter() + fluid = fluid_parameters if fluid_parameters is not None else FluidParameter() + pipe = pipe_parameters if pipe_parameters is not None else PipeParameter() + borehole = borehole_parameters if borehole_parameters is not None \ + else BoreholeParameter() + design = design_parameters if pipe_parameters is not None \ + else GHEDesignParameter() + u_temp = 18.3 if soil._undisturbed_temperature is None \ + else soil.undisturbed_temperature + + # return a dictionary with all of the inputs + return { + 'fluid': { + 'fluid_name': fluid.fluid_type, + 'concentration_percent': fluid.concentration, + 'temperature': fluid.temperature + }, + 'grout': { + 'conductivity': soil.grout_conductivity, + 'rho_cp': soil.grout_heat_capacity + }, + 'soil': { + 'conductivity': soil.conductivity, + 'rho_cp': soil.heat_capacity, + 'undisturbed_temp': u_temp + }, + 'pipe': { + 'inner_diameter': pipe.inner_diameter, + 'outer_diameter': pipe.outer_diameter, + 'shank_spacing': pipe.shank_spacing, + 'roughness': pipe.roughness, + 'conductivity': pipe.conductivity, + 'rho_cp': pipe.heat_capacity, + 'arrangement': pipe.arrangement.upper() + }, + 'borehole': { + 'buried_depth': borehole.buried_depth, + 'diameter': borehole.diameter + }, + 'simulation': { + 'num_months': design.month_count + }, + 'geometric_constraints': { + 'b_min': borehole.min_spacing, + 'b_max_x': borehole.max_spacing, + 'b_max_y': borehole.max_spacing, + 'max_height': borehole.max_depth, + 'min_height': borehole.min_depth, + 'property_boundary': site_boundaries, + 'no_go_boundaries': site_holes, + 'method': 'BIRECTANGLECONSTRAINED' + }, + 'design': { + 'flow_rate': design.flow_rate, + 'flow_type': 'BOREHOLE', + 'max_eft': design.max_eft, + 'min_eft': design.min_eft + }, + 'loads': { + 'ground_loads': thermal_load.values + } + }
+ + +
+[docs] + @staticmethod + def assign_junction_buildings(junctions, buildings, tolerance=0.01): + """Assign building_identifiers to a list of junctions using dragonfly Buildings. + + Junctions will be assigned to a given Building if they are touching + the footprint of that building in 2D space. + + Args: + junctions: An array of ThermalJunction objects to be associated + with Dragonfly Buildings. + buildings: An array of Dragonfly Building objects in the same units + system as the ThermalLoop geometry. + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # get the footprints of the Buildings in 2D space + footprint_2d, bldg_ids = GHEThermalLoop._building_footprints( + buildings, tolerance) + + # loop through connectors and associate them with the Buildings + for jct in junctions: + for bldg_poly, bldg_id in zip(footprint_2d, bldg_ids): + if bldg_poly.is_point_on_edge(jct.geometry, tolerance): + jct.building_identifier = bldg_id + break + return junctions
+ + + def _junctions_from_connectors(self, connectors, tolerance): + """Get a list of ThermalJunction objects given a list of ThermalConnectors. + """ + # loop through the connectors and find all unique junction objects + junctions, connector_junction_ids = [], [] + for connector in connectors: + verts = connector.geometry.vertices + end_pts, jct_ids = (verts[0], verts[-1]), [] + for jct_pt in end_pts: + for exist_jct in junctions: + if jct_pt.is_equivalent(exist_jct.geometry, tolerance): + jct_ids.append(exist_jct.identifier) + break + else: # we have found a new unique junction + new_jct_id = str(uuid.uuid4()) + junctions.append(ThermalJunction(new_jct_id, jct_pt)) + jct_ids.append(new_jct_id) + connector_junction_ids.append(jct_ids) + + # loop through district system objects to determine adjacent junctions + for jct in junctions: + for ds_obj in self.ground_heat_exchangers: + if ds_obj.geometry.is_point_on_edge(jct.geometry, tolerance): + jct.system_identifier = ds_obj.identifier + break + return junctions, connector_junction_ids + + @staticmethod + def _building_footprints(buildings, tolerance=0.01): + """Get Polygon2Ds for each Dragonfly Building footprint.""" + # get the footprints of the Buildings in 2D space + footprint_2d, bldg_ids = [], [] + for bldg in buildings: + footprint = bldg.footprint(tolerance) + for face3d in footprint: + pts_2d = [Point2D(pt.x, pt.y) for pt in face3d.vertices] + footprint_2d.append(Polygon2D(pts_2d)) + bldg_ids.append(bldg.identifier) + return footprint_2d, bldg_ids + + def __copy__(self): + new_loop = GHEThermalLoop( + self.identifier, + tuple(ghe.duplicate() for ghe in self.ground_heat_exchangers), + tuple(conn.duplicate() for conn in self.connectors), self.clockwise_flow, + self.soil_parameters.duplicate(), self.fluid_parameters.duplicate(), + self.pipe_parameters.duplicate(), self.borehole_parameters.duplicate()) + new_loop._display_name = self._display_name + return new_loop + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'GHEThermalLoop: {}'.format(self.display_name)
+ + + +
+[docs] +class FourthGenThermalLoop(object): + """Represents a Fourth Generation Heating/Cooling Thermal Loop in a DES. + + Args: + identifier: Text string for a unique thermal loop ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + chilled_water_setpoint: A number for the temperature of chilled water + in the DES in degrees C. (Default: 6). + hot_water_setpoint: A number for the temperature of hot water in the DES + in degrees C. (Default: 54). + + Properties: + * identifier + * display_name + * chilled_water_setpoint + * hot_water_setpoint + """ + __slots__ = ( + '_identifier', '_display_name', '_chilled_water_setpoint', '_hot_water_setpoint') + + def __init__(self, identifier, chilled_water_setpoint=6, hot_water_setpoint=54): + """Initialize GHEThermalLoop.""" + self.identifier = identifier + self._display_name = None + self.chilled_water_setpoint = chilled_water_setpoint + self.hot_water_setpoint = hot_water_setpoint + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an FourthGenThermalLoop from a dictionary. + + Args: + data: A dictionary representation of an FourthGenThermalLoop object. + """ + # check the type of dictionary + assert data['type'] == 'FourthGenThermalLoop', 'Expected FourthGenThermalLoop ' \ + 'dictionary. Got {}.'.format(data['type']) + cwt = data['chilled_water_setpoint'] if 'chilled_water_setpoint' in data \ + and data['chilled_water_setpoint'] is not None else 6 + hwt = data['hot_water_setpoint'] if 'hot_water_setpoint' in data \ + and data['hot_water_setpoint'] is not None else 54 + loop = cls(data['identifier'], cwt, hwt) + if 'display_name' in data and data['display_name'] is not None: + loop.display_name = data['display_name'] + return loop
+ + + @property + def identifier(self): + """Get or set the text string for unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, identifier): + self._identifier = valid_ep_string(identifier, 'identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def chilled_water_setpoint(self): + """Get or set a number for the chilled water setpoint in degrees C.""" + return self._chilled_water_setpoint + + @chilled_water_setpoint.setter + def chilled_water_setpoint(self, value): + self._chilled_water_setpoint = \ + float_in_range(value, 0, 20, 'chilled water setpoint') + + @property + def hot_water_setpoint(self): + """Get or set a number for the hot water setpoint in degrees C.""" + return self._hot_water_setpoint + + @hot_water_setpoint.setter + def hot_water_setpoint(self, value): + self._hot_water_setpoint = \ + float_in_range(value, 24, 100, 'hot water setpoint') + +
+[docs] + def to_dict(self): + """FourthGenThermalLoop dictionary representation.""" + base = {'type': 'FourthGenThermalLoop'} + base['identifier'] = self.identifier + base['chilled_water_setpoint'] = self.chilled_water_setpoint + base['hot_water_setpoint'] = self.hot_water_setpoint + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_des_param_dict(self, buildings, tolerance=0.01): + """Get the DES System Parameter dictionary for the ThermalLoop. + + Args: + buildings: An array of Dragonfly Building objects that are along the + FourthGenThermalLoop. Buildings that do not have their footprint + touching the loop's ThermalConnectors are automatically excluded + in the result. + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # set up a dictionary to be updated with the params + des_dict = {} + # add the relevant buildings to the DES parameter dictionary + bldg_array = [] + for bldg in buildings: + b_dict = { + 'geojson_id': bldg.identifier, + 'load_model': 'time_series', + 'load_model_parameters': { + 'time_series': { + 'filepath': 'To be populated', + 'delta_temp_air_cooling': 10, + 'delta_temp_air_heating': 18, + 'has_liquid_cooling': True, + 'has_liquid_heating': True, + 'has_electric_cooling': False, + 'has_electric_heating': False, + 'max_electrical_load': 0, + 'temp_chw_return': 12, + 'temp_chw_supply': 7, + 'temp_hw_return': 35, + 'temp_hw_supply': 40, + 'temp_setpoint_cooling': 24, + 'temp_setpoint_heating': 20 + } + }, + 'ets_model': 'Indirect Heating and Cooling', + 'ets_indirect_parameters': { + 'heat_flow_nominal': 8000, + 'heat_exchanger_efficiency': 0.8, + 'nominal_mass_flow_district': 0, + 'nominal_mass_flow_building': 0, + 'valve_pressure_drop': 6000, + 'heat_exchanger_secondary_pressure_drop': 500, + 'heat_exchanger_primary_pressure_drop': 500, + 'cooling_supply_water_temperature_building': 7, + 'heating_supply_water_temperature_building': 50, + 'delta_temp_chw_building': 5, + 'delta_temp_chw_district': 8, + 'delta_temp_hw_building': 15, + 'delta_temp_hw_district': 20, + 'cooling_controller_y_max': 1, + 'cooling_controller_y_min': 0, + 'heating_controller_y_max': 1, + 'heating_controller_y_min': 0 + } + } + bldg_array.append(b_dict) + des_dict['buildings'] = bldg_array + + # add the ground loop parameters + des_param = { + 'fourth_generation': { + 'central_cooling_plant_parameters': { + 'heat_flow_nominal': 7999, + 'cooling_tower_fan_power_nominal': 4999, + 'mass_chw_flow_nominal': 9.9, + 'chiller_water_flow_minimum': 9.9, + 'mass_cw_flow_nominal': 9.9, + 'chw_pump_head': 300000, + 'cw_pump_head': 200000, + 'pressure_drop_chw_nominal': 5999, + 'pressure_drop_cw_nominal': 5999, + 'pressure_drop_setpoint': 49999, + 'temp_setpoint_chw': self.chilled_water_setpoint, + 'pressure_drop_chw_valve_nominal': 5999, + 'pressure_drop_cw_pum_nominal': 5999, + 'temp_air_wb_nominal': 24.9, + 'temp_cw_in_nominal': 34.9, + 'cooling_tower_water_temperature_difference_nominal': 6.56, + 'delta_temp_approach': 3.25, + 'ratio_water_air_nominal': 0.6 + }, + 'central_heating_plant_parameters': { + 'heat_flow_nominal': 8001, + 'mass_hhw_flow_nominal': 11, + 'boiler_water_flow_minimum': 11, + 'pressure_drop_hhw_nominal': 55001, + 'pressure_drop_setpoint': 50000, + 'temp_setpoint_hhw': self.hot_water_setpoint, + 'pressure_drop_hhw_valve_nominal': 6001, + 'chp_installed': False + } + } + } + des_dict['district_system'] = des_param + return des_dict
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_loop = FourthGenThermalLoop( + self.identifier, self.chilled_water_setpoint, self.hot_water_setpoint) + new_loop._display_name = self._display_name + return new_loop + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'FourthGenThermalLoop: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/measure.html b/docs/_modules/dragonfly_energy/measure.html new file mode 100644 index 00000000..1ac017fa --- /dev/null +++ b/docs/_modules/dragonfly_energy/measure.html @@ -0,0 +1,1165 @@ + + + + + + + dragonfly_energy.measure — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.measure

+# coding=utf-8
+"""Mange OpenStudio measures that can be mapped to different buildings in a model."""
+from __future__ import division
+
+import os
+import xml.etree.ElementTree as ElementTree
+from honeybee_energy.measure import Measure, MeasureArgument
+
+
+
+[docs] +class MapperMeasure(Measure): + """An OpenStudio measure that can be mapped to different buildings in a model. + + Args: + folder: Path to the folder in which the measure exists. This folder + must contain a measure.rb and a measure.xml file. Other files are + optional. + + Properties: + * folder + * metadata_file + * program_file + * resources_folder + * identifier + * display_name + * description + * type + * arguments + """ + __slots__ = () + + def __init__(self, folder): + """Initialize MapperMeasure.""" + Measure.__init__(self, folder) + +
+[docs] + @classmethod + def from_dict(cls, data, folder='.'): + """Initialize a MapperMeasure from a dictionary. + + Args: + data: A dictionary in the format below. + folder: Path to a destination folder to save the measure files. (Default '.') + + .. code-block:: python + + { + "type": "MapperMeasure", + "identifier": string, # Measure identifier + "xml_data": string, # XML file data as string + "rb_data": string, # Ruby file data as string + "resource_data": {}, # Dictionary of strings for any resource ruby files + "argument_values": [], # List of values for each of the measure arguments + } + """ + assert data['type'] == 'MapperMeasure', \ + 'Expected MapperMeasure dictionary. Got {}.'.format(data['type']) + fp = os.path.join(folder, data['identifier']) + if not os.path.isdir(fp): + os.makedirs(fp) + + # write out the contents of the measure + xml_fp = os.path.join(fp, 'measure.xml') + cls._decompress_to_file(data['xml_data'], xml_fp) + rb_fp = os.path.join(fp, 'measure.rb') + cls._decompress_to_file(data['rb_data'], rb_fp) + if 'resource_data' in data and data['resource_data'] is not None: + resource_path = os.path.join(fp, 'resources') + os.makedirs(resource_path) + for f_name, res in data['resource_data'].items(): + res_fp = os.path.join(resource_path, f_name) + cls._decompress_to_file(res, res_fp) + + # create the measure object and assign the arguments + new_measure = cls(fp) + for arg, val in zip(new_measure.arguments, data['argument_values']): + if val is not None: + arg.value = val + return new_measure
+ + +
+[docs] + def to_dict(self): + """Convert MapperMeasure to a dictionary.""" + base = Measure.to_dict(self) + base['type'] = 'MapperMeasure' + return base
+ + +
+[docs] + def to_osw_dict(self, full_path=False): + """Get a Python dictionary that can be written to an OSW JSON. + + Specifically, this dictionary can be appended to the "steps" key of the + OpenStudio Workflow (.osw) JSON dictionary in order to include the measure + in the workflow. + + Note that this method does not perform any checks to validate that the + Measure has all required values and only arguments with values will be + included in the dictionary. Validation should be done separately with + the validate method. + + Args: + full_path: Boolean to note whether the full path to the measure should + be written under the 'measure_dir_name' key or just the measure + base name. (Default: False) + """ + meas_dir = self.folder if full_path else os.path.basename(self.folder) + base = {'measure_dir_name': meas_dir, 'arguments': {}} + for arg in self._arguments: + if arg.value is not None: + base['arguments'][arg.identifier] = arg.value \ + if not isinstance(arg.value, tuple) else arg.value[0] + return base
+ + + def _parse_metadata_file(self): + """Parse measure properties from the measure.xml file.""" + # create an element tree object + tree = ElementTree.parse(self._metadata_file) + root = tree.getroot() + + # parse the measure properties from the element tree + self._identifier = root.find('name').text + self._display_name = root.find('display_name').text + self._description = root.find('description').text + self._type = None + for atr in root.find('attributes'): + if atr.find('name').text == 'Measure Type': + self._type = atr.find('value').text + + # parse the measure arguments + self._arguments = [] + for arg in root.find('arguments'): + arg_obj = MapperMeasureArgument(arg) + if arg_obj.model_dependent: + # TODO: Figure out how to implement model-dependent arguments + raise NotImplementedError( + 'Model dependent arguments are not yet supported and measure ' + 'argument is "{}" model dependent.'.format(arg_obj.identifier)) + self._arguments.append(arg_obj) + + def __repr__(self): + return 'MapperMeasure: {}'.format(self.display_name)
+ + + +
+[docs] +class MapperMeasureArgument(MeasureArgument): + """Object representing a single mapper measure argument. + + Args: + xml_element: A Python XML Element object taken from the <arguments> section + of the measure.xml file. + + Properties: + * identifier + * display_name + * value + * default_value + * type + * type_text + * required + * description + * model_dependent + * valid_choices + """ + __slots__ = () + + def __init__(self, xml_element): + """Initialize MeasureArgument.""" + MeasureArgument.__init__(self, xml_element) + + @property + def value(self): + """Get or set the value or list of values for the argument. + + When using a list, the length of it must match the number of buildings in + the dragonfly Model and each value corresponds to a building under the + Model.buildings property. + + If not set, this will be equal to the default_value and, if no default + value is included for this argument, it will be None. + """ + if self._value is not None: + return self._value + return self._default_value + + @value.setter + def value(self, val): + if val is not None: + e_msg = 'Value for measure argument "' + self.identifier + \ + '" must be a {}. Got {}' + if not isinstance(val, (list, tuple)): + val = (val,) + try: + val = tuple(self._type(v) for v in val) + except Exception: + raise TypeError(e_msg.format(self._type, type(val))) + if self._valid_choices: + assert all(v in self._valid_choices for v in val), \ + 'Choice measure argument "{}" ' \ + 'must be one of the following:\n{}\nGot {}'.format( + self.identifier, self._valid_choices, val) + self._value = val if val is None or len(val) != 1 else val[0]
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/colorobj.html b/docs/_modules/dragonfly_energy/opendss/colorobj.html new file mode 100644 index 00000000..6e70d8e0 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/colorobj.html @@ -0,0 +1,1287 @@ + + + + + + + dragonfly_energy.opendss.colorobj — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.colorobj

+"""Module for coloring OpenDSS geometry with attributes."""
+from ladybug_geometry.geometry2d import Point2D
+from ladybug_geometry.geometry3d import Point3D
+from ladybug.datacollection import HourlyContinuousCollection, \
+    HourlyDiscontinuousCollection
+from ladybug.graphic import GraphicContainer
+from ladybug.legend import LegendParameters
+from honeybee.typing import int_in_range
+from honeybee.colorobj import _ColorObject
+
+from .network import ElectricalNetwork
+
+
+
+[docs] +class ColorNetwork(_ColorObject): + """Object for visualizing ElectricalNetwork attributes. + + Args: + network: An ElectricalNetwork object, which will be colored with the attribute. + attr_name: A text string of an attribute that the input network equipment has. + This can have '.' that separate the nested attributes from one another. + For example, 'properties.kva' for the kva rating of transformers. + legend_parameters: An optional LegendParameter object to change the display + of the results (Default: None). + + Properties: + * network + * attr_name + * legend_parameters + * attr_name_end + * attributes + * attributes_unique + * attributes_original + * geometries + * graphic_container + * min_point + * max_point + """ + __slots__ = ('_network', '_geometries') + + def __init__(self, network, attr_name, legend_parameters=None): + """Initialize ColorNetwork.""" + assert isinstance(network, ElectricalNetwork), 'Expected ElectricalNetwork for' \ + ' ColorNetwork. Got {}.'.format(type(network)) + self._network = network + all_obj = (network.substation,) + network.transformers + network.connectors + self._geometries = tuple(obj.geometry for obj in all_obj) + self._min_point, self._max_point = _calculate_min_max(all_obj) + + # assign the legend parameters of this object + self.legend_parameters = legend_parameters + + # get the attributes of the input equipment + self._process_attribute_name(attr_name) + self._process_attributes(all_obj) + + @property + def network(self): + """Get the ElectricalNetwork associated with this object.""" + return self._network + + @property + def geometries(self): + """A tuple of Polygon2D, Polyline2D and LineSegment2D aligned with attributes. + """ + return self._geometries + + @property + def min_point(self): + """Get a Point3D for the minimum of the box around the objects.""" + return Point3D(self._min_point.x, self._min_point.y, 0) + + @property + def max_point(self): + """Get a Point3D for the maximum of the box around the objects.""" + return Point3D(self._max_point.x, self._max_point.y, 0) + + def __repr__(self): + """Color ElectricalNetwork representation.""" + return 'Color Network: {} [{}]'.format( + self.network.display_name, self.attr_name_end)
+ + + +
+[docs] +class ColorNetworkResults(object): + """Class for coloring ElectricalNetwork geometry with simulation results. + + Args: + data_collections: An array of data collections of the same data type, + which will be used to color the network with simulation results. Data + collections should all have headers with metadata dictionaries with 'type' + and 'name' keys. These keys will be used to match the data in the collections + to the input electrical network. + network: An ElectricalNetwork object, which will be colored with the attribute. + legend_parameters: An optional LegendParameter object to change the display + of the ColorNetworkResults (Default: None). + attribute: Text to note the attribute of the data collections with which the + network geometry should be colored. Typical values are max, min, average, + median, or total. This input can also be an integer (greater than or equal + to 0) to select a specific step of the data collections for which result + values will be generated. (Default: "Max" to color with the peak value). + + Properties: + * data_collections + * network + * legend_parameters + * attribute + * matched_geometries + * matched_data + * matched_values + * graphic_container + * title_text + * data_type_text + * data_type + * unit + * analysis_period + * min_point + * max_point + """ + __slots__ = ( + '_data_collections', '_network', '_legend_parameters', '_attribute', + '_matched_geometries', '_matched_data', '_matched_values', + '_base_collection', '_base_type', '_base_unit', '_min_point', '_max_point') + + def __init__(self, data_collections, network, + legend_parameters=None, attribute='max'): + """Initialize ColorNetworkResults.""" + # check the input collections + accept_cols = (HourlyContinuousCollection, HourlyDiscontinuousCollection) + try: + data_collections = list(data_collections) + except TypeError: + raise TypeError('Input data_collections must be an array. Got {}.'.format( + type(data_collections))) + assert len(data_collections) > 0, \ + 'ColorNetworkResults must have at least one data_collection.' + for i, coll in enumerate(data_collections): + assert isinstance(coll, accept_cols), 'Expected hourly ' \ + 'data collection for ColorNetworkResults. Got {}.'.format(type(coll)) + self._base_collection = data_collections[0] + self._base_type = self._base_collection.header.data_type + self._base_unit = self._base_collection.header.unit + for coll in data_collections[1:]: + assert coll.header.unit == self._base_unit, \ + 'ColorNetworkResults data_collections must all have matching units. ' \ + '{} != {}.'.format(coll.header.unit, self._base_unit) + assert len(coll.values) == len(self._base_collection.values), \ + 'ColorNetworkResults data_collections must be aligned with one another' \ + '.{} != {}'.format(len(coll.values), len(self._base_collection.values)) + self._data_collections = data_collections + + # process the input electrical network + assert isinstance(network, ElectricalNetwork), 'Expected ElectricalNetwork for' \ + ' ColorNetworkResults. Got {}.'.format(type(network)) + self._network = network + all_obj = (network.substation,) + network.transformers + network.connectors + self._min_point, self._max_point = _calculate_min_max(all_obj) + geo_dict = {obj.identifier.lower().replace(':', ''): obj.geometry + for obj in all_obj} + self._matched_geometries, self._matched_data = [], [] + for dat in self._data_collections: + try: + self._matched_geometries.append( + geo_dict[dat.header.metadata['name'].replace(':', '')]) + self._matched_data.append(dat) + except KeyError: # data could not be matched + pass + + # assign the other properties of this object + self.legend_parameters = legend_parameters + self.attribute = attribute + + @property + def data_collections(self): + """Get a tuple of data collections assigned to this object.""" + return tuple(self._data_collections) + + @property + def network(self): + """Get the ElectricalNetwork associated with this object.""" + return self._network + + @property + def geometries(self): + """A tuple of Polygon2D, Polyline2D and LineSegment2D aligned with attributes. + """ + return self._geometries + + @property + def legend_parameters(self): + """Get or set the legend parameters.""" + return self._legend_parameters + + @legend_parameters.setter + def legend_parameters(self, value): + if value is not None: + assert isinstance(value, LegendParameters), \ + 'Expected LegendParameters. Got {}.'.format(type(value)) + self._legend_parameters = value.duplicate() + else: + self._legend_parameters = LegendParameters() + + @property + def attribute(self): + """Get or set text for the data attribute or an integer a specific data step.""" + return self._attribute + + @attribute.setter + def attribute(self, value): + if not hasattr(self._base_collection, value): + value = int_in_range( + value, 0, len(self._base_collection) - 1, 'simulation step') + self._attribute = value + + @property + def matched_geometries(self): + """Get a tuple of geometries that were matched with the data collections.""" + return tuple(self._matched_geometries) + + @property + def matched_data(self): + """Get a tuple of data collections aligned with the matched_geometries.""" + return tuple(self._matched_data) + + @property + def matched_values(self): + """Get an array of numbers that correspond to the matched_geometries. + + These values are derived from the data_collections but they will be + averaged/totaled or for a specific time step depending on the + other inputs to this object. + """ + if isinstance(self.attribute, int): # specific index from all collections + return tuple(data[self._attribute] for data in self._matched_data) + else: # data collection property + return tuple(getattr(data, self._attribute) + for data in self._matched_data) + + @property + def graphic_container(self): + """Get a ladybug GraphicContainer that relates to this object. + + The GraphicContainer possesses almost all things needed to visualize the + object including the legend, value_colors, lower_title_location, + upper_title_location, etc. + """ + return GraphicContainer( + self.matched_values, self.min_point, self.max_point, + self.legend_parameters, self.data_type, str(self.unit)) + + @property + def title_text(self): + """Text string for the title of the object.""" + d_type_text = self.data_type + if isinstance(self.attribute, int): # specific index from all collections + time_text = self.time_interval_text(self.attribute) + else: # average or total the data + time_text = str(self.analysis_period).split('@')[0] + d_type_text = '{} {}'.format(self.attribute.capitalize(), d_type_text) + return '{}\n{}'.format('{} ({})'.format(d_type_text, self.unit), time_text) + + @property + def data_type(self): + """Text for the data type.""" + return self._base_type + + @property + def unit(self): + """The unit of this object's data collections.""" + return self._base_unit + + @property + def analysis_period(self): + """The analysis_period of this object's data collections.""" + return self._base_collection.header.analysis_period + + @property + def min_point(self): + """Get a Point3D for the minimum of the box around the objects.""" + return Point3D(self._min_point.x, self._min_point.y, 0) + + @property + def max_point(self): + """Get a Point3D for the maximum of the box around the objects.""" + return Point3D(self._max_point.x, self._max_point.y, 0) + +
+[docs] + def time_interval_text(self, simulation_step): + """Get text for a specific time simulation_step of the data collections. + + Args: + simulation_step: An integer for the step of simulation for which + text should be generated. + """ + return str(self._base_collection.datetimes[simulation_step])
+ + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __repr__(self): + """Color Network representation.""" + return 'Color Network results: {} [{} results]'.format( + self.network.display_name, len(self._data_collections))
+ + + +def _calculate_min_max(net_objs): + """Calculate maximum and minimum Point3D for a set of objects.""" + st_rm_min, st_rm_max = net_objs[0].geometry.min, net_objs[0].geometry.max + min_pt = [st_rm_min.x, st_rm_min.y] + max_pt = [st_rm_max.x, st_rm_max.y] + + for obj in net_objs[1:]: + rm_min, rm_max = obj.geometry.min, obj.geometry.max + if rm_min.x < min_pt[0]: + min_pt[0] = rm_min.x + if rm_min.y < min_pt[1]: + min_pt[1] = rm_min.y + if rm_max.x > max_pt[0]: + max_pt[0] = rm_max.x + if rm_max.y > max_pt[1]: + max_pt[1] = rm_max.y + + return Point2D(min_pt[0], min_pt[1]), Point2D(max_pt[0], max_pt[1]) +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/connector.html b/docs/_modules/dragonfly_energy/opendss/connector.html new file mode 100644 index 00000000..9509d7d1 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/connector.html @@ -0,0 +1,1171 @@ + + + + + + + dragonfly_energy.opendss.connector — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.connector

+# coding=utf-8
+"""Electrical connector in OpenDSS."""
+from __future__ import division
+
+from .._base import _GeometryBase
+from .powerline import PowerLine
+
+from ladybug_geometry.geometry2d.line import LineSegment2D
+from ladybug_geometry.geometry2d.polyline import Polyline2D
+from dragonfly.projection import polygon_to_lon_lat
+
+
+
+[docs] +class ElectricalConnector(_GeometryBase): + """Represents an electrical connector carrying wires in OpenDSS. + + Args: + identifier: Text string for a unique electrical connector ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + geometry: A LineSegment2D or Polyline2D representing the geometry of the + electrical connector. + power_line: A PowerLine object representing the wires carried along the + electrical connector and their arrangement. + + Properties: + * identifier + * display_name + * geometry + * power_line + * phase_count + * nominal_voltage + """ + __slots__ = ('_power_line',) + + def __init__(self, identifier, geometry, power_line): + """Initialize ElectricalConnector.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, (LineSegment2D, Polyline2D)), 'Expected ' \ + 'ladybug_geometry LineSegment2D or Polyline2D. Got {}'.format(type(geometry)) + self._geometry = geometry + self.power_line = power_line + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an ElectricalConnector from a dictionary. + + Args: + data: A dictionary representation of an ElectricalConnector object. + """ + # check the type of dictionary + assert data['type'] == 'ElectricalConnector', 'Expected ElectricalConnector ' \ + 'dictionary. Got {}.'.format(data['type']) + power_line = PowerLine.from_dict(data['power_line']) + geo = LineSegment2D.from_dict(data['geometry']) \ + if data['geometry']['type'] == 'LineSegment2D' \ + else Polyline2D.from_dict(data['geometry']) + con = cls(data['identifier'], geo, power_line) + if 'display_name' in data and data['display_name'] is not None: + con.display_name = data['display_name'] + return con
+ + +
+[docs] + @classmethod + def from_dict_abridged(cls, data, power_lines): + """Initialize an ElectricalConnector from an abridged dictionary. + + Args: + data: A ElectricalConnectorAbridged dictionary. + power_lines: A dictionary with identifiers of PowerLines as keys and Python + PowerLine objects as values. + """ + assert data['type'] == 'ElectricalConnectorAbridged', \ + 'Expected ElectricalConnectorAbridged. Got {}.'.format(data['type']) + try: + power_line = power_lines[data['power_line']] + except KeyError as e: + raise ValueError('Failed to find "{}" in power lines.'.format(e)) + geo = LineSegment2D.from_dict(data['geometry']) \ + if data['geometry']['type'] == 'LineSegment2D' \ + else Polyline2D.from_dict(data['geometry']) + con = cls(data['identifier'], geo, power_line) + if 'display_name' in data and data['display_name'] is not None: + con.display_name = data['display_name'] + return con
+ + +
+[docs] + @classmethod + def from_rnm_geojson_dict( + cls, data, origin_lon_lat, conversion_factors, power_lines): + """Get an ElectricalConnector from a dictionary as it appears in an RNM GeoJSON. + + Args: + data: A GeoJSON dictionary representation of an ElectricalConnector feature. + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + power_lines: A dictionary with identifiers of PowerLines as keys and Python + PowerLine objects as values. + """ + geo = cls._geojson_coordinates_to_line2d( + data['geometry']['coordinates'], origin_lon_lat, conversion_factors) + try: + power_line = power_lines[data['properties']['Equip']] + except KeyError as e: + raise ValueError('Failed to find "{}" in power lines.'.format(e)) + return cls(data['properties']['Code'], geo, power_line)
+ + + @property + def geometry(self): + """Get a LineSegment2D or Polyline2D representing the electrical connector.""" + return self._geometry + + @property + def power_line(self): + """Get or set the PowerLine object carried along the electrical connector.""" + return self._power_line + + @power_line.setter + def power_line(self, value): + assert isinstance(value, PowerLine), 'Expected PowerLine object' \ + ' for electrical connector power_line. Got {}.'.format(type(value)) + value.lock() # lock to avoid editing + self._power_line = value + + @property + def phase_count(self): + """Get an integer for the number of phases this connector supports.""" + return self._power_line.phase_count + + @property + def nominal_voltage(self): + """Get an integer for the nominal voltage of this connector.""" + return self._power_line.nominal_voltage + +
+[docs] + def to_dict(self, abridged=False): + """ElectricalConnector dictionary representation. + + Args: + abridged: Boolean to note whether the full dictionary describing the + object should be returned (False) or just an abridged version (True), + which only specifies the identifier of the power line. (Default: False). + """ + base = {'type': 'ElectricalConnector'} if not \ + abridged else {'type': 'ElectricalConnectorAbridged'} + base['identifier'] = self.identifier + base['geometry'] = self.geometry.to_dict() + base['power_line'] = self.power_line.identifier if abridged \ + else self.power_line.to_dict() + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, start_id, end_id, origin_lon_lat, conversion_factors): + """Get ElectricalConnector dictionary as it appears in an URBANopt geoJSON. + + Args: + start_id: Identifier of the junction at the start of the wire. + end_id: Identifier of the junction at the end of the wire. + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + if isinstance(self.geometry, LineSegment2D): + pts = [(pt.x, pt.y) for pt in (self.geometry.p1, self.geometry.p2)] + else: # it's a polyline + pts = [(pt.x, pt.y) for pt in self.geometry.vertices] + coords = polygon_to_lon_lat(pts, origin_lon_lat, conversion_factors) + return { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'type': 'ElectricalConnector', + 'startJunctionId': start_id, + 'endJunctionId': end_id, + 'total_length': round(self.geometry.length, 2), + 'connector_type': 'Wire', + 'electrical_catalog_name': self.power_line.identifier, + 'name': self.display_name + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': coords + } + }
+ + + def __copy__(self): + new_con = ElectricalConnector(self.identifier, self.geometry, self.power_line) + new_con._display_name = self._display_name + return new_con + + def __repr__(self): + return 'ElectricalConnector: {}, [{} wires]'.format( + self.display_name, self.power_line.wire_count)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/junction.html b/docs/_modules/dragonfly_energy/opendss/junction.html new file mode 100644 index 00000000..75d348d5 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/junction.html @@ -0,0 +1,1070 @@ + + + + + + + dragonfly_energy.opendss.junction — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.junction

+# coding=utf-8
+"""Electrical junction in OpenDSS."""
+from __future__ import division
+
+from .._base import _GeometryBase
+
+from ladybug_geometry.geometry2d.pointvector import Point2D
+from honeybee.typing import valid_ep_string
+from dragonfly.projection import polygon_to_lon_lat
+
+
+
+[docs] +class ElectricalJunction(_GeometryBase): + """Represents an electrical junction connecting two or more objects in OpenDSS. + + Args: + identifier: Text string for a unique electrical junction ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + geometry: A LineSegment2D or Polyline2D representing the geometry of the + electrical junction. + system_identifier: An optional text string for the identifier of a district + system object associated with the junction. District system objects + include Transformers and Substations. (Default: None). + building_identifier: An optional text string for the identifier of a Building + object associated with the junction. (Default: None). + + Properties: + * identifier + * display_name + * geometry + * system_identifier + * building_identifier + """ + __slots__ = ('_system_identifier', '_building_identifier') + + def __init__(self, identifier, geometry, system_identifier=None, + building_identifier=None): + """Initialize ElectricalJunction.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, Point2D), 'Expected ladybug_geometry ' \ + 'Point2D for ElectricalJunction. Got {}'.format(type(geometry)) + self._geometry = geometry + self.system_identifier = system_identifier + self.building_identifier = building_identifier + + @property + def geometry(self): + """Get a Point2D representing the ElectricalJunction.""" + return self._geometry + + @property + def system_identifier(self): + """Get or set a text string for the ID of a Transformer or Substation.""" + return self._system_identifier + + @system_identifier.setter + def system_identifier(self, value): + self._system_identifier = valid_ep_string(value, 'system_identifier') \ + if value is not None else None + + @property + def building_identifier(self): + """Get or set a text string for the ID of a dragonfly Building.""" + return self._building_identifier + + @building_identifier.setter + def building_identifier(self, value): + self._building_identifier = valid_ep_string(value, 'building_identifier') \ + if value is not None else None + +
+[docs] + def to_geojson_dict(self, origin_lon_lat, conversion_factors): + """Get an ElectricalJunction dictionary as it appears in an URBANopt geoJSON. + + Args: + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + pt = (self.geometry.x, self.geometry.y) + coord = polygon_to_lon_lat([pt], origin_lon_lat, conversion_factors)[0] + geo_dict = { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'type': 'ElectricalJunction' + }, + 'geometry': { + 'type': 'Point', + 'coordinates': coord + } + } + if self._system_identifier is not None: + geo_dict['properties']['DSId'] = self._system_identifier + if self._building_identifier is not None: + geo_dict['properties']['buildingId'] = self._building_identifier + return geo_dict
+ + + def __copy__(self): + new_jct = ElectricalJunction( + self.identifier, self.geometry, self._system_identifier, + self._building_identifier) + new_jct._display_name = self._display_name + return new_jct + + def __repr__(self): + return 'ElectricalJunction: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/lib/powerlines.html b/docs/_modules/dragonfly_energy/opendss/lib/powerlines.html new file mode 100644 index 00000000..18f07ae2 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/lib/powerlines.html @@ -0,0 +1,992 @@ + + + + + + + dragonfly_energy.opendss.lib.powerlines — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.lib.powerlines

+"""Library of power lines that come standard with dragonfly."""
+import os
+import json
+
+from ..powerline import PowerLine
+from .wires import _wires
+
+
+# load the  defaults
+_power_lines = {}
+_data_path = os.path.join(os.path.dirname(__file__), 'extended_catalog.json')
+with open(_data_path) as json_file:
+    _all_data = json.load(json_file)['LINES']
+    _default_data = _all_data[1]['#Interurban Zone A:'] + \
+        _all_data[2]['#Urban-Overhead'] + _all_data[3]['#Urban-Underground']
+for _t_dict in _default_data:
+    _t_obj = PowerLine.from_electrical_database_dict(_t_dict, _wires)
+    _t_obj.lock()
+    _power_lines[_t_dict['Name']] = _t_obj
+
+POWER_LINES = tuple(_power_lines.keys())
+
+
+
+[docs] +def power_line_by_identifier(power_line_identifier): + """Get power line properties from the library given the identifier. + + Args: + power_line_identifier: A text string for the identifier of the power line. + """ + try: + return _power_lines[power_line_identifier] + except KeyError: + raise ValueError( + '"{}" was not found in the power line library.'.format( + power_line_identifier))
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/lib/transformers.html b/docs/_modules/dragonfly_energy/opendss/lib/transformers.html new file mode 100644 index 00000000..ccedc909 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/lib/transformers.html @@ -0,0 +1,993 @@ + + + + + + + dragonfly_energy.opendss.lib.transformers — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.lib.transformers

+"""Library of transformer properties that come standard with dragonfly."""
+from ..transformerprop import TransformerProperties
+
+import os
+import json
+
+# load the  defaults
+_transformers = {}
+_data_path = os.path.join(os.path.dirname(__file__), 'extended_catalog.json')
+with open(_data_path) as json_file:
+    _all_data = json.load(json_file)['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS']
+    for dat in reversed(_all_data):  # the last interurban one has transformers
+        if '#Interurban:' in dat:
+            _default_data = dat['#Interurban:']
+            break
+for _t_dict in _default_data:
+    _t_obj = TransformerProperties.from_electrical_database_dict(_t_dict)
+    _t_obj.lock()
+    _transformers[_t_dict['Name']] = _t_obj
+
+TRANSFORMER_PROPERTIES = tuple(_transformers.keys())
+
+
+
+[docs] +def transformer_prop_by_identifier(transformer_identifier): + """Get transformer properties from the library given the identifier. + + Args: + transformer_identifier: A text string for the identifier of the transformer + properties. + """ + try: + return _transformers[transformer_identifier] + except KeyError: + raise ValueError( + '"{}" was not found in the transformer property library.'.format( + transformer_identifier))
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/lib/wires.html b/docs/_modules/dragonfly_energy/opendss/lib/wires.html new file mode 100644 index 00000000..5b1324c7 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/lib/wires.html @@ -0,0 +1,987 @@ + + + + + + + dragonfly_energy.opendss.lib.wires — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.lib.wires

+"""Library of wires that come standard with dragonfly."""
+import os
+import json
+from ..wire import Wire
+
+
+# load the  defaults
+_wires = {}
+_data_path = os.path.join(os.path.dirname(__file__), 'extended_catalog.json')
+with open(_data_path) as json_file:
+    _default_data = json.load(json_file)['WIRES']['WIRES CATALOG']
+for _t_dict in _default_data:
+    _t_obj = Wire.from_electrical_database_dict(_t_dict)
+    _t_obj.lock()
+    _wires[_t_dict['nameclass']] = _t_obj
+
+WIRES = tuple(_wires.keys())
+
+
+
+[docs] +def wire_by_identifier(wire_identifier): + """Get wire properties from the library given the identifier. + + Args: + wire_identifier: A text string for the identifier of the wire. + """ + try: + return _wires[wire_identifier] + except KeyError: + raise ValueError( + '"{}" was not found in the wire library.'.format(wire_identifier))
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/network.html b/docs/_modules/dragonfly_energy/opendss/network.html new file mode 100644 index 00000000..a2ac2162 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/network.html @@ -0,0 +1,1805 @@ + + + + + + + dragonfly_energy.opendss.network — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.network

+# coding=utf-8
+"""Electrical network in OpenDSS."""
+import os
+import uuid
+import json
+
+from ladybug_geometry.geometry2d import Point2D, Polygon2D
+from ladybug.location import Location
+from honeybee.typing import valid_ep_string
+from honeybee.units import conversion_factor_to_meters
+from dragonfly.projection import meters_to_long_lat_factors, \
+    origin_long_lat_from_location
+
+from .substation import Substation
+from .transformer import Transformer
+from .connector import ElectricalConnector
+from .junction import ElectricalJunction
+from .road import Road
+from .transformerprop import TransformerProperties
+from .powerline import PowerLine
+from .wire import Wire
+from .lib.powerlines import power_line_by_identifier
+
+
+
+[docs] +class ElectricalNetwork(object): + """Represents an electrical network in OpenDSS. + + This includes a substation, transformers, and all electrical connectors needed + to connect these objects to Dragonfly Buildings. + + Args: + identifier: Text string for a unique electrical network ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + substation: A Substation object representing the electrical substation + supplying the network with electricity. + transformers: An array of Transformer objects that are included within the + electrical network. Generally, there should always be a transformer + somewhere between the substation and a given building. + connectors: An array of ElectricalConnector objects that are included + within the electrical network. In order for a given connector to be + valid within the network, each end of the connector must touch either + another connector or a building footprint/transformer/substation. In + order for the network as a whole to be valid, all Buildings and + Transformers must be connected back to the Substation via connectors. + + Properties: + * identifier + * display_name + * substation + * transformers + * connectors + * transformer_properties + * power_lines + * wires + """ + __slots__ = ('_identifier', '_display_name', '_substation', + '_transformers', '_connectors') + + def __init__(self, identifier, substation, transformers, connectors): + """Initialize ElectricalNetwork.""" + self.identifier = identifier + self._display_name = None + self.substation = substation + self.transformers = transformers + self.connectors = connectors + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an ElectricalNetwork from a dictionary. + + Args: + data: A dictionary representation of an ElectricalNetwork object. + """ + # check the type of dictionary + assert data['type'] == 'ElectricalNetwork', 'Expected ElectricalNetwork ' \ + 'dictionary. Got {}.'.format(data['type']) + # re-serialize transformer properties and wires + t_props = {tp['identifier']: TransformerProperties.from_dict(tp) + for tp in data['transformer_properties']} + wires = {w['identifier']: Wire.from_dict(w) for w in data['wires']} + power_lines = {pl['identifier']: PowerLine.from_dict_abridged(pl, wires) + for pl in data['power_lines']} + # re-serialize geometry objects + substation = Substation.from_dict(data['substation']) + transformers = [Transformer.from_dict_abridged(trans, t_props) + for trans in data['transformers']] + conns = [ElectricalConnector.from_dict_abridged(c, power_lines) + for c in data['connectors']] + net = cls(data['identifier'], substation, transformers, conns) + if 'display_name' in data and data['display_name'] is not None: + net.display_name = data['display_name'] + return net
+ + +
+[docs] + @classmethod + def from_rnm_geojson( + cls, geojson_file_path, location=None, point=None, units='Meters'): + """Get an ElectricalNetwork from a dictionary as it appears in an RNM GeoJSON. + + Args: + geojson_file_path: Text for the full path to the geojson file to load + as a ElectricalNetwork. + location: An optional ladybug location object with longitude and + latitude data defining the origin of the geojson file. If None, + an attempt will be made to sense the location from the project + point in the GeoJSON (if it exists). If nothing is found, the + origin is autocalcualted as the bottom-left corner of the bounding + box of all building footprints in the geojson file. (Default: None). + point: A ladybug_geometry Point2D for where the location object exists + within the space of a scene. The coordinates of this point are + expected to be in the units input. If None, an attempt will be + made to sense the CAD coordinates from the GeoJSON if they + exist. If not found, they will default to (0, 0). + units: Text for the units system in which the model geometry + exists. Default: 'Meters'. Choose from the following: + + * Meters + * Millimeters + * Feet + * Inches + * Centimeters + + Note that this method assumes the point coordinates are in the + same units. + """ + # parse the geoJSON into a dictionary + with open(geojson_file_path, 'r') as fp: + data = json.load(fp) + + # extract the CAD coordinates and location from the GeoJSON if they exist + if 'project' in data: + prd = data['project'] + if 'latitude' in prd and 'longitude' in prd and location is None: + location = Location(latitude=prd['latitude'], longitude=prd['longitude']) + if 'cad_coordinates' in prd and point is None: + point = Point2D(*prd['cad_coordinates']) + if point is None: # just use the world origin if no point was found + point = Point2D(0, 0) + + # Get the list of substation, transformer and electrical connector data + transf_data, connector_data, subst_data = [], [], None + for obj_data in data['features']: + if 'type' in obj_data['properties']: + if obj_data['properties']['type'] == 'Line': + connector_data.append(obj_data) + elif obj_data['properties']['type'] == 'DistribTransf': + transf_data.append(obj_data) + elif 'Substation' in obj_data['properties']['type']: + subst_data = obj_data + + # if model units is not Meters, convert non-meter user inputs to meters + scale_to_meters = conversion_factor_to_meters(units) + if units != 'Meters': + point = point.scale(scale_to_meters) + + # Get long and lat in the geojson that correspond to the model origin (point). + # If location is None, derive coordinates from the geojson geometry. + if location is None: + point_lon_lat = cls._bottom_left_coordinate_from_geojson(connector_data) + location = Location(longitude=point_lon_lat[0], latitude=point_lon_lat[1]) + + # The model point may not be at (0, 0), so shift the longitude and latitude to + # get the equivalent point in longitude and latitude for (0, 0) in the model. + origin_lon_lat = origin_long_lat_from_location(location, point) + _convert_facs = meters_to_long_lat_factors(origin_lon_lat) + convert_facs = 1 / _convert_facs[0], 1 / _convert_facs[1] + + # extract the connectors + power_line_dict = { + p['properties']['Equip']: power_line_by_identifier(p['properties']['Equip']) + for p in connector_data + } + connectors = [] + for con_data in connector_data: + con_obj = ElectricalConnector.from_rnm_geojson_dict( + con_data, origin_lon_lat, convert_facs, power_line_dict) + connectors.append(con_obj) + # extract the transformers + transformers = [] + for trn_data in transf_data: + trn_obj = Transformer.from_rnm_geojson_dict( + trn_data, origin_lon_lat, convert_facs) + transformers.append(trn_obj) + # extract the substation + substation = Substation.from_rnm_geojson_dict( + subst_data, origin_lon_lat, convert_facs) + + # create the network and adjust for the units + base_name = os.path.basename(geojson_file_path) + net_id = base_name.replace('.json', '').replace('.geojson', '') + net = cls(net_id, substation, transformers, connectors) + if units != 'Meters': + net.convert_to_units(units) + return net
+ + + @staticmethod + def _bottom_left_coordinate_from_geojson(connector_data): + """Calculate the bottom-left bounding box coordinate from geojson coordinates. + + Args: + connector_data: a list of dictionaries containing geojson geometries that + represent electrical connectors. + + Returns: + The bottom-left most corner of the bounding box around the coordinates. + """ + xs, ys = [], [] + for conn in connector_data: + conn_coords = conn['geometry']['coordinates'] + if conn['geometry']['type'] == 'LineString': + for pt in conn_coords: + xs.append(pt[0]) + ys.append(pt[1]) + return min(xs), min(ys) + + @property + def identifier(self): + """Get or set the text string for unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, identifier): + self._identifier = valid_ep_string(identifier, 'identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def substation(self): + """Get or set a Substation object for the network's substation.""" + return self._substation + + @substation.setter + def substation(self, value): + assert isinstance(value, Substation), \ + 'Expected Substation for electrical network. Got {}.'.format(type(value)) + self._substation = value + + @property + def transformers(self): + """Get or set the list of Transformer objects within the network.""" + return self._transformers + + @transformers.setter + def transformers(self, values): + try: + if not isinstance(values, tuple): + values = tuple(values) + except TypeError: + raise TypeError('Expected list or tuple for electrical network ' + 'transformers. Got {}'.format(type(values))) + for t in values: + assert isinstance(t, Transformer), 'Expected Transformer object' \ + ' for electrical network transformers. Got {}.'.format(type(t)) + self._transformers = values + + @property + def connectors(self): + """Get or set the list of ElectricalConnector objects within the network.""" + return self._connectors + + @connectors.setter + def connectors(self, values): + try: + if not isinstance(values, tuple): + values = tuple(values) + except TypeError: + raise TypeError('Expected list or tuple for electrical network connectors. ' + 'Got {}'.format(type(values))) + for c in values: + assert isinstance(c, ElectricalConnector), 'Expected ElectricalConnector ' \ + 'object for electrical network connectors. Got {}.'.format(type(c)) + assert len(values) > 0, 'ElectricalNetwork must possess at least one connector.' + self._connectors = values + + @property + def transformer_properties(self): + """A list of all unique TransformerProperties in the network.""" + t_props = [] + for trans in self.transformers: + if not self._instance_in_array(trans.properties, t_props): + t_props.append(trans.properties) + return list(set(t_props)) # catch duplicated/equivalent objects + + @property + def power_lines(self): + """A list of all unique PowerLines in the network.""" + power_lines = [] + for connector in self.connectors: + if not self._instance_in_array(connector.power_line, power_lines): + power_lines.append(connector.power_line) + return list(set(power_lines)) # catch duplicated/equivalent objects + + @property + def wires(self): + """A list of all unique Wires in the network.""" + wires = [] + for p_line in self.power_lines: + for wire in p_line.wires: + if not self._instance_in_array(wire, wires): + wires.append(wire) + return list(set(wires)) # catch duplicated/equivalent objects + +
+[docs] + def junctions(self, tolerance=0.01): + """Get a list of ElectricalJunction objects for the unique network junctions. + + The resulting ElectricalJunction objects will be associated with Transformers + and Substations that they are in contact with across the network (within + the tolerance). However, they won't have any building_identifier associated + with them. The assign_junction_buildings method on this object can be used + to associate the junctions with an array of Dragonfly Buildings. + + Args: + tolerance: The minimum difference between the coordinate values of two + faces at which they can be considered centered adjacent. (Default: 0.01, + suitable for objects in meters). + + Returns: + A tuple with two items. + + - junctions - A list of lists of the unique ElectricalJunction objects + that exist across the network. + + - connector_junction_ids - A list of lists that align with the connectors + in the network. Each sub-list contains two string values for the junction + IDs for each of the start and end of each of the connectors. + """ + # loop through the connectors and find all unique junction objects + junctions, connector_junction_ids = [], [] + for connector in self.connectors: + verts = connector.geometry.vertices + end_pts, jct_ids = (verts[0], verts[-1]), [] + for jct_pt in end_pts: + for exist_jct in junctions: + if jct_pt.is_equivalent(exist_jct.geometry, tolerance): + jct_ids.append(exist_jct.identifier) + break + else: # we have found a new unique junction + new_jct_id = str(uuid.uuid4()) + junctions.append(ElectricalJunction(new_jct_id, jct_pt)) + jct_ids.append(new_jct_id) + connector_junction_ids.append(jct_ids) + + # loop through connectors and associate them with Transformers or the Substation + all_ds_objs = self.transformers + (self.substation,) + for jct in junctions: + for ds_obj in all_ds_objs: + if ds_obj.geometry.is_point_on_edge(jct.geometry, tolerance): + jct.system_identifier = ds_obj.identifier + break + return junctions, connector_junction_ids
+ + +
+[docs] + def move(self, moving_vec): + """Move this object along a vector. + + Args: + moving_vec: A ladybug_geometry Vector3D with the direction and distance + to move the object. + """ + self._substation.move(moving_vec) + for transformer in self.transformers: + transformer.move(moving_vec) + for connector in self.connectors: + connector.move(moving_vec)
+ + +
+[docs] + def rotate_xy(self, angle, origin): + """Rotate this object counterclockwise in the XY plane by a certain angle. + + Args: + angle: An angle in degrees. + origin: A ladybug_geometry Point3D for the origin around which the + object will be rotated. + """ + self._substation.rotate_xy(angle, origin) + for transformer in self.transformers: + transformer.rotate_xy(angle, origin) + for connector in self.connectors: + connector.rotate_xy(angle, origin)
+ + +
+[docs] + def reflect(self, plane): + """Reflect this object across a plane. + + Args: + plane: A ladybug_geometry Plane across which the object will be reflected. + """ + self._substation.reflect(plane) + for transformer in self.transformers: + transformer.reflect(plane) + for connector in self.connectors: + connector.reflect(plane)
+ + +
+[docs] + def scale(self, factor, origin=None): + """Scale this object by a factor from an origin point. + + Args: + factor: A number representing how much the object should be scaled. + origin: A ladybug_geometry Point3D representing the origin from which + to scale. If None, it will be scaled from the World origin (0, 0, 0). + """ + self._substation.scale(factor, origin) + for transformer in self.transformers: + transformer.scale(factor, origin) + for connector in self.connectors: + connector.scale(factor, origin)
+ + +
+[docs] + def convert_to_units(self, units='Meters', starting_units='Meters'): + """Convert all of the geometry in this ElectricalNetwork to certain units. + + Args: + units: Text for the units to which the Model geometry should be + converted. (Default: Meters). Choose from the following: + + * Meters + * Millimeters + * Feet + * Inches + * Centimeters + + starting_units: The starting units system of the network. (Default: Meters). + """ + if starting_units != units: + scale_fac1 = conversion_factor_to_meters(starting_units) + scale_fac2 = conversion_factor_to_meters(units) + scale_fac = scale_fac1 / scale_fac2 + self.scale(scale_fac)
+ + +
+[docs] + def to_dict(self): + """ElectricalNetwork dictionary representation.""" + base = {'type': 'ElectricalNetwork'} + base['identifier'] = self.identifier + base['substation'] = self.substation.to_dict() + base['transformers'] = [trans.to_dict(True) for trans in self.transformers] + base['connectors'] = [c.to_dict(True) for c in self.connectors] + base['transformer_properties'] = \ + [tp.to_dict() for tp in self.transformer_properties] + base['power_lines'] = [pl.to_dict(True) for pl in self.power_lines] + base['wires'] = [w.to_dict() for w in self.wires] + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, buildings, location, point=Point2D(0, 0), tolerance=0.01): + """Get ElectricalNetwork dictionary as it appears in an URBANopt geoJSON. + + The resulting dictionary array can be directly appended to the "features" + key of a base geoJSON dict in order to represent the network in the + geoJSON. Note that, in order to successfully run OpenDSS, you will also + have to write an electrical_database.json from this ElectricalNetwork using + the to_electrical_database_dict method. + + Args: + buildings: An array of Dragonfly Building objects in the same units + system as the ElectricalNetwork geometry. + location: A ladybug Location object possessing longitude and latitude data. + point: A ladybug_geometry Point2D for where the location object exists + within the space of a scene. The coordinates of this point are + expected to be in the units of this Model. (Default: (0, 0)). + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # get the conversion factors over to (longitude, latitude) + origin_lon_lat = origin_long_lat_from_location(location, point) + convert_facs = meters_to_long_lat_factors(origin_lon_lat) + + # translate substation and transformers into the geoJSON features list + features_list = [self.substation.to_geojson_dict(origin_lon_lat, convert_facs)] + for trans in self.transformers: + features_list.append(trans.to_geojson_dict(origin_lon_lat, convert_facs)) + + # translate connectors and junctions into the geoJSON features list + junctions, connector_jct_ids = self.junctions(tolerance) + for conn, jct_ids in zip(self.connectors, connector_jct_ids): + conn_dict = conn.to_geojson_dict( + jct_ids[0], jct_ids[1], origin_lon_lat, convert_facs) + features_list.append(conn_dict) + final_junctions = self.assign_junction_buildings(junctions, buildings, tolerance) + for jct in final_junctions: + features_list.append(jct.to_geojson_dict(origin_lon_lat, convert_facs)) + return features_list
+ + +
+[docs] + def to_electrical_database_dict(self): + """Get ElectricalNetwork as it should appear in the electrical_database.json.""" + catalog_json = os.path.join( + os.path.dirname(__file__), 'lib', 'extended_catalog.json') + with open(catalog_json) as inf: + base = json.load(inf) + base['SUBSTATIONS AND DISTRIBUTION TRANSFORMERS'] = [ + { + '#Interurban:': + [tp.to_electrical_database_dict() for tp in self.transformer_properties] + } + ] + base['LINES'][1]['#Interurban Zone A:'] = \ + [pl.to_electrical_database_dict() for pl in self.power_lines] + base['WIRES']['WIRES CATALOG'] = \ + [wire.to_electrical_database_dict() for wire in self.wires] + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + +
+[docs] + @staticmethod + def assign_junction_buildings(junctions, buildings, tolerance=0.01): + """Assign building_identifiers to a list of junctions using dragonfly Buildings. + + Junctions will be assigned to a given Building if they are touching + the footprint of that building in 2D space. + + Args: + junctions: An array of ElectricalJunction objects to be associated + with Dragonfly Buildings. + buildings: An array of Dragonfly Building objects in the same units + system as the ElectricalNetwork geometry. + tolerance: The minimum difference between the coordinate values of two + geometries at which they are considered co-located. (Default: 0.01, + suitable for objects in meters). + """ + # get the footprints of the Buildings in 2D space + footprint_2d, bldg_ids = [], [] + for bldg in buildings: + footprint = bldg.footprint(tolerance) + for face3d in footprint: + pts_2d = [Point2D(pt.x, pt.y) for pt in face3d.vertices] + footprint_2d.append(Polygon2D(pts_2d)) + bldg_ids.append(bldg.identifier) + + # loop through connectors and associate them with the Buildings + for jct in junctions: + for bldg_poly, bldg_id in zip(footprint_2d, bldg_ids): + if bldg_poly.is_point_on_edge(jct.geometry, tolerance): + jct.building_identifier = bldg_id + break + return junctions
+ + + @staticmethod + def _instance_in_array(object_instance, object_array): + """Check if a specific object instance is already in an array. + + This can be much faster than `if object_instance in object_array` + when you expect to be testing a lot of the same instance of an object for + inclusion in an array since the builtin method uses an == operator to + test inclusion. + """ + for val in object_array: + if val is object_instance: + return True + return False + + def __copy__(self): + new_net = ElectricalNetwork( + self.identifier, self.substation.duplicate(), + tuple(trans.duplicate() for trans in self.transformers), + tuple(conn.duplicate() for conn in self.connectors)) + new_net._display_name = self._display_name + return new_net + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'ElectricalNetwork: {}'.format(self.display_name)
+ + + +
+[docs] +class RoadNetwork(object): + """Represents a road network for RNM input. + + This includes a substation and roads that will be used to lay out the + road network. + + Args: + identifier: Text string for a unique road network ID. Must contain only + characters that are acceptable in RNM nad OpenDSS. This will be used to + identify the object across the exported geoJSON RNM, and OpenDSS files. + substation: A Substation object representing the road substation + supplying the network with electricity. + roads: An array of Road objects that are included within the road network. + + Properties: + * identifier + * display_name + * substation + * roads + """ + __slots__ = ('_identifier', '_display_name', '_substation', '_roads') + + def __init__(self, identifier, substation, roads): + """Initialize RoadNetwork.""" + self.identifier = identifier + self._display_name = None + self.substation = substation + self.roads = roads + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an RoadNetwork from a dictionary. + + Args: + data: A dictionary representation of an RoadNetwork object. + """ + # check the type of dictionary + assert data['type'] == 'RoadNetwork', 'Expected RoadNetwork ' \ + 'dictionary. Got {}.'.format(data['type']) + # re-serialize geometry objects + substation = Substation.from_dict(data['substation']) + roads = [Road.from_dict(r) for r in data['roads']] + net = cls(data['identifier'], substation, roads) + if 'display_name' in data and data['display_name'] is not None: + net.display_name = data['display_name'] + return net
+ + + @property + def identifier(self): + """Get or set the text string for unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, identifier): + self._identifier = valid_ep_string(identifier, 'identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def substation(self): + """Get or set a Substation object for the network's substation.""" + return self._substation + + @substation.setter + def substation(self, value): + assert isinstance(value, Substation), \ + 'Expected Substation for road network. Got {}.'.format(type(value)) + self._substation = value + + @property + def roads(self): + """Get or set the list of Road objects within the network.""" + return self._roads + + @roads.setter + def roads(self, values): + try: + if not isinstance(values, tuple): + values = tuple(values) + except TypeError: + raise TypeError('Expected list or tuple of roads. ' + 'Got {}'.format(type(values))) + for r in values: + assert isinstance(r, Road), 'Expected Road ' \ + 'object for road network. Got {}.'.format(type(r)) + assert len(values) > 0, 'RoadNetwork must possess at least one road.' + self._roads = values + +
+[docs] + def move(self, moving_vec): + """Move this object along a vector. + + Args: + moving_vec: A ladybug_geometry Vector3D with the direction and distance + to move the object. + """ + self._substation.move(moving_vec) + for road in self.roads: + road.move(moving_vec)
+ + +
+[docs] + def rotate_xy(self, angle, origin): + """Rotate this object counterclockwise in the XY plane by a certain angle. + + Args: + angle: An angle in degrees. + origin: A ladybug_geometry Point3D for the origin around which the + object will be rotated. + """ + self._substation.rotate_xy(angle, origin) + for road in self.roads: + road.rotate_xy(angle, origin)
+ + +
+[docs] + def reflect(self, plane): + """Reflect this object across a plane. + + Args: + plane: A ladybug_geometry Plane across which the object will be reflected. + """ + self._substation.reflect(plane) + for road in self.roads: + road.reflect(plane)
+ + +
+[docs] + def scale(self, factor, origin=None): + """Scale this object by a factor from an origin point. + + Args: + factor: A number representing how much the object should be scaled. + origin: A ladybug_geometry Point3D representing the origin from which + to scale. If None, it will be scaled from the World origin (0, 0, 0). + """ + self._substation.scale(factor, origin) + for road in self.roads: + road.scale(factor, origin)
+ + +
+[docs] + def to_dict(self): + """RoadNetwork dictionary representation.""" + base = {'type': 'RoadNetwork'} + base['identifier'] = self.identifier + base['substation'] = self.substation.to_dict() + base['roads'] = [r.to_dict() for r in self.roads] + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, location, point=Point2D(0, 0)): + """Get RoadNetwork dictionary as it appears in an URBANopt geoJSON. + + The resulting dictionary array can be directly appended to the "features" + key of a base geoJSON dict in order to represent the network in the + geoJSON. + + Args: + location: A ladybug Location object possessing longitude and latitude data. + point: A ladybug_geometry Point2D for where the location object exists + within the space of a scene. The coordinates of this point are + expected to be in the units of this Model. (Default: (0, 0)). + """ + # get the conversion factors over to (longitude, latitude) + origin_lon_lat = origin_long_lat_from_location(location, point) + convert_facs = meters_to_long_lat_factors(origin_lon_lat) + + # translate substation and transformers into the geoJSON features list + features_list = [self.substation.to_geojson_dict(origin_lon_lat, convert_facs)] + for road in self.roads: + features_list.append(road.to_geojson_dict(origin_lon_lat, convert_facs)) + return features_list
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_net = RoadNetwork( + self.identifier, self.substation.duplicate(), + tuple(r.duplicate() for r in self.roads)) + new_net._display_name = self._display_name + return new_net + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'RoadNetwork: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/powerline.html b/docs/_modules/dragonfly_energy/opendss/powerline.html new file mode 100644 index 00000000..67aee7e4 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/powerline.html @@ -0,0 +1,1349 @@ + + + + + + + dragonfly_energy.opendss.powerline — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.powerline

+# coding=utf-8
+"""PowerLine in OpenDSS."""
+from __future__ import division
+
+from honeybee._lockable import lockable
+from honeybee.typing import float_in_range, float_positive, int_positive, valid_ep_string
+
+from .wire import Wire
+
+
+
+[docs] +@lockable +class PowerLine(object): + """Represents the properties of a power line in OpenDSS. + + Args: + identifier: Text string for a unique wire property ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + wires: An array of Wire objects for the wires contained within the power line. + heights: An array of numbers that align with the wires and denote the height + of the wire above the ground in meters. Negative values indicate wires + below ground. + relative_xs: An array of numbers that align with the wires and denote the + X offset relative to the wire line geometry in meters. For convenience, + one of the conductors in a given wire is usually assigned the 0 position. + phases: An array of text values that align with the wires and denote the + phases of the wire. Must be one of the following values (A, B, C, N, S1, S2). + phase_count: An optional integer for the number of phases carried along the + power line. If None, it wil be inferred from the phases with only + phases A, B, C considered distinct from one another. (Default: None). + nominal_voltage: An optional number for the nominal voltage along the + power line. This is not required for OpenDSS simulation since this + value can be inferred from surrounding transformers and substations + but it it sometimes useful to assign a value directly to the + power line object. (Default: None). + + Properties: + * identifier + * display_name + * wires + * heights + * relative_xs + * phases + * phase_count + * nominal_voltage + * wire_count + * wire_ids + """ + __slots__ = ( + '_locked', '_display_name', '_identifier', '_wire_count', '_wires', '_heights', + '_relative_xs', '_phases', '_phase_count', '_nominal_voltage' + ) + VALID_PHASES = ('A', 'B', 'C', 'N', 'S1', 'S2') + + def __init__(self, identifier, wires, heights, relative_xs, phases, + phase_count=None, nominal_voltage=None): + """Initialize PowerLine""" + self._locked = False # unlocked by default + self._display_name = None + self.identifier = identifier + self._wire_count = len(wires) + assert self._wire_count > 0, 'PowerLine must possess at least one wire.' + self.wires = wires + self.heights = heights + self.relative_xs = relative_xs + self.phases = phases + self.phase_count = phase_count + self.nominal_voltage = nominal_voltage + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a PowerLine object from a dictionary. + + Args: + data: A dictionary representation of a PowerLine object in the format below. + + .. code-block:: python + + { + 'type': 'PowerLine', + 'identifier': '3P_OH_AL_ACSR_477kcmil_Hawk_12_47_0', # unique identifier + 'wires': [{}], # a list of wire definitions for the wires in the line + 'heights': [16], # height of the wire above the ground in meters + 'relative_x': [0], # number for the x offset from the wire line in meters + 'phases': ['A'] # text for the phases of the wire + } + """ + wires = [Wire.from_dict(wd) for wd in data['wires']] + pc = data['phase_count'] if 'phase_count' in data else None + vol = data['nominal_voltage'] if 'nominal_voltage' in data else None + p_line = cls(data['identifier'], wires, data['heights'], + data['relative_xs'], data['phases'], pc, vol) + if 'display_name' in data and data['display_name'] is not None: + p_line.display_name = data['display_name'] + return p_line
+ + +
+[docs] + @classmethod + def from_dict_abridged(cls, data, wires): + """Create a PowerLine object from a dictionary. + + Args: + data: A dictionary representation of a PowerLine object in the format below. + wires: A dictionary with identifiers of wires as keys and Python + wire objects as values. + + .. code-block:: python + + { + 'type': 'PowerLine', + 'identifier': '3P_OH_AL_ACSR_477kcmil_Hawk_12_47_0', # unique identifier + 'wires': [''], # a list of wire identifiers for the wires in the line + 'heights': [16], # height of the wire above the ground in meters + 'relative_x': [0], # number for the x offset from the wire line in meters + 'phases': ['A'] # text for the phases of the wire + } + """ + try: + wires = [wires[wd] for wd in data['wires']] + except KeyError as e: + raise ValueError('Failed to find {} in wires.'.format(e)) + pc = data['phase_count'] if 'phase_count' in data else None + vol = data['nominal_voltage'] if 'nominal_voltage' in data else None + p_line = cls(data['identifier'], wires, data['heights'], + data['relative_xs'], data['phases'], pc, vol) + if 'display_name' in data and data['display_name'] is not None: + p_line.display_name = data['display_name'] + return p_line
+ + +
+[docs] + @classmethod + def from_electrical_database_dict(cls, data, wires): + """Create a PowerLine from an dictionary as it appears in a database.json. + + Args: + data: A dictionary representation of a PowerLine object in the format below. + wires: A dictionary with identifiers of wires as keys and Python + wire objects as values. + + .. code-block:: python + + { + 'Name': '3P_OH_AL_ACSR_336kcmil_Merlin_12_47_0', # unique identifier + "Line geometry": [ + { + "wire": "OH ACSR 336kcmil", + "phase": "A", + "x (m)": 0.0, + "height (m)": 10 + }, + { + "wire": "OH ACSR 336kcmil", + "phase": "B", + "x (m)": 0.304, + "height (m)": 10 + } + ] + } + """ + try: + wire_ids = [w_i['wire'] for w_i in data['Line geometry']] + wire_objs = [wires[w_id] for w_id in wire_ids] + except KeyError as e: + raise ValueError('Failed to find {} in wires.'.format(e)) + heights = [w_i['height (m)'] for w_i in data['Line geometry']] + rel_xs = [w_i['x (m)'] for w_i in data['Line geometry']] + phases = [w_i['phase'] for w_i in data['Line geometry']] + pc = data['Nphases'] if 'Nphases' in data else None + vol = data['Voltage(kV)'] if 'Voltage(kV)' in data else None + return cls(data['Name'], wire_objs, heights, rel_xs, phases, pc, vol)
+ + + @property + def identifier(self): + """Get or set a text string for the unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, value): + self._identifier = valid_ep_string(value, 'power line identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def wires(self): + """Get or set an array of Wire objects for the phases of the wires.""" + return self._wires + + @wires.setter + def wires(self, value): + for w in value: + assert isinstance(w, Wire), 'Expected Wire object. Got {}.'.format(type(w)) + self._wires = tuple(value) + assert len(self._wires) == self._wire_count, \ + 'Number of wires cannot be changed from {} to {}. Initialize a new ' \ + 'PowerLine object to change the number of wires.'.format( + len(self._wires), self._wire_count) + + @property + def heights(self): + """Get or set an array of numbers for the heights above the ground in meters.""" + return self._heights + + @heights.setter + def heights(self, value): + self._heights = tuple( + float_in_range(h, input_name='power line height') for h in value) + assert len(self._heights) == self._wire_count, \ + 'Number of heights [{}] does not match the number of wires [{}].'.format( + len(self._heights), self._wire_count) + + @property + def relative_xs(self): + """Get or set a array of numbers for the X offset relative to the wire in meters. + """ + return self._relative_xs + + @relative_xs.setter + def relative_xs(self, value): + self._relative_xs = tuple( + float_in_range(x, input_name='power line relative_x') for x in value) + assert len(self._relative_xs) == self._wire_count, \ + 'Number of heights [{}] does not match the number of wires [{}].'.format( + len(self._relative_xs), self._wire_count) + + @property + def phases(self): + """Get or set an array of text for the phases of the wires.""" + return self._phases + + @phases.setter + def phases(self, value): + for p in value: + assert p in self.VALID_PHASES, 'Phase "{}" is not acceptable. ' \ + 'Choose from the following:\n{}'.format(p, '\n'.join(self.VALID_PHASES)) + self._phases = tuple(value) + assert len(self._phases) == self._wire_count, \ + 'Number of heights [{}] does not match the number of wires [{}].'.format( + len(self._phases), self._wire_count) + + @property + def phase_count(self): + """Get or set an integer for the number of phases carried along the line.""" + if self._phase_count is not None: + return self._phase_count + all_phases = [p for p in self._phases if p in ('A', 'B', 'C')] + return len(all_phases) if len(all_phases) != 0 else 1 + + @phase_count.setter + def phase_count(self, value): + if value is not None: + value = int_positive(value, 'power line phase count') + self._phase_count = value + + @property + def nominal_voltage(self): + """Get or set a number for nominal voltage of the power line in kiloVolts.""" + return self._nominal_voltage + + @nominal_voltage.setter + def nominal_voltage(self, value): + if value is not None: + value = float_positive(value, 'nominal voltage') + self._nominal_voltage = value + + @property + def wire_count(self): + """Get an integer for the number of wires in the power line.""" + return self._wire_count + + @property + def wire_ids(self): + """Get a list of wire identifiers in the power line.""" + return [wire.identifier for wire in self._wires] + +
+[docs] + def to_dict(self, abridged=False): + """Get PowerLine dictionary. + + Args: + abridged: Boolean to note whether the full dictionary describing the + object should be returned (False) or just an abridged version (True), + which only specifies the identifiers of wires. (Default: False). + """ + base = {'type': 'PowerLine'} if not abridged else {'type': 'PowerLineAbridged'} + base['identifier'] = self.identifier + base['wires'] = self.wire_ids if abridged else [w.to_dict() for w in self.wires] + base['heights'] = self.heights + base['relative_xs'] = self.relative_xs + base['phases'] = self.phases + if self._phase_count is not None: + base['phase_count'] = self._phase_count + if self._nominal_voltage is not None: + base['nominal_voltage'] = self._nominal_voltage + if self._display_name is not None: + base['display_name'] = self._display_name + return base
+ + +
+[docs] + def to_electrical_database_dict(self): + """Get Wire as it should appear in the URBANopt database.json.""" + base = {'Name': self.identifier} + line_geo = [] + all_props = zip(self.wires, self.heights, self.relative_xs, self.phases) + for wire, hgt, r_x, pha in all_props: + w_dict = { + 'wire': wire.identifier, + 'phase': pha, + 'x (m)': r_x, + 'height (m)': hgt + } + line_geo.append(w_dict) + base['Line geometry'] = line_geo + if self._phase_count is not None: + base['Nphases'] = self._phase_count + if self._nominal_voltage is not None: + base['Voltage(kV)'] = self._nominal_voltage + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_obj = PowerLine( + self.identifier, self.wires, self.heights, self.relative_xs, self.phases, + self._phase_count, self._nominal_voltage) + new_obj._display_name = self._display_name + return new_obj + + def __key(self): + """A tuple based on the object properties, useful for hashing.""" + return (self.identifier,) + tuple(hash(w) for w in self.wires) + \ + self.heights + self.relative_xs + self.phases + \ + (self._phase_count, self._nominal_voltage) + + def __hash__(self): + return hash(self.__key()) + + def __eq__(self, other): + return isinstance(other, PowerLine) and self.__key() == other.__key() + + def __ne__(self, other): + return not self.__eq__(other) + + def __len__(self): + return len(self._wires) + + def __getitem__(self, key): + return self._wires[key] + + def __iter__(self): + return iter(self._wires) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent wire.""" + return 'PowerLine: {}'.format(self.identifier)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/result.html b/docs/_modules/dragonfly_energy/opendss/result.html new file mode 100644 index 00000000..b1ae173a --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/result.html @@ -0,0 +1,1098 @@ + + + + + + + dragonfly_energy.opendss.result — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.result

+"""Module for parsing OpenDSS results to data collections."""
+import os
+import datetime
+
+from ladybug.datacollection import HourlyContinuousCollection
+from ladybug.header import Header
+from ladybug.analysisperiod import AnalysisPeriod
+from ladybug.datatype.generic import GenericType
+from ladybug.datatype.fraction import Fraction
+from ladybug.futil import csv_to_matrix
+
+
+
+[docs] +class OpenDSSResult(object): + """Object for parsing OpenDSS CSV result files into Ladybug DataCollections. + + Args: + file_paths: A list of file paths to CSV files that were generated by OpenDSS. + + Properties: + * file_paths + * factor_data + * condition_data + * peak_factors + * average_factors + """ + # data types for the various outputs from OpenDSS + IS_OVERLOADED = GenericType( + 'Is Overloaded', 'condition', + unit_descr={1: 'Overloaded', 0: 'Normal'}) + VOLTAGE_CONDITION = GenericType( + 'Voltage Condition', 'condition', + unit_descr={-1: 'Undervoltage', 0: 'Normal', 1: 'Overvoltage'}) + + def __init__(self, file_paths): + """Initialize OpenDSSResult""" + # check the file paths + for fp in file_paths: + assert os.path.isfile(fp), 'No file was found at {}'.format(fp) + assert fp.endswith('.csv'), \ + '{} is not an SQL file ending in .sql or .db.'.format(fp) + self._file_paths = tuple(file_paths) + + # parse the csv data + factors, condition = [], [] + for result_file in self._file_paths: + # parse the data and figure out the time series properties + data = csv_to_matrix(result_file) + data.pop(0) # remove the header from the CSV column + a_period = self._extract_analysis_period(data) + + # figure out the type of object to write into the metadata + obj_name = os.path.basename(result_file).replace('.csv', '') + if obj_name.startswith('Line.'): + obj_name = obj_name.replace('Line.', '') + obj_type = 'Electrical Connector Loading' + elif obj_name.startswith('Transformer.'): + obj_name = obj_name.replace('Transformer.', '') + obj_type = 'Transformer Loading' + else: + obj_type = 'Building Voltage' + metadata = {'type': obj_type, 'name': obj_name} + + # output the data collection of factors + result_vals = [float(data[i][1]) for i in range(len(data))] + header = Header(Fraction('Loading Factor'), 'fraction', a_period, metadata) + factors.append(HourlyContinuousCollection(header, result_vals)) + + # output the data collection of conditions + if len(data[0]) == 4: # building voltage results + cond_vals = [] + for row in data: + cond = 0 if row[2] == 'False' else 1 + if cond != 1 and row[3] == 'True\n': + cond = -1 + cond_vals.append(cond) + header = Header( + self.VOLTAGE_CONDITION, self.VOLTAGE_CONDITION.units[0], + a_period, metadata) + condition.append(HourlyContinuousCollection(header, cond_vals)) + else: # transformer or connector load + cond_vals = [] + for row in data: + cond = 0 if row[2] == 'False\n' else 1 + cond_vals.append(cond) + header = Header( + self.IS_OVERLOADED, self.IS_OVERLOADED.units[0], + a_period, metadata) + condition.append(HourlyContinuousCollection(header, cond_vals)) + self._factor_data = tuple(factors) + self._condition_data = tuple(condition) + + @property + def file_paths(self): + """Get a tuple of file paths to CSV files.""" + return self._file_paths + + @property + def factor_data(self): + """Get a tuple of loading factors associated with the input file paths.""" + return self._factor_data + + @property + def condition_data(self): + """Get a tuple of conditions associated with the input file paths.""" + return self._condition_data + + @property + def peak_factors(self): + """Get a list of numbers for the peak factors across the data.""" + return [dat.max for dat in self._factor_data] + + @property + def average_factors(self): + """Get a list of numbers for the average factors across the data.""" + return [dat.average for dat in self._factor_data] + + @staticmethod + def _date_str_to_datetime(date_str): + """Get a datetime object from a string.""" + return datetime.datetime.strptime(date_str, '%Y/%m/%d %H:%M:%S') + + @staticmethod + def _extract_analysis_period(data): + """Extract an AnalysisPeriod from CSV data.""" + dts = [OpenDSSResult._date_str_to_datetime(data[i][0]) for i in (0, 1, -2)] + timestep = int(3600 / (dts[1] - dts[0]).total_seconds()) + leap_year = True if dts[0].year % 4 == 0 else False + a_period = AnalysisPeriod( + dts[0].month, dts[0].day, 0, dts[-1].month, dts[-1].day, 23, + timestep=timestep, is_leap_year=leap_year) + return a_period + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __repr__(self): + return 'OpenDSS Result [{} files]'.format(len(self.file_paths))
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/road.html b/docs/_modules/dragonfly_energy/opendss/road.html new file mode 100644 index 00000000..c9e8b60f --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/road.html @@ -0,0 +1,1070 @@ + + + + + + + dragonfly_energy.opendss.road — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.road

+# coding=utf-8
+"""Road along which RNM will place electrical connectors."""
+from __future__ import division
+
+from ladybug_geometry.geometry2d.line import LineSegment2D
+from ladybug_geometry.geometry2d.polyline import Polyline2D
+from dragonfly.projection import polygon_to_lon_lat
+
+from .._base import _GeometryBase
+
+
+
+[docs] +class Road(_GeometryBase): + """Represents a road along which RNM will place electrical connectors. + + Args: + identifier: Text string for a unique road ID. Must contain only characters + that are acceptable in RNM and OpenDSS. This will be used to + identify the object across the exported geoJSON, RNM and OpenDSS files. + geometry: A LineSegment2D or Polyline2D representing the geometry of the road. + + Properties: + * identifier + * display_name + * geometry + """ + __slots__ = () + + def __init__(self, identifier, geometry): + """Initialize Road.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, (LineSegment2D, Polyline2D)), 'Expected ' \ + 'ladybug_geometry LineSegment2D or Polyline2D. Got {}'.format(type(geometry)) + self._geometry = geometry + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize a Road from a dictionary. + + Args: + data: A dictionary representation of a Road object. + """ + # check the type of dictionary + assert data['type'] == 'Road', 'Expected Road ' \ + 'dictionary. Got {}.'.format(data['type']) + geo = LineSegment2D.from_dict(data['geometry']) \ + if data['geometry']['type'] == 'LineSegment2D' \ + else Polyline2D.from_dict(data['geometry']) + road = cls(data['identifier'], geo) + if 'display_name' in data and data['display_name'] is not None: + road.display_name = data['display_name'] + return road
+ + + @property + def geometry(self): + """Get a LineSegment2D or Polyline2D representing the road geometry.""" + return self._geometry + +
+[docs] + def to_dict(self): + """Road dictionary representation.""" + base = {'type': 'Road'} + base['identifier'] = self.identifier + base['geometry'] = self.geometry.to_dict() + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, origin_lon_lat, conversion_factors): + """Get Road dictionary as it appears in an URBANopt geoJSON. + + Args: + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + if isinstance(self.geometry, LineSegment2D): + pts = [(pt.x, pt.y) for pt in (self.geometry.p1, self.geometry.p2)] + else: # it's a polyline + pts = [(pt.x, pt.y) for pt in self.geometry.vertices] + coords = polygon_to_lon_lat(pts, origin_lon_lat, conversion_factors) + return { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'type': 'Road', + 'total_length': round(self.geometry.length, 1), + 'name': self.display_name + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': coords + } + }
+ + + def __copy__(self): + new_con = Road(self.identifier, self.geometry) + new_con._display_name = self._display_name + return new_con + + def __repr__(self): + return 'Road: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/substation.html b/docs/_modules/dragonfly_energy/opendss/substation.html new file mode 100644 index 00000000..2e074b8d --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/substation.html @@ -0,0 +1,1088 @@ + + + + + + + dragonfly_energy.opendss.substation — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.substation

+# coding=utf-8
+"""Electrical substation in OpenDSS."""
+from .._base import _GeometryBase
+
+from ladybug_geometry.geometry2d.polygon import Polygon2D
+from dragonfly.projection import polygon_to_lon_lat
+
+
+
+[docs] +class Substation(_GeometryBase): + """Represents a substation in OpenDSS. + + Args: + identifier: Text string for a unique electrical substation ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + geometry: A Polygon2D representing the geometry of the electrical substation. + + Properties: + * identifier + * display_name + * geometry + """ + __slots__ = () + + def __init__(self, identifier, geometry): + """Initialize Substation.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, Polygon2D), 'Expected ladybug_geometry ' \ + 'Polygon2D for Substation geometry. Got {}'.format(type(geometry)) + self._geometry = geometry + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize a Substation from a dictionary. + + Args: + data: A dictionary representation of an Substation object. + """ + # check the type of dictionary + assert data['type'] == 'Substation', 'Expected Substation ' \ + 'dictionary. Got {}.'.format(data['type']) + geo = Polygon2D.from_dict(data['geometry']) + trans = cls(data['identifier'], geo) + if 'display_name' in data and data['display_name'] is not None: + trans.display_name = data['display_name'] + return trans
+ + +
+[docs] + @classmethod + def from_rnm_geojson_dict( + cls, data, origin_lon_lat, conversion_factors): + """Get a Substation from a dictionary as it appears in an RNM GeoJSON. + + Args: + data: A GeoJSON dictionary representation of an Substation feature. + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + geo = cls._geojson_coordinates_to_polygon2d( + data['geometry']['coordinates'], origin_lon_lat, conversion_factors) + return cls(data['properties']['Code'], geo)
+ + + @property + def geometry(self): + """Get a Polygon2D representing the substation.""" + return self._geometry + +
+[docs] + def to_dict(self): + """Substation dictionary representation.""" + base = {'type': 'Substation'} + base['identifier'] = self.identifier + base['geometry'] = self.geometry.to_dict() + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, origin_lon_lat, conversion_factors): + """Get Substation dictionary as it appears in an URBANopt geoJSON. + + Args: + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + pts = [(pt.x, pt.y) for pt in self.geometry.vertices] + coords = [polygon_to_lon_lat(pts, origin_lon_lat, conversion_factors)] + coords[0].append(coords[0][0]) + return { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'geometryType': 'Rectangle', + 'name': self.display_name, + 'type': 'District System', + 'footprint_area': round(self.geometry.area, 1), + 'footprint_perimeter': round(self.geometry.perimeter, 1), + 'district_system_type': 'Electrical Substation', + }, + 'geometry': { + 'type': 'Polygon', + 'coordinates': coords + } + }
+ + + def __copy__(self): + new_con = Substation(self.identifier, self.geometry) + new_con._display_name = self._display_name + return new_con + + def __repr__(self): + return 'Substation: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/transformer.html b/docs/_modules/dragonfly_energy/opendss/transformer.html new file mode 100644 index 00000000..5232a819 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/transformer.html @@ -0,0 +1,1166 @@ + + + + + + + dragonfly_energy.opendss.transformer — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.transformer

+# coding=utf-8
+"""Electrical transformer in OpenDSS."""
+from __future__ import division
+
+from .._base import _GeometryBase
+from .transformerprop import TransformerProperties
+
+from ladybug_geometry.geometry2d.polygon import Polygon2D
+from dragonfly.projection import polygon_to_lon_lat
+
+
+
+[docs] +class Transformer(_GeometryBase): + """Represents a transformer in OpenDSS. + + Args: + identifier: Text string for a unique electrical transformer ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + geometry: A Polygon2D representing the geometry of the electrical transformer. + properties: A TransformerProperties object representing the properties of + the electrical transformer. + + Properties: + * identifier + * display_name + * geometry + * properties + * phase_count + * nominal_voltage + """ + __slots__ = ('_properties',) + + def __init__(self, identifier, geometry, properties): + """Initialize Transformer.""" + _GeometryBase.__init__(self, identifier) # process the identifier + assert isinstance(geometry, Polygon2D), 'Expected ladybug_geometry ' \ + 'Polygon2D for Transformer geometry. Got {}'.format(type(geometry)) + self._geometry = geometry + self.properties = properties + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an Transformer from a dictionary. + + Args: + data: A dictionary representation of an Transformer object. + """ + assert data['type'] == 'Transformer', 'Expected Transformer ' \ + 'dictionary. Got {}.'.format(data['type']) + props = TransformerProperties.from_dict(data['properties']) + geo = Polygon2D.from_dict(data['geometry']) + trans = cls(data['identifier'], geo, props) + if 'display_name' in data and data['display_name'] is not None: + trans.display_name = data['display_name'] + return trans
+ + +
+[docs] + @classmethod + def from_dict_abridged(cls, data, properties): + """Initialize a Transformer from an abridged dictionary. + + Args: + data: A TransformerAbridged dictionary. + properties: A dictionary with identifiers of TransformerProperties + as keys and Python TransformerProperties objects as values. + """ + assert data['type'] == 'TransformerAbridged', \ + 'Expected TransformerAbridged. Got {}.'.format(data['type']) + try: + props = properties[data['properties']] + except KeyError as e: + raise ValueError('Failed to find "{}" in properties.'.format(e)) + geo = Polygon2D.from_dict(data['geometry']) + trans = cls(data['identifier'], geo, props) + if 'display_name' in data and data['display_name'] is not None: + trans.display_name = data['display_name'] + return trans
+ + +
+[docs] + @classmethod + def from_rnm_geojson_dict( + cls, data, origin_lon_lat, conversion_factors): + """Get a Transformer from a dictionary as it appears in an RNM GeoJSON. + + Args: + data: A GeoJSON dictionary representation of an Transformer feature. + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + geo = cls._geojson_coordinates_to_polygon2d( + data['geometry']['coordinates'], origin_lon_lat, conversion_factors) + prop = data['properties'] + kva_rating = 100 if 'Snom' not in prop else prop['Snom'] + resis = 0.1 if 'LV_R(ohm)' not in prop else prop['LV_R(ohm)'] + t_props = TransformerProperties( + prop['Code'], kva_rating, resistance=resis, + reactance=prop['Xsc(pu)'], phase_count=prop['Phases'], + high_voltage=prop['Vnom1'], low_voltage=prop['Vnom2']) + return cls(prop['Code'], geo, t_props)
+ + + @property + def geometry(self): + """Get a Polygon2D representing the transformer.""" + return self._geometry + + @property + def properties(self): + """Get or set a TransformerProperties object for the transformer.""" + return self._properties + + @properties.setter + def properties(self, value): + assert isinstance(value, TransformerProperties), \ + 'Expected TransformerProperties object' \ + ' for transformer properties. Got {}.'.format(type(value)) + value.lock() + self._properties = value + + @property + def phase_count(self): + """Get an integer for the number of phases this transformer supports.""" + return self._properties.phase_count + + @property + def nominal_voltage(self): + """Get an integer for the higher voltage of this transformer.""" + return self._properties.high_voltage + +
+[docs] + def to_dict(self, abridged=False): + """Transformer dictionary representation. + + Args: + abridged: Boolean to note whether the full dictionary describing the + object should be returned (False) or just an abridged version (True), + which only specifies the identifiers of properties. (Default: False). + """ + base = {'type': 'Transformer'} if not \ + abridged else {'type': 'TransformerAbridged'} + base['identifier'] = self.identifier + base['geometry'] = self.geometry.to_dict() + base['properties'] = self.properties.to_dict() if not abridged \ + else self.properties.identifier + if self._display_name is not None: + base['display_name'] = self.display_name + return base
+ + +
+[docs] + def to_geojson_dict(self, origin_lon_lat, conversion_factors): + """Get Transformer dictionary as it appears in an URBANopt geoJSON. + + Args: + origin_lon_lat: An array of two numbers in degrees. The first value + represents the longitude of the scene origin in degrees (between -180 + and +180). The second value represents latitude of the scene origin + in degrees (between -90 and +90). Note that the "scene origin" is the + (0, 0) coordinate in the 2D space of the input polygon. + conversion_factors: A tuple with two values used to translate between + meters and longitude, latitude. + """ + pts = [(pt.x, pt.y) for pt in self.geometry.vertices] + coords = [polygon_to_lon_lat(pts, origin_lon_lat, conversion_factors)] + coords[0].append(coords[0][0]) + base = { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'geometryType': 'Rectangle', + 'name': self.display_name, + 'type': 'District System', + 'footprint_area': round(self.geometry.area, 1), + 'footprint_perimeter': round(self.geometry.perimeter, 1), + 'district_system_type': 'Transformer', + 'electrical_catalog_name': self.properties.identifier + }, + 'geometry': { + 'type': 'Polygon', + 'coordinates': coords + } + } + if self.properties.phase_count == 3: + base['properties']['phases'] = ['A', 'B', 'C'] + elif self.properties.phase_count == 1: + base['properties']['phases'] = ['A'] + return base
+ + + def __copy__(self): + new_con = Transformer(self.identifier, self.geometry, self.properties) + new_con._display_name = self._display_name + return new_con + + def __repr__(self): + return 'Transformer: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/transformerprop.html b/docs/_modules/dragonfly_energy/opendss/transformerprop.html new file mode 100644 index 00000000..f310eeb6 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/transformerprop.html @@ -0,0 +1,1275 @@ + + + + + + + dragonfly_energy.opendss.transformerprop — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.transformerprop

+# coding=utf-8
+"""Transformer properties in OpenDSS."""
+from __future__ import division
+
+from honeybee._lockable import lockable
+from honeybee.typing import float_positive, int_in_range, valid_ep_string
+
+
+
+[docs] +@lockable +class TransformerProperties(object): + """Represents the properties of a Transformer in OpenDSS. + + Args: + identifier: Text string for a unique wire property ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + kva: Base kVA rating of the transformer in kiloVolt-Amps. + resistance: A number for the electrical resistance of the transformer + in ohms. (Default: 0.1). + reactance: A number for the electrical reactance of the transformer + in per-unit values (p.u. transf). (Default: 0.1). + phase_count: An integer for the number of phases in the transformer. Typically, + this is either 1 or 3. (Default: 3). + high_voltage: A number for the high voltage of the transformer in + kiloVolts. (Default: 13.2). + low_voltage: A number for the low voltage of the transformer in + kiloVolts. (Default: 0.48). + is_center_tap: Boolean for whether the transformer is center-tapped + (True) or not (False). (Default: False). + connection: Text for the type of internal connection in the transformer, either + "Wye-Wye", "Wye-Delta", "Delta-Wye" or "Delta-Delta". (Default: "Wye-Wye"). + + Properties: + * identifier + * display_name + * kva + * resistance + * reactance + * phase_count + * high_voltage + * low_voltage + * is_center_tap + * connection + """ + __slots__ = ( + '_locked', '_display_name', '_identifier', '_kva', '_resistance', '_reactance', + '_phase_count', '_high_voltage', '_low_voltage', '_is_center_tap', '_connection') + + VALID_CONNECTIONS = ('Wye-Wye', 'Wye-Delta', 'Delta-Wye', 'Delta-Delta') + + def __init__(self, identifier, kva, resistance=0.1, reactance=0.1, + phase_count=3, high_voltage=13.2, low_voltage=0.48, + is_center_tap=False, connection='Wye-Wye'): + """Initialize TransformerProperties""" + self._locked = False # unlocked by default + self._display_name = None + self.identifier = identifier + self.kva = kva + self.resistance = resistance + self.reactance = reactance + self.phase_count = phase_count + self.high_voltage = high_voltage + self.low_voltage = low_voltage + self.is_center_tap = is_center_tap + self.connection = connection + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a TransformerProperties object from a dictionary. + + Args: + data: A dictionary representation of a TransformerProperties object + in the format below. + + .. code-block:: python + + { + 'type': 'TransformerProperties', + 'identifier': 'Transformer--50KVA PM', # unique identifier + 'kva': 50, # kVA rating of the transformer + 'resistance': 0.1, # transformer resistance in ohms + 'reactance': 0.1, # transformer reactance in ohms + 'phase_count': 3, # number of transformer phases + 'high_voltage': 13.2, # transformer high voltage in kV + 'low_voltage': 0.48, # transformer low voltage in kV + 'is_center_tap': False, # boolean for if the transformer is center-tapped + 'connection': 'Wye-Wye' # text for the type of connection + } + """ + resistance = data['resistance'] if 'resistance' in data else 0.1 + reactance = data['reactance'] if 'reactance' in data else 0.1 + phases = data['phase_count'] if 'phase_count' in data else 3 + hv = data['high_voltage'] if 'high_voltage' in data else 13.2 + lv = data['low_voltage'] if 'low_voltage' in data else 0.48 + icp = data['is_center_tap'] if 'is_center_tap' in data else False + con = data['connection'] if 'connection' in data else 'Wye-Wye' + wire = cls(data['identifier'], data['kva'], resistance, reactance, + phases, hv, lv, icp, con) + if 'display_name' in data and data['display_name'] is not None: + wire.display_name = data['display_name'] + return wire
+ + +
+[docs] + @classmethod + def from_electrical_database_dict(cls, data): + """Create from a dictionary as it appears in electrical_database.json. + + Args: + data: A dictionary representation of an TransformerProperties object + in the format below. + + .. code-block:: python + + { + 'Name': 'MAT_1I_230_69', # unique identifier + 'Installed Power(kVA)': 50, # kVA rating of the transformer + 'Low-voltage-side short-circuit resistance (ohms)': 0.1, # resistance (ohms) + 'Reactance (p.u. transf)': 0.1, # transformer reactance in ohms + 'Nphases': 3, # number of transformer phases + 'Primary Voltage (kV)': 13.2, # transformer high voltage in kV + 'Secondary Voltage (kV)': 0.48, # transformer low voltage in kV + 'Centertap': False, # boolean for if the transformer is center-tapped + 'connection': 'Wye-Wye' # text for the type of connection + } + """ + return cls( + data['Name'], data['Installed Power(kVA)'], + data['Low-voltage-side short-circuit resistance (ohms)'], + data['Reactance (p.u. transf)'], data['Nphases'], + data['Primary Voltage (kV)'], data['Secondary Voltage (kV)'], + data['Centertap'], data['connection'])
+ + + @property + def identifier(self): + """Get or set a text string for the unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, value): + self._identifier = valid_ep_string(value, 'transformer properties identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def kva(self): + """Get or set a number for the base kVA rating of the transformer in kVA.""" + return self._kva + + @kva.setter + def kva(self, value): + self._kva = float_positive(value, input_name='kva') + + @property + def resistance(self): + """Get or set a number for the resistance of the transformer in ohms.""" + return self._resistance + + @resistance.setter + def resistance(self, value): + self._resistance = float_positive(value, 'resistance') + + @property + def reactance(self): + """Get or set a number for the reactance of the transformer in p.u. transf.""" + return self._reactance + + @reactance.setter + def reactance(self, value): + self._reactance = float_positive(value, 'reactance') + + @property + def phase_count(self): + """Get or set an integer for the number of phases of the transformer.""" + return self._phase_count + + @phase_count.setter + def phase_count(self, value): + self._phase_count = int_in_range(value, 1, 3, 'transformer phase count') + + @property + def high_voltage(self): + """Get or set a number for the high voltage of the transformer in kiloVolts.""" + return self._high_voltage + + @high_voltage.setter + def high_voltage(self, value): + self._high_voltage = float_positive(value, 'high voltage') + + @property + def low_voltage(self): + """Get or set a number for the low voltage of the transformer in kiloVolts.""" + return self._low_voltage + + @low_voltage.setter + def low_voltage(self, value): + self._low_voltage = float_positive(value, 'low voltage') + + @property + def is_center_tap(self): + """Get or set a boolean for whether the transformer is center-tapped.""" + return self._is_center_tap + + @is_center_tap.setter + def is_center_tap(self, value): + self._is_center_tap = bool(value) + + @property + def connection(self): + """Get or set text for the type of internal connection in the transformer.""" + return self._connection + + @connection.setter + def connection(self, value): + assert value in self.VALID_CONNECTIONS, 'Phase "{}" is not acceptable. ' \ + 'Choose from the following:\n{}'.format( + value, '\n'.join(self.VALID_CONNECTIONS)) + self._connection = value + +
+[docs] + def to_dict(self): + """Get TransformerProperties dictionary.""" + base = { + 'type': 'TransformerProperties', + 'identifier': self.identifier, + 'kva': self.kva, + 'resistance': self.resistance, + 'reactance': self.reactance, + 'phase_count': self.phase_count, + 'high_voltage': self.high_voltage, + 'low_voltage': self.low_voltage, + 'is_center_tap': self.is_center_tap, + 'connection': self.connection} + if self._display_name is not None: + base['display_name'] = self._display_name + return base
+ + +
+[docs] + def to_electrical_database_dict(self): + """Get Wire as it should appear in the URBANopt electrical_database.json.""" + return { + 'Name': self.identifier, + 'Type': 'I', + 'Voltage level': 'MV-LV', + 'Installed Power(kVA)': self.kva, + 'Guaranteed Power(kVA)': self.kva, + 'Low-voltage-side short-circuit resistance (ohms)': self.resistance, + 'Reactance (p.u. transf)': self.reactance, + 'Nphases': self.phase_count, + 'Primary Voltage (kV)': self.high_voltage, + 'Secondary Voltage (kV)': self.low_voltage, + 'Centertap': self.is_center_tap, + 'connection': self.connection + }
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_obj = TransformerProperties( + self.identifier, self.kva, self.resistance, self.reactance, self.phase_count, + self.high_voltage, self.low_voltage, self.is_center_tap, self.connection) + new_obj._display_name = self._display_name + return new_obj + + def __key(self): + """A tuple based on the object properties, useful for hashing.""" + return ( + self.identifier, self.kva, self.resistance, self.reactance, + self.phase_count, self.high_voltage, self.low_voltage, + self.is_center_tap, self.connection) + + def __hash__(self): + return hash(self.__key()) + + def __eq__(self, other): + return isinstance(other, TransformerProperties) and self.__key() == other.__key() + + def __ne__(self, other): + return not self.__eq__(other) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent TransformerProperties.""" + return 'TransformerProperties: {}'.format(self.identifier)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/opendss/wire.html b/docs/_modules/dragonfly_energy/opendss/wire.html new file mode 100644 index 00000000..63f7e827 --- /dev/null +++ b/docs/_modules/dragonfly_energy/opendss/wire.html @@ -0,0 +1,1480 @@ + + + + + + + dragonfly_energy.opendss.wire — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.opendss.wire

+# coding=utf-8
+"""Wire in OpenDSS."""
+from __future__ import division
+
+from honeybee._lockable import lockable
+from honeybee.typing import float_positive, int_positive, valid_ep_string
+
+
+
+[docs] +@lockable +class Wire(object): + """Represents the properties of a wire in OpenDSS. + + Args: + identifier: Text string for a unique wire property ID. Must contain only + characters that are acceptable in OpenDSS. This will be used to + identify the object across the exported geoJSON and OpenDSS files. + ampacity: A number for the ampacity of the wire in amps. (Default: 220). + geometrical_mean_radius: A number for the geometric mean of distances between + the strands of the conductor in millimeters. (Default: 3.9). + resistance: A number for the electrical resistance of the conductor in + ohms per kilometer of wire. (Default: 0.3937). + diameter: A number for the diameter of the wire in millimeters. (Default: 10). + voltage_level: Text to denote the level of voltage that the wire is designed to + carry. Choose from ("LV", "MV", "LV or MV"). (Default: "MV"). + wire_type: Text for the type of wire, denoting whether the wire is overhead + (OH) or underground (UG). Choose from ('OH', 'UG', 'UG concentric + neutral'). (Default: "OH"). + concentric_properties: A ConcentricProperties object to denote the concentric + neutral properties of the Wire. This must be specified when the wire_type + is "UG concentric neutral." (Default: None). + + Properties: + * identifier + * display_name + * ampacity + * geometrical_mean_radius + * resistance + * diameter + * voltage_level + * wire_type + * concentric_properties + """ + __slots__ = ( + '_locked', '_display_name', '_identifier', '_ampacity', + '_geometrical_mean_radius', '_resistance', '_diameter', + '_voltage_level', '_wire_type', '_concentric_properties' + ) + + VALID_VOLTAGE_LEVELS = ('LV', 'MV', 'LV or MV') + VALID_WIRE_TYPES = ('OH', 'UG', 'UG concentric neutral') + + def __init__(self, identifier, ampacity=220, geometrical_mean_radius=3.9, + resistance=0.3937, diameter=10, voltage_level='MV', + wire_type='OH', concentric_properties=None): + """Initialize Wire""" + self._locked = False # unlocked by default + self._display_name = None + self.identifier = identifier + self.ampacity = ampacity + self.geometrical_mean_radius = geometrical_mean_radius + self.resistance = resistance + self.diameter = diameter + self.voltage_level = voltage_level + self.concentric_properties = concentric_properties + self.wire_type = wire_type + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a Wire object from a dictionary. + + Args: + data: A dictionary representation of a Wire object in the format below. + + .. code-block:: python + + { + 'type': 'Wire', + 'identifier': 'OH AL 2/0 A', # unique wire property identifier + 'ampacity': 220, # ampacity of the wire in amps + 'geometrical_mean_radius': 3.9, # gmr in mm + 'resistance': 0.3937, # resistance of the wire in ohms/km + 'diameter': 10, # diameter of the wire in mm + 'voltage_level': 'LV or MV', # text for the voltage level + 'wire_type': 'OH' # text for the type of wire + } + """ + amp = data['ampacity'] if 'ampacity' in data else 220 + gmr = data['geometrical_mean_radius'] if 'geometrical_mean_radius' in data \ + else 3.9 + res = data['resistance'] if 'resistance' in data else 0.3937 + dim = data['diameter'] if 'diameter' in data else 10 + vl = data['voltage_level'] if 'voltage_level' in data else 'MV' + wt = data['wire_type'] if 'wire_type' in data else 'OH' + c_prop = ConcentricProperties.from_dict(data['concentric_properties']) \ + if 'concentric_properties' in data and \ + data['concentric_properties'] is not None else None + wire = cls(data['identifier'], amp, gmr, res, dim, vl, wt, c_prop) + if 'display_name' in data and data['display_name'] is not None: + wire.display_name = data['display_name'] + return wire
+ + +
+[docs] + @classmethod + def from_electrical_database_dict(cls, data): + """Create a Wire from an dictionary as it appears in electrical_database.json. + + Args: + data: A dictionary representation of a Wire object in the format below. + + .. code-block:: python + + { + 'nameclass': 'OH AL 2/0 A', # unique wire property identifier + 'ampacity (A)': 220, # ampacity of the wire in amps + 'gmr (mm)': 3.9, # gmr in meters + 'resistance (ohm/km)': 0.3937, # resistance of the wire in ohms/km + 'diameter (mm)': 10, # diameter of the wire in meters + 'voltage level': 'MV', + 'type': 'OH' + } + """ + c_prop = None + if data['type'] == 'UG concentric neutral': + c_prop = ConcentricProperties( + data['gmr neutral (mm)'], + data['resistance neutral (ohm/km)'], + data['concentric diameter neutral strand (mm)'], + data['concentric neutral outside diameter (mm)'], + data['# concentric neutral strands'] + ) + return cls( + data['nameclass'], data['ampacity (A)'], data['gmr (mm)'], + data['resistance (ohm/km)'], data['diameter (mm)'], + data['voltage level'], data['type'], c_prop)
+ + + @property + def identifier(self): + """Get or set a text string for the unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, value): + self._identifier = valid_ep_string(value, 'wire identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def ampacity(self): + """Get or set a number for the ampacity of the wire in amps.""" + return self._ampacity + + @ampacity.setter + def ampacity(self, value): + self._ampacity = float_positive(value, 'ampacity') + + @property + def geometrical_mean_radius(self): + """Get or set a number for the geometrical mean radius of the wire in mm.""" + return self._geometrical_mean_radius + + @geometrical_mean_radius.setter + def geometrical_mean_radius(self, value): + self._geometrical_mean_radius = float_positive(value, 'geometrical_mean_radius') + + @property + def resistance(self): + """Get or set a number for the resistance of the wire in ohms per km of wire. + """ + return self._resistance + + @resistance.setter + def resistance(self, value): + self._resistance = float_positive(value, 'resistance') + + @property + def diameter(self): + """Get or set a number for the diameter of the wire in mm.""" + return self._diameter + + @diameter.setter + def diameter(self, value): + self._diameter = float_positive(value, 'diameter') + + @property + def voltage_level(self): + """Get or set text for the voltage level of the wire. (LV, MV, LV or MV).""" + return self._voltage_level + + @voltage_level.setter + def voltage_level(self, value): + assert value in self.VALID_VOLTAGE_LEVELS, 'Voltage level "{}" is not ' \ + 'acceptable. Choose from:\n{}'.format(value, '\n'.join(self.VALID_PHASES)) + self._voltage_level = value + + @property + def wire_type(self): + """Get or set a text string for the type of wire.""" + return self._wire_type + + @wire_type.setter + def wire_type(self, value): + assert value in self.VALID_WIRE_TYPES, 'Wire type "{}" is not acceptable. ' \ + 'Choose from:\n{}'.format(value, '\n'.join(self.VALID_WIRE_TYPES)) + if value == 'UG concentric neutral': + assert self.concentric_properties is not None, 'Wire concentric_properties' \ + ' must be specified in order to use "UG concentric neutral."' + self._wire_type = value + + @property + def concentric_properties(self): + """Get or set an array of Wire objects for the phases of the wires.""" + return self._concentric_properties + + @concentric_properties.setter + def concentric_properties(self, value): + if value is not None: + assert isinstance(value, ConcentricProperties), \ + 'Expected Wire ConcentricProperties. Got {}.'.format(type(value)) + self._concentric_properties = value + +
+[docs] + def to_dict(self): + """Get Wire dictionary.""" + base = { + 'type': 'Wire', + 'identifier': self.identifier, + 'ampacity': self.ampacity, + 'geometrical_mean_radius': self.geometrical_mean_radius, + 'resistance': self.resistance, + 'diameter': self.diameter, + 'voltage_level': self.voltage_level, + 'wire_type': self.wire_type + } + if self.concentric_properties is not None: + base['concentric_properties'] = self.concentric_properties.to_dict() + if self._display_name is not None: + base['display_name'] = self._display_name + return base
+ + +
+[docs] + def to_electrical_database_dict(self): + """Get Wire as it should appear in the URBANopt electrical database.json.""" + base = { + 'nameclass': self.identifier, + 'ampacity (A)': self.ampacity, + 'gmr (mm)': self.geometrical_mean_radius, + 'resistance (ohm/km)': self.resistance, + 'diameter (mm)': self.diameter, + 'voltage level': self.voltage_level, + 'type': self.wire_type + } + if self.concentric_properties is not None: + c_prop = self.concentric_properties + base['gmr neutral (mm)'] = c_prop.geometrical_mean_radius + base['resistance neutral (ohm/km)'] = c_prop.resistance + base['concentric diameter neutral strand (mm)'] = \ + c_prop.concentric_strand_diameter + base['concentric neutral outside diameter (mm)'] = \ + c_prop.concentric_outside_diameter + base['# concentric neutral strands'] = c_prop.strand_count + return base
+ + +
+[docs] + def lock(self): + """The lock() method will also lock the concentric_properties.""" + self._locked = True + if self.concentric_properties is not None: + self.concentric_properties.lock()
+ + +
+[docs] + def unlock(self): + """The unlock() method will also unlock the concentric_properties.""" + self._locked = False + if self.concentric_properties is not None: + self.concentric_properties.unlock()
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_obj = Wire( + self.identifier, self.ampacity, self.geometrical_mean_radius, + self.resistance, self.diameter, self.voltage_level, self.wire_type) + new_obj._display_name = self._display_name + return new_obj + + def __key(self): + """A tuple based on the object properties, useful for hashing.""" + return ( + self.identifier, self.ampacity, self.geometrical_mean_radius, + self.resistance, self.diameter, self.voltage_level, self.wire_type) + + def __hash__(self): + return hash(self.__key()) + + def __eq__(self, other): + return isinstance(other, Wire) and self.__key() == other.__key() + + def __ne__(self, other): + return not self.__eq__(other) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent wire.""" + return 'Wire: {}'.format(self.identifier)
+ + + +
+[docs] +@lockable +class ConcentricProperties(object): + """Represents the concentric neutral properties of a wire in OpenDSS. + + Args: + geometrical_mean_radius: A number for the geometric mean of distances between + the neutral strands in millimeters. (Default: 0.8). + resistance: A number for the electrical resistance of the neutral strands in + ohms per kilometer of wire. (Default: 5.8). + concentric_strand_diameter: A number for the diameter of the neutral strand + in millimeters. (Default: 2). + concentric_outside_diameter:A number for the outside diameter of the neutral + strand in millimeters. (Default: 45). + strand_count: A positive integer for the number of concentric neutral + strands. (Default: 24) + + Properties: + * geometrical_mean_radius + * resistance + * concentric_strand_diameter + * concentric_outside_diameter + * strand_count + """ + __slots__ = ( + '_locked', '_geometrical_mean_radius', '_resistance', + '_concentric_strand_diameter', '_concentric_outside_diameter', '_strand_count' + ) + + def __init__( + self, geometrical_mean_radius=0.8, resistance=5.8, + concentric_strand_diameter=2, concentric_outside_diameter=45, + strand_count=24): + """Initialize ConcentricProperties""" + self._locked = False # unlocked by default + self.geometrical_mean_radius = geometrical_mean_radius + self.resistance = resistance + self.concentric_strand_diameter = concentric_strand_diameter + self.concentric_outside_diameter = concentric_outside_diameter + self.strand_count = strand_count + +
+[docs] + @classmethod + def from_dict(cls, data): + """Create a ConcentricProperties object from a dictionary. + + Args: + data: A dictionary representation of a ConcentricProperties object + in the format below. + + .. code-block:: python + + { + 'type': 'ConcentricProperties', + 'geometrical_mean_radius': 0.8, # gmr in mm + 'resistance': 5.8, # resistance of the wire in ohms/km + 'concentric_strand_diameter': 2, # diameter of the wire in mm + 'concentric_outside_diameter': 45, # outside diameter of the wire in mm + 'strand_count': 24 # integer for the number of strands + } + """ + gmr = data['geometrical_mean_radius'] if 'geometrical_mean_radius' in data \ + else 0.8 + res = data['resistance'] if 'resistance' in data else 5.8 + s_dim = data['concentric_strand_diameter'] if 'concentric_strand_diameter' \ + in data else 2 + o_dim = data['concentric_outside_diameter'] if 'concentric_outside_diameter' \ + in data else 45 + sc = data['strand_count'] if 'strand_count' in data else 24 + return cls(gmr, res, s_dim, o_dim, sc)
+ + + @property + def geometrical_mean_radius(self): + """Get or set a number for the geometrical mean radius of the strands in mm.""" + return self._geometrical_mean_radius + + @geometrical_mean_radius.setter + def geometrical_mean_radius(self, value): + self._geometrical_mean_radius = float_positive(value, 'geometrical_mean_radius') + + @property + def resistance(self): + """Get or set a number for the resistance of the strands in ohms per km of wire. + """ + return self._resistance + + @resistance.setter + def resistance(self, value): + self._resistance = float_positive(value, 'resistance') + + @property + def concentric_strand_diameter(self): + """Get or set a number for the diameter of the neutral strand in mm.""" + return self._concentric_strand_diameter + + @concentric_strand_diameter.setter + def concentric_strand_diameter(self, value): + self._concentric_strand_diameter = \ + float_positive(value, 'concentric_strand_diameter') + + @property + def concentric_outside_diameter(self): + """Get or set a number for the outside diameter of the neutral strand in mm.""" + return self._concentric_outside_diameter + + @concentric_outside_diameter.setter + def concentric_outside_diameter(self, value): + self._concentric_outside_diameter = \ + float_positive(value, 'concentric_outside_diameter') + + @property + def strand_count(self): + """Get or set a positive integer for the number of concentric neutral strands. + """ + return self._strand_count + + @strand_count.setter + def strand_count(self, value): + self._strand_count = int_positive(value, 'strand_count') + +
+[docs] + def to_dict(self): + """Get ConcentricProperties dictionary.""" + return { + 'type': 'ConcentricProperties', + 'geometrical_mean_radius': self.geometrical_mean_radius, + 'resistance': self.resistance, + 'concentric_strand_diameter': self.concentric_strand_diameter, + 'concentric_outside_diameter': self.concentric_outside_diameter, + 'strand_count': self.strand_count + }
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_obj = ConcentricProperties( + self.geometrical_mean_radius, self.resistance, + self.concentric_strand_diameter, self.concentric_outside_diameter, + self.strand_count) + return new_obj + + def __key(self): + """A tuple based on the object properties, useful for hashing.""" + return ( + self.geometrical_mean_radius, self.resistance, + self.concentric_strand_diameter, self.concentric_outside_diameter, + self.strand_count) + + def __hash__(self): + return hash(self.__key()) + + def __eq__(self, other): + return isinstance(other, Wire) and self.__key() == other.__key() + + def __ne__(self, other): + return not self.__eq__(other) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString method.""" + return self.__repr__()
+ + + def __repr__(self): + """Represent wire.""" + return 'ConcentricProperties: [{} strands]'.format(self.strand_count)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/properties/building.html b/docs/_modules/dragonfly_energy/properties/building.html new file mode 100644 index 00000000..272fe0af --- /dev/null +++ b/docs/_modules/dragonfly_energy/properties/building.html @@ -0,0 +1,1359 @@ + + + + + + + dragonfly_energy.properties.building — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.properties.building

+# coding=utf-8
+"""Building Energy Properties."""
+import os
+import json
+
+from honeybee_energy.config import folders
+from honeybee_energy.programtype import ProgramType
+from honeybee_energy.constructionset import ConstructionSet
+from honeybee_energy.hvac._base import _HVACSystem
+from honeybee_energy.hvac.idealair import IdealAirSystem
+from honeybee_energy.hvac import HVAC_TYPES_DICT
+from honeybee_energy.shw import SHWSystem
+
+from honeybee_energy.lib.constructionsets import generic_construction_set, \
+    construction_set_by_identifier
+from honeybee_energy.lib.programtypes import building_program_type_by_identifier
+
+
+
+[docs] +class BuildingEnergyProperties(object): + """Energy Properties for Dragonfly Building. + + Args: + host: A dragonfly_core Building object that hosts these properties. + construction_set: A honeybee ConstructionSet object to specify all + default constructions for the Faces of the Building. If None, the + Building will use the honeybee default construction set, which is not + representative of a particular building code or climate zone. + Default: None. + + Properties: + * host + * construction_set + """ + _HVAC_REGISTRY = None + _HVAC_TYPES_DICT = HVAC_TYPES_DICT + _VINTAGE_MAP = { + 'DOE Ref Pre-1980': ('pre_1980', 'DOE_Ref_Pre_1980'), + 'DOE Ref 1980-2004': ('1980_2004', 'DOE_Ref_1980_2004'), + '90.1-2004': ('2004', 'ASHRAE_2004'), + '90.1-2007': ('2007', 'ASHRAE_2007'), + '90.1-2010': ('2010', 'ASHRAE_2010'), + '90.1-2013': ('2013', 'ASHRAE_2013'), + '90.1-2016': ('2016', 'ASHRAE_2016'), + '90.1-2019': ('2019', 'ASHRAE_2019') + } + __slots__ = ('_host', '_construction_set') + + def __init__(self, host, construction_set=None): + """Initialize Building energy properties.""" + self._host = host + self.construction_set = construction_set + + @property + def host(self): + """Get the Building object hosting these properties.""" + return self._host + + @property + def construction_set(self): + """Get or set the Building ConstructionSet object. + + If not set, it will be the Honeybee default generic ConstructionSet. + """ + if self._construction_set is not None: # set by the user + return self._construction_set + else: + return generic_construction_set + + @construction_set.setter + def construction_set(self, value): + if value is not None: + assert isinstance(value, ConstructionSet), \ + 'Expected ConstructionSet. Got {}'.format(type(value)) + value.lock() # lock in case construction set has multiple references + self._construction_set = value + +
+[docs] + def averaged_program_type(self, identifier=None, timestep_resolution=1): + """Get a ProgramType that is averaged across all of the children Room2Ds. + + The weights used in the averaging process are the floor area weights and they + account for the multipliers on the child Story objects. + + Args: + identifier: A unique ID text string for the new averaged ProgramType. + Must be < 100 characters and not contain any EnergyPlus special + characters. This will be used to identify the object across a model + and in the exported IDF. If None, the resulting ProgramType will + use the identifier of the host Building. (Default: None) + timestep_resolution: An optional integer for the timestep resolution + at which the schedules will be averaged. Any schedule details + smaller than this timestep will be lost in the averaging process. + Default: 1. + """ + # get the default identifier of the ProgramType if None + identifier = identifier if identifier is not None else \ + '{}_Program'.format(self.host.identifier) + + # compute the floor area weights and programs + flr_areas = [] + program_types = [] + for story in self.host.unique_stories: + for room in story.room_2ds: + flr_areas.append(room.floor_area * story.multiplier) + program_types.append(room.properties.energy.program_type) + total_area = sum(flr_areas) + weights = [room_area / total_area for room_area in flr_areas] + + # compute the averaged program + return ProgramType.average( + identifier, program_types, weights, timestep_resolution)
+ + +
+[docs] + def set_all_room_2d_program_type(self, program_type): + """Set all of the children Room2Ds of this Building to have the same ProgramType. + + Args: + program_type: A ProgramType to assign to all children Room2Ds. + """ + assert isinstance(program_type, ProgramType), 'Expected ProgramType for ' \ + 'Building set_all_room_2d_program_type. Got {}'.format(type(program_type)) + for room_2d in self.host.unique_room_2ds: + room_2d.properties.energy.program_type = program_type
+ + +
+[docs] + def set_all_program_type_from_building_type(self, building_type): + """Set the children Room2Ds to have a program mix from a building_type. + + Args: + building_type: A text string for the type of building. This must appear + under the BUILDING_TYPES constant of the honeybee_energy.lib.programtypes + module to be successful. + """ + program = building_program_type_by_identifier(building_type) + self.set_all_room_2d_program_type(program)
+ + +
+[docs] + def set_all_room_2d_hvac(self, hvac, conditioned_only=True): + """Set all children Room2Ds of this Building to have the same HVAC system. + + Args: + hvac: An HVAC system with properties that will be assigned to all + children Room2Ds. + conditioned_only: Boolean to note whether the input hvac should only + be applied to rooms that are already conditioned. If False, the + hvac will be applied to all rooms. (Default: True). + """ + assert isinstance(hvac, _HVACSystem), 'Expected HVACSystem for Building.' \ + 'set_all_room_2d_hvac. Got {}'.format(type(hvac)) + + new_hvac = hvac.duplicate() + new_hvac._identifier = '{}_{}'.format(hvac.identifier, self.host.identifier) + for room_2d in self.host.unique_room_2ds: + if not conditioned_only or room_2d.properties.energy.is_conditioned: + room_2d.properties.energy.hvac = new_hvac
+ + +
+[docs] + def add_default_ideal_air(self): + """Add a default IdealAirSystem to all children Room2Ds of this Story. + + The identifier of the systems will be derived from the room identifiers. + """ + for room_2d in self.host.unique_room_2ds: + room_2d.properties.energy.add_default_ideal_air()
+ + +
+[docs] + def set_all_room_2d_shw(self, shw): + """Set all children Room2Ds of this Building to have the same SHW system. + + Args: + shw: A Service Hot Water (SHW) system with properties that will be + assigned to all children Room2Ds. + """ + assert isinstance(shw, SHWSystem), 'Expected SHWSystem for Building.' \ + 'set_all_room_2d_shw. Got {}'.format(type(shw)) + + new_shw = shw.duplicate() + new_shw._identifier = '{}_{}'.format(shw.identifier, self.host.identifier) + for room_2d in self.host.unique_room_2ds: + room_2d.properties.energy.shw = new_shw
+ + +
+[docs] + def diversify(self, occupancy_stdev=20, lighting_stdev=20, + electric_equip_stdev=20, gas_equip_stdev=20, hot_water_stdev=20, + infiltration_stdev=20, schedule_offset=1, timestep=1): + """Diversify the ProgramTypes assigned to this Building's Room2Ds. + + This method uses a random number generator and gaussian distribution to + generate loads that vary about the original "mean" programs. Note that the + randomly generated values can be set to something predictable by using the + native Python random.seed() method before running this method. + + In addition to diversifying load values, approximately 2/3 of the schedules + in the resulting Room2Ds will be offset from the mean by the input + schedule_offset (1/3 ahead and another 1/3 behind). + + Args: + occupancy_stdev: A number between 0 and 100 for the percent of the + occupancy people_per_area representing one standard deviation + of diversification from the mean. (Default 20 percent). + lighting_stdev: A number between 0 and 100 for the percent of the + lighting watts_per_area representing one standard deviation + of diversification from the mean. (Default 20 percent). + electric_equip_stdev: A number between 0 and 100 for the percent of the + electric equipment watts_per_area representing one standard deviation + of diversification from the mean. (Default 20 percent). + gas_equip_stdev: A number between 0 and 100 for the percent of the + gas equipment watts_per_area representing one standard deviation + of diversification from the mean. (Default 20 percent). + hot_water_stdev: A number between 0 and 100 for the percent of the + service hot water flow_per_area representing one standard deviation + of diversification from the mean. (Default 20 percent). + infiltration_stdev: A number between 0 and 100 for the percent of the + infiltration flow_per_exterior_area representing one standard deviation + of diversification from the mean. (Default 20 percent). + schedule_offset: A positive integer for the number of timesteps at which all + schedules of the resulting programs will be shifted - roughly 1/3 of + the programs ahead and another 1/3 behind. (Default: 1). + timestep: An integer for the number of timesteps per hour at which the + shifting is occurring. This must be a value between 1 and 60, which + is evenly divisible by 60. 1 indicates that each step is an hour + while 60 indicates that each step is a minute. (Default: 1). + """ + # build a dictionary with the unique ProgramTypes and their assigned rooms + program_dict = {} + for room_2d in self.host.unique_room_2ds: + p_type = room_2d.properties.energy.program_type + try: # see if we have already found the program + program_dict[p_type.identifier][1].append(room_2d) + except KeyError: # this is the firs time encountering the program + program_dict[p_type.identifier] = [p_type, [room_2d]] + + # loop through the dictionary and generate + assign diversified programs + for prog_list in program_dict.values(): + prog, rooms = prog_list[0], prog_list[1] + div_programs = prog.diversify( + len(rooms), occupancy_stdev, lighting_stdev, electric_equip_stdev, + gas_equip_stdev, hot_water_stdev, infiltration_stdev, + schedule_offset, timestep) + for room, d_prog in zip(rooms, div_programs): + room.properties.energy.program_type = d_prog
+ + +
+[docs] + @classmethod + def from_dict(cls, data, host): + """Create BuildingEnergyProperties from a dictionary. + + Note that the dictionary must be a non-abridged version for this + classmethod to work. + + Args: + data: A dictionary representation of BuildingEnergyProperties. + host: A Building object that hosts these properties. + """ + assert data['type'] == 'BuildingEnergyProperties', \ + 'Expected BuildingEnergyProperties. Got {}.'.format(data['type']) + + new_prop = cls(host) + if 'construction_set' in data and data['construction_set'] is not None: + new_prop.construction_set = \ + ConstructionSet.from_dict(data['construction_set']) + + return new_prop
+ + +
+[docs] + def apply_properties_from_dict(self, abridged_data, construction_sets): + """Apply properties from a BuildingEnergyPropertiesAbridged dictionary. + + Args: + abridged_data: A BuildingEnergyPropertiesAbridged dictionary (typically + coming from a Model). + construction_sets: A dictionary of ConstructionSets with identifiers + of the sets as keys, which will be used to re-assign construction_sets. + """ + if 'construction_set' in abridged_data and \ + abridged_data['construction_set'] is not None: + self.construction_set = construction_sets[abridged_data['construction_set']]
+ + +
+[docs] + def to_dict(self, abridged=False): + """Return Building energy properties as a dictionary. + + Args: + abridged: Boolean for whether the full dictionary of the Building should + be written (False) or just the identifier of the the individual + properties (True). Default: False. + """ + base = {'energy': {}} + base['energy']['type'] = 'BuildingEnergyProperties' if not \ + abridged else 'BuildingEnergyPropertiesAbridged' + + # write the ConstructionSet into the dictionary + if self._construction_set is not None: + base['energy']['construction_set'] = \ + self._construction_set.identifier if abridged else \ + self._construction_set.to_dict() + + return base
+ + +
+[docs] + def apply_properties_from_geojson_dict(self, data): + """Apply properties from a geoJSON dictionary. + + Args: + data: A dictionary representation of a geoJSON feature properties. + Specifically, this should be the "properties" key describing + a Polygon or MultiPolygon object. + """ + # determine the vintage of the building + template = data['template'] if 'template' in data else '90.1-2019' + vintage = self._VINTAGE_MAP[template] + + # assign the construction set based on climate zone + if 'climate_zone' in data: + zone_int = str(data['climate_zone'])[0] + c_set_id = '{}::{}{}::SteelFramed'.format( + vintage[0], 'ClimateZone', zone_int) + try: + self.construction_set = construction_set_by_identifier(c_set_id) + except ValueError: # not a construction set in the library + pass + + # assign the program based on the building type + if 'building_type' in data: + try: + self.set_all_program_type_from_building_type(data['building_type']) + except ValueError: # not a building type in the library + pass + + # assign the HVAC based on the name + if 'system_type' in data: + hvac_instance = self._hvac_from_long_name(data['system_type'], vintage[1]) + if hvac_instance is not None: + self.set_all_room_2d_hvac(hvac_instance, False)
+ + +
+[docs] + def duplicate(self, new_host=None): + """Get a copy of this object. + + new_host: A new Building object that hosts these properties. + If None, the properties will be duplicated with the same host. + """ + _host = new_host or self._host + return BuildingEnergyProperties(_host, self._construction_set)
+ + + def _hvac_from_long_name(self, hvac_long_name, vintage='ASHRAE_2013'): + """Get an HVAC class instance from it's long name (as found in a geoJSON).""" + hvac_reg = None + if BuildingEnergyProperties._HVAC_REGISTRY is None: + ext_folder = [f for f in folders.standards_extension_folders + if f.endswith('honeybee_energy_standards')] + if len(ext_folder) == 1: + hvac_reg = os.path.join(ext_folder[0], 'hvac_registry.json') + if os.path.isfile(hvac_reg): + with open(hvac_reg, 'r') as f: + BuildingEnergyProperties._HVAC_REGISTRY = json.load(f) + BuildingEnergyProperties._HVAC_REGISTRY['Ideal Air System'] = \ + 'IdealAirSystem' + hvac_reg = BuildingEnergyProperties._HVAC_REGISTRY + if hvac_reg is not None: + try: + hvac_class = self._HVAC_TYPES_DICT[hvac_reg[hvac_long_name]] + except KeyError: # HVAC type is not in the library + return None + if hvac_class is IdealAirSystem: + return IdealAirSystem('{} {}'.format(self.host.identifier, 'Ideal Air')) + else: # assume it is an HVAC template + hvac_id = '{} {}'.format(self.host.identifier, hvac_reg[hvac_long_name]) + return hvac_class(hvac_id, vintage, hvac_reg[hvac_long_name]) + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'Building Energy Properties: {}'.format(self.host.identifier)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/properties/context.html b/docs/_modules/dragonfly_energy/properties/context.html new file mode 100644 index 00000000..d61eb82a --- /dev/null +++ b/docs/_modules/dragonfly_energy/properties/context.html @@ -0,0 +1,1168 @@ + + + + + + + dragonfly_energy.properties.context — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.properties.context

+# coding=utf-8
+"""Context Shade Energy Properties."""
+from honeybee.shade import Shade
+from honeybee_energy.construction.shade import ShadeConstruction
+from honeybee_energy.schedule.ruleset import ScheduleRuleset
+from honeybee_energy.schedule.fixedinterval import ScheduleFixedInterval
+from honeybee_energy.properties.shade import ShadeEnergyProperties
+from honeybee_energy.properties.shademesh import ShadeMeshEnergyProperties
+
+from honeybee_energy.lib.constructions import generic_context
+
+
+
+[docs] +class ContextShadeEnergyProperties(object): + """Energy Properties for Dragonfly ContextShade. + + Args: + host_shade: A dragonfly_core ContextShade object that hosts these properties. + construction: An optional ShadeConstruction object to set the reflectance + and specularity of the ContextShade. The default is a completely + diffuse construction with 0.2 visible and solar reflectance. + transmittance_schedule: An optional schedule to set the transmittance + of the shade, which can vary throughout the day or year. Default + is a completely opaque object. + + Properties: + * host + * construction + * transmittance_schedule + * is_construction_set_by_user + """ + + __slots__ = ('_host', '_construction', '_transmittance_schedule') + + def __init__(self, host_shade, construction=None, transmittance_schedule=None): + """Initialize ContextShade energy properties.""" + self._host = host_shade + self.construction = construction + self.transmittance_schedule = transmittance_schedule + + @property + def host(self): + """Get the Shade object hosting these properties.""" + return self._host + + @property + def construction(self): + """Get or set a ShadeConstruction for the context shade.""" + if self._construction: # set by user + return self._construction + else: + return generic_context + + @construction.setter + def construction(self, value): + if value is not None: + assert isinstance(value, ShadeConstruction), \ + 'Expected ShadeConstruction. Got {}.'.format(type(value)) + value.lock() # lock editing in case construction has multiple references + self._construction = value + + @property + def transmittance_schedule(self): + """Get or set the transmittance schedule of the shade.""" + return self._transmittance_schedule + + @transmittance_schedule.setter + def transmittance_schedule(self, value): + if value is not None: + assert isinstance(value, (ScheduleRuleset, ScheduleFixedInterval)), \ + 'Expected schedule for shade transmittance schedule. ' \ + 'Got {}.'.format(type(value)) + if value.schedule_type_limit is not None: + assert value.schedule_type_limit.unit == 'fraction', 'Transmittance ' \ + 'schedule should be fractional [Dimensionless]. Got a schedule ' \ + 'of unit_type [{}].'.format(value.schedule_type_limit.unit_type) + value.lock() # lock editing in case schedule has multiple references + self._transmittance_schedule = value + + @property + def is_construction_set_by_user(self): + """Boolean noting if construction is user-set.""" + return self._construction is not None + +
+[docs] + @classmethod + def from_dict(cls, data, host): + """Create ContextShadeEnergyProperties from a dictionary. + + Note that the dictionary must be a non-abridged version for this + classmethod to work. + + Args: + data: A dictionary representation of ContextShadeEnergyProperties. + host: A ContextShade object that hosts these properties. + """ + assert data['type'] == 'ContextShadeEnergyProperties', \ + 'Expected ContextShadeEnergyProperties. Got {}.'.format(data['type']) + + new_prop = cls(host) + if 'construction' in data and data['construction'] is not None: + new_prop.construction = ShadeConstruction.from_dict(data['construction']) + if 'transmittance_schedule' in data and \ + data['transmittance_schedule'] is not None: + sch_dict = data['transmittance_schedule'] + if sch_dict['type'] == 'ScheduleRuleset': + new_prop.transmittance_schedule = \ + ScheduleRuleset.from_dict(data['transmittance_schedule']) + elif sch_dict['type'] == 'ScheduleFixedInterval': + new_prop.transmittance_schedule = \ + ScheduleFixedInterval.from_dict(data['transmittance_schedule']) + else: + raise ValueError( + 'Expected non-abridged Schedule dictionary for ContextShade ' + 'transmittance_schedule. Got {}.'.format(sch_dict['type'])) + return new_prop
+ + +
+[docs] + def apply_properties_from_dict(self, abridged_data, constructions, schedules): + """Apply properties from a ContextShadeEnergyPropertiesAbridged dictionary. + + Args: + abridged_data: A ContextShadeEnergyPropertiesAbridged dictionary (typically + coming from a Model). + constructions: A dictionary of constructions with constructions identifiers + as keys, which will be used to re-assign constructions. + schedules: A dictionary of schedules with schedule identifiers as keys, + which will be used to re-assign schedules. + """ + if 'construction' in abridged_data and abridged_data['construction'] is not None: + self.construction = constructions[abridged_data['construction']] + if 'transmittance_schedule' in abridged_data and \ + abridged_data['transmittance_schedule'] is not None: + self.transmittance_schedule = \ + schedules[abridged_data['transmittance_schedule']]
+ + +
+[docs] + def to_dict(self, abridged=False): + """Return energy properties as a dictionary. + + Args: + abridged: Boolean to note whether the full dictionary describing the + object should be returned (False) or just an abridged version (True). + Default: False. + """ + base = {'energy': {}} + base['energy']['type'] = 'ContextShadeEnergyProperties' if not \ + abridged else 'ContextShadeEnergyPropertiesAbridged' + if self._construction is not None: + base['energy']['construction'] = self._construction.identifier if abridged \ + else self._construction.to_dict() + if self.transmittance_schedule is not None: + base['energy']['transmittance_schedule'] = \ + self.transmittance_schedule.identifier if abridged else \ + self.transmittance_schedule.to_dict() + return base
+ + +
+[docs] + def to_honeybee(self, new_host): + """Get a honeybee version of this object. + + Args: + new_host: A honeybee-core Shade object that will host these properties. + """ + return ShadeEnergyProperties(new_host, self._construction, + self._transmittance_schedule) \ + if isinstance(new_host, Shade) else \ + ShadeMeshEnergyProperties(new_host, self._construction, + self._transmittance_schedule)
+ + +
+[docs] + def from_honeybee(self, hb_properties): + """Transfer energy attributes from a Honeybee Shade to Dragonfly ContextShade. + + Args: + hb_properties: The ShadeEnergyProperties of the honeybee Shade that is being + translated to a Dragonfly ContextShade. + """ + self._construction = hb_properties._construction + self._transmittance_schedule = hb_properties._transmittance_schedule
+ + +
+[docs] + def duplicate(self, new_host=None): + """Get a copy of this object. + + new_host: A new ContextShade object that hosts these properties. + If None, the properties will be duplicated with the same host. + """ + _host = new_host or self._host + return ContextShadeEnergyProperties(_host, self._construction, + self._transmittance_schedule)
+ + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'Context Shade Energy Properties: {}'.format(self.host.identifier)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/properties/model.html b/docs/_modules/dragonfly_energy/properties/model.html new file mode 100644 index 00000000..aa1cd260 --- /dev/null +++ b/docs/_modules/dragonfly_energy/properties/model.html @@ -0,0 +1,1682 @@ + + + + + + + dragonfly_energy.properties.model — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.properties.model

+# coding=utf-8
+"""Model Energy Properties."""
+try:
+    from itertools import izip as zip  # python 2
+except ImportError:
+    pass   # python 3
+
+from honeybee.extensionutil import room_extension_dicts
+from honeybee_energy.construction.windowshade import WindowConstructionShade
+from honeybee_energy.construction.dynamic import WindowConstructionDynamic
+from honeybee_energy.construction.air import AirBoundaryConstruction
+import honeybee_energy.properties.model as hb_model_properties
+from honeybee_energy.lib.constructions import generic_context
+from honeybee_energy.lib.constructionsets import generic_construction_set
+from honeybee.checkdup import check_duplicate_identifiers
+
+from dragonfly.extensionutil import model_extension_dicts
+
+
+
+[docs] +class ModelEnergyProperties(object): + """Energy Properties for Dragonfly Model. + + Args: + host: A dragonfly_core Model object that hosts these properties. + + Properties: + * host + * materials + * constructions + * face_constructions + * shade_constructions + * construction_sets + * global_construction_set + * schedule_type_limits + * schedules + * construction_schedules + * shade_schedules + * program_type_schedules + * hvac_schedules + * misc_room_schedules + * program_types + * hvacs + * shws + """ + + def __init__(self, host): + """Initialize Model energy properties.""" + self._host = host + + @property + def host(self): + """Get the Model object hosting these properties.""" + return self._host + + @property + def materials(self): + """Get a list of all unique materials contained within the model. + + This includes materials across all Room2Ds, Stories, and Building + ConstructionSets but it does NOT include the Honeybee generic default + construction set. + """ + materials = [] + for constr in self.constructions: + try: + materials.extend(constr.materials) + if constr.has_frame: + materials.append(constr.frame) + if isinstance(constr, WindowConstructionShade): + if constr.is_switchable_glazing: + materials.append(constr.switched_glass_material) + if constr.shade_location == 'Between': + materials.append(constr.window_construction.materials[-2]) + except AttributeError: + pass # ShadeConstruction + return list(set(materials)) + + @property + def constructions(self): + """Get a list of all unique constructions in the model. + + This includes materials across all Room2Ds, Stories, and Building + ConstructionSets but it does NOT include the Honeybee generic default + construction set. + """ + bldg_constrs = [] + for cnstr_set in self.construction_sets: + bldg_constrs.extend(cnstr_set.modified_constructions_unique) + all_constrs = bldg_constrs + self.face_constructions + self.shade_constructions + return list(set(all_constrs)) + + @property + def face_constructions(self): + """Get a list of all unique constructions assigned to Faces, Apertures and Doors. + + These objects only exist under the Building.room_3ds property + """ + constructions = [] + for bldg in self.host.buildings: + for face in bldg.room_3d_faces: + self._check_and_add_obj_construction(face, constructions) + for ap in face.apertures: + self._check_and_add_obj_construction(ap, constructions) + for dr in face.doors: + self._check_and_add_obj_construction(dr, constructions) + return list(set(constructions)) + + @property + def shade_constructions(self): + """Get a list of all unique constructions assigned to ContextShades in the model. + + This will also include any Shade objects assigned to the 3D Honeybee Rooms + of any Model Buildings. + """ + constructions = [] + for shade in self.host.context_shades: + self._check_and_add_obj_construction(shade, constructions) + for bldg in self.host.buildings: + for shd in bldg.room_3d_shades: + self._check_and_add_obj_construction(shd, constructions) + return list(set(constructions)) + + @property + def construction_sets(self): + """Get a list of all unique ConstructionSets in the Model. + + Note that this includes ConstructionSets assigned to individual Stories, + Room2Ds and 3D Honeybee Rooms in the Model's Buildings. + """ + construction_sets = [] + for bldg in self.host.buildings: + self._check_and_add_obj_constr_set(bldg, construction_sets) + for story in bldg.unique_stories: + self._check_and_add_obj_constr_set(story, construction_sets) + for room in story.room_2ds: + self._check_and_add_obj_constr_set(room, construction_sets) + for room in bldg.room_3ds: + self._check_and_add_obj_constr_set(room, construction_sets) + return list(set(construction_sets)) # catch equivalent construction sets + + @property + def global_construction_set(self): + """The global energy construction set. + + This is what is used whenever there is no construction_set assigned to a + Room2D, a parent Story, or a parent Building. + """ + return generic_construction_set + + @property + def schedule_type_limits(self): + """Get a list of all unique schedule type limits contained within the model. + + This includes schedules across all ContextShades and Room2Ds. + """ + type_limits = [] + for sched in self.schedules: + t_lim = sched.schedule_type_limit + if t_lim is not None and not self._instance_in_array(t_lim, type_limits): + type_limits.append(t_lim) + return list(set(type_limits)) + + @property + def schedules(self): + """Get a list of all unique schedules in the model. + + This includes schedules across all ProgramTypes and ContextShades. + """ + all_scheds = self.hvac_schedules + self.program_type_schedules + \ + self.misc_room_schedules + self.shade_schedules + self.construction_schedules + return list(set(all_scheds)) + + @property + def construction_schedules(self): + """Get a list of all unique schedules assigned to constructions in the model. + + This includes schedules on al AirBoundaryConstructions, WindowConstructionShade, + and WindowConstructionDynamic. + """ + schedules = [] + for constr in self.constructions: + if isinstance(constr, AirBoundaryConstruction): + self._check_and_add_schedule(constr.air_mixing_schedule, schedules) + elif isinstance(constr, WindowConstructionShade): + if constr.schedule is not None: + self._check_and_add_schedule(constr.schedule, schedules) + elif isinstance(constr, WindowConstructionDynamic): + self._check_and_add_schedule(constr.schedule, schedules) + return list(set(schedules)) + + @property + def shade_schedules(self): + """Get a list of all unique schedules assigned to ContextShades in the model. + """ + schedules = [] + for shade in self.host._context_shades: + self._check_and_add_shade_schedule(shade, schedules) + return list(set(schedules)) + + @property + def program_type_schedules(self): + """A list of all unique schedules assigned to ProgramTypes in the model.""" + schedules = [] + for p_type in self.program_types: + for sched in p_type.schedules: + self._check_and_add_schedule(sched, schedules) + return list(set(schedules)) + + @property + def hvac_schedules(self): + """Get a list of all unique HVAC-assigned schedules in the model.""" + schedules = [] + for hvac in self.hvacs: + for sched in hvac.schedules: + self._check_and_add_schedule(sched, schedules) + return list(set(schedules)) + + @property + def misc_room_schedules(self): + """Get a list of all unique schedules assigned directly to Rooms in the model. + + This includes schedules for process loads and window ventilation control + that are assigned to Room2Ds. It also includes any schedules assigned directly + to 3D Honeybee Rooms of the model (not through the room program). + + Note that this does not include schedules from ProgramTypes assigned to the + rooms. For this, use the program_type_schedules property. + """ + scheds = [] + for bldg in self.host._buildings: + for story in bldg: + for room in story: + window_vent = room.properties.energy._window_vent_control + processes = room.properties.energy._process_loads + if window_vent is not None: + self._check_and_add_schedule(window_vent.schedule, scheds) + if len(processes) != 0: + for process in processes: + self._check_and_add_schedule(process.schedule, scheds) + for room in bldg.room_3ds: + people = room.properties.energy._people + lighting = room.properties.energy._lighting + electric_equipment = room.properties.energy._electric_equipment + gas_equipment = room.properties.energy._gas_equipment + shw = room.properties.energy._service_hot_water + infiltration = room.properties.energy._infiltration + ventilation = room.properties.energy._ventilation + setpoint = room.properties.energy._setpoint + window_vent = room.properties.energy._window_vent_control + processes = room.properties.energy._process_loads + fans = room.properties.energy._fans + if people is not None: + self._check_and_add_schedule(people.occupancy_schedule, scheds) + self._check_and_add_schedule(people.activity_schedule, scheds) + if lighting is not None: + self._check_and_add_schedule(lighting.schedule, scheds) + if electric_equipment is not None: + self._check_and_add_schedule(electric_equipment.schedule, scheds) + if gas_equipment is not None: + self._check_and_add_schedule(gas_equipment.schedule, scheds) + if shw is not None: + self._check_and_add_schedule(shw.schedule, scheds) + if infiltration is not None: + self._check_and_add_schedule(infiltration.schedule, scheds) + if ventilation is not None and ventilation.schedule is not None: + self._check_and_add_schedule(ventilation.schedule, scheds) + if setpoint is not None: + self._check_and_add_schedule(setpoint.heating_schedule, scheds) + self._check_and_add_schedule(setpoint.cooling_schedule, scheds) + if setpoint.humidifying_schedule is not None: + self._check_and_add_schedule( + setpoint.humidifying_schedule, scheds) + self._check_and_add_schedule( + setpoint.dehumidifying_schedule, scheds) + if window_vent is not None: + self._check_and_add_schedule(window_vent.schedule, scheds) + if len(processes) != 0: + for process in processes: + self._check_and_add_schedule(process.schedule, scheds) + if len(fans) != 0: + for fan in fans: + self._check_and_add_schedule(fan.control.schedule, scheds) + return list(set(scheds)) + + @property + def program_types(self): + """Get a list of all unique ProgramTypes in the Model. + + This includes ProgramTypes assigned to both Room2Ds and 3D Honeybee Rooms. + """ + program_types = [] + for bldg in self.host._buildings: + for story in bldg: + for room in story: + if room.properties.energy._program_type is not None: + if not self._instance_in_array( + room.properties.energy._program_type, program_types): + program_types.append(room.properties.energy._program_type) + for room in bldg.room_3ds: + if room.properties.energy._program_type is not None: + if not self._instance_in_array( + room.properties.energy._program_type, program_types): + program_types.append(room.properties.energy._program_type) + return list(set(program_types)) # catch equivalent program types + + @property + def hvacs(self): + """Get a list of all unique HVAC systems in the Model.""" + hvacs = [] + for bldg in self.host._buildings: + for story in bldg: + for room in story: + if room.properties.energy._hvac is not None: + if not self._instance_in_array( + room.properties.energy._hvac, hvacs): + hvacs.append(room.properties.energy._hvac) + for room in bldg.room_3ds: + if room.properties.energy._hvac is not None: + if not self._instance_in_array(room.properties.energy._hvac, hvacs): + hvacs.append(room.properties.energy._hvac) + return hvacs + + @property + def shws(self): + """Get a list of all unique Service Hot Water (SHW) systems in the Model.""" + shws = [] + for bldg in self.host._buildings: + for story in bldg: + for room in story: + if room.properties.energy._shw is not None: + if not self._instance_in_array( + room.properties.energy._shw, shws): + shws.append(room.properties.energy._shw) + for room in bldg.room_3ds: + if room.properties.energy._shw is not None: + if not self._instance_in_array(room.properties.energy._shw, shws): + shws.append(room.properties.energy._shw) + return shws + +
+[docs] + def check_all(self, raise_exception=True): + """Check all of the aspects of the Model energy properties. + + Args: + raise_exception: Boolean to note whether a ValueError should be raised + if any errors are found. If False, this method will simply + return a text string with all errors that were found. + + Returns: + A text string with all errors that were found. This string will be empty + of no errors were found. + """ + msgs = [] + # perform checks for key honeybee model schema rules + msgs.append(self.check_duplicate_construction_set_identifiers(False)) + msgs.append(self.check_duplicate_program_type_identifiers(False)) + msgs.append(self.check_duplicate_hvac_identifiers(False)) + msgs.append(self.check_duplicate_shw_identifiers(False)) + # output a final report of errors or raise an exception + full_msgs = [msg for msg in msgs if msg != ''] + full_msg = '\n'.join(full_msgs) + if raise_exception and len(full_msgs) != 0: + raise ValueError(full_msg) + return full_msg
+ + +
+[docs] + def check_duplicate_construction_set_identifiers( + self, raise_exception=True, detailed=False): + """Check that there are no duplicate ConstructionSet identifiers in the model. + + Args: + raise_exception: Boolean to note whether a ValueError should be raised + if duplicate identifiers are found. (Default: True). + detailed: Boolean for whether the returned object is a detailed list of + dicts with error info or a string with a message. (Default: False). + + Returns: + A string with the message or a list with a dictionary if detailed is True. + """ + return check_duplicate_identifiers( + self.construction_sets, raise_exception, 'ConstructionSet', + detailed, '020003', 'Energy', + error_type='Duplicate ConstructionSet Identifier')
+ + +
+[docs] + def check_duplicate_program_type_identifiers( + self, raise_exception=True, detailed=False): + """Check that there are no duplicate ProgramType identifiers in the model. + + Args: + raise_exception: Boolean to note whether a ValueError should be raised + if duplicate identifiers are found. (Default: True). + detailed: Boolean for whether the returned object is a detailed list of + dicts with error info or a string with a message. (Default: False). + + Returns: + A string with the message or a list with a dictionary if detailed is True. + """ + return check_duplicate_identifiers( + self.program_types, raise_exception, 'ProgramType', + detailed, '020006', 'Energy', error_type='Duplicate ProgramType Identifier')
+ + +
+[docs] + def check_duplicate_hvac_identifiers(self, raise_exception=True, detailed=False): + """Check that there are no duplicate HVAC identifiers in the model. + + Args: + raise_exception: Boolean to note whether a ValueError should be raised + if duplicate identifiers are found. (Default: True). + detailed: Boolean for whether the returned object is a detailed list of + dicts with error info or a string with a message. (Default: False). + + Returns: + A string with the message or a list with a dictionary if detailed is True. + """ + return check_duplicate_identifiers( + self.hvacs, raise_exception, 'HVAC', detailed, '020007', 'Energy', + error_type='Duplicate HVAC Identifier')
+ + +
+[docs] + def check_duplicate_shw_identifiers(self, raise_exception=True, detailed=False): + """Check that there are no duplicate SHW identifiers in the model. + + Args: + raise_exception: Boolean to note whether a ValueError should be raised + if duplicate identifiers are found. (Default: True). + detailed: Boolean for whether the returned object is a detailed list of + dicts with error info or a string with a message. (Default: False). + + Returns: + A string with the message or a list with a dictionary if detailed is True. + """ + return check_duplicate_identifiers( + self.shws, raise_exception, 'SHW', detailed, '020008', 'Energy', + error_type='Duplicate SHW Identifier')
+ + +
+[docs] + def apply_properties_from_dict(self, data): + """Apply the energy properties of a dictionary to the host Model of this object. + + Args: + data: A dictionary representation of an entire dragonfly-core Model. + Note that this dictionary must have ModelEnergyProperties in order + for this method to successfully apply the energy properties. + """ + assert 'energy' in data['properties'], \ + 'Dictionary possesses no ModelEnergyProperties.' + _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \ + hb_model_properties.ModelEnergyProperties.load_properties_from_dict(data) + + # collect lists of energy property dictionaries + building_e_dicts, story_e_dicts, room2d_e_dicts, context_e_dicts = \ + model_extension_dicts(data, 'energy', [], [], [], []) + + # apply energy properties to objects using the energy property dictionaries + for bldg, b_dict in zip(self.host.buildings, building_e_dicts): + if b_dict is not None: + bldg.properties.energy.apply_properties_from_dict( + b_dict, construction_sets) + if bldg.has_room_3ds and b_dict is not None and 'room_3ds' in b_dict and \ + b_dict['room_3ds'] is not None: + room_e_dicts, face_e_dicts, shd_e_dicts, ap_e_dicts, dr_e_dicts = \ + room_extension_dicts(b_dict['room_3ds'], 'energy', [], [], [], [], []) + for room, r_dict in zip(bldg.room_3ds, room_e_dicts): + if r_dict is not None: + room.properties.energy.apply_properties_from_dict( + r_dict, construction_sets, program_types, hvacs, shws, + schedules, constructions) + for face, f_dict in zip(bldg.room_3d_faces, face_e_dicts): + if f_dict is not None: + face.properties.energy.apply_properties_from_dict( + f_dict, constructions) + for aperture, a_dict in zip(bldg.room_3d_apertures, ap_e_dicts): + if a_dict is not None: + aperture.properties.energy.apply_properties_from_dict( + a_dict, constructions) + for door, d_dict in zip(bldg.room_3d_doors, dr_e_dicts): + if d_dict is not None: + door.properties.energy.apply_properties_from_dict( + d_dict, constructions) + for shade, s_dict in zip(bldg.room_3d_shades, shd_e_dicts): + if s_dict is not None: + shade.properties.energy.apply_properties_from_dict( + s_dict, constructions, schedules) + for story, s_dict in zip(self.host.stories, story_e_dicts): + if s_dict is not None: + story.properties.energy.apply_properties_from_dict( + s_dict, construction_sets) + for room, r_dict in zip(self.host.room_2ds, room2d_e_dicts): + if r_dict is not None: + room.properties.energy.apply_properties_from_dict( + r_dict, construction_sets, program_types, hvacs, shws, schedules) + for shade, s_dict in zip(self.host.context_shades, context_e_dicts): + if s_dict is not None: + shade.properties.energy.apply_properties_from_dict( + s_dict, constructions, schedules)
+ + +
+[docs] + def to_dict(self): + """Return Model energy properties as a dictionary.""" + base = {'energy': {'type': 'ModelEnergyProperties'}} + + # add all materials, constructions and construction sets to the dictionary + schs = self._add_constr_type_objs_to_dict(base) + + # add all schedule type limits, schedules, and program types to the dictionary + self._add_sched_type_objs_to_dict(base, schs) + + return base
+ + +
+[docs] + def to_honeybee(self, new_host): + """Get a honeybee version of this object. + + Args: + new_host: A honeybee-core Model object that will host these properties. + """ + return hb_model_properties.ModelEnergyProperties(new_host)
+ + +
+[docs] + def duplicate(self, new_host=None): + """Get a copy of this Model. + + Args: + new_host: A new Model object that hosts these properties. + If None, the properties will be duplicated with the same host. + """ + _host = new_host or self._host + return ModelEnergyProperties(_host)
+ + + def _add_constr_type_objs_to_dict(self, base): + """Add materials, constructions and construction sets to a base dictionary. + + Args: + base: A base dictionary for a Dragonfly Model. + """ + # add the global construction set to the dictionary + gs = self.global_construction_set.to_dict(abridged=True, none_for_defaults=False) + gs['type'] = 'GlobalConstructionSet' + del gs['identifier'] + g_constr = self.global_construction_set.constructions_unique + g_materials = [] + for constr in g_constr: + try: + g_materials.extend(constr.materials) + except AttributeError: + pass # ShadeConstruction or AirBoundaryConstruction + gs['context_construction'] = generic_context.identifier + gs['constructions'] = [generic_context.to_dict()] + for cnst in g_constr: + try: + gs['constructions'].append(cnst.to_dict(abridged=True)) + except TypeError: # ShadeConstruction + gs['constructions'].append(cnst.to_dict()) + gs['materials'] = [mat.to_dict() for mat in set(g_materials)] + base['energy']['global_construction_set'] = gs + + # add all ConstructionSets to the dictionary + base['energy']['construction_sets'] = [] + construction_sets = self.construction_sets + for cnstr_set in construction_sets: + base['energy']['construction_sets'].append(cnstr_set.to_dict(abridged=True)) + + # add all unique Constructions to the dictionary + room_constrs = [] + for cnstr_set in construction_sets: + room_constrs.extend(cnstr_set.modified_constructions_unique) + mass_constrs = [] + for bldg in self.host.buildings: + for room in bldg.room_3ds: + for int_mass in room.properties.energy._internal_masses: + constr = int_mass.construction + if not self._instance_in_array(constr, mass_constrs): + mass_constrs.append(constr) + all_constrs = room_constrs + self.face_constructions + self.shade_constructions + constructions = tuple(set(all_constrs)) + base['energy']['constructions'] = [] + for cnst in constructions: + try: + base['energy']['constructions'].append(cnst.to_dict(abridged=True)) + except TypeError: # ShadeConstruction + base['energy']['constructions'].append(cnst.to_dict()) + + # add all unique Materials to the dictionary + materials = [] + for cnstr in constructions: + try: + materials.extend(cnstr.materials) + if cnstr.has_frame: + materials.append(cnstr.frame) + if isinstance(cnstr, WindowConstructionShade): + if cnstr.is_switchable_glazing: + materials.append(cnstr.switched_glass_material) + if cnstr.shade_location == 'Between': + materials.append(cnstr.window_construction.materials[-2]) + except AttributeError: + pass # ShadeConstruction + base['energy']['materials'] = [mat.to_dict() for mat in set(materials)] + + # extract all of the schedules from the constructions + schedules = [] + for constr in constructions: + if isinstance(constr, AirBoundaryConstruction): + self._check_and_add_schedule(constr.air_mixing_schedule, schedules) + elif isinstance(constr, WindowConstructionShade): + if constr.schedule is not None: + self._check_and_add_schedule(constr.schedule, schedules) + elif isinstance(constr, WindowConstructionDynamic): + self._check_and_add_schedule(constr.schedule, schedules) + return schedules + + def _add_sched_type_objs_to_dict(self, base, schs): + """Add schedule type limits, schedules, and program types to a base dictionary. + + Args: + base: A base dictionary for a Dragonfly Model. + schs: A list of additional schedules to be serialized to the + base dictionary. + """ + # add all unique hvacs to the dictionary + hvacs = self.hvacs + base['energy']['hvacs'] = [] + for hvac in hvacs: + base['energy']['hvacs'].append(hvac.to_dict(abridged=True)) + + # add all unique shws to the dictionary + base['energy']['shws'] = [shw.to_dict() for shw in self.shws] + + # add all unique ProgramTypes to the dictionary + program_types = self.program_types + base['energy']['program_types'] = [] + for p_type in program_types: + base['energy']['program_types'].append(p_type.to_dict(abridged=True)) + + # add all unique Schedules to the dictionary + p_type_scheds = [] + for p_type in program_types: + for sched in p_type.schedules: + self._check_and_add_schedule(sched, p_type_scheds) + hvac_scheds = [] + for hvac in hvacs: + for sched in hvac.schedules: + self._check_and_add_schedule(sched, hvac_scheds) + all_scheds = hvac_scheds + p_type_scheds + self.misc_room_schedules + \ + self.shade_schedules + schs + schedules = tuple(set(all_scheds)) + base['energy']['schedules'] = [] + for sched in schedules: + base['energy']['schedules'].append(sched.to_dict(abridged=True)) + + # add all unique ScheduleTypeLimits to the dictionary + type_limits = [] + for sched in schedules: + t_lim = sched.schedule_type_limit + if t_lim is not None and not self._instance_in_array(t_lim, type_limits): + type_limits.append(t_lim) + base['energy']['schedule_type_limits'] = \ + [s_typ.to_dict() for s_typ in set(type_limits)] + + def _check_and_add_obj_constr_set(self, obj, construction_sets): + """Check if a construction set is assigned to an object and add it to a list.""" + c_set = obj.properties.energy._construction_set + if c_set is not None: + if not self._instance_in_array(c_set, construction_sets): + construction_sets.append(c_set) + + def _check_and_add_obj_construction(self, obj, constructions): + """Check if a construction is assigned to an object and add it to a list.""" + constr = obj.properties.energy._construction + if constr is not None: + if not self._instance_in_array(constr, constructions): + constructions.append(constr) + + def _check_and_add_shade_schedule(self, obj, schedules): + """Check if a schedule is assigned to a shade and add it to a list.""" + sched = obj.properties.energy._transmittance_schedule + if sched is not None: + if not self._instance_in_array(sched, schedules): + schedules.append(sched) + + def _check_and_add_schedule(self, sched, schedules): + """Check if a schedule is in a list and add it if not.""" + if not self._instance_in_array(sched, schedules): + schedules.append(sched) + + @staticmethod + def _instance_in_array(object_instance, object_array): + """Check if a specific object instance is already in an array. + + This can be much faster than `if object_instance in object_array` + when you expect to be testing a lot of the same instance of an object for + inclusion in an array since the builtin method uses an == operator to + test inclusion. + """ + for val in object_array: + if val is object_instance: + return True + return False + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'Model Energy Properties: {}'.format(self.host.identifier)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/properties/room2d.html b/docs/_modules/dragonfly_energy/properties/room2d.html new file mode 100644 index 00000000..76b46f23 --- /dev/null +++ b/docs/_modules/dragonfly_energy/properties/room2d.html @@ -0,0 +1,1428 @@ + + + + + + + dragonfly_energy.properties.room2d — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.properties.room2d

+# coding=utf-8
+"""Room2D Energy Properties."""
+from honeybee.boundarycondition import Outdoors
+from honeybee_energy.properties.room import RoomEnergyProperties
+from honeybee_energy.programtype import ProgramType
+from honeybee_energy.constructionset import ConstructionSet
+from honeybee_energy.hvac import HVAC_TYPES_DICT
+from honeybee_energy.hvac._base import _HVACSystem
+from honeybee_energy.hvac.idealair import IdealAirSystem
+from honeybee_energy.shw import SHWSystem
+from honeybee_energy.ventcool.control import VentilationControl
+from honeybee_energy.ventcool.opening import VentilationOpening
+from honeybee_energy.load.process import Process
+
+from honeybee_energy.lib.constructionsets import generic_construction_set
+from honeybee_energy.lib.programtypes import plenum_program
+
+
+
+[docs] +class Room2DEnergyProperties(object): + """Energy Properties for Dragonfly Room2D. + + Args: + host: A dragonfly_core Room2D object that hosts these properties. + program_type: A honeybee ProgramType object to specify all default + schedules and loads for the Room2D. If None, the Room2D will have a + Plenum program (with no loads or setpoints). Default: None. + construction_set: A honeybee ConstructionSet object to specify all + default constructions for the Faces of the Room2D. If None, the + Room2D will use the honeybee default construction set, which is not + representative of a particular building code or climate zone. + Default: None. + hvac: A honeybee HVAC object (such as an IdealAirSystem) that specifies + how the Room2D is conditioned. If None, it will be assumed that the + Room2D is not conditioned. Default: None. + + Properties: + * host + * program_type + * construction_set + * hvac + * shw + * window_vent_control + * window_vent_opening + * process_loads + * total_process_load + * is_conditioned + * has_window_opening + """ + + __slots__ = ('_host', '_program_type', '_construction_set', '_hvac', '_shw', + '_window_vent_control', '_window_vent_opening', '_process_loads') + + def __init__( + self, host, program_type=None, construction_set=None, hvac=None, shw=None): + """Initialize Room2D energy properties.""" + self._host = host + self.program_type = program_type + self.construction_set = construction_set + self.hvac = hvac + self.shw = shw + self._window_vent_control = None # set to None by default + self._window_vent_opening = None # set to None by default + self._process_loads = [] + + @property + def host(self): + """Get the Room2D object hosting these properties.""" + return self._host + + @property + def program_type(self): + """Get or set the ProgramType object for the Room2D. + + If not set, it will default to a plenum ProgramType (with no loads assigned). + """ + if self._program_type is not None: # set by the user + return self._program_type + else: + return plenum_program + + @program_type.setter + def program_type(self, value): + if value is not None: + assert isinstance(value, ProgramType), 'Expected ProgramType for Room2D ' \ + 'program_type. Got {}'.format(type(value)) + value.lock() # lock in case program type has multiple references + self._program_type = value + + @property + def construction_set(self): + """Get or set the Room2D ConstructionSet object. + + If not set, it will be set by the parent Story or will be the Honeybee + default generic ConstructionSet. + """ + if self._construction_set is not None: # set by the user + return self._construction_set + elif self._host.has_parent: # set by parent story + return self._host.parent.properties.energy.construction_set + else: + return generic_construction_set + + @construction_set.setter + def construction_set(self, value): + if value is not None: + assert isinstance(value, ConstructionSet), \ + 'Expected ConstructionSet. Got {}'.format(type(value)) + value.lock() # lock in case construction set has multiple references + self._construction_set = value + + @property + def hvac(self): + """Get or set the HVAC object for the Room2D. + + If None, it will be assumed that the Room2D is not conditioned. + """ + return self._hvac + + @hvac.setter + def hvac(self, value): + if value is not None: + assert isinstance(value, _HVACSystem), \ + 'Expected HVACSystem for Room2D hvac. Got {}'.format(type(value)) + value.lock() # lock in case hvac has multiple references + self._hvac = value + + @property + def shw(self): + """Get or set the SHWSystem object for the Room2D. + + If None, all hot water loads will be met with a system that doesn't compute + fuel or electricity usage. + """ + return self._shw + + @shw.setter + def shw(self, value): + if value is not None: + assert isinstance(value, SHWSystem), \ + 'Expected SHWSystem for Room shw. Got {}'.format(type(value)) + value.lock() # lock in case shw has multiple references + self._shw = value + + @property + def window_vent_control(self): + """Get or set a VentilationControl object to dictate the opening of windows. + + If None or no window_vent_opening object is assigned to this Room2D, + the windows will never open. + """ + return self._window_vent_control + + @window_vent_control.setter + def window_vent_control(self, value): + if value is not None: + assert isinstance(value, VentilationControl), 'Expected VentilationControl' \ + ' object for Room2D window_vent_control. Got {}'.format(type(value)) + value.lock() # lock because we don't duplicate the object + self._window_vent_control = value + + @property + def window_vent_opening(self): + """Get or set a VentilationOpening object for the operability of all windows. + + If None or no window_vent_control object is assigned to this Room2D, + the windows will never open. + """ + return self._window_vent_opening + + @window_vent_opening.setter + def window_vent_opening(self, value): + if value is not None: + assert isinstance(value, VentilationOpening), 'Expected VentilationOpening' \ + ' for Room2D window_vent_opening. Got {}'.format(type(value)) + self._window_vent_opening = value + + @property + def process_loads(self): + """Get or set an array of Process objects for process loads within the Room2D.""" + return tuple(self._process_loads) + + @process_loads.setter + def process_loads(self, value): + for val in value: + assert isinstance(val, Process), 'Expected Process ' \ + 'object for Room process_loads. Got {}'.format(type(val)) + val.lock() # lock because we don't duplicate the object + self._process_loads = list(value) + + @property + def total_process_load(self): + """Get a number for the total process load in W within the room.""" + return sum([load.watts for load in self._process_loads]) + + @property + def is_conditioned(self): + """Boolean to note whether the Room is conditioned.""" + return self._hvac is not None + + @property + def has_window_opening(self): + """Boolean to note whether the Room has operable windows with controls.""" + return self._window_vent_opening is not None + +
+[docs] + def add_default_ideal_air(self): + """Add a default IdealAirSystem to this Room2D. + + The identifier of this system will be derived from the room identifier + and will align with the naming convention that EnergyPlus uses for + templates Ideal Air systems. + """ + hvac_id = '{} Ideal Loads Air System'.format(self.host.identifier) + self.hvac = IdealAirSystem(hvac_id)
+ + +
+[docs] + def add_process_load(self, process_load): + """Add a Process load to this Room2D. + + Args: + process_load: A Process load to add to this Room. + """ + assert isinstance(process_load, Process), \ + 'Expected Process load object. Got {}.'.format(type(process_load)) + process_load.lock() # lock because we don't duplicate the object + self._process_loads.append(process_load)
+ + +
+[docs] + def remove_process_loads(self): + """Remove all Process loads from the Room.""" + self._process_loads = []
+ + +
+[docs] + @classmethod + def from_dict(cls, data, host): + """Create Room2DEnergyProperties from a dictionary. + + Note that the dictionary must be a non-abridged version for this + classmethod to work. + + Args: + data: A dictionary representation of Room2DEnergyProperties in the + format below. + host: A Room2D object that hosts these properties. + + .. code-block:: python + + { + "type": 'Room2DEnergyProperties', + "construction_set": {}, # A ConstructionSet dictionary + "program_type": {}, # A ProgramType dictionary + "hvac": {}, # A HVACSystem dictionary + "shw": {}, # A SHWSystem dictionary + "daylighting_control": {}, # A DaylightingControl dictionary + "window_vent_control": {} # A VentilationControl dictionary + "process_loads": [] # An array of Process dictionaries + } + """ + assert data['type'] == 'Room2DEnergyProperties', \ + 'Expected Room2DEnergyProperties. Got {}.'.format(data['type']) + + new_prop = cls(host) + if 'construction_set' in data and data['construction_set'] is not None: + new_prop.construction_set = \ + ConstructionSet.from_dict(data['construction_set']) + if 'program_type' in data and data['program_type'] is not None: + new_prop.program_type = ProgramType.from_dict(data['program_type']) + if 'hvac' in data and data['hvac'] is not None: + hvac_class = HVAC_TYPES_DICT[data['hvac']['type']] + new_prop.hvac = hvac_class.from_dict(data['hvac']) + if 'shw' in data and data['shw'] is not None: + new_prop.shw = SHWSystem.from_dict(data['shw']) + cls._deserialize_window_vent(new_prop, data, {}) + if 'process_loads' in data and data['process_loads'] is not None: + new_prop.process_loads = \ + [Process.from_dict(dat) for dat in data['process_loads']] + return new_prop
+ + +
+[docs] + def apply_properties_from_dict(self, abridged_data, construction_sets, + program_types, hvacs, shws, schedules): + """Apply properties from a Room2DEnergyPropertiesAbridged dictionary. + + Args: + abridged_data: A Room2DEnergyPropertiesAbridged dictionary (typically + coming from a Model). + construction_sets: A dictionary of ConstructionSets with identifiers + of the sets as keys, which will be used to re-assign construction_sets. + program_types: A dictionary of ProgramTypes with identifiers of the + types ask keys, which will be used to re-assign program_types. + hvacs: A dictionary of HVACSystems with the identifiers of the + systems as keys, which will be used to re-assign hvac to the Room. + shws: A dictionary of SHWSystems with the identifiers of the systems as + keys, which will be used to re-assign shw to the Room. + schedules: A dictionary of Schedules with identifiers of the schedules as + keys, which will be used to re-assign schedules. + """ + if 'construction_set' in abridged_data and \ + abridged_data['construction_set'] is not None: + self.construction_set = construction_sets[abridged_data['construction_set']] + if 'program_type' in abridged_data and abridged_data['program_type'] is not None: + self.program_type = program_types[abridged_data['program_type']] + if 'hvac' in abridged_data and abridged_data['hvac'] is not None: + self.hvac = hvacs[abridged_data['hvac']] + if 'shw' in abridged_data and abridged_data['shw'] is not None: + self.shw = shws[abridged_data['shw']] + self._deserialize_window_vent(self, abridged_data, schedules) + if 'process_loads' in abridged_data and \ + abridged_data['process_loads'] is not None: + for dat in abridged_data['process_loads']: + if dat['type'] == 'Process': + self._process_loads.append(Process.from_dict(dat)) + else: + self._process_loads.append( + Process.from_dict_abridged(dat, schedules) + )
+ + +
+[docs] + def to_dict(self, abridged=False): + """Return Room2D energy properties as a dictionary. + + Args: + abridged: Boolean for whether the full dictionary of the Room2D should + be written (False) or just the identifier of the the individual + properties (True). Default: False. + """ + base = {'energy': {}} + base['energy']['type'] = 'Room2DEnergyProperties' if not \ + abridged else 'Room2DEnergyPropertiesAbridged' + + # write the ProgramType into the dictionary + if self._program_type is not None: + base['energy']['program_type'] = self._program_type.identifier if abridged \ + else self._program_type.to_dict() + + # write the ConstructionSet into the dictionary + if self._construction_set is not None: + base['energy']['construction_set'] = \ + self._construction_set.identifier if abridged else \ + self._construction_set.to_dict() + + # write the hvac into the dictionary + if self._hvac is not None: + base['energy']['hvac'] = \ + self._hvac.identifier if abridged else self._hvac.to_dict() + + # write the shw into the dictionary + if self._shw is not None: + base['energy']['shw'] = \ + self._shw.identifier if abridged else self._shw.to_dict() + + # write the window_vent_control and window_vent_opening into the dictionary + if self._window_vent_control is not None: + base['energy']['window_vent_control'] = \ + self.window_vent_control.to_dict(abridged) + if self._window_vent_opening is not None: + base['energy']['window_vent_opening'] = self.window_vent_opening.to_dict() + + # write the process_loads into the dictionary + if len(self._process_loads) != 0: + base['energy']['process_loads'] = \ + [p.to_dict(abridged) for p in self._process_loads] + + return base
+ + +
+[docs] + def to_honeybee(self, new_host): + """Get a honeybee version of this object. + + Args: + new_host: A honeybee-core Room object that will host these properties. + """ + constr_set = self.construction_set # includes story and building-assigned sets + hb_constr = constr_set if constr_set is not generic_construction_set else None + hb_prop = RoomEnergyProperties( + new_host, self._program_type, hb_constr, self._hvac, self._shw) + if self._window_vent_control is not None: + hb_prop.window_vent_control = self.window_vent_control + if self._window_vent_opening is not None: + for face in new_host.faces: # set all apertures to be operable + for ap in face.apertures: + if isinstance(ap.boundary_condition, Outdoors): + ap.is_operable = True + hb_prop.assign_ventilation_opening(self.window_vent_opening) + if len(self._process_loads) != 0: + hb_prop.process_loads = self.process_loads + return hb_prop
+ + +
+[docs] + def from_honeybee(self, hb_properties): + """Transfer energy attributes from a Honeybee Room to Dragonfly Room2D. + + Args: + hb_properties: The RoomEnergyProperties of the honeybee Room that is being + translated to a Dragonfly Room2D. + """ + self._program_type = hb_properties._program_type + self._construction_set = hb_properties._construction_set + self._hvac = hb_properties._hvac + self._shw = hb_properties._shw + self._process_loads = hb_properties._process_loads[:] # copy the list + if hb_properties._window_vent_control is not None: + self._window_vent_control = hb_properties._window_vent_control + for face in hb_properties.host.faces: + for ap in face.apertures: + if ap.properties.energy.vent_opening is not None: + self._window_vent_opening = ap.properties.energy.vent_opening + break + if self._window_vent_opening is not None: + break
+ + +
+[docs] + def duplicate(self, new_host=None): + """Get a copy of this object. + + Args: + new_host: A new Room2D object that hosts these properties. + If None, the properties will be duplicated with the same host. + """ + _host = new_host or self._host + hb_prop = Room2DEnergyProperties( + _host, self._program_type, self._construction_set, self._hvac, self._shw) + hb_prop._window_vent_control = self._window_vent_control + hb_prop._window_vent_opening = self._window_vent_opening + hb_prop._process_loads = self._process_loads[:] # copy process load list + return hb_prop
+ + + @staticmethod + def _deserialize_window_vent(new_prop, data, schedules): + """Re-serialize window ventilation objects from a dict and apply to new_prop. + + Args: + new_prop: A Room2DEnergyProperties to apply the window ventilation to. + data: A dictionary representation of Room2DEnergyProperties. + """ + if 'window_vent_control' in data and data['window_vent_control'] is not None: + wvc = data['window_vent_control'] + new_prop.window_vent_control = \ + VentilationControl.from_dict_abridged(wvc, schedules) \ + if wvc['type'] == 'VentilationControlAbridged' else \ + VentilationControl.from_dict(wvc) + if 'window_vent_opening' in data and data['window_vent_opening'] is not None: + new_prop.window_vent_opening = \ + VentilationOpening.from_dict(data['window_vent_opening']) + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'Room2D Energy Properties: {}'.format(self.host.identifier)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/properties/story.html b/docs/_modules/dragonfly_energy/properties/story.html new file mode 100644 index 00000000..1ec0c62a --- /dev/null +++ b/docs/_modules/dragonfly_energy/properties/story.html @@ -0,0 +1,1199 @@ + + + + + + + dragonfly_energy.properties.story — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.properties.story

+# coding=utf-8
+"""Story Energy Properties."""
+from honeybee_energy.programtype import ProgramType
+from honeybee_energy.constructionset import ConstructionSet
+from honeybee_energy.hvac._base import _HVACSystem
+from honeybee_energy.shw import SHWSystem
+
+from honeybee_energy.lib.constructionsets import generic_construction_set
+
+
+
+[docs] +class StoryEnergyProperties(object): + """Energy Properties for Dragonfly Story. + + Args: + host: A dragonfly_core Story object that hosts these properties. + construction_set: A honeybee ConstructionSet object to specify all + default constructions for the Faces of the Story. If None, the + Story will use the honeybee default construction set, which is not + representative of a particular building code or climate zone. + Default: None. + + Properties: + * host + * construction_set + """ + + __slots__ = ('_host', '_construction_set') + + def __init__(self, host, construction_set=None): + """Initialize Story energy properties.""" + self._host = host + self.construction_set = construction_set + + @property + def host(self): + """Get the Story object hosting these properties.""" + return self._host + + @property + def construction_set(self): + """Get or set the Story ConstructionSet object. + + If not set, it will be set by the parent Building or will be the Honeybee + default generic ConstructionSet. + """ + if self._construction_set is not None: # set by the user + return self._construction_set + elif self._host.has_parent: # set by parent building + return self._host.parent.properties.energy.construction_set + else: + return generic_construction_set + + @construction_set.setter + def construction_set(self, value): + if value is not None: + assert isinstance(value, ConstructionSet), \ + 'Expected ConstructionSet. Got {}'.format(type(value)) + value.lock() # lock in case construction set has multiple references + self._construction_set = value + +
+[docs] + def averaged_program_type(self, identifier=None, timestep_resolution=1): + """Get a ProgramType that is averaged across all of the children Room2Ds. + + The weights used in the averaging process are the floor area weights. + + Args: + identifier: A unique ID text string for the new averaged ProgramType. + Must be < 100 characters and not contain any EnergyPlus special + characters. This will be used to identify the object across a model + and in the exported IDF. If None, the resulting ProgramType will + use the identifier of the host Building. (Default: None) + timestep_resolution: An optional integer for the timestep resolution + at which the schedules will be averaged. Any schedule details + smaller than this timestep will be lost in the averaging process. + Default: 1. + """ + # get the default identifier of the ProgramType if None + identifier = identifier if identifier is not None else \ + '{}_Program'.format(self.host.identifier) + + # compute the floor area weights + flr_areas = [room.floor_area for room in self.host.room_2ds] + total_area = sum(flr_areas) + weights = [room_area / total_area for room_area in flr_areas] + + # compute the averaged program + program_types = [room.properties.energy.program_type + for room in self.host.room_2ds] + return ProgramType.average( + identifier, program_types, weights, timestep_resolution)
+ + +
+[docs] + def set_all_room_2d_program_type(self, program_type): + """Set all of the children Room2Ds of this Story to have the same ProgramType. + + Args: + program_type: A ProgramType to assign to all children Room2Ds. + """ + assert isinstance(program_type, ProgramType), 'Expected ProgramType for Story ' \ + 'set_all_room_2d_program_type. Got {}'.format(type(program_type)) + for room_2d in self.host.room_2ds: + room_2d.properties.energy.program_type = program_type
+ + +
+[docs] + def set_all_room_2d_hvac(self, hvac, conditioned_only=True): + """Set all children Room2Ds of this Story to have the same HVAC system. + + Args: + hvac: An HVAC system with properties that will be assigned to all + children Room2Ds. + conditioned_only: Boolean to note whether the input hvac should only + be applied to rooms that are already conditioned. If False, the + hvac will be applied to all rooms. (Default: True). + """ + assert isinstance(hvac, _HVACSystem), 'Expected HVACSystem for Story.' \ + 'set_all_room_2d_hvac. Got {}'.format(type(hvac)) + + new_hvac = hvac.duplicate() + new_hvac._identifier = '{}_{}'.format(hvac.identifier, self.host.identifier) + for room_2d in self.host.room_2ds: + if not conditioned_only or room_2d.properties.energy.is_conditioned: + room_2d.properties.energy.hvac = new_hvac
+ + +
+[docs] + def add_default_ideal_air(self): + """Add a default IdealAirSystem to all children Room2Ds of this Story. + + The identifier of the systems will be derived from the room identifiers. + """ + for room_2d in self.host.room_2ds: + room_2d.properties.energy.add_default_ideal_air()
+ + +
+[docs] + def set_all_room_2d_shw(self, shw): + """Set all children Room2Ds of this Story to have the same HVAC system. + + Args: + shw: An HVAC system with properties that will be assigned to all + children Room2Ds. + """ + assert isinstance(shw, SHWSystem), 'Expected SHWSystem for Story.' \ + 'set_all_room_2d_shw. Got {}'.format(type(shw)) + + new_shw = shw.duplicate() + new_shw._identifier = '{}_{}'.format(shw.identifier, self.host.identifier) + for room_2d in self.host.room_2ds: + room_2d.properties.energy.shw = new_shw
+ + +
+[docs] + @classmethod + def from_dict(cls, data, host): + """Create StoryEnergyProperties from a dictionary. + + Note that the dictionary must be a non-abridged version for this + classmethod to work. + + Args: + data: A dictionary representation of StoryEnergyProperties. + host: A Story object that hosts these properties. + """ + assert data['type'] == 'StoryEnergyProperties', \ + 'Expected StoryEnergyProperties. Got {}.'.format(data['type']) + + new_prop = cls(host) + if 'construction_set' in data and data['construction_set'] is not None: + new_prop.construction_set = \ + ConstructionSet.from_dict(data['construction_set']) + + return new_prop
+ + +
+[docs] + def apply_properties_from_dict(self, abridged_data, construction_sets): + """Apply properties from a StoryEnergyPropertiesAbridged dictionary. + + Args: + abridged_data: A StoryEnergyPropertiesAbridged dictionary (typically + coming from a Model). + construction_sets: A dictionary of ConstructionSets with identifiers + of the sets as keys, which will be used to re-assign construction_sets. + """ + if 'construction_set' in abridged_data and \ + abridged_data['construction_set'] is not None: + self.construction_set = construction_sets[abridged_data['construction_set']]
+ + +
+[docs] + def to_dict(self, abridged=False): + """Return Story energy properties as a dictionary. + + Args: + abridged: Boolean for whether the full dictionary of the Story should + be written (False) or just the identifier of the the individual + properties (True). Default: False. + """ + base = {'energy': {}} + base['energy']['type'] = 'StoryEnergyProperties' if not \ + abridged else 'StoryEnergyPropertiesAbridged' + + # write the ConstructionSet into the dictionary + if self._construction_set is not None: + base['energy']['construction_set'] = \ + self._construction_set.identifier if abridged else \ + self._construction_set.to_dict() + + return base
+ + +
+[docs] + def duplicate(self, new_host=None): + """Get a copy of this object. + + new_host: A new Story object that hosts these properties. + If None, the properties will be duplicated with the same host. + """ + _host = new_host or self._host + return StoryEnergyProperties(_host, self._construction_set)
+ + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'Story Energy Properties: {}'.format(self.host.identifier)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/reopt.html b/docs/_modules/dragonfly_energy/reopt.html new file mode 100644 index 00000000..b904e648 --- /dev/null +++ b/docs/_modules/dragonfly_energy/reopt.html @@ -0,0 +1,1668 @@ + + + + + + + dragonfly_energy.reopt — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.reopt

+# coding=utf-8
+"""Complete set of REopt Simulation Settings."""
+from __future__ import division
+
+import os
+import json
+import math
+
+from ladybug_geometry.geometry2d.pointvector import Point2D, Vector2D
+from ladybug_geometry.geometry2d.polygon import Polygon2D
+from honeybee.typing import float_positive, float_in_range, int_positive, \
+    valid_ep_string, valid_string
+from dragonfly.projection import polygon_to_lon_lat, meters_to_long_lat_factors, \
+    origin_long_lat_from_location
+
+
+
+[docs] +class REoptParameter(object): + """Complete set of REopt Simulation Settings. + + Args: + financial_parameter: A FinancialParameter object to describe the parameters + of the financial analysis. If None, a set of defaults will be + generated. (Default: None). + wind_parameter: A WindParameter object to set the cost and max amount of + wind to include in the analysis. If None, no wind will be included + in the analysis. (Default: None). + pv_parameter: A PVParameter object to set the cost and max amount of + photovoltaic to include in the analysis. If None, no PV will be included + in the analysis. (Default: None). + storage_parameter: A StorageParameter object to set the cost and max amount of + electricity storage to include in the analysis. If None, no storage + will be included in the analysis. (Default: None). + generator_parameter: A GeneratorParameter object to set the cost and max amount + of generators to include in the analysis. If None, no generators + will be included in the analysis. (Default: None). + + Properties: + * financial_parameter + * wind_parameter + * pv_parameter + * storage_parameter + * generator_parameter + """ + def __init__(self, financial_parameter=None, wind_parameter=None, pv_parameter=None, + storage_parameter=None, generator_parameter=None): + """Initialize SimulationParameter.""" + self.financial_parameter = financial_parameter + self.wind_parameter = wind_parameter + self.pv_parameter = pv_parameter + self.storage_parameter = storage_parameter + self.generator_parameter = generator_parameter + + @property + def financial_parameter(self): + """Get or set a FinancialParameter object for financial settings.""" + return self._financial_parameter + + @financial_parameter.setter + def financial_parameter(self, value): + if value is not None: + assert isinstance(value, FinancialParameter), 'Expected ' \ + 'FinancialParameter. Got {}.'.format(type(value)) + self._financial_parameter = value + else: + self._financial_parameter = FinancialParameter() + + @property + def wind_parameter(self): + """Get or set a WindParameter object for wind settings.""" + return self._wind_parameter + + @wind_parameter.setter + def wind_parameter(self, value): + if value is not None: + assert isinstance(value, WindParameter), 'Expected ' \ + 'WindParameter. Got {}.'.format(type(value)) + self._wind_parameter = value + else: + self._wind_parameter = WindParameter() + + @property + def pv_parameter(self): + """Get or set a PVParameter object for photovoltaic settings.""" + return self._pv_parameter + + @pv_parameter.setter + def pv_parameter(self, value): + if value is not None: + assert isinstance(value, PVParameter), 'Expected ' \ + 'PVParameter. Got {}.'.format(type(value)) + self._pv_parameter = value + else: + self._pv_parameter = PVParameter() + + @property + def storage_parameter(self): + """Get or set a StorageParameter object for electricity storage settings.""" + return self._storage_parameter + + @storage_parameter.setter + def storage_parameter(self, value): + if value is not None: + assert isinstance(value, StorageParameter), 'Expected ' \ + 'StorageParameter. Got {}.'.format(type(value)) + self._storage_parameter = value + else: + self._storage_parameter = StorageParameter() + + @property + def generator_parameter(self): + """Get or set a GeneratorParameter object for electricity storage settings.""" + return self._generator_parameter + + @generator_parameter.setter + def generator_parameter(self, value): + if value is not None: + assert isinstance(value, GeneratorParameter), 'Expected ' \ + 'GeneratorParameter. Got {}.'.format(type(value)) + self._generator_parameter = value + else: + self._generator_parameter = GeneratorParameter() + +
+[docs] + def to_assumptions_dict(self, base_file, urdb_label): + """Get REoptParameter as a dictionary representation in the REopt Lite schema. + + Full documentation of the REopt Lite schema can be found at. + https://developer.nrel.gov/docs/energy-optimization/reopt-v1/ + + Args: + base_file: A JSON file in the REopt Lite schema containing a base set + of assumptions that will be modified based on the properties of + this object. + urdb_label: Text string for the Utility Rate Database (URDB) label + for the particular electrical utility rate for the + optimization. The label is the last term of the URL of a + utility rate detail page (eg. the label for the rate at + https://openei.org/apps/IURDB/rate/view/5b0d83af5457a3f276733305 + is 5b0d83af5457a3f276733305). + """ + # load up the base dictionary + assert os.path.isfile(base_file), \ + 'No base JSON file found at {}.'.format(base_file) + with open(base_file, 'r') as base_f: + base_dict = json.load(base_f) + # apply this object's properties + site_dict = base_dict['Scenario']['Site'] + site_dict['ElectricTariff']['urdb_label'] = urdb_label + self.financial_parameter.apply_to_dict(site_dict['Financial']) + self.wind_parameter.apply_to_dict(site_dict['Wind']) + self.pv_parameter.apply_to_dict(site_dict['PV']) + self.storage_parameter.apply_to_dict(site_dict['Storage']) + self.generator_parameter.apply_to_dict(site_dict['Generator']) + return base_dict
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __copy__(self): + return REoptParameter( + self.financial_parameter.duplicate(), self.wind_parameter.duplicate(), + self.pv_parameter.duplicate(), self.storage_parameter.duplicate(), + self.generator_parameter.duplicate()) + + def __repr__(self): + return 'REoptParameter:'
+ + + +
+[docs] +class FinancialParameter(object): + """Complete set of Financial settings for REopt. + + Args: + analysis_years: An integer for the number of years over which cost will + be optimized. (Default: 25). + escalation_rate: A number between 0 and 1 for the escalation rate over + the analysis. (Default: 0.023). + tax_rate: A number between 0 and 1 for the rate at which the owner/host + of the system is taxed. (Default: 0.26). + discount_rate: A number between 0 and 1 for the discount rate for the + owner/host of the system. (Default: 0.083). + + Properties: + * analysis_years + * escalation_rate + * tax_rate + * discount_rate + """ + def __init__(self, analysis_years=25, escalation_rate=0.023, + tax_rate=0.26, discount_rate=0.083): + """Initialize FinancialParameter.""" + self.analysis_years = analysis_years + self.escalation_rate = escalation_rate + self.tax_rate = tax_rate + self.discount_rate = discount_rate + + @property + def analysis_years(self): + """Get or set a integer for the number of years to run the analysis.""" + return self._analysis_years + + @analysis_years.setter + def analysis_years(self, value): + self._analysis_years = int_positive( + value, input_name='financial parameter analysis years') + + @property + def escalation_rate(self): + """Get or set a fractional number for the escalation rate.""" + return self._escalation_rate + + @escalation_rate.setter + def escalation_rate(self, value): + self._escalation_rate = float_in_range( + value, 0, 1, input_name='financial parameter escalation rate') + + @property + def tax_rate(self): + """Get or set a fractional number for the tax rate.""" + return self._tax_rate + + @tax_rate.setter + def tax_rate(self, value): + self._tax_rate = float_in_range( + value, 0, 1, input_name='financial parameter tax rate') + + @property + def discount_rate(self): + """Get or set a fractional number for the discount rate.""" + return self._discount_rate + + @discount_rate.setter + def discount_rate(self, value): + self._discount_rate = float_in_range( + value, 0, 1, input_name='financial parameter discount rate') + +
+[docs] + def apply_to_dict(self, base_dict): + """Apply this object's properties to a 'Financial' object of REopt schema.""" + base_dict['analysis_years'] = self.analysis_years + base_dict['escalation_pct'] = self.escalation_rate + base_dict['offtaker_tax_pct'] = self.tax_rate + base_dict['offtaker_discount_pct'] = self.discount_rate
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __copy__(self): + return FinancialParameter( + self.analysis_years, self.escalation_rate, self.tax_rate, self.discount_rate) + + def __repr__(self): + return 'REopt FinancialParameter:'
+ + + +class _SourceParameter(object): + """Base class for all REopt energy sources. + + Args: + max_kw: A number for the maximum installed kilowatts of the energy + source. (Default: 0). + dollars_per_kw: A number for the installation cost of the energy source in + US dollars. (Default: 500). + + Properties: + * max_kw + * dollars_per_kw + """ + def __init__(self, max_kw=0, dollars_per_kw=500): + self.max_kw = max_kw + self.dollars_per_kw = dollars_per_kw + + @property + def max_kw(self): + """Get or set a number for the maximum installed kilowatts.""" + return self._max_kw + + @max_kw.setter + def max_kw(self, value): + self._max_kw = float_positive(value, input_name='reopt max kw') + + @property + def dollars_per_kw(self): + """Get or set a number for the installation cost in US dollars.""" + return self._dollars_per_kw + + @dollars_per_kw.setter + def dollars_per_kw(self, value): + self._dollars_per_kw = float_positive(value, input_name='reopt dollars per kw') + + def apply_to_dict(self, base_dict): + """Apply this object's properties to an object of REopt schema.""" + base_dict['max_kw'] = self.max_kw + base_dict['installed_cost_us_dollars_per_kw'] = self.dollars_per_kw + + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__() + + def __copy__(self): + return self.__class__(self.max_kw, self.dollars_per_kw) + + +
+[docs] +class WindParameter(_SourceParameter): + """Wind settings for REopt. + + Args: + max_kw: A number for the maximum installed kilowatts. (Default: 0). + dollars_per_kw: A number for installation cost in US dollars. (Default: 3013). + + Properties: + * max_kw + * dollars_per_kw + """ + def __init__(self, max_kw=0, dollars_per_kw=3013): + _SourceParameter.__init__(self, max_kw, dollars_per_kw) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __repr__(self): + return 'REopt WindParameter: {} kW'.format(self.max_kw)
+ + + +
+[docs] +class PVParameter(_SourceParameter): + """Photovoltaic settings for REopt. + + Args: + max_kw: A number for the maximum installed kilowatts. (Default: 0). + dollars_per_kw: A number for installation cost in US dollars. (Default: 1600). + + Properties: + * max_kw + * dollars_per_kw + * max_kw_ground + * dollars_per_kw_ground + """ + def __init__(self, max_kw=0, dollars_per_kw=1600, + max_kw_ground=0, dollars_per_kw_ground=2200): + _SourceParameter.__init__(self, max_kw, dollars_per_kw) + self.max_kw_ground = max_kw_ground + self.dollars_per_kw_ground = dollars_per_kw_ground + + @property + def max_kw_ground(self): + """Get or set a number for the maximum installed kilowatts of Ground PV.""" + return self._max_kw_ground + + @max_kw_ground.setter + def max_kw_ground(self, value): + self._max_kw_ground = float_positive(value, input_name='reopt max kw') + + @property + def dollars_per_kw_ground(self): + """Get or set a number for the installation cost of ground PV in US dollars.""" + return self._dollars_per_kw_ground + + @dollars_per_kw_ground.setter + def dollars_per_kw_ground(self, value): + self._dollars_per_kw_ground = \ + float_positive(value, input_name='reopt dollars per kw') + +
+[docs] + def apply_to_dict(self, base_dict): + """Apply this object's properties to an object of REopt schema.""" + if isinstance(base_dict, dict): + base_dict['max_kw'] = self.max_kw + base_dict['installed_cost_us_dollars_per_kw'] = self.dollars_per_kw + else: + base_dict[0]['max_kw'] = self.max_kw + base_dict[0]['installed_cost_us_dollars_per_kw'] = self.dollars_per_kw + base_dict[1]['max_kw'] = self.max_kw_ground + base_dict[1]['installed_cost_us_dollars_per_kw'] = self.dollars_per_kw_ground
+ + + def __copy__(self): + return PVParameter(self.max_kw, self.dollars_per_kw, + self.max_kw_ground, self.dollars_per_kw_ground) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __repr__(self): + return 'REopt PVParameter: {} kW'.format(self.max_kw)
+ + + +
+[docs] +class StorageParameter(_SourceParameter): + """Electrical storage settings for REopt. + + Args: + max_kw: A number for the maximum installed kilowatts. (Default: 0). + dollars_per_kw: A number for installation cost in US dollars. (Default: 840). + + Properties: + * max_kw + * dollars_per_kw + """ + def __init__(self, max_kw=0, dollars_per_kw=840): + _SourceParameter.__init__(self, max_kw, dollars_per_kw) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __repr__(self): + return 'REopt StorageParameter: {} kW'.format(self.max_kw)
+ + + +
+[docs] +class GeneratorParameter(_SourceParameter): + """Generator settings for REopt. + + Args: + max_kw: A number for the maximum installed kilowatts. (Default: 0). + dollars_per_kw: A number for installation cost in US dollars. (Default: 500). + + Properties: + * max_kw + * dollars_per_kw + """ + def __init__(self, max_kw=0, dollars_per_kw=500): + _SourceParameter.__init__(self, max_kw, dollars_per_kw) + +
+[docs] + def ToString(self): + """Overwrite .NET ToString.""" + return self.__repr__()
+ + + def __repr__(self): + return 'REopt GeneratorParameter: {} kW'.format(self.max_kw)
+ + + +
+[docs] +class GroundMountPV(object): + """Represents a ground-mounted photovoltaic system in REopt. + + Args: + identifier: Text string for a unique Photovoltaic ID. Must contain only + characters that are acceptable in REopt. This will be used to + identify the object across the exported geoJSON and REopt files. + geometry: A Polygon2D representing the geometry of the PV field. + building_identifier: An optional identifier of a dragonfly Building with + which the photovoltaic system is associated. If None, the PV system + will be assumed to be a community PV field that isn't associated with + a particular building meter. (Default: None). + + Properties: + * identifier + * display_name + * geometry + * building_identifier + """ + __slots__ = ('_identifier', '_display_name', '_geometry', '_building_identifier') + + def __init__(self, identifier, geometry, building_identifier=None): + """Initialize GroundMountPV.""" + self.identifier = identifier + self._display_name = None + assert isinstance(geometry, Polygon2D), 'Expected ladybug_geometry ' \ + 'Polygon2D for GroundMountPV geometry. Got {}'.format(type(geometry)) + self._geometry = geometry + self.building_identifier = building_identifier + +
+[docs] + @classmethod + def from_dict(cls, data): + """Initialize an GroundMountPV from a dictionary. + + Args: + data: A dictionary representation of an GroundMountPV object. + """ + # check the type of dictionary + assert data['type'] == 'GroundMountPV', 'Expected GroundMountPV ' \ + 'dictionary. Got {}.'.format(data['type']) + geo = Polygon2D.from_dict(data['geometry']) + pv_obj = cls(data['identifier'], geo) + if 'display_name' in data and data['display_name'] is not None: + pv_obj.display_name = data['display_name'] + if 'building_identifier' in data and data['building_identifier'] is not None: + pv_obj.building_identifier = data['building_identifier'] + return pv_obj
+ + + @property + def identifier(self): + """Get or set the text string for unique object identifier.""" + return self._identifier + + @identifier.setter + def identifier(self, identifier): + self._identifier = valid_ep_string(identifier, 'identifier') + + @property + def display_name(self): + """Get or set a string for the object name without any character restrictions. + + If not set, this will be equal to the identifier. + """ + if self._display_name is None: + return self._identifier + return self._display_name + + @display_name.setter + def display_name(self, value): + try: + self._display_name = str(value) + except UnicodeEncodeError: # Python 2 machine lacking the character set + self._display_name = value # keep it as unicode + + @property + def building_identifier(self): + """Get or set the text string for a Building associated with the PV field.""" + return self._building_identifier + + @building_identifier.setter + def building_identifier(self, identifier): + if identifier is not None: + identifier = valid_string(identifier, 'building identifier') + self._building_identifier = identifier + + @property + def geometry(self): + """Get a Polygon2D representing the photovoltaic field.""" + return self._geometry + +
+[docs] + def move(self, moving_vec): + """Move this object along a vector. + + Args: + moving_vec: A ladybug_geometry Vector3D with the direction and distance + to move the object. + """ + self._geometry = self._geometry.move(Vector2D(moving_vec.x, moving_vec.y))
+ + +
+[docs] + def rotate_xy(self, angle, origin): + """Rotate this object counterclockwise in the XY plane by a certain angle. + + Args: + angle: An angle in degrees. + origin: A ladybug_geometry Point3D for the origin around which the + object will be rotated. + """ + self._geometry = self._geometry.rotate( + math.radians(angle), Point2D(origin.x, origin.y))
+ + +
+[docs] + def reflect(self, plane): + """Reflect this object across a plane. + + Args: + plane: A ladybug_geometry Plane across which the object will be reflected. + """ + assert plane.n.z == 0, \ + 'Plane normal must be in XY plane to use it on dragonfly object reflect.' + norm = Vector2D(plane.n.x, plane.n.y) + origin = Point2D(plane.o.x, plane.o.y) + self._geometry = self._geometry.reflect(norm, origin)
+ + +
+[docs] + def scale(self, factor, origin=None): + """Scale this object by a factor from an origin point. + + Args: + factor: A number representing how much the object should be scaled. + origin: A ladybug_geometry Point3D representing the origin from which + to scale. If None, it will be scaled from the World origin (0, 0, 0). + """ + ori = Point2D(origin.x, origin.y) if origin is not None else None + self._geometry = self._geometry.scale(factor, ori)
+ + +
+[docs] + def to_dict(self): + """GroundMountPV dictionary representation.""" + base = {'type': 'GroundMountPV'} + base['identifier'] = self.identifier + base['geometry'] = self.geometry.to_dict() + if self._display_name is not None: + base['display_name'] = self.display_name + if self._building_identifier is not None: + base['building_identifier'] = self.building_identifier + return base
+ + +
+[docs] + def to_geojson_dict(self, location, point): + """Get GroundMountPV dictionary as it appears in an URBANopt geoJSON. + + Args: + location: A ladybug Location object possessing longitude and latitude data. + point: A ladybug_geometry Point2D for where the location object exists + within the space of a scene. The coordinates of this point are + expected to be in the units of this Model. (Default: (0, 0)). + """ + # get the conversion factors over to (longitude, latitude) + origin_lon_lat = origin_long_lat_from_location(location, point) + convert_facs = meters_to_long_lat_factors(origin_lon_lat) + + # create the GeoJSON dictionary + pts = [(pt.x, pt.y) for pt in self.geometry.vertices] + coords = [polygon_to_lon_lat(pts, origin_lon_lat, convert_facs)] + base = { + 'type': 'Feature', + 'properties': { + 'id': self.identifier, + 'geometryType': 'Rectangle', + 'name': self.display_name, + 'type': 'District System', + 'footprint_area': self.geometry.area, + 'footprint_perimeter': self.geometry.perimeter + }, + 'geometry': { + 'type': 'Polygon', + 'coordinates': coords + } + } + if self.building_identifier is None: + base['properties']['district_system_type'] = 'Community Photovoltaic' + else: + base['properties']['district_system_type'] = 'Ground Mount Photovoltaic' + base['properties']['associated_building_id'] = self.building_identifier + return base
+ + +
+[docs] + def duplicate(self): + """Get a copy of this object.""" + return self.__copy__()
+ + + def __copy__(self): + new_obj = GroundMountPV(self.identifier, self.geometry, self.building_identifier) + new_obj._display_name = self._display_name + return new_obj + +
+[docs] + def ToString(self): + return self.__repr__()
+ + + def __repr__(self): + return 'GroundMountPV: {}'.format(self.display_name)
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/run.html b/docs/_modules/dragonfly_energy/run.html new file mode 100644 index 00000000..28d77dc7 --- /dev/null +++ b/docs/_modules/dragonfly_energy/run.html @@ -0,0 +1,2344 @@ + + + + + + + dragonfly_energy.run — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.run

+# coding=utf-8
+"""Module for running geoJSON and OpenStudio files through URBANopt."""
+from __future__ import division
+
+import os
+import json
+import shutil
+import subprocess
+
+from ladybug.futil import preparedir, write_to_file
+from ladybug.epw import EPW
+from ladybug.config import folders as lb_folders
+from honeybee.config import folders as hb_folders
+from honeybee_energy.config import folders as hb_energy_folders
+from honeybee_energy.result.emissions import emissions_region
+
+from .config import folders
+from .measure import MapperMeasure
+from .reopt import REoptParameter
+
+# Custom environment used to run Python packages without conflicts
+PYTHON_ENV = os.environ.copy()
+PYTHON_ENV['PYTHONHOME'] = ''
+
+# Number to prevent GHE Designer simulations that would max out the memory
+MAX_BOREHOLES = 10000
+
+
+
+[docs] +def base_honeybee_osw( + project_directory, sim_par_json=None, additional_measures=None, + additional_mapper_measures=None, base_osw=None, epw_file=None, + skip_report=True, emissions_year=None): + """Create a honeybee_workflow.osw to be used as a base in URBANopt simulations. + + This method will also copy the Honeybee.rb mapper to this folder if it is + available in the config of this library. + + Args: + project_directory: Full path to a folder out of which the URBANopt simulation + will be run. This is the folder that contains the feature geoJSON. + sim_par_json: Optional file path to the SimulationParameter JSON. + If None, the OpenStudio models generated in the URBANopt run will + not have everything they need to be simulate-able unless such + parameters are supplied from one of the additional_measures or the + base_osw. (Default: None). + additional_measures: An optional array of honeybee-energy Measure objects + to be included in the output osw. These Measure objects must have + values for all required input arguments or an exception will be + raised while running this function. (Default: None). + additional_mapper_measures: An optional array of dragonfly-energy MapperMeasure + objects to be included in the output osw. These MapperMeasure objects + must have values for all required input arguments or an exception will + be raised while running this function. (Default: None). + base_osw: Optional file path to an existing OSW JSON be used as the base + for the honeybee_workflow.osw. This is another way that outside measures + can be incorporated into the workflow. (Default: None). + epw_file: Optional file path to an EPW that should be associated with the + output energy model. (Default: None). + skip_report: Set to True to have the URBANopt default feature reporting + measure skipped as part of the workflow. If False, the measure will + be run after all simulations are complete. Note that this input + has no effect if the default_feature_reports measure is already + in the base_osw or additional_measures. (Default: True). + emissions_year: An optional integer to set the year for which carbon emissions + will be computed. If not for a historical year, values must be an even + number and be between 2020 and 2050. If None, no carbon emission + calculations will be included in the simulation. (Default: None). + + Returns: + The file path to the honeybee_workflow.osw written out by this method. + This is used as the base for translating all features in the geoJSON. + """ + # create a dictionary representation of the .osw with steps to run + # the model measure and the simulation parameter measure + if base_osw is None: + osw_dict = {'steps': [], 'name': None, 'description': None} + else: + assert os.path.isfile(base_osw), 'No base OSW file found at {}.'.format(base_osw) + with open(base_osw, 'r') as base_file: + osw_dict = json.load(base_file) + + # add a simulation parameter step if it is specified + if sim_par_json is not None: + sim_par_dict = { + 'arguments': { + 'simulation_parameter_json': sim_par_json + }, + 'measure_dir_name': 'from_honeybee_simulation_parameter' + } + osw_dict['steps'].insert(0, sim_par_dict) + + # add the model json serialization into the steps + model_measure_dict = { + 'arguments': { + 'model_json': 'model_json_to_be_mapped.json' + }, + 'measure_dir_name': 'from_honeybee_model' + } + osw_dict['steps'].insert(0, model_measure_dict) + + # assign the measure_paths to the osw_dict + if 'measure_paths' not in osw_dict: + osw_dict['measure_paths'] = [] + if hb_energy_folders.honeybee_openstudio_gem_path: # add honeybee-openstudio measure + m_dir = os.path.join(hb_energy_folders.honeybee_openstudio_gem_path, 'measures') + osw_dict['measure_paths'].append(m_dir) + + # add the emissions reporting if a year has been selected + if emissions_year is not None and epw_file is not None: + epw_obj = EPW(epw_file) + ems_region = emissions_region(epw_obj.location) + if ems_region is not None: + if emissions_year < 2020: + hist_yr, fut_yr = emissions_year, 2030 + else: + hist_yr, fut_yr = 2019, emissions_year + emissions_measure_dict = { + 'arguments': { + 'future_subregion': ems_region[0], + 'hourly_historical_subregion': ems_region[2], + 'annual_historical_subregion': ems_region[1], + 'future_year': str(fut_yr), + 'hourly_historical_year': '2019', + 'annual_historical_year': str(hist_yr) + }, + 'measure_dir_name': 'add_ems_emissions_reporting' + } + osw_dict['steps'].append(emissions_measure_dict) + + # add any additional measures to the osw_dict + if additional_measures or additional_mapper_measures: + measures = [] + if additional_measures is not None: + measures.extend(additional_measures) + if additional_mapper_measures is not None: + measures.extend(additional_mapper_measures) + measure_paths = set() # set of all unique measure paths + # ensure measures are correctly ordered + m_dict = {'ModelMeasure': [], 'EnergyPlusMeasure': [], 'ReportingMeasure': []} + for measure in measures: + m_dict[measure.type].append(measure) + sorted_measures = m_dict['ModelMeasure'] + m_dict['EnergyPlusMeasure'] + \ + m_dict['ReportingMeasure'] + for measure in sorted_measures: + measure.validate() # ensure that all required arguments have values + measure_paths.add(os.path.dirname(measure.folder)) + osw_dict['steps'].append(measure.to_osw_dict()) # add measure to workflow + if isinstance(measure, MapperMeasure): + _add_mapper_measure(project_directory, measure) + for m_path in measure_paths: # add outside measure paths + osw_dict['measure_paths'].append(m_path) + + # add default feature reports if they aren't in the steps + all_measures = [step['measure_dir_name'] for step in osw_dict['steps']] + if 'default_feature_reports' not in all_measures: + report_measure_dict = { + 'arguments': { + 'feature_id': None, + 'feature_name': None, + 'feature_type': None, + 'feature_location': None + }, + 'measure_dir_name': 'default_feature_reports' + } + if skip_report: + report_measure_dict['arguments']['__SKIP__'] = True + osw_dict['steps'].append(report_measure_dict) + + # if there is a system parameter JSON, make sure the EPW is copied and referenced + if epw_file is not None: + osw_dict['weather_file'] = epw_file + sys_param_file = os.path.join(project_directory, 'system_params.json') + if os.path.isfile(sys_param_file): + # make sure the Modelica measure runs as part of the simulation + modelica_measures = [ + { + 'measure_dir_name': 'export_time_series_modelica', + 'arguments': {'__SKIP__': False} + }, + { + 'measure_dir_name': 'export_modelica_loads', + 'arguments': {'__SKIP__': False} + }, + ] + osw_dict['steps'].extend(modelica_measures) + + # copy the EPW to the project directory + epw_f_name = os.path.split(epw_file)[-1] + target_epw = os.path.join(project_directory, epw_f_name) + shutil.copy(epw_file, target_epw) + # create a MOS file from the EPW + epw_obj = EPW(target_epw) + mos_file = os.path.join( + project_directory, epw_f_name.replace('.epw', '.mos')) + epw_obj.to_mos(mos_file) + # find the path to the feature GeoJSON + feature_geojson = None + for fp in os.listdir(project_directory): + if fp.endswith('.geojson'): + feature_geojson = os.path.join(project_directory, fp) + break + if not feature_geojson: + raise ValueError( + 'No feature geojson file was found in: {}'.format(project_directory)) + # write the EPW path into the GeoJSON + with open(feature_geojson, 'r') as gjf: + geo_dict = json.load(gjf) + if 'project' in geo_dict: + if 'weather_filename' not in geo_dict['project']: + geo_dict['project']['weather_filename'] = epw_f_name + with open(feature_geojson, 'w') as fp: + json.dump(geo_dict, fp, indent=4) + + # if the DES system is GSHP, specify any autocalculated ground temperatures + with open(sys_param_file, 'r') as spf: + sys_dict = json.load(spf) + if 'district_system' in sys_dict: + if 'fifth_generation' in sys_dict['district_system']: + g5_par = sys_dict['district_system']['fifth_generation'] + if 'ghe_parameters' in g5_par: + ghe_par = g5_par['ghe_parameters'] + if 'soil' in ghe_par and 'undisturbed_temp' in ghe_par['soil']: + soil_par = ghe_par['soil'] + if soil_par['undisturbed_temp'] == 'Autocalculate': + epw_obj = EPW(epw_file) + soil_par['undisturbed_temp'] = \ + epw_obj.dry_bulb_temperature.average + with open(sys_param_file, 'w') as fp: + json.dump(sys_dict, fp, indent=4) + + # write the dictionary to a honeybee_workflow.osw + mappers_dir = os.path.join(project_directory, 'mappers') + if not os.path.isdir(mappers_dir): + preparedir(mappers_dir) + osw_json = os.path.join(mappers_dir, 'honeybee_workflow.osw') + with open(osw_json, 'w') as fp: + json.dump(osw_dict, fp, indent=4) + + # copy the Honeybee.rb mapper if it exists in the config + if folders.mapper_path: + shutil.copy(folders.mapper_path, os.path.join(mappers_dir, 'Honeybee.rb')) + + return os.path.abspath(osw_json)
+ + + +
+[docs] +def prepare_urbanopt_folder(feature_geojson, cpu_count=None, verbose=False): + """Prepare a directory with a feature geoJSON for URBANopt simulation. + + This includes copying the Gemfile to the folder and generating the runner.conf + to specify the number of CPUs to be used in the simulation. Lastly, the + the scenario .csv file will be generated from the feature_geojson. + + Args: + feature_geojson: An URBANopt feature geoJSON to be prepared for URBANopt + simulation. + cpu_count: A positive integer for the number of CPUs to use in the + simulation. This number should not exceed the number of CPUs on the + machine running the simulation and should be lower if other tasks + are running while the simulation is running. If set to None, it + should automatically default to one less than the number of CPUs + currently available on the machine (or 1 if the machine has only + one processor). (Default: None). + verbose: Boolean to note if the simulation should be run with verbose + reporting of progress. (Default: False). + + Returns: + Path to the .csv file for the URBANopt scenario. + """ + # copy the Gemfile into the folder containing the feature_geojson + assert folders.urbanopt_gemfile_path, \ + 'No URBANopt Gemfile was found in dragonfly_energy.config.folders.\n' \ + 'This file must exist to run URBANopt.' + folders.check_urbanopt_version() + uo_folder = os.path.dirname(feature_geojson) + shutil.copy(folders.urbanopt_gemfile_path, os.path.join(uo_folder, 'Gemfile')) + + # auto-assign the number of processors if None + cpu_count = _recommended_processor_count() if cpu_count is None else cpu_count + + # generate the runner.conf to set the number of CPUs based on the input + runner_dict = { + 'file_version': '0.1.0', + 'max_datapoints': 1000000000, + 'num_parallel': cpu_count, + 'run_simulations': True, + 'verbose': verbose + } + runner_conf = os.path.join(uo_folder, 'runner.conf') + with open(runner_conf, 'w') as fp: + json.dump(runner_dict, fp, indent=2) + + # generate the scenario csv file + return _make_scenario(feature_geojson)
+ + + +
+[docs] +def run_urbanopt(feature_geojson, scenario_csv, cpu_count=None): + """Run a feature and scenario file through URBANopt on any operating system. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + cpu_count: This input is deprecated and currently not used given that + the runner.conf generated in the prepare_urbanopt_folder step + correctly identifies the number of CPUs to be used. + + Returns: + A series of file paths to the simulation output files + + - osm -- Array of paths to .osm files for all generated OpenStudio models. + + - idf -- Array of paths to .idf files containing the input for the + EnergyPlus simulation. + + - sql -- Array of paths to .sqlite files containing all simulation results. + + - zsz -- Array of paths to .csv files containing detailed zone load + information recorded over the course of the design days. + + - rdd -- Array of paths to .rdd files containing all possible outputs + that can be requested from the simulation. + + - html -- Array of paths to .html files containing all summary reports. + + - err -- Array of paths to .err files containing all errors and warnings + from the simulation. + """ + folders.check_urbanopt_version() + # run the simulation + if os.name == 'nt': # we are on Windows + directory, stderr = \ + _run_urbanopt_windows(feature_geojson, scenario_csv) + else: # we are on Mac, Linux, or some other unix-based system + directory, stderr = \ + _run_urbanopt_unix(feature_geojson, scenario_csv) + + # output the simulation files + return _output_urbanopt_files(directory, stderr)
+ + + +
+[docs] +def run_default_report(feature_geojson, scenario_csv): + """Generate default reports after an URBANopt simulation is run. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + A series of file paths to the report output files + + - csv -- Path to a CSV file containing default scenario results. + + - report_json -- Path to a JSON file containing default scenario results. + """ + folders.check_urbanopt_version() + assert os.path.isfile(feature_geojson), \ + 'No feature_geojson as found at the specified path: {}.'.format(feature_geojson) + assert os.path.isfile(scenario_csv), \ + 'No scenario_csv as found at the specified path: {}.'.format(scenario_csv) + # run the report command + if os.name == 'nt': # we are on Windows + return _run_default_report_windows(feature_geojson, scenario_csv) + else: # we are on Mac, Linux, or some other unix-based system + return _run_default_report_unix(feature_geojson, scenario_csv)
+ + + +
+[docs] +def run_reopt(feature_geojson, scenario_csv, urdb_label, reopt_parameters=None, + developer_key=None): + """Run a feature and scenario file through REopt post processing. + + Note that the URBANopt simulation must already be run with the input feature_geojson + and scenario_csv in order for the post-processing to be successful. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + urdb_label: Text string for the Utility Rate Database (URDB) label for the + particular electrical utility rate for the optimization. The label is + the last term of the URL of a utility rate detail page (eg. the label for + the rate at https://openei.org/apps/IURDB/rate/view/5b0d83af5457a3f276733305 + is 5b0d83af5457a3f276733305). Utility rates for specific locations + can be looked up in the REopt Lite tool (https://reopt.nrel.gov/tool) + and the label can be obtained by clicking on "Rate Details" link + for a particular selected rate. + reopt_parameters: A REoptParameter object to describe the major assumptions + of the REopt analysis. If None some default parameters will be + generated for a typical analysis. (Default: None). + developer_key: Text string for the NREL developer key. You can get a developer + key at (https://developer.nrel.gov/). (Default: None). + + Returns: + A series of file paths to the simulation output files + + - csv -- Path to a CSV file containing scenario optimization results. + + - report_json -- Path to a JSON file containing scenario optimization results. + """ + # run checks on the inputs + folders.check_urbanopt_version() + assert folders.reopt_assumptions_path, \ + 'No REopt assumptions were found in dragonfly_energy.config.folders.' + assert os.path.isfile(feature_geojson), \ + 'No feature_geojson as found at the specified path: {}.'.format(feature_geojson) + assert os.path.isfile(scenario_csv), \ + 'No scenario_csv as found at the specified path: {}.'.format(scenario_csv) + developer_key = developer_key if developer_key is not None \ + else 'bo8jGuFfk7DBDpTlzShuxWuAGletBq1j5AhcUhCD' + project_folder = os.path.dirname(feature_geojson) + + # write the parameter file + if reopt_parameters is None: # generate some defaults + reopt_parameters = REoptParameter() + reopt_parameters.pv_parameter.max_kw = 1000000000 + reopt_parameters.storage_parameter.max_kw = 1000000 + reopt_parameters.generator_parameter.max_kw = 1000000000 + else: + assert isinstance(reopt_parameters, REoptParameter), \ + 'Expected REoptParameter. Got {}.'.format(type(reopt_parameters)) + reopt_folder = os.path.join(project_folder, 'reopt') + if not os.path.isdir(reopt_folder): + os.mkdir(reopt_folder) + reopt_par_json = os.path.join(reopt_folder, 'reopt_assumptions.json') + reopt_dict = reopt_parameters.to_assumptions_dict( + folders.reopt_assumptions_path, urdb_label) + with open(reopt_par_json, 'w') as fp: + json.dump(reopt_dict, fp, indent=4) + + # run the simulation + if os.name == 'nt': # we are on Windows + directory = _run_reopt_windows(feature_geojson, scenario_csv, developer_key) + else: # we are on Mac, Linux, or some other unix-based system + directory = _run_reopt_unix(feature_geojson, scenario_csv, developer_key) + + # output the simulation files + return _output_reopt_files(directory)
+ + + +
+[docs] +def run_rnm(feature_geojson, scenario_csv, underground_ratio=0.9, lv_only=True, + nodes_per_building=1): + """Run a feature and scenario file through RNM post processing. + + Note that the URBANopt simulation must already be run with the input feature_geojson + and scenario_csv in order for the RNM post-processing to be successful. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + underground_ratio: A number between 0 and 1 for the ratio of overall cables + that are underground vs. overhead in the analysis. (Default: 0.9). + lv_only: Boolean to note whether to consider only low voltage consumers + in the analysis. (Default: True). + nodes_per_building: Positive integer for the maximum number of low voltage + nodes to represent a single building. (Default: 1). + + Returns: + Path to a folder that contains all of the RNM output files. + """ + # load the information from the GeoJSON + geo_dict, project_dict = None, None + if os.path.isfile(feature_geojson): + with open(feature_geojson, 'r') as fg: + geo_dict = json.load(fg) + project_dict = geo_dict['project'] + # change the GeoJSON to have the RNM inputs + if geo_dict is not None: + proj = geo_dict['project'] + if 'underground_cables_ratio' not in proj or \ + proj['underground_cables_ratio'] != underground_ratio or \ + 'only_lv_consumers' not in proj or \ + proj['only_lv_consumers'] != lv_only or \ + 'max_number_of_lv_nodes_per_building' not in proj or \ + proj['max_number_of_lv_nodes_per_building'] != nodes_per_building: + geo_dict['project']['underground_cables_ratio'] = underground_ratio + geo_dict['project']['only_lv_consumers'] = lv_only + geo_dict['project']['max_number_of_lv_nodes_per_building'] = \ + nodes_per_building + with open(feature_geojson, 'w') as fp: + json.dump(geo_dict, fp, indent=4) + # run the simulation + folders.check_urbanopt_version() + if os.name == 'nt': # we are on Windows + directory = _run_rnm_windows(feature_geojson, scenario_csv) + else: # we are on Mac, Linux, or some other unix-based system + directory = _run_rnm_unix(feature_geojson, scenario_csv) + # get the path to the results folder + scenario_name = os.path.basename(scenario_csv).replace('.csv', '') + rnm_path = os.path.join(directory, 'run', scenario_name, 'rnm-us', 'results') + # copy the project information into the RNM GeoJSON + if os.path.isdir(rnm_path): + rnm_geojson = os.path.join(rnm_path, 'GeoJSON', 'Distribution_system.json') + with open(rnm_geojson, 'r') as fg: + rnm_dict = json.load(fg) + rnm_dict['project'] = project_dict + with open(rnm_geojson, 'w') as fp: + json.dump(rnm_dict, fp, indent=4) + return rnm_path
+ + + +
+[docs] +def run_des_sys_param(feature_geojson, scenario_csv): + """Run the GMT command to add the time series building loads to the sys param JSON. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + """ + # get the directory and parse the system parameter file + directory = os.path.dirname(feature_geojson) + sys_param_file = os.path.join(directory, 'system_params.json') + assert os.path.isfile(sys_param_file), \ + 'No DES system parameter was found for this model.\n' \ + 'Make sure that the des_loop_ was assigned in the GeoJSON export\n' \ + 'before running the URBANopt simulation.' + + # parse the system parameter file to understand the type of system + with open(sys_param_file, 'r') as spf: + sp_dict = json.load(spf) + des_dict = sp_dict['district_system'] + ghe_sys = True if 'fifth_generation' in des_dict and \ + 'ghe_parameters' in des_dict['fifth_generation'] else False + + # run the command that adds the building loads to the system parameter + ext = '.exe' if os.name == 'nt' else '' + shell = True if os.name == 'nt' else False + uo_des_exe = os.path.join( + hb_folders.python_scripts_path, 'uo_des{}'.format(ext)) + build_cmd = '"{des_exe}" build-sys-param "{sp_file}" "{scenario}" "{feature}" ' \ + 'time_series -o'.format( + des_exe=uo_des_exe, sp_file=sys_param_file, + scenario=scenario_csv, feature=feature_geojson) + if ghe_sys: + build_cmd = '{} --ghe'.format(build_cmd) + process = subprocess.Popen( + build_cmd, stderr=subprocess.PIPE, shell=shell, env=PYTHON_ENV + ) + stderr = process.communicate() + if not os.path.isfile(sys_param_file): + msg = 'Failed to add building loads to the DES system parameter file.\n' \ + 'No file found at:\n{}\n{}'.format(sys_param_file, stderr[1]) + print(msg) + raise Exception(msg) + else: + print(stderr[1]) + + # after the loads have been added, put pack the properties of the DES + with open(sys_param_file, 'r') as spf: + sp_dict = json.load(spf) + if ghe_sys: + original_ghe_par = des_dict['fifth_generation']['ghe_parameters'] + ghe_par = sp_dict['district_system']['fifth_generation']['ghe_parameters'] + ghe_par['fluid'] = original_ghe_par['fluid'] + ghe_par['grout'] = original_ghe_par['grout'] + ghe_par['soil'] = original_ghe_par['soil'] + ghe_par['pipe'] = original_ghe_par['pipe'] + ghe_par['geometric_constraints'] = original_ghe_par['geometric_constraints'] + ghe_par['ghe_specific_params'] = original_ghe_par['ghe_specific_params'] + else: + sp_dict['district_system'] = des_dict + with open(sys_param_file, 'w') as spf: + json.dump(sp_dict, spf, indent=2) + + # if the DES system has a ground heat exchanger, run the thermal network package + if ghe_sys: + # check to be sure the user will not max out their RAM + total_area = 0 + for ghe_sp in ghe_par['ghe_specific_params']: + ghe_len = ghe_sp['ghe_geometric_params']['length_of_ghe'] + ghe_wth = ghe_sp['ghe_geometric_params']['width_of_ghe'] + total_area += ghe_len * ghe_wth + bh_count = int(total_area / (ghe_par['geometric_constraints']['b_min'] ** 2)) + if bh_count > MAX_BOREHOLES: + msg = 'The inputs suggest that there may be as many as {} boreholes in the ' \ + 'GHE field\nand this will cause your machine to run out of memory.\n' \ + 'A smaller GHE field or a larger minimum borehole spacing is needed ' \ + 'such that fewer\nthan {} boreholes are generated and the sizing ' \ + 'simulation can succeed.'.format(bh_count, MAX_BOREHOLES) + raise ValueError(msg) + # run the GHE Designer to size the system + tn_exe = os.path.join( + hb_folders.python_scripts_path, 'thermalnetwork{}'.format(ext)) + scn_name = os.path.basename(scenario_csv).replace('.csv', '') + scn_dir = os.path.join(directory, 'run', scn_name) + ghe_dir = os.path.join(scn_dir, 'ghe_dir') + build_cmd = \ + '"{tn_exe}" -y "{sp_file}" -s "{scenario}" -f "{feature}" -o {out_p}'.format( + tn_exe=tn_exe, sp_file=sys_param_file, + scenario=scn_dir, feature=feature_geojson, out_p=ghe_dir) + process = subprocess.Popen( + build_cmd, stderr=subprocess.PIPE, shell=False, env=PYTHON_ENV + ) + # if any errors were found in the sizing simulation, raise them to the user + stderr = process.communicate()[1] + stderr_str = str(stderr.strip()) + print(stderr_str) + if 'ValueError' in stderr_str: # pass the exception onto the user + msg = stderr_str.split('ValueError: ')[-1].strip() + raise ValueError(msg) + # add the borehole length and count to the system parameter file + with open(sys_param_file, 'r') as spf: + sp_dict = json.load(spf) + ghe_par_dict = sp_dict['district_system']['fifth_generation']['ghe_parameters'] + for ghe_s_par in ghe_par_dict['ghe_specific_params']: + r_dir = ghe_par['ghe_dir'] + res_file = os.path.join(r_dir, ghe_s_par['ghe_id'], 'SimulationSummary.json') + with open(res_file, 'r') as rf: + res_dict = json.load(rf) + ghe_s_par['borehole']['length_of_boreholes'] = \ + res_dict['ghe_system']['active_borehole_length']['value'] + ghe_s_par['borehole']['number_of_boreholes'] = \ + res_dict['ghe_system']['number_of_boreholes'] + with open(sys_param_file, 'w') as spf: + json.dump(sp_dict, spf, indent=2) + return sys_param_file
+ + + +
+[docs] +def run_des_modelica(sys_param_json, feature_geojson, scenario_csv): + """Run the GMT command to create the Modelica files from the system param JSON. + + Args: + sys_param_json: The full path to a system parameter JSON from which the + Modelica files will be generated. + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + The path to the folder where the Modelica files have been written. + """ + # run the simulation + if os.name == 'nt': # we are on Windows + modelica_dir, stderr = \ + _generate_modelica_windows(sys_param_json, feature_geojson, scenario_csv) + else: # we are on Mac, Linux, or some other unix-based system + modelica_dir, stderr = \ + _generate_modelica_unix(sys_param_json, feature_geojson, scenario_csv) + if not os.path.isdir(modelica_dir): + msg = 'Failed to translate DES to Modelica.\n' \ + 'No results were found at:\n{}\n{}'.format(modelica_dir, stderr) + print(msg) + raise Exception(msg) + else: + _add_water_heating_patch(modelica_dir) + return modelica_dir
+ + + +
+[docs] +def run_modelica_docker(modelica_project_dir): + """Execute Modelica files of a DES. + + Args: + modelica_project_dir: The full path to the folder in which the Modelica + files were written. + + Returns: + The path to where the results have been written. + """ + # run the simulation + if os.name == 'nt': # we are on Windows + modelica_dir, stderr = _run_modelica_windows(modelica_project_dir) + else: # we are on Mac, Linux, or some other unix-based system + modelica_dir, stderr = _run_modelica_unix(modelica_project_dir) + if not os.path.isdir(modelica_dir): + msg = 'Failed to execute Modelica simulation.\n' \ + 'No results were found at:\n{}\n{}'.format(modelica_dir, stderr) + print(msg) + raise Exception(msg) + return modelica_dir
+ + + +def _add_mapper_measure(project_directory, mapper_measure): + """Add mapper measure arguments to a geoJSON and the mapper_measures.json. + + Args: + project_directory: Full path to a folder out of which the URBANopt simulation + will be run. This is the folder that contains the feature geoJSON. + mapper_measure: A MapperMeasure object to add. + """ + # find the feature geoJSON and parse in the dictionary + for proj_file in os.listdir(project_directory): + if proj_file.endswith('geojson'): + geojson_file = os.path.join(project_directory, proj_file) + break + with open(geojson_file, 'r') as base_file: + geojson_dict = json.load(base_file) + + # find or start the mapper_measures.json + mapper_dir = os.path.join(project_directory, 'mappers') + map_meas_file = os.path.join(mapper_dir, 'mapper_measures.json') + if os.path.isfile(map_meas_file): + with open(map_meas_file, 'r') as base_file: + map_meas_list = json.load(base_file) + else: + map_meas_list = [] + + # loop through the mapper measure and assign any mapper arguments + for m_arg in mapper_measure.arguments: + if isinstance(m_arg.value, tuple): # argument to map to buildings + for i, feat in enumerate(geojson_dict['features']): + try: + if feat['properties']['type'] == 'Building': + feat['properties'][m_arg.identifier] = m_arg.value[i] + except IndexError: + raise ValueError( + 'Number of MapperMeasure arguments ({}) does not equal the ' + 'number of buildings in the model ({}).'.format( + len(m_arg.value), len(geojson_dict['features']))) + except KeyError: # definitely not a building + pass + m_arg_info = [ + os.path.basename(mapper_measure.folder), + m_arg.identifier, m_arg.identifier] + map_meas_list.append(m_arg_info) + + # write the geoJSON and the mapper_measures.json + if not os.path.isdir(mapper_dir): + os.mkdir(mapper_dir) + with open(geojson_file, 'w') as fp: + json.dump(geojson_dict, fp, indent=4) + with open(map_meas_file, 'w') as fp: + json.dump(map_meas_list, fp, indent=4) + + +def _make_scenario(feature_geojson): + """Generate a scenario CSV file for URBANopt simulation. + + Args: + feature_geojson: The full path to a .geojson file. + """ + # load the geoJSON to a dictionary + with open(feature_geojson, 'r') as base_file: + geo_dict = json.load(base_file) + + # loop through the building features and add them to the CSV + scenario_matrix = [['Feature Id', 'Feature Name', 'Mapper Class']] + hb_mapper = 'URBANopt::Scenario::HoneybeeMapper' + for feature in geo_dict['features']: + try: + if feature['properties']['type'] == 'Building': + props = feature['properties'] + f_row = [props['id'], props['name'], hb_mapper] + scenario_matrix.append(f_row) + except KeyError: # definitely not a building + pass + + # write the scenario CSV file + uo_folder = os.path.dirname(feature_geojson) + scenario = os.path.join(uo_folder, 'honeybee_scenario.csv') + with open(scenario, 'w') as fp: + for row in scenario_matrix: + fp.write('{}\n'.format(','.join(row))) + return scenario + + +def _recommended_processor_count(): + """Get an integer for one less than the number of processors on this machine. + + This method should work on all of the major operating systems and in + both IronPython and cPython. If, for whatever reason, the number of + processors could not be sensed, a value of 1 will be returned. + """ + try: # assume that we are in cPython + cpu_count = os.cpu_count() + except AttributeError: # we are probably in IronPython + try: + from System.Environment import ProcessorCount + cpu_count = ProcessorCount + except ImportError: # no idea what Python this is; let's play it safe + cpu_count = 1 + return 1 if cpu_count is None or cpu_count <= 1 else cpu_count - 1 + + +def _run_urbanopt_windows(feature_geojson, scenario_csv): + """Run a feature and scenario file through URBANopt on a Windows-based os. + + A batch file will be used to run the simulation. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + A tuple with two values. + + - directory -- Path to the folder out of which the simulation was run. + + - stderr -- The standard error message, which should get to the user + in the event of simulation failure. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the batch file to call URBANopt CLI + working_drive = directory[:2] + batch = '{}\ncd {}\ncall "{}"\nuo run -f "{}" -s "{}"'.format( + working_drive, working_drive, folders.urbanopt_env_path, + feature_geojson, scenario_csv) + batch_file = os.path.join(directory, 'run_simulation.bat') + write_to_file(batch_file, batch, True) + # run the batch file + process = subprocess.Popen( + '"{}"'.format(batch_file), stderr=subprocess.PIPE, env=PYTHON_ENV + ) + result = process.communicate() + stderr = result[1] + return directory, stderr + + +def _run_urbanopt_unix(feature_geojson, scenario_csv): + """Run a feature and scenario file through URBANopt on a Unix-based os. + + This includes both Mac OS and Linux since a shell will be used to run + the simulation. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + A tuple with two values. + + - directory -- Path to the folder out of which the simulation was run. + + - stderr -- The standard error message, which should get to the user + in the event of simulation failure. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the shell script to call URBANopt CLI + shell = '#!/usr/bin/env bash\nsource "{}"\nuo run -f "{}" -s "{}"'.format( + folders.urbanopt_env_path, feature_geojson, scenario_csv) + shell_file = os.path.join(directory, 'run_simulation.sh') + write_to_file(shell_file, shell, True) + # make the shell script executable using subprocess.check_call + # this is more reliable than native Python chmod on Mac + subprocess.check_call(['chmod', 'u+x', shell_file]) + # run the shell script + process = subprocess.Popen( + '"{}"'.format(shell_file), stderr=subprocess.PIPE, env=PYTHON_ENV, shell=True + ) + result = process.communicate() + stderr = result[1] + return directory, stderr + + +def _check_urbanopt_file(feature_geojson, scenario_csv): + """Prepare an OSW file to be run through URBANopt CLI. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + The folder in which the OSW exists and out of which the OpenStudio CLI + will operate. + """ + # check the input files + assert os.path.isfile(feature_geojson), \ + 'No feature file found at {}.'.format(feature_geojson) + assert os.path.isfile(scenario_csv), \ + 'No scenario file found at {}.'.format(scenario_csv) + return os.path.split(feature_geojson)[0] + + +def _output_urbanopt_files(directory, stderr=''): + """Get the paths to the simulation output files given the urbanopt directory. + + Args: + directory: The path to where the URBANopt feature and scenario files + were simulated. + stderr: The URBANopt standard error message, which will be returned to + the user in the event that no simulation folder was found. + + Returns: + A series of file paths to the simulation output files + + - osm -- Array of paths to .osm files for all generated OpenStudio models. + + - idf -- Array of paths to .idf files containing the input for the + EnergyPlus simulation. + + - sql -- Array of paths to .sqlite files containing all simulation results. + + - zsz -- Array of paths to .csv files containing detailed zone load + information recorded over the course of the design days. + + - rdd -- Array of paths to .rdd files containing all possible outputs + that can be requested from the simulation. + + - html -- Array of paths to .htm files containing all summary reports. + + - err -- Array of paths to .err files containing all errors and + warnings from the simulation. + """ + # empty list which will be filled with simulation output files + osm = [] + idf = [] + sql = [] + zsz = [] + rdd = [] + html = [] + err = [] + + # parse the GeoJSON so that we can get the correct order of result files + sim_dir = os.path.join(directory, 'run', 'honeybee_scenario') + if not os.path.isdir(sim_dir): + msg = 'The URBANopt simulation failed to run.\n' \ + 'No results were found at:\n{}\n{}'.format(sim_dir, stderr) + print(msg) + raise Exception(msg) + geojson = [f for f in os.listdir(directory) if f.endswith('.geojson')] + if len(geojson) == 1: + geo_file = os.path.join(directory, geojson[0]) + with open(geo_file, 'r') as base_file: + geo_dict = json.load(base_file) + bldg_names = [] + for ft in geo_dict['features']: + if 'properties' in ft and 'type' in ft['properties']: + if ft['properties']['type'] == 'Building' and 'id' in ft['properties']: + bldg_names.append(ft['properties']['id']) + else: + bldg_names = os.listdir(sim_dir) + + # generate paths to the simulation files and check their existence + for bldg_name in bldg_names: + bldg_dir = os.path.join(sim_dir, bldg_name) + osm_file = os.path.join(bldg_dir, 'in.osm') + if os.path.isfile(osm_file): + osm.append(osm_file) + idf_file = os.path.join(bldg_dir, 'in.idf') + if os.path.isfile(idf_file): + idf.append(idf_file) + sql_file = os.path.join(bldg_dir, 'eplusout.sql') + if os.path.isfile(sql_file): + sql.append(sql_file) + zsz_file = os.path.join(bldg_dir, 'epluszsz.csv') + if os.path.isfile(zsz_file): + zsz.append(zsz_file) + rdd_file = os.path.join(bldg_dir, 'eplusout.rdd') + if os.path.isfile(rdd_file): + rdd.append(rdd_file) + html_file = os.path.join(bldg_dir, 'eplustbl.htm') + if os.path.isfile(html_file): + html.append(html_file) + err_file = os.path.join(bldg_dir, 'eplusout.err') + if os.path.isfile(err_file): + err.append(err_file) + + return osm, idf, sql, zsz, rdd, html, err + + +def _run_default_report_windows(feature_geojson, scenario_csv): + """Generate default reports on a Windows-based os. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + Paths to the scenario CSV and JSON reports. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the batch file to call URBANopt CLI + working_drive = directory[:2] + batch = '{}\ncd {}\ncall "{}"\nuo process --default -f "{}" -s "{}"'.format( + working_drive, working_drive, folders.urbanopt_env_path, + feature_geojson, scenario_csv) + batch_file = os.path.join(directory, 'run_default_report.bat') + write_to_file(batch_file, batch, True) + # run the batch file and return output files + os.system('"{}"'.format(batch_file)) + result_folder = os.path.basename(scenario_csv).lower().replace('.csv', '') + run_folder = os.path.join(directory, 'run', result_folder) + return os.path.join(run_folder, 'default_scenario_report.csv'), \ + os.path.join(run_folder, 'default_scenario_report.json') + + +def _run_default_report_unix(feature_geojson, scenario_csv): + """Generate default reports on a Unix-based os. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + Paths to the scenario CSV and JSON reports. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the shell script to call OpenStudio CLI + shell = '#!/usr/bin/env bash\nsource "{}"\n' \ + 'uo process --default -f "{}" -s "{}"'.format( + folders.urbanopt_env_path, feature_geojson, scenario_csv) + shell_file = os.path.join(directory, 'run_default_report.sh') + write_to_file(shell_file, shell, True) + # make the shell script executable using subprocess.check_call + # this is more reliable than native Python chmod on Mac + subprocess.check_call(['chmod', 'u+x', shell_file]) + # run the shell script + subprocess.call(shell_file) + result_folder = os.path.basename(scenario_csv).lower().replace('.csv', '') + run_folder = os.path.join(directory, 'run', result_folder) + return os.path.join(run_folder, 'default_scenario_report.csv'), \ + os.path.join(run_folder, 'default_scenario_report.json') + + +def _run_reopt_windows(feature_geojson, scenario_csv, developer_key): + """Run a feature and scenario file through REopt on a Windows-based os. + + A batch file will be used to run the simulation. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + developer_key: Text string for the NREL developer key. + + Returns: + Path to the folder in which results should be contained. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the batch file to call URBANopt CLI + working_drive = directory[:2] + batch = '{}\ncd {}\ncall "{}"\nSET GEM_DEVELOPER_KEY={}\n' \ + 'uo process --reopt-scenario -f "{}" -s "{}"'.format( + working_drive, working_drive, folders.urbanopt_env_path, developer_key, + feature_geojson, scenario_csv) + batch_file = os.path.join(directory, 'run_reopt.bat') + write_to_file(batch_file, batch, True) + # run the batch file + os.system('"{}"'.format(batch_file)) + result_folder = os.path.basename(scenario_csv).lower().replace('.csv', '') + return os.path.join(directory, 'run', result_folder) + + +def _run_reopt_unix(feature_geojson, scenario_csv, developer_key): + """Run a feature and scenario file through REopt on a Unix-based os. + + This includes both Mac OS and Linux since a shell will be used to run + the simulation. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + developer_key: Text string for the NREL developer key. + + Returns: + Path to the folder in which results should be contained. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the shell script to call OpenStudio CLI + shell = '#!/usr/bin/env bash\nsource "{}"\nGEM_DEVELOPER_KEY={}\n' \ + 'uo process --reopt-scenario -f "{}" -s "{}"'.format( + folders.urbanopt_env_path, developer_key, feature_geojson, scenario_csv) + shell_file = os.path.join(directory, 'run_reopt.sh') + write_to_file(shell_file, shell, True) + # make the shell script executable using subprocess.check_call + # this is more reliable than native Python chmod on Mac + subprocess.check_call(['chmod', 'u+x', shell_file]) + # run the shell script + subprocess.call(shell_file) + result_folder = os.path.basename(scenario_csv).lower().replace('.csv', '') + return os.path.join(directory, 'run', result_folder) + + +def _output_reopt_files(directory): + """Get the paths to the simulation output files given the reopt directory. + + Args: + directory: The path to the folder in which results should be contained. + + Returns: + A series of file paths to the simulation output files + + - csv -- Path to a CSV file containing scenario optimization results. + + - report_json -- Path to a JSON file containing scenario optimization results. + """ + # generate paths to the simulation files + csv_file = os.path.join(directory, 'scenario_optimization.csv') + report_json_file = os.path.join(directory, 'scenario_optimization.json') + # check that the simulation files exist + csv = csv_file if os.path.isfile(csv_file) else None + report_json = report_json_file if os.path.isfile(report_json_file) else None + return csv, report_json + + +def _run_rnm_windows(feature_geojson, scenario_csv): + """Run a feature and scenario file through RNM on a Windows-based os. + + A batch file will be used to run the simulation. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + Path to the project folder. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the batch file to call URBANopt CLI + working_drive = directory[:2] + batch = '{}\ncd {}\ncall "{}"\nuo rnm --feature "{}" --scenario "{}"'.format( + working_drive, working_drive, folders.urbanopt_env_path, + feature_geojson, scenario_csv) + batch_file = os.path.join(directory, 'run_rnm.bat') + write_to_file(batch_file, batch, True) + # run the batch file + os.system('"{}"'.format(batch_file)) + return directory + + +def _run_rnm_unix(feature_geojson, scenario_csv): + """Run a feature and scenario file through RNM on a Unix-based os. + + This includes both Mac OS and Linux since a shell script will be used to run + the simulation. + + Args: + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + Path to the project folder. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # Write the shell script to call URBANopt CLI + shell = '#!/usr/bin/env bash\nsource "{}"\nuo rnm --feature "{}" -s-scenario ' \ + '"{}"'.format(folders.urbanopt_env_path, feature_geojson, scenario_csv) + shell_file = os.path.join(directory, 'run_rnm.sh') + write_to_file(shell_file, shell, True) + # make the shell script executable using subprocess.check_call + # this is more reliable than native Python chmod on Mac + subprocess.check_call(['chmod', 'u+x', shell_file]) + # run the shell script + subprocess.call(shell_file) + return directory + + +def _generate_modelica_windows(sys_param_json, feature_geojson, scenario_csv): + """Generate Modelica files for a DES on a Windows-based os. + + A batch file will be used to run the simulation. + + Args: + sys_param_json: The full path to a system parameter JSON from which the + Modelica files will be generated. + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + A tuple with two values. + + - modelica_dir -- Path to the folder in which the Modelica files were written. + + - stderr -- The standard error message, which should get to the user + in the event of simulation failure. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # get the path to the MBL installation + install_directory = os.path.join(lb_folders.ladybug_tools_folder, 'resources') + mbl_dir = os.path.join(install_directory, 'mbl') + assert os.path.isdir(mbl_dir), \ + 'No Modelica Buildings Library installation was found on this machine.' + # get the paths to the output files + scn_name = os.path.basename(scenario_csv).replace('.csv', '') + modelica_dir = os.path.join(directory, 'run', scn_name, 'des_modelica') + uo_des_exe = os.path.join(hb_folders.python_scripts_path, 'uo_des.exe') + # Write the batch file to call the GMT + working_drive = directory[:2] + batch = '{}\ncd {}\ncall "{}"\nSET "MODELICAPATH={}"\n"{}" create-model ' \ + '"{}" "{}" "{}" --overwrite'.format( + working_drive, working_drive, folders.urbanopt_env_path, mbl_dir, + uo_des_exe, sys_param_json, feature_geojson, modelica_dir) + batch_file = os.path.join(directory, 'generate_modelica.bat') + write_to_file(batch_file, batch, True) + # run the batch file + process = subprocess.Popen( + '"{}"'.format(batch_file), stderr=subprocess.PIPE, env=PYTHON_ENV + ) + result = process.communicate() + stderr = result[1] + return modelica_dir, stderr + + +def _generate_modelica_unix(sys_param_json, feature_geojson, scenario_csv): + """Generate Modelica files for a DES on a Unix-based os. + + This includes both Mac OS and Linux since a shell will be used to run + the simulation. + + Args: + sys_param_json: The full path to a system parameter JSON from which the + Modelica files will be generated. + feature_geojson: The full path to a .geojson file containing the + footprints of buildings to be simulated. + scenario_csv: The full path to a .csv file for the URBANopt scenario. + + Returns: + A tuple with two values. + + - directory -- Path to the folder out of which the simulation was run. + + - stderr -- The standard error message, which should get to the user + in the event of simulation failure. + """ + # check the input file + directory = _check_urbanopt_file(feature_geojson, scenario_csv) + # get the path to the MBL installation + install_directory = os.path.join(lb_folders.ladybug_tools_folder, 'resources') + mbl_dir = os.path.join(install_directory, 'mbl') + assert os.path.isdir(mbl_dir), \ + 'No Modelica Buildings Library installation was found on this machine.' + # get the paths to the output files + scn_name = os.path.basename(scenario_csv).replace('.csv', '') + modelica_dir = os.path.join(directory, 'run', scn_name, 'des_modelica') + uo_des_exe = os.path.join(hb_folders.python_scripts_path, 'uo_des') + # write the shell script to call the GMT + shell = '#!/usr/bin/env bash\nsource "{}"\nexport MODELICAPATH="{}"\n' \ + '"{}" create-model "{}" "{}" "{}" --overwrite'.format( + folders.urbanopt_env_path, mbl_dir, + uo_des_exe, sys_param_json, feature_geojson, modelica_dir) + shell_file = os.path.join(directory, 'generate_modelica.sh') + write_to_file(shell_file, shell, True) + # make the shell script executable using subprocess.check_call + # this is more reliable than native Python chmod on Mac + subprocess.check_call(['chmod', 'u+x', shell_file]) + # run the shell script + process = subprocess.Popen( + '"{}"'.format(shell_file), stderr=subprocess.PIPE, env=PYTHON_ENV, shell=True + ) + result = process.communicate() + stderr = result[1] + return modelica_dir, stderr + + +def _add_water_heating_patch(modelica_dir): + """Add a dummy value for water heating for MBL 10 limitation.""" + data_dir = os.path.join(modelica_dir, 'Loads', 'Resources', 'Data') + if os.path.isdir(data_dir): + for bldg_dir in os.listdir(data_dir): + mo_load_file = os.path.join(data_dir, bldg_dir, 'modelica.mos') + if os.path.isfile(mo_load_file): + fixed_lines, fl_found = [], False + with open(mo_load_file, 'r') as mlf: + for line in mlf: + if line == '#Peak water heating load = 0 Watts\n': + nl = '#Peak water heating load = 1 Watts\n' + fixed_lines.append(nl) + elif not fl_found and ';' in line: + split_vals = line.split(';') + split_vals[-1] = '1.0\n' + fixed_lines.append(';'.join(split_vals)) + fl_found = True + else: + fixed_lines.append(line) + with open(mo_load_file, 'w') as mlf: + mlf.write(''.join(fixed_lines)) + + +def _run_modelica_windows(modelica_project_dir): + """Execute Modelica files of a DES on a Windows-based OS. + + A batch file will be used to run the simulation. + + Args: + modelica_project_dir: The full path to the folder in which the Modelica + files were written. + + Returns: + A tuple with two values. + + - results -- Path to where the results were written. + + - stderr -- The standard error message, which should get to the user + in the event of simulation failure. + """ + # make sure that docker is installed + assert folders.docker_version_str is not None, \ + 'No Docker installation was found on this machine.\n' \ + 'This is needed to execute Modelica simulations.' + # get the paths to the output files + directory = os.path.dirname(modelica_project_dir) + project_name = os.path.basename(modelica_project_dir) + results = os.path.join( + modelica_project_dir, + '{}.Districts.DistrictEnergySystem_results'.format(project_name)) + uo_des_exe = os.path.join(hb_folders.python_scripts_path, 'uo_des.exe') + # Write the batch file to call the GMT + working_drive = modelica_project_dir[:2] + batch = '{}\ncd {}\ncall "{}"\n"{}" run-model "{}"'.format( + working_drive, working_drive, folders.urbanopt_env_path, + uo_des_exe, modelica_project_dir) + batch_file = os.path.join(directory, 'run_modelica.bat') + write_to_file(batch_file, batch, True) + # run the batch file + process = subprocess.Popen( + '"{}"'.format(batch_file), stderr=subprocess.PIPE, env=PYTHON_ENV + ) + result = process.communicate() + stderr = result[1] + return results, stderr + + +def _run_modelica_unix(modelica_project_dir): + """Execute Modelica files of a DES on a Unix-based OS. + + This includes both Mac OS and Linux since a shell will be used to run + the simulation. + + Args: + modelica_project_dir: The full path to the folder in which the Modelica + files were written. + + Returns: + A tuple with two values. + + - results -- Path to where the results were written. + + - stderr -- The standard error message, which should get to the user + in the event of simulation failure. + """ + # make sure that docker is installed + assert folders.docker_version_str is not None, \ + 'No Docker installation was found on this machine.\n' \ + 'This is needed to execute Modelica simulations.' + # get the paths to the output files + directory = os.path.dirname(modelica_project_dir) + project_name = os.path.basename(modelica_project_dir) + results = os.path.join( + modelica_project_dir, + '{}.Districts.DistrictEnergySystem_results'.format(project_name)) + uo_des_exe = os.path.join(hb_folders.python_scripts_path, 'uo_des') + # write the shell script to call the GMT + shell = '#!/usr/bin/env bash\nsource "{}"\n"{}" run-model "{}"'.format( + folders.urbanopt_env_path, uo_des_exe, modelica_project_dir) + shell_file = os.path.join(directory, 'run_modelica.sh') + write_to_file(shell_file, shell, True) + # make the shell script executable using subprocess.check_call + # this is more reliable than native Python chmod on Mac + subprocess.check_call(['chmod', 'u+x', shell_file]) + # run the shell script + process = subprocess.Popen( + '"{}"'.format(shell_file), stderr=subprocess.PIPE, env=PYTHON_ENV, shell=True + ) + result = process.communicate() + stderr = result[1] + return results, stderr +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/dragonfly_energy/writer.html b/docs/_modules/dragonfly_energy/writer.html new file mode 100644 index 00000000..6fdb5fbc --- /dev/null +++ b/docs/_modules/dragonfly_energy/writer.html @@ -0,0 +1,1177 @@ + + + + + + + dragonfly_energy.writer — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +

Source code for dragonfly_energy.writer

+# coding=utf-8
+"""Methods to write files for URBANopt simulation from a Model."""
+import sys
+import os
+import re
+import json
+
+from ladybug_geometry.geometry2d import Point2D
+from ladybug.futil import nukedir, preparedir
+from honeybee.config import folders
+from honeybee.model import Model as hb_model
+
+
+
+[docs] +def model_to_urbanopt( + model, location, point=Point2D(0, 0), shade_distance=None, use_multiplier=True, + add_plenum=False, solve_ceiling_adjacencies=False, + des_loop=None, electrical_network=None, road_network=None, ground_pv=None, + folder=None, tolerance=0.01 +): + r"""Generate an URBANopt feature geoJSON and honeybee JSONs from a dragonfly Model. + + Args: + model: A dragonfly Model for which an URBANopt feature geoJSON and + corresponding honeybee Model JSONs will be returned. + location: A ladybug Location object possessing longitude and latitude data. + point: A ladybug_geometry Point2D for where the location object exists + within the space of a scene. The coordinates of this point are + expected to be in the units of this Model. (Default: (0, 0)). + shade_distance: An optional number to note the distance beyond which other + objects' shade should not be exported into a given honeybee Model. This + is helpful for reducing the simulation run time of each Model when other + connected buildings are too far away to have a meaningful impact on + the results. If None, all other buildings will be included as context + shade in each and every Model. Set to 0 to exclude all neighboring + buildings from the resulting models. (Default: None). + use_multiplier: If True, the multipliers on the Model's Stories will be + passed along to the generated Honeybee Room objects, indicating the + simulation will be run once for each unique room and then results + will be multiplied. If False, full geometry objects will be written + for each and every floor in the building that are represented through + multipliers and all resulting multipliers will be 1. (Default: True). + add_plenum: Boolean to indicate whether ceiling/floor plenums should + be auto-generated for the Rooms. (Default: False). + solve_ceiling_adjacencies: Boolean to note whether adjacencies should be + solved between interior stories when Room2Ds perfectly match one + another in their floor plate. This ensures that Surface boundary + conditions are used instead of Adiabatic ones. Note that this input + has no effect when the object_per_model is Story. (Default: False). + des_loop: An optional District Energy System (DES) ThermalLoop that's + associated with the dragonfly Model. (Default: None). + electrical_network: An optional OpenDSS ElectricalNetwork that's associated + with the dragonfly Model. (Default: None). + road_network: An optional RNM RoadNetwork that's associated with the + dragonfly Model. (Default: None). + ground_pv: An optional list of REopt GroundMountPV objects representing + ground-mounted photovoltaic fields to be included in the REopt + simulation. (Default: None). + folder: An optional folder to be used as the root of the model's + URBANopt folder. If None, the files will be written into a sub-directory + of the honeybee-core default_simulation_folder. + tolerance: The minimum distance between points at which they are + not considered touching. (Default: 0.01, suitable for objects + in meters). + + Returns: + A tuple with three values. + + feature_geojson -- The path to an URBANopt feature geoJSON that has + been written by this method. + + hb_model_jsons -- An array of file paths to honeybee Model JSONs that + correspond to the detailed_model_filename keys in the feature_geojson. + + hb_models -- An array of honeybee Model objects that were generated in + process of writing the URBANopt files. + """ + # make sure the model is in meters and, if it's not, duplicate and scale it + conversion_factor, original_units = None, 'Meters' + if model.units != 'Meters': + original_units = model.units + conversion_factor = hb_model.conversion_factor_to_meters(model.units) + point = point.scale(conversion_factor) + if shade_distance is not None: + shade_distance = shade_distance * conversion_factor + tolerance = tolerance * conversion_factor + model = model.duplicate() # duplicate the model to avoid mutating the input + model.convert_to_units('Meters') + if des_loop is not None: + des_loop.scale(conversion_factor) + if electrical_network is not None: + electrical_network.scale(conversion_factor) + if road_network is not None: + road_network.scale(conversion_factor) + if ground_pv is not None: + for g_pv in ground_pv: + g_pv.scale(conversion_factor) + + # prepare the folder for simulation + tr_msg = 'The following simulation folder is too long to be used with URBANopt:' \ + '\n{}\nSpecify a shorter folder path in which to write the GeoJSON.' + if folder is None: # use the default simulation folder + assert len(folders.default_simulation_folder) < 55, \ + tr_msg.format(folders.default_simulation_folder) + sim_dir = re.sub(r'[^.A-Za-z0-9_-]', '_', model.display_name) + folder = os.path.join(folders.default_simulation_folder, sim_dir) + if len(folder) >= 60: + tr_len = 58 - len(folders.default_simulation_folder) + folder = os.path.join(folders.default_simulation_folder, sim_dir[:tr_len]) + else: + assert len(folder) < 60, tr_msg.format(folder) + + # get rid of all simulation files that exists in the folder already + dir_to_delete = ('hb_json', 'mappers', 'run') + ext_to_delete = ('.bat', '.geojson', '.epw', '.mos') + file_to_delete = ( + 'Gemfile', 'Gemfile.lock', 'honeybee_scenario.csv', 'runner.conf', + 'simulation_parameter.json', 'system_params.json', + 'electrical_database.json', 'network.json' + ) + if os.path.isdir(folder): + files = os.listdir(folder) + for f in files: + path = os.path.join(folder, f) + if os.path.isdir(path): + if f in dir_to_delete: + nukedir(path, True) + else: + if f in file_to_delete: + os.remove(path) + elif f.endswith(ext_to_delete): + os.remove(path) + else: + preparedir(folder) # create the directory if it's not there + + # prepare the folder into which honeybee Model JSONs will be written + hb_model_folder = os.path.join(folder, 'hb_json') # folder for honeybee JSONs + preparedir(hb_model_folder) + + # create GeoJSON dictionary + geojson_dict = model.to_geojson_dict(location, point, tolerance=tolerance) + for feature_dict in geojson_dict['features']: # add the detailed model filename + if feature_dict['properties']['type'] == 'Building': + bldg_id = feature_dict['properties']['id'] + feature_dict['properties']['detailed_model_filename'] = \ + os.path.join(hb_model_folder, '{}.json'.format(bldg_id)) + + # add the DES to the GeoJSON dictionary + if des_loop is not None: + if hasattr(des_loop, 'to_geojson_dict'): + des_features = des_loop.to_geojson_dict( + model.buildings, location, point, tolerance=tolerance) + geojson_dict['features'].extend(des_features) + sys_p_json = os.path.join(folder, 'system_params.json') + with open(sys_p_json, 'w') as fp: + des_dict = des_loop.to_des_param_dict(model.buildings, tolerance=tolerance) + json.dump(des_dict, fp, indent=2) + if conversion_factor is not None: + des_loop.scale(1 / conversion_factor) + + # add the electrical network to the GeoJSON dictionary + if electrical_network is not None: + electric_features = electrical_network.to_geojson_dict( + model.buildings, location, point, tolerance=tolerance) + geojson_dict['features'].extend(electric_features) + electric_json = os.path.join(folder, 'electrical_database.json') + with open(electric_json, 'w') as fp: + json.dump(electrical_network.to_electrical_database_dict(), fp, indent=4) + if conversion_factor is not None: + electrical_network.scale(1 / conversion_factor) + + # add the road network to the GeoJSON dictionary + if road_network is not None: + road_features = road_network.to_geojson_dict(location, point) + geojson_dict['features'].extend(road_features) + if conversion_factor is not None: + road_network.scale(1 / conversion_factor) + + # add the ground-mounted PV to the GeoJSON dictionary + if ground_pv is not None and len(ground_pv) != 0: + pv_features = [g_pv.to_geojson_dict(location, point) for g_pv in ground_pv] + geojson_dict['features'].extend(pv_features) + if conversion_factor is not None: + for g_pv in ground_pv: + g_pv.scale(1 / conversion_factor) + + # write out the GeoJSON file + feature_geojson = os.path.join(folder, '{}.geojson'.format(model.identifier)) + if (sys.version_info < (3, 0)): # we need to manually encode it as UTF-8 + with open(feature_geojson, 'wb') as fp: + obj_str = json.dumps(geojson_dict, indent=4, ensure_ascii=False) + fp.write(obj_str.encode('utf-8')) + else: + with open(feature_geojson, 'w', encoding='utf-8') as fp: + obj_str = json.dump(geojson_dict, fp, indent=4, ensure_ascii=False) + + # write out the honeybee Model JSONs from the model + hb_model_jsons = [] + hb_models = model.to_honeybee( + 'Building', shade_distance, use_multiplier, add_plenum, + solve_ceiling_adjacencies=solve_ceiling_adjacencies, tolerance=tolerance) + for bldg_model in hb_models: + try: + bldg_model.remove_degenerate_geometry(0.01) + except ValueError: + error = 'Failed to remove degenerate Geometry.\nYour Model units system is: {}. ' \ + 'Is this correct?'.format(original_units) + raise ValueError(error) + model_dict = bldg_model.to_dict(triangulate_sub_faces=True) + bldg_model.properties.energy.add_autocal_properties_to_dict(model_dict) + bld_path = os.path.join(hb_model_folder, '{}.json'.format(bldg_model.identifier)) + if (sys.version_info < (3, 0)): # we need to manually encode it as UTF-8 + with open(bld_path, 'wb') as fp: + obj_str = json.dumps(model_dict, indent=4, ensure_ascii=False) + fp.write(obj_str.encode('utf-8')) + else: + with open(bld_path, 'w', encoding='utf-8') as fp: + obj_str = json.dump(model_dict, fp, indent=4, ensure_ascii=False) + hb_model_jsons.append(bld_path) + + return feature_geojson, hb_model_jsons, hb_models
+ +
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/docs/_modules/index.html b/docs/_modules/index.html new file mode 100644 index 00000000..de9817c1 --- /dev/null +++ b/docs/_modules/index.html @@ -0,0 +1,982 @@ + + + + + + + Overview: module code — dragonfly-energy documentation + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + +
+
+ + + \ No newline at end of file diff --git a/docs/_sources/cli/index.rst.txt b/docs/_sources/cli/index.rst.txt new file mode 100644 index 00000000..6e2a4ff0 --- /dev/null +++ b/docs/_sources/cli/index.rst.txt @@ -0,0 +1,16 @@ +CLI Docs +======== + +Installation +------------ + +To check if the command line is installed correctly use ``dragonfly-energy --help`` + +Commands +-------- +.. toctree:: + :maxdepth: 1 + + translate + simulate + install diff --git a/docs/_sources/cli/install.rst.txt b/docs/_sources/cli/install.rst.txt new file mode 100644 index 00000000..7edf3270 --- /dev/null +++ b/docs/_sources/cli/install.rst.txt @@ -0,0 +1,6 @@ +install +======= + +.. click:: dragonfly_energy.cli.install:install + :prog: dragonfly-energy install + :show-nested: diff --git a/docs/_sources/cli/simulate.rst.txt b/docs/_sources/cli/simulate.rst.txt new file mode 100644 index 00000000..8407287b --- /dev/null +++ b/docs/_sources/cli/simulate.rst.txt @@ -0,0 +1,6 @@ +simulate +======== + +.. click:: dragonfly_energy.cli.simulate:simulate + :prog: dragonfly-energy simulate + :show-nested: diff --git a/docs/_sources/cli/translate.rst.txt b/docs/_sources/cli/translate.rst.txt new file mode 100644 index 00000000..60746e07 --- /dev/null +++ b/docs/_sources/cli/translate.rst.txt @@ -0,0 +1,6 @@ +translate +========= + +.. click:: dragonfly_energy.cli.translate:translate + :prog: dragonfly-energy translate + :show-nested: diff --git a/docs/_sources/dragonfly_energy.cli.install.rst.txt b/docs/_sources/dragonfly_energy.cli.install.rst.txt new file mode 100644 index 00000000..c882e208 --- /dev/null +++ b/docs/_sources/dragonfly_energy.cli.install.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.cli.install module +==================================== + +.. automodule:: dragonfly_energy.cli.install + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.cli.rst.txt b/docs/_sources/dragonfly_energy.cli.rst.txt new file mode 100644 index 00000000..98b0008f --- /dev/null +++ b/docs/_sources/dragonfly_energy.cli.rst.txt @@ -0,0 +1,20 @@ +dragonfly\_energy.cli package +============================= + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.cli.install + dragonfly_energy.cli.simulate + dragonfly_energy.cli.translate + +Module contents +--------------- + +.. automodule:: dragonfly_energy.cli + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.cli.simulate.rst.txt b/docs/_sources/dragonfly_energy.cli.simulate.rst.txt new file mode 100644 index 00000000..80f795d0 --- /dev/null +++ b/docs/_sources/dragonfly_energy.cli.simulate.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.cli.simulate module +===================================== + +.. automodule:: dragonfly_energy.cli.simulate + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.cli.translate.rst.txt b/docs/_sources/dragonfly_energy.cli.translate.rst.txt new file mode 100644 index 00000000..c0393d4a --- /dev/null +++ b/docs/_sources/dragonfly_energy.cli.translate.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.cli.translate module +====================================== + +.. automodule:: dragonfly_energy.cli.translate + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.config.rst.txt b/docs/_sources/dragonfly_energy.config.rst.txt new file mode 100644 index 00000000..4fb5bf7f --- /dev/null +++ b/docs/_sources/dragonfly_energy.config.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.config module +=============================== + +.. automodule:: dragonfly_energy.config + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.des.connector.rst.txt b/docs/_sources/dragonfly_energy.des.connector.rst.txt new file mode 100644 index 00000000..2d8b6406 --- /dev/null +++ b/docs/_sources/dragonfly_energy.des.connector.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.des.connector module +====================================== + +.. automodule:: dragonfly_energy.des.connector + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.des.ghe.rst.txt b/docs/_sources/dragonfly_energy.des.ghe.rst.txt new file mode 100644 index 00000000..491c1da4 --- /dev/null +++ b/docs/_sources/dragonfly_energy.des.ghe.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.des.ghe module +================================ + +.. automodule:: dragonfly_energy.des.ghe + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.des.junction.rst.txt b/docs/_sources/dragonfly_energy.des.junction.rst.txt new file mode 100644 index 00000000..50b43c40 --- /dev/null +++ b/docs/_sources/dragonfly_energy.des.junction.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.des.junction module +===================================== + +.. automodule:: dragonfly_energy.des.junction + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.des.loop.rst.txt b/docs/_sources/dragonfly_energy.des.loop.rst.txt new file mode 100644 index 00000000..ee554e3a --- /dev/null +++ b/docs/_sources/dragonfly_energy.des.loop.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.des.loop module +================================= + +.. automodule:: dragonfly_energy.des.loop + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.des.rst.txt b/docs/_sources/dragonfly_energy.des.rst.txt new file mode 100644 index 00000000..afbc82a2 --- /dev/null +++ b/docs/_sources/dragonfly_energy.des.rst.txt @@ -0,0 +1,21 @@ +dragonfly\_energy.des package +============================= + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.des.connector + dragonfly_energy.des.ghe + dragonfly_energy.des.junction + dragonfly_energy.des.loop + +Module contents +--------------- + +.. automodule:: dragonfly_energy.des + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.measure.rst.txt b/docs/_sources/dragonfly_energy.measure.rst.txt new file mode 100644 index 00000000..75920b75 --- /dev/null +++ b/docs/_sources/dragonfly_energy.measure.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.measure module +================================ + +.. automodule:: dragonfly_energy.measure + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.colorobj.rst.txt b/docs/_sources/dragonfly_energy.opendss.colorobj.rst.txt new file mode 100644 index 00000000..8bafc496 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.colorobj.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.colorobj module +========================================= + +.. automodule:: dragonfly_energy.opendss.colorobj + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.connector.rst.txt b/docs/_sources/dragonfly_energy.opendss.connector.rst.txt new file mode 100644 index 00000000..f319b1a2 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.connector.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.connector module +========================================== + +.. automodule:: dragonfly_energy.opendss.connector + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.junction.rst.txt b/docs/_sources/dragonfly_energy.opendss.junction.rst.txt new file mode 100644 index 00000000..c17e23d9 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.junction.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.junction module +========================================= + +.. automodule:: dragonfly_energy.opendss.junction + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.lib.powerlines.rst.txt b/docs/_sources/dragonfly_energy.opendss.lib.powerlines.rst.txt new file mode 100644 index 00000000..4a33954a --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.lib.powerlines.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.lib.powerlines module +=============================================== + +.. automodule:: dragonfly_energy.opendss.lib.powerlines + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.lib.rst.txt b/docs/_sources/dragonfly_energy.opendss.lib.rst.txt new file mode 100644 index 00000000..3900a9e5 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.lib.rst.txt @@ -0,0 +1,20 @@ +dragonfly\_energy.opendss.lib package +===================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.opendss.lib.powerlines + dragonfly_energy.opendss.lib.transformers + dragonfly_energy.opendss.lib.wires + +Module contents +--------------- + +.. automodule:: dragonfly_energy.opendss.lib + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.lib.transformers.rst.txt b/docs/_sources/dragonfly_energy.opendss.lib.transformers.rst.txt new file mode 100644 index 00000000..5f53fc7a --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.lib.transformers.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.lib.transformers module +================================================= + +.. automodule:: dragonfly_energy.opendss.lib.transformers + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.lib.wires.rst.txt b/docs/_sources/dragonfly_energy.opendss.lib.wires.rst.txt new file mode 100644 index 00000000..2050a013 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.lib.wires.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.lib.wires module +========================================== + +.. automodule:: dragonfly_energy.opendss.lib.wires + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.network.rst.txt b/docs/_sources/dragonfly_energy.opendss.network.rst.txt new file mode 100644 index 00000000..4a4e4c3c --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.network.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.network module +======================================== + +.. automodule:: dragonfly_energy.opendss.network + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.powerline.rst.txt b/docs/_sources/dragonfly_energy.opendss.powerline.rst.txt new file mode 100644 index 00000000..72bb97bd --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.powerline.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.powerline module +========================================== + +.. automodule:: dragonfly_energy.opendss.powerline + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.result.rst.txt b/docs/_sources/dragonfly_energy.opendss.result.rst.txt new file mode 100644 index 00000000..0d05391a --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.result.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.result module +======================================= + +.. automodule:: dragonfly_energy.opendss.result + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.road.rst.txt b/docs/_sources/dragonfly_energy.opendss.road.rst.txt new file mode 100644 index 00000000..cfdae290 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.road.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.road module +===================================== + +.. automodule:: dragonfly_energy.opendss.road + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.rst.txt b/docs/_sources/dragonfly_energy.opendss.rst.txt new file mode 100644 index 00000000..245e0566 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.rst.txt @@ -0,0 +1,36 @@ +dragonfly\_energy.opendss package +================================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.opendss.lib + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.opendss.colorobj + dragonfly_energy.opendss.connector + dragonfly_energy.opendss.junction + dragonfly_energy.opendss.network + dragonfly_energy.opendss.powerline + dragonfly_energy.opendss.result + dragonfly_energy.opendss.road + dragonfly_energy.opendss.substation + dragonfly_energy.opendss.transformer + dragonfly_energy.opendss.transformerprop + dragonfly_energy.opendss.wire + +Module contents +--------------- + +.. automodule:: dragonfly_energy.opendss + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.substation.rst.txt b/docs/_sources/dragonfly_energy.opendss.substation.rst.txt new file mode 100644 index 00000000..62027106 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.substation.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.substation module +=========================================== + +.. automodule:: dragonfly_energy.opendss.substation + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.transformer.rst.txt b/docs/_sources/dragonfly_energy.opendss.transformer.rst.txt new file mode 100644 index 00000000..24848492 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.transformer.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.transformer module +============================================ + +.. automodule:: dragonfly_energy.opendss.transformer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.transformerprop.rst.txt b/docs/_sources/dragonfly_energy.opendss.transformerprop.rst.txt new file mode 100644 index 00000000..940c2ff4 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.transformerprop.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.transformerprop module +================================================ + +.. automodule:: dragonfly_energy.opendss.transformerprop + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.opendss.wire.rst.txt b/docs/_sources/dragonfly_energy.opendss.wire.rst.txt new file mode 100644 index 00000000..9a682f34 --- /dev/null +++ b/docs/_sources/dragonfly_energy.opendss.wire.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.opendss.wire module +===================================== + +.. automodule:: dragonfly_energy.opendss.wire + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.properties.building.rst.txt b/docs/_sources/dragonfly_energy.properties.building.rst.txt new file mode 100644 index 00000000..010c7ed8 --- /dev/null +++ b/docs/_sources/dragonfly_energy.properties.building.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.properties.building module +============================================ + +.. automodule:: dragonfly_energy.properties.building + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.properties.context.rst.txt b/docs/_sources/dragonfly_energy.properties.context.rst.txt new file mode 100644 index 00000000..f2a6e0fe --- /dev/null +++ b/docs/_sources/dragonfly_energy.properties.context.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.properties.context module +=========================================== + +.. automodule:: dragonfly_energy.properties.context + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.properties.model.rst.txt b/docs/_sources/dragonfly_energy.properties.model.rst.txt new file mode 100644 index 00000000..733ca189 --- /dev/null +++ b/docs/_sources/dragonfly_energy.properties.model.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.properties.model module +========================================= + +.. automodule:: dragonfly_energy.properties.model + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.properties.room2d.rst.txt b/docs/_sources/dragonfly_energy.properties.room2d.rst.txt new file mode 100644 index 00000000..3bd0fd0a --- /dev/null +++ b/docs/_sources/dragonfly_energy.properties.room2d.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.properties.room2d module +========================================== + +.. automodule:: dragonfly_energy.properties.room2d + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.properties.rst.txt b/docs/_sources/dragonfly_energy.properties.rst.txt new file mode 100644 index 00000000..0b1da7eb --- /dev/null +++ b/docs/_sources/dragonfly_energy.properties.rst.txt @@ -0,0 +1,22 @@ +dragonfly\_energy.properties package +==================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.properties.building + dragonfly_energy.properties.context + dragonfly_energy.properties.model + dragonfly_energy.properties.room2d + dragonfly_energy.properties.story + +Module contents +--------------- + +.. automodule:: dragonfly_energy.properties + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.properties.story.rst.txt b/docs/_sources/dragonfly_energy.properties.story.rst.txt new file mode 100644 index 00000000..38570b70 --- /dev/null +++ b/docs/_sources/dragonfly_energy.properties.story.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.properties.story module +========================================= + +.. automodule:: dragonfly_energy.properties.story + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.reopt.rst.txt b/docs/_sources/dragonfly_energy.reopt.rst.txt new file mode 100644 index 00000000..c495f02e --- /dev/null +++ b/docs/_sources/dragonfly_energy.reopt.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.reopt module +============================== + +.. automodule:: dragonfly_energy.reopt + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.rst.txt b/docs/_sources/dragonfly_energy.rst.txt new file mode 100644 index 00000000..5e25ca7c --- /dev/null +++ b/docs/_sources/dragonfly_energy.rst.txt @@ -0,0 +1,33 @@ +dragonfly\_energy package +========================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.cli + dragonfly_energy.des + dragonfly_energy.opendss + dragonfly_energy.properties + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy.config + dragonfly_energy.measure + dragonfly_energy.reopt + dragonfly_energy.run + dragonfly_energy.writer + +Module contents +--------------- + +.. automodule:: dragonfly_energy + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.run.rst.txt b/docs/_sources/dragonfly_energy.run.rst.txt new file mode 100644 index 00000000..26d596b9 --- /dev/null +++ b/docs/_sources/dragonfly_energy.run.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.run module +============================ + +.. automodule:: dragonfly_energy.run + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/dragonfly_energy.writer.rst.txt b/docs/_sources/dragonfly_energy.writer.rst.txt new file mode 100644 index 00000000..881d1d28 --- /dev/null +++ b/docs/_sources/dragonfly_energy.writer.rst.txt @@ -0,0 +1,7 @@ +dragonfly\_energy.writer module +=============================== + +.. automodule:: dragonfly_energy.writer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt new file mode 100644 index 00000000..e80919fe --- /dev/null +++ b/docs/_sources/index.rst.txt @@ -0,0 +1,43 @@ +Welcome to dragonfly-energy's documentation! +============================================ + +.. image:: http://www.ladybug.tools/assets/img/dragonfly.png + +`EnergyPlus `_ extension for `dragonfly `_ + +Dragonfly-energy adds EnergyPlus/OpenStudio functionalities to dragonfly for energy simulation. + + +Installation +============ + +``pip install -U dragonfly-energy``. + +To check if the command line is installed correctly use ``dragonfly-energy --help`` + +CLI Docs +============= + +For command line interface documentation and API documentation see the pages below. + + +.. toctree:: + :maxdepth: 2 + + cli/index + + +dragonfly_energy +================ + +.. toctree:: + :maxdepth: 4 + + modules + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/_sources/modules.rst.txt b/docs/_sources/modules.rst.txt new file mode 100644 index 00000000..8be11389 --- /dev/null +++ b/docs/_sources/modules.rst.txt @@ -0,0 +1,7 @@ +dragonfly_energy +================ + +.. toctree:: + :maxdepth: 4 + + dragonfly_energy diff --git a/docs/_static/basic.css b/docs/_static/basic.css new file mode 100644 index 00000000..f316efcb --- /dev/null +++ b/docs/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/_static/bootstrap-2.3.2/css/bootstrap-responsive.css b/docs/_static/bootstrap-2.3.2/css/bootstrap-responsive.css new file mode 100644 index 00000000..09e88ce3 --- /dev/null +++ b/docs/_static/bootstrap-2.3.2/css/bootstrap-responsive.css @@ -0,0 +1,1109 @@ +/*! + * Bootstrap Responsive v2.3.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +@-ms-viewport { + width: device-width; +} + +.hidden { + display: none; + visibility: hidden; +} + +.visible-phone { + display: none !important; +} + +.visible-tablet { + display: none !important; +} + +.hidden-desktop { + display: none !important; +} + +.visible-desktop { + display: inherit !important; +} + +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} + +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} + +.visible-print { + display: none !important; +} + +@media print { + .visible-print { + display: inherit !important; + } + .hidden-print { + display: none !important; + } +} + +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.564102564102564%; + *margin-left: 2.5109110747408616%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.564102564102564%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.45299145299145%; + *width: 91.39979996362975%; + } + .row-fluid .span10 { + width: 82.90598290598291%; + *width: 82.8527914166212%; + } + .row-fluid .span9 { + width: 74.35897435897436%; + *width: 74.30578286961266%; + } + .row-fluid .span8 { + width: 65.81196581196582%; + *width: 65.75877432260411%; + } + .row-fluid .span7 { + width: 57.26495726495726%; + *width: 57.21176577559556%; + } + .row-fluid .span6 { + width: 48.717948717948715%; + *width: 48.664757228587014%; + } + .row-fluid .span5 { + width: 40.17094017094017%; + *width: 40.11774868157847%; + } + .row-fluid .span4 { + width: 31.623931623931625%; + *width: 31.570740134569924%; + } + .row-fluid .span3 { + width: 23.076923076923077%; + *width: 23.023731587561375%; + } + .row-fluid .span2 { + width: 14.52991452991453%; + *width: 14.476723040552828%; + } + .row-fluid .span1 { + width: 5.982905982905983%; + *width: 5.929714493544281%; + } + .row-fluid .offset12 { + margin-left: 105.12820512820512%; + *margin-left: 105.02182214948171%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256410257%; + *margin-left: 102.45771958537915%; + } + .row-fluid .offset11 { + margin-left: 96.58119658119658%; + *margin-left: 96.47481360247316%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709401709402%; + *margin-left: 93.91071103837061%; + } + .row-fluid .offset10 { + margin-left: 88.03418803418803%; + *margin-left: 87.92780505546462%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547008548%; + *margin-left: 85.36370249136206%; + } + .row-fluid .offset9 { + margin-left: 79.48717948717949%; + *margin-left: 79.38079650845607%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692307693%; + *margin-left: 76.81669394435352%; + } + .row-fluid .offset8 { + margin-left: 70.94017094017094%; + *margin-left: 70.83378796144753%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606837606839%; + *margin-left: 68.26968539734497%; + } + .row-fluid .offset7 { + margin-left: 62.393162393162385%; + *margin-left: 62.28677941443899%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905982905982%; + *margin-left: 59.72267685033642%; + } + .row-fluid .offset6 { + margin-left: 53.84615384615384%; + *margin-left: 53.739770867430444%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128205128%; + *margin-left: 51.175668303327875%; + } + .row-fluid .offset5 { + margin-left: 45.299145299145295%; + *margin-left: 45.1927623204219%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504273504273%; + *margin-left: 42.62865975631933%; + } + .row-fluid .offset4 { + margin-left: 36.75213675213675%; + *margin-left: 36.645753773413354%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803418803419%; + *margin-left: 34.081651209310785%; + } + .row-fluid .offset3 { + margin-left: 28.205128205128204%; + *margin-left: 28.0987452264048%; + } + .row-fluid .offset3:first-child { + margin-left: 25.641025641025642%; + *margin-left: 25.53464266230224%; + } + .row-fluid .offset2 { + margin-left: 19.65811965811966%; + *margin-left: 19.551736679396257%; + } + .row-fluid .offset2:first-child { + margin-left: 17.094017094017094%; + *margin-left: 16.98763411529369%; + } + .row-fluid .offset1 { + margin-left: 11.11111111111111%; + *margin-left: 11.004728132387708%; + } + .row-fluid .offset1:first-child { + margin-left: 8.547008547008547%; + *margin-left: 8.440625568285142%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} + +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + line-height: 0; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + line-height: 0; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.7624309392265194%; + *margin-left: 2.709239449864817%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.7624309392265194%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; + } + .row-fluid .span11 { + width: 91.43646408839778%; + *width: 91.38327259903608%; + } + .row-fluid .span10 { + width: 82.87292817679558%; + *width: 82.81973668743387%; + } + .row-fluid .span9 { + width: 74.30939226519337%; + *width: 74.25620077583166%; + } + .row-fluid .span8 { + width: 65.74585635359117%; + *width: 65.69266486422946%; + } + .row-fluid .span7 { + width: 57.18232044198895%; + *width: 57.12912895262725%; + } + .row-fluid .span6 { + width: 48.61878453038674%; + *width: 48.56559304102504%; + } + .row-fluid .span5 { + width: 40.05524861878453%; + *width: 40.00205712942283%; + } + .row-fluid .span4 { + width: 31.491712707182323%; + *width: 31.43852121782062%; + } + .row-fluid .span3 { + width: 22.92817679558011%; + *width: 22.87498530621841%; + } + .row-fluid .span2 { + width: 14.3646408839779%; + *width: 14.311449394616199%; + } + .row-fluid .span1 { + width: 5.801104972375691%; + *width: 5.747913483013988%; + } + .row-fluid .offset12 { + margin-left: 105.52486187845304%; + *margin-left: 105.41847889972962%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243093922652%; + *margin-left: 102.6560479605031%; + } + .row-fluid .offset11 { + margin-left: 96.96132596685082%; + *margin-left: 96.8549429881274%; + } + .row-fluid .offset11:first-child { + margin-left: 94.1988950276243%; + *margin-left: 94.09251204890089%; + } + .row-fluid .offset10 { + margin-left: 88.39779005524862%; + *margin-left: 88.2914070765252%; + } + .row-fluid .offset10:first-child { + margin-left: 85.6353591160221%; + *margin-left: 85.52897613729868%; + } + .row-fluid .offset9 { + margin-left: 79.8342541436464%; + *margin-left: 79.72787116492299%; + } + .row-fluid .offset9:first-child { + margin-left: 77.07182320441989%; + *margin-left: 76.96544022569647%; + } + .row-fluid .offset8 { + margin-left: 71.2707182320442%; + *margin-left: 71.16433525332079%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729281768%; + *margin-left: 68.40190431409427%; + } + .row-fluid .offset7 { + margin-left: 62.70718232044199%; + *margin-left: 62.600799341718584%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138121547%; + *margin-left: 59.838368402492065%; + } + .row-fluid .offset6 { + margin-left: 54.14364640883978%; + *margin-left: 54.037263430116376%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121546961326%; + *margin-left: 51.27483249088986%; + } + .row-fluid .offset5 { + margin-left: 45.58011049723757%; + *margin-left: 45.47372751851417%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767955801105%; + *margin-left: 42.71129657928765%; + } + .row-fluid .offset4 { + margin-left: 37.01657458563536%; + *margin-left: 36.91019160691196%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414364640884%; + *margin-left: 34.14776066768544%; + } + .row-fluid .offset3 { + margin-left: 28.45303867403315%; + *margin-left: 28.346655695309746%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773480663%; + *margin-left: 25.584224756083227%; + } + .row-fluid .offset2 { + margin-left: 19.88950276243094%; + *margin-left: 19.783119783707537%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182320442%; + *margin-left: 17.02068884448102%; + } + .row-fluid .offset1 { + margin-left: 11.32596685082873%; + *margin-left: 11.219583872105325%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591160221%; + *margin-left: 8.457152932878806%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} + +@media (max-width: 767px) { + body { + padding-right: 20px; + padding-left: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-right: -20px; + margin-left: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + width: auto; + clear: none; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + display: block; + float: none; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + right: 20px; + left: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} + +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 20px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-right: 10px; + padding-left: 10px; + } + .media .pull-left, + .media .pull-right { + display: block; + float: none; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + right: 10px; + left: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} + +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 20px; + } + .navbar-fixed-bottom { + margin-top: 20px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-right: 10px; + padding-left: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .nav > li > a:focus, + .nav-collapse .dropdown-menu a:hover, + .nav-collapse .dropdown-menu a:focus { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .nav > li > a:focus, + .navbar-inverse .nav-collapse .dropdown-menu a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:focus { + background-color: #111111; + } + .nav-collapse.in .btn-group { + padding: 0; + margin-top: 5px; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + display: none; + float: none; + max-width: none; + padding: 0; + margin: 0 15px; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10px 15px; + margin: 10px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + height: 0; + overflow: hidden; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-right: 10px; + padding-left: 10px; + } +} + +@media (min-width: 980px) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff --git a/docs/_static/bootstrap-2.3.2/css/bootstrap-responsive.min.css b/docs/_static/bootstrap-2.3.2/css/bootstrap-responsive.min.css new file mode 100644 index 00000000..f4ede63f --- /dev/null +++ b/docs/_static/bootstrap-2.3.2/css/bootstrap-responsive.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Responsive v2.3.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} diff --git a/docs/_static/bootstrap-2.3.2/css/bootstrap.css b/docs/_static/bootstrap-2.3.2/css/bootstrap.css new file mode 100644 index 00000000..b725064a --- /dev/null +++ b/docs/_static/bootstrap-2.3.2/css/bootstrap.css @@ -0,0 +1,6167 @@ +/*! + * Bootstrap v2.3.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ + +.clearfix { + *zoom: 1; +} + +.clearfix:before, +.clearfix:after { + display: table; + line-height: 0; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +audio:not([controls]) { + display: none; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +a:hover, +a:active { + outline: 0; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + width: auto\9; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} + +#map_canvas img, +.google-maps img { + max-width: none; +} + +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} + +button, +input { + *overflow: visible; + line-height: normal; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} + +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 0.5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } +} + +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: #ffffff; +} + +a { + color: #0088cc; + text-decoration: none; +} + +a:hover, +a:focus { + color: #005580; + text-decoration: underline; +} + +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} + +.row { + margin-left: -20px; + *zoom: 1; +} + +.row:before, +.row:after { + display: table; + line-height: 0; + content: ""; +} + +.row:after { + clear: both; +} + +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} + +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.span12 { + width: 940px; +} + +.span11 { + width: 860px; +} + +.span10 { + width: 780px; +} + +.span9 { + width: 700px; +} + +.span8 { + width: 620px; +} + +.span7 { + width: 540px; +} + +.span6 { + width: 460px; +} + +.span5 { + width: 380px; +} + +.span4 { + width: 300px; +} + +.span3 { + width: 220px; +} + +.span2 { + width: 140px; +} + +.span1 { + width: 60px; +} + +.offset12 { + margin-left: 980px; +} + +.offset11 { + margin-left: 900px; +} + +.offset10 { + margin-left: 820px; +} + +.offset9 { + margin-left: 740px; +} + +.offset8 { + margin-left: 660px; +} + +.offset7 { + margin-left: 580px; +} + +.offset6 { + margin-left: 500px; +} + +.offset5 { + margin-left: 420px; +} + +.offset4 { + margin-left: 340px; +} + +.offset3 { + margin-left: 260px; +} + +.offset2 { + margin-left: 180px; +} + +.offset1 { + margin-left: 100px; +} + +.row-fluid { + width: 100%; + *zoom: 1; +} + +.row-fluid:before, +.row-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.row-fluid:after { + clear: both; +} + +.row-fluid [class*="span"] { + display: block; + float: left; + width: 100%; + min-height: 30px; + margin-left: 2.127659574468085%; + *margin-left: 2.074468085106383%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} + +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.127659574468085%; +} + +.row-fluid .span12 { + width: 100%; + *width: 99.94680851063829%; +} + +.row-fluid .span11 { + width: 91.48936170212765%; + *width: 91.43617021276594%; +} + +.row-fluid .span10 { + width: 82.97872340425532%; + *width: 82.92553191489361%; +} + +.row-fluid .span9 { + width: 74.46808510638297%; + *width: 74.41489361702126%; +} + +.row-fluid .span8 { + width: 65.95744680851064%; + *width: 65.90425531914893%; +} + +.row-fluid .span7 { + width: 57.44680851063829%; + *width: 57.39361702127659%; +} + +.row-fluid .span6 { + width: 48.93617021276595%; + *width: 48.88297872340425%; +} + +.row-fluid .span5 { + width: 40.42553191489362%; + *width: 40.37234042553192%; +} + +.row-fluid .span4 { + width: 31.914893617021278%; + *width: 31.861702127659576%; +} + +.row-fluid .span3 { + width: 23.404255319148934%; + *width: 23.351063829787233%; +} + +.row-fluid .span2 { + width: 14.893617021276595%; + *width: 14.840425531914894%; +} + +.row-fluid .span1 { + width: 6.382978723404255%; + *width: 6.329787234042553%; +} + +.row-fluid .offset12 { + margin-left: 104.25531914893617%; + *margin-left: 104.14893617021275%; +} + +.row-fluid .offset12:first-child { + margin-left: 102.12765957446808%; + *margin-left: 102.02127659574467%; +} + +.row-fluid .offset11 { + margin-left: 95.74468085106382%; + *margin-left: 95.6382978723404%; +} + +.row-fluid .offset11:first-child { + margin-left: 93.61702127659574%; + *margin-left: 93.51063829787232%; +} + +.row-fluid .offset10 { + margin-left: 87.23404255319149%; + *margin-left: 87.12765957446807%; +} + +.row-fluid .offset10:first-child { + margin-left: 85.1063829787234%; + *margin-left: 84.99999999999999%; +} + +.row-fluid .offset9 { + margin-left: 78.72340425531914%; + *margin-left: 78.61702127659572%; +} + +.row-fluid .offset9:first-child { + margin-left: 76.59574468085106%; + *margin-left: 76.48936170212764%; +} + +.row-fluid .offset8 { + margin-left: 70.2127659574468%; + *margin-left: 70.10638297872339%; +} + +.row-fluid .offset8:first-child { + margin-left: 68.08510638297872%; + *margin-left: 67.9787234042553%; +} + +.row-fluid .offset7 { + margin-left: 61.70212765957446%; + *margin-left: 61.59574468085106%; +} + +.row-fluid .offset7:first-child { + margin-left: 59.574468085106375%; + *margin-left: 59.46808510638297%; +} + +.row-fluid .offset6 { + margin-left: 53.191489361702125%; + *margin-left: 53.085106382978715%; +} + +.row-fluid .offset6:first-child { + margin-left: 51.063829787234035%; + *margin-left: 50.95744680851063%; +} + +.row-fluid .offset5 { + margin-left: 44.68085106382979%; + *margin-left: 44.57446808510638%; +} + +.row-fluid .offset5:first-child { + margin-left: 42.5531914893617%; + *margin-left: 42.4468085106383%; +} + +.row-fluid .offset4 { + margin-left: 36.170212765957444%; + *margin-left: 36.06382978723405%; +} + +.row-fluid .offset4:first-child { + margin-left: 34.04255319148936%; + *margin-left: 33.93617021276596%; +} + +.row-fluid .offset3 { + margin-left: 27.659574468085104%; + *margin-left: 27.5531914893617%; +} + +.row-fluid .offset3:first-child { + margin-left: 25.53191489361702%; + *margin-left: 25.425531914893618%; +} + +.row-fluid .offset2 { + margin-left: 19.148936170212764%; + *margin-left: 19.04255319148936%; +} + +.row-fluid .offset2:first-child { + margin-left: 17.02127659574468%; + *margin-left: 16.914893617021278%; +} + +.row-fluid .offset1 { + margin-left: 10.638297872340425%; + *margin-left: 10.53191489361702%; +} + +.row-fluid .offset1:first-child { + margin-left: 8.51063829787234%; + *margin-left: 8.404255319148938%; +} + +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} + +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} + +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} + +.container:before, +.container:after { + display: table; + line-height: 0; + content: ""; +} + +.container:after { + clear: both; +} + +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} + +.container-fluid:before, +.container-fluid:after { + display: table; + line-height: 0; + content: ""; +} + +.container-fluid:after { + clear: both; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 21px; + font-weight: 200; + line-height: 30px; +} + +small { + font-size: 85%; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +cite { + font-style: normal; +} + +.muted { + color: #999999; +} + +a.muted:hover, +a.muted:focus { + color: #808080; +} + +.text-warning { + color: #c09853; +} + +a.text-warning:hover, +a.text-warning:focus { + color: #a47e3c; +} + +.text-error { + color: #b94a48; +} + +a.text-error:hover, +a.text-error:focus { + color: #953b39; +} + +.text-info { + color: #3a87ad; +} + +a.text-info:hover, +a.text-info:focus { + color: #2d6987; +} + +.text-success { + color: #468847; +} + +a.text-success:hover, +a.text-success:focus { + color: #356635; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10px 0; + font-family: inherit; + font-weight: bold; + line-height: 20px; + color: inherit; + text-rendering: optimizelegibility; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} + +h1, +h2, +h3 { + line-height: 40px; +} + +h1 { + font-size: 38.5px; +} + +h2 { + font-size: 31.5px; +} + +h3 { + font-size: 24.5px; +} + +h4 { + font-size: 17.5px; +} + +h5 { + font-size: 14px; +} + +h6 { + font-size: 11.9px; +} + +h1 small { + font-size: 24.5px; +} + +h2 small { + font-size: 17.5px; +} + +h3 small { + font-size: 14px; +} + +h4 small { + font-size: 14px; +} + +.page-header { + padding-bottom: 9px; + margin: 20px 0 30px; + border-bottom: 1px solid #eeeeee; +} + +ul, +ol { + padding: 0; + margin: 0 0 10px 25px; +} + +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} + +li { + line-height: 20px; +} + +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} + +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; +} + +ul.inline > li, +ol.inline > li { + display: inline-block; + *display: inline; + padding-right: 5px; + padding-left: 5px; + *zoom: 1; +} + +dl { + margin-bottom: 20px; +} + +dt, +dd { + line-height: 20px; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 10px; +} + +.dl-horizontal { + *zoom: 1; +} + +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + line-height: 0; + content: ""; +} + +.dl-horizontal:after { + clear: both; +} + +.dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; +} + +.dl-horizontal dd { + margin-left: 180px; +} + +hr { + margin: 20px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} + +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 0 0 0 15px; + margin: 0 0 20px; + border-left: 5px solid #eeeeee; +} + +blockquote p { + margin-bottom: 0; + font-size: 17.5px; + font-weight: 300; + line-height: 1.25; +} + +blockquote small { + display: block; + line-height: 20px; + color: #999999; +} + +blockquote small:before { + content: '\2014 \00A0'; +} + +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} + +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} + +blockquote.pull-right small:before { + content: ''; +} + +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} + +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} + +address { + display: block; + margin-bottom: 20px; + font-style: normal; + line-height: 20px; +} + +code, +pre { + padding: 0 3px 2px; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +code { + padding: 2px 4px; + color: #d14; + white-space: nowrap; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 20px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +pre.prettyprint { + margin-bottom: 20px; +} + +pre code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +form { + margin: 0 0 20px; +} + +fieldset { + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: 40px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +legend small { + font-size: 15px; + color: #999999; +} + +label, +input, +button, +select, +textarea { + font-size: 14px; + font-weight: normal; + line-height: 20px; +} + +input, +button, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +label { + display: block; + margin-bottom: 5px; +} + +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 20px; + padding: 4px 6px; + margin-bottom: 10px; + font-size: 14px; + line-height: 20px; + color: #555555; + vertical-align: middle; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +input, +textarea, +.uneditable-input { + width: 206px; +} + +textarea { + height: auto; +} + +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} + +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + *margin-top: 0; + line-height: normal; +} + +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} + +select, +input[type="file"] { + height: 30px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + + line-height: 30px; +} + +select { + width: 220px; + background-color: #ffffff; + border: 1px solid #cccccc; +} + +select[multiple], +select[size] { + height: auto; +} + +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.uneditable-input, +.uneditable-textarea { + color: #999999; + cursor: not-allowed; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); +} + +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} + +.uneditable-textarea { + width: auto; + height: auto; +} + +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} + +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} + +.radio, +.checkbox { + min-height: 20px; + padding-left: 20px; +} + +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} + +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} + +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} + +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} + +.input-mini { + width: 60px; +} + +.input-small { + width: 90px; +} + +.input-medium { + width: 150px; +} + +.input-large { + width: 210px; +} + +.input-xlarge { + width: 270px; +} + +.input-xxlarge { + width: 530px; +} + +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} + +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} + +input, +textarea, +.uneditable-input { + margin-left: 0; +} + +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} + +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} + +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} + +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} + +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} + +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} + +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} + +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} + +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} + +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} + +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} + +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} + +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} + +.controls-row { + *zoom: 1; +} + +.controls-row:before, +.controls-row:after { + display: table; + line-height: 0; + content: ""; +} + +.controls-row:after { + clear: both; +} + +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} + +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} + +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} + +.control-group.warning .control-label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} + +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} + +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} + +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} + +.control-group.error .control-label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} + +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} + +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} + +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} + +.control-group.success .control-label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} + +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} + +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} + +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} + +.control-group.info .control-label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} + +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} + +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} + +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} + +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; +} + +input:focus:invalid:focus, +textarea:focus:invalid:focus, +select:focus:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} + +.form-actions { + padding: 19px 20px 20px; + margin-top: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} + +.form-actions:before, +.form-actions:after { + display: table; + line-height: 0; + content: ""; +} + +.form-actions:after { + clear: both; +} + +.help-block, +.help-inline { + color: #595959; +} + +.help-block { + display: block; + margin-bottom: 10px; +} + +.help-inline { + display: inline-block; + *display: inline; + padding-left: 5px; + vertical-align: middle; + *zoom: 1; +} + +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: 10px; + font-size: 0; + white-space: nowrap; + vertical-align: middle; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu, +.input-append .popover, +.input-prepend .popover { + font-size: 14px; +} + +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} + +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 20px; + min-width: 16px; + padding: 4px 5px; + font-size: 14px; + font-weight: normal; + line-height: 20px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} + +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn, +.input-append .btn-group > .dropdown-toggle, +.input-prepend .btn-group > .dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} + +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} + +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-append input + .btn-group .btn:last-child, +.input-append select + .btn-group .btn:last-child, +.input-append .uneditable-input + .btn-group .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} + +.input-append .add-on:last-child, +.input-append .btn:last-child, +.input-append .btn-group:last-child > .dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} + +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +/* Allow for input prepend/append in search forms */ + +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} + +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} + +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + margin-bottom: 0; + vertical-align: middle; + *zoom: 1; +} + +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} + +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} + +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} + +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} + +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} + +.control-group { + margin-bottom: 10px; +} + +legend + .control-group { + margin-top: 20px; + -webkit-margin-top-collapse: separate; +} + +.form-horizontal .control-group { + margin-bottom: 20px; + *zoom: 1; +} + +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + line-height: 0; + content: ""; +} + +.form-horizontal .control-group:after { + clear: both; +} + +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} + +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} + +.form-horizontal .controls:first-child { + *padding-left: 180px; +} + +.form-horizontal .help-block { + margin-bottom: 0; +} + +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block, +.form-horizontal .uneditable-input + .help-block, +.form-horizontal .input-prepend + .help-block, +.form-horizontal .input-append + .help-block { + margin-top: 10px; +} + +.form-horizontal .form-actions { + padding-left: 180px; +} + +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} + +.table { + width: 100%; + margin-bottom: 20px; +} + +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} + +.table th { + font-weight: bold; +} + +.table thead th { + vertical-align: bottom; +} + +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} + +.table tbody + tbody { + border-top: 2px solid #dddddd; +} + +.table .table { + background-color: #ffffff; +} + +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} + +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} + +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} + +.table-bordered thead:first-child tr:first-child > th:first-child, +.table-bordered tbody:first-child tr:first-child > td:first-child, +.table-bordered tbody:first-child tr:first-child > th:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered thead:first-child tr:first-child > th:last-child, +.table-bordered tbody:first-child tr:first-child > td:last-child, +.table-bordered tbody:first-child tr:first-child > th:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:first-child, +.table-bordered tbody:last-child tr:last-child > td:first-child, +.table-bordered tbody:last-child tr:last-child > th:first-child, +.table-bordered tfoot:last-child tr:last-child > td:first-child, +.table-bordered tfoot:last-child tr:last-child > th:first-child { + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.table-bordered thead:last-child tr:last-child > th:last-child, +.table-bordered tbody:last-child tr:last-child > td:last-child, +.table-bordered tbody:last-child tr:last-child > th:last-child, +.table-bordered tfoot:last-child tr:last-child > td:last-child, +.table-bordered tfoot:last-child tr:last-child > th:last-child { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { + -webkit-border-bottom-left-radius: 0; + border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; +} + +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; +} + +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; +} + +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; +} + +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} + +.table-hover tbody tr:hover > td, +.table-hover tbody tr:hover > th { + background-color: #f5f5f5; +} + +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} + +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} + +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} + +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} + +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} + +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} + +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} + +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} + +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} + +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} + +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} + +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} + +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} + +.table tbody tr.success > td { + background-color: #dff0d8; +} + +.table tbody tr.error > td { + background-color: #f2dede; +} + +.table tbody tr.warning > td { + background-color: #fcf8e3; +} + +.table tbody tr.info > td { + background-color: #d9edf7; +} + +.table-hover tbody tr.success:hover > td { + background-color: #d0e9c6; +} + +.table-hover tbody tr.error:hover > td { + background-color: #ebcccc; +} + +.table-hover tbody tr.warning:hover > td { + background-color: #faf2cc; +} + +.table-hover tbody tr.info:hover > td { + background-color: #c4e3f3; +} + +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + margin-top: 1px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; +} + +/* White icons with optional class, or on hover/focus/active states of certain elements */ + +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("../img/glyphicons-halflings-white.png"); +} + +.icon-glass { + background-position: 0 0; +} + +.icon-music { + background-position: -24px 0; +} + +.icon-search { + background-position: -48px 0; +} + +.icon-envelope { + background-position: -72px 0; +} + +.icon-heart { + background-position: -96px 0; +} + +.icon-star { + background-position: -120px 0; +} + +.icon-star-empty { + background-position: -144px 0; +} + +.icon-user { + background-position: -168px 0; +} + +.icon-film { + background-position: -192px 0; +} + +.icon-th-large { + background-position: -216px 0; +} + +.icon-th { + background-position: -240px 0; +} + +.icon-th-list { + background-position: -264px 0; +} + +.icon-ok { + background-position: -288px 0; +} + +.icon-remove { + background-position: -312px 0; +} + +.icon-zoom-in { + background-position: -336px 0; +} + +.icon-zoom-out { + background-position: -360px 0; +} + +.icon-off { + background-position: -384px 0; +} + +.icon-signal { + background-position: -408px 0; +} + +.icon-cog { + background-position: -432px 0; +} + +.icon-trash { + background-position: -456px 0; +} + +.icon-home { + background-position: 0 -24px; +} + +.icon-file { + background-position: -24px -24px; +} + +.icon-time { + background-position: -48px -24px; +} + +.icon-road { + background-position: -72px -24px; +} + +.icon-download-alt { + background-position: -96px -24px; +} + +.icon-download { + background-position: -120px -24px; +} + +.icon-upload { + background-position: -144px -24px; +} + +.icon-inbox { + background-position: -168px -24px; +} + +.icon-play-circle { + background-position: -192px -24px; +} + +.icon-repeat { + background-position: -216px -24px; +} + +.icon-refresh { + background-position: -240px -24px; +} + +.icon-list-alt { + background-position: -264px -24px; +} + +.icon-lock { + background-position: -287px -24px; +} + +.icon-flag { + background-position: -312px -24px; +} + +.icon-headphones { + background-position: -336px -24px; +} + +.icon-volume-off { + background-position: -360px -24px; +} + +.icon-volume-down { + background-position: -384px -24px; +} + +.icon-volume-up { + background-position: -408px -24px; +} + +.icon-qrcode { + background-position: -432px -24px; +} + +.icon-barcode { + background-position: -456px -24px; +} + +.icon-tag { + background-position: 0 -48px; +} + +.icon-tags { + background-position: -25px -48px; +} + +.icon-book { + background-position: -48px -48px; +} + +.icon-bookmark { + background-position: -72px -48px; +} + +.icon-print { + background-position: -96px -48px; +} + +.icon-camera { + background-position: -120px -48px; +} + +.icon-font { + background-position: -144px -48px; +} + +.icon-bold { + background-position: -167px -48px; +} + +.icon-italic { + background-position: -192px -48px; +} + +.icon-text-height { + background-position: -216px -48px; +} + +.icon-text-width { + background-position: -240px -48px; +} + +.icon-align-left { + background-position: -264px -48px; +} + +.icon-align-center { + background-position: -288px -48px; +} + +.icon-align-right { + background-position: -312px -48px; +} + +.icon-align-justify { + background-position: -336px -48px; +} + +.icon-list { + background-position: -360px -48px; +} + +.icon-indent-left { + background-position: -384px -48px; +} + +.icon-indent-right { + background-position: -408px -48px; +} + +.icon-facetime-video { + background-position: -432px -48px; +} + +.icon-picture { + background-position: -456px -48px; +} + +.icon-pencil { + background-position: 0 -72px; +} + +.icon-map-marker { + background-position: -24px -72px; +} + +.icon-adjust { + background-position: -48px -72px; +} + +.icon-tint { + background-position: -72px -72px; +} + +.icon-edit { + background-position: -96px -72px; +} + +.icon-share { + background-position: -120px -72px; +} + +.icon-check { + background-position: -144px -72px; +} + +.icon-move { + background-position: -168px -72px; +} + +.icon-step-backward { + background-position: -192px -72px; +} + +.icon-fast-backward { + background-position: -216px -72px; +} + +.icon-backward { + background-position: -240px -72px; +} + +.icon-play { + background-position: -264px -72px; +} + +.icon-pause { + background-position: -288px -72px; +} + +.icon-stop { + background-position: -312px -72px; +} + +.icon-forward { + background-position: -336px -72px; +} + +.icon-fast-forward { + background-position: -360px -72px; +} + +.icon-step-forward { + background-position: -384px -72px; +} + +.icon-eject { + background-position: -408px -72px; +} + +.icon-chevron-left { + background-position: -432px -72px; +} + +.icon-chevron-right { + background-position: -456px -72px; +} + +.icon-plus-sign { + background-position: 0 -96px; +} + +.icon-minus-sign { + background-position: -24px -96px; +} + +.icon-remove-sign { + background-position: -48px -96px; +} + +.icon-ok-sign { + background-position: -72px -96px; +} + +.icon-question-sign { + background-position: -96px -96px; +} + +.icon-info-sign { + background-position: -120px -96px; +} + +.icon-screenshot { + background-position: -144px -96px; +} + +.icon-remove-circle { + background-position: -168px -96px; +} + +.icon-ok-circle { + background-position: -192px -96px; +} + +.icon-ban-circle { + background-position: -216px -96px; +} + +.icon-arrow-left { + background-position: -240px -96px; +} + +.icon-arrow-right { + background-position: -264px -96px; +} + +.icon-arrow-up { + background-position: -289px -96px; +} + +.icon-arrow-down { + background-position: -312px -96px; +} + +.icon-share-alt { + background-position: -336px -96px; +} + +.icon-resize-full { + background-position: -360px -96px; +} + +.icon-resize-small { + background-position: -384px -96px; +} + +.icon-plus { + background-position: -408px -96px; +} + +.icon-minus { + background-position: -433px -96px; +} + +.icon-asterisk { + background-position: -456px -96px; +} + +.icon-exclamation-sign { + background-position: 0 -120px; +} + +.icon-gift { + background-position: -24px -120px; +} + +.icon-leaf { + background-position: -48px -120px; +} + +.icon-fire { + background-position: -72px -120px; +} + +.icon-eye-open { + background-position: -96px -120px; +} + +.icon-eye-close { + background-position: -120px -120px; +} + +.icon-warning-sign { + background-position: -144px -120px; +} + +.icon-plane { + background-position: -168px -120px; +} + +.icon-calendar { + background-position: -192px -120px; +} + +.icon-random { + width: 16px; + background-position: -216px -120px; +} + +.icon-comment { + background-position: -240px -120px; +} + +.icon-magnet { + background-position: -264px -120px; +} + +.icon-chevron-up { + background-position: -288px -120px; +} + +.icon-chevron-down { + background-position: -313px -119px; +} + +.icon-retweet { + background-position: -336px -120px; +} + +.icon-shopping-cart { + background-position: -360px -120px; +} + +.icon-folder-close { + width: 16px; + background-position: -384px -120px; +} + +.icon-folder-open { + width: 16px; + background-position: -408px -120px; +} + +.icon-resize-vertical { + background-position: -432px -119px; +} + +.icon-resize-horizontal { + background-position: -456px -118px; +} + +.icon-hdd { + background-position: 0 -144px; +} + +.icon-bullhorn { + background-position: -24px -144px; +} + +.icon-bell { + background-position: -48px -144px; +} + +.icon-certificate { + background-position: -72px -144px; +} + +.icon-thumbs-up { + background-position: -96px -144px; +} + +.icon-thumbs-down { + background-position: -120px -144px; +} + +.icon-hand-right { + background-position: -144px -144px; +} + +.icon-hand-left { + background-position: -168px -144px; +} + +.icon-hand-up { + background-position: -192px -144px; +} + +.icon-hand-down { + background-position: -216px -144px; +} + +.icon-circle-arrow-right { + background-position: -240px -144px; +} + +.icon-circle-arrow-left { + background-position: -264px -144px; +} + +.icon-circle-arrow-up { + background-position: -288px -144px; +} + +.icon-circle-arrow-down { + background-position: -312px -144px; +} + +.icon-globe { + background-position: -336px -144px; +} + +.icon-wrench { + background-position: -360px -144px; +} + +.icon-tasks { + background-position: -384px -144px; +} + +.icon-filter { + background-position: -408px -144px; +} + +.icon-briefcase { + background-position: -432px -144px; +} + +.icon-fullscreen { + background-position: -456px -144px; +} + +.dropup, +.dropdown { + position: relative; +} + +.dropdown-toggle { + *margin-bottom: -3px; +} + +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} + +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 20px; + color: #333333; + white-space: nowrap; +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + outline: 0; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} + +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} + +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: default; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.open { + *z-index: 1000; +} + +.open > .dropdown-menu { + display: block; +} + +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} + +.dropdown-submenu { + position: relative; +} + +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} + +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} + +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} + +.dropdown-submenu > a:after { + display: block; + float: right; + width: 0; + height: 0; + margin-top: 5px; + margin-right: -10px; + border-color: transparent; + border-left-color: #cccccc; + border-style: solid; + border-width: 5px 0 5px 5px; + content: " "; +} + +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} + +.dropdown-submenu.pull-left { + float: none; +} + +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.dropdown .dropdown-menu .nav-header { + padding-right: 20px; + padding-left: 20px; +} + +.typeahead { + z-index: 1051; + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} + +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} + +.collapse.in { + height: auto; +} + +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 20px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} + +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} + +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.btn { + display: inline-block; + *display: inline; + padding: 4px 12px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 14px; + line-height: 20px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + cursor: pointer; + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + border: 1px solid #cccccc; + *border: 0; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #b3b3b3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:focus, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:first-child { + *margin-left: 0; +} + +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active, +.btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-large { + padding: 11px 19px; + font-size: 17.5px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} + +.btn-small { + padding: 2px 10px; + font-size: 11.9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} + +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} + +.btn-mini { + padding: 0 6px; + font-size: 10.5px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; + padding-right: 0; + padding-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} + +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + *background-color: #0044cc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} + +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + *background-color: #f89406; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} + +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + *background-color: #bd362f; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} + +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + *background-color: #51a351; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} + +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} + +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + *background-color: #2f96b4; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} + +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} + +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + *background-color: #222222; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.btn-inverse:hover, +.btn-inverse:focus, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} + +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} + +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} + +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} + +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} + +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} + +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.btn-link { + color: #0088cc; + cursor: pointer; + border-color: transparent; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-link:hover, +.btn-link:focus { + color: #005580; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: #333333; + text-decoration: none; +} + +.btn-group { + position: relative; + display: inline-block; + *display: inline; + *margin-left: .3em; + font-size: 0; + white-space: nowrap; + vertical-align: middle; + *zoom: 1; +} + +.btn-group:first-child { + *margin-left: 0; +} + +.btn-group + .btn-group { + margin-left: 5px; +} + +.btn-toolbar { + margin-top: 10px; + margin-bottom: 10px; + font-size: 0; +} + +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group { + margin-left: 5px; +} + +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group > .btn + .btn { + margin-left: -1px; +} + +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: 14px; +} + +.btn-group > .btn-mini { + font-size: 10.5px; +} + +.btn-group > .btn-small { + font-size: 11.9px; +} + +.btn-group > .btn-large { + font-size: 17.5px; +} + +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group > .btn + .dropdown-toggle { + *padding-top: 5px; + padding-right: 8px; + *padding-bottom: 5px; + padding-left: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group > .btn-mini + .dropdown-toggle { + *padding-top: 2px; + padding-right: 5px; + *padding-bottom: 2px; + padding-left: 5px; +} + +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} + +.btn-group > .btn-large + .dropdown-toggle { + *padding-top: 7px; + padding-right: 12px; + *padding-bottom: 7px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} + +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} + +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} + +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} + +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} + +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} + +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} + +.btn .caret { + margin-top: 8px; + margin-left: 0; +} + +.btn-large .caret { + margin-top: 6px; +} + +.btn-large .caret { + border-top-width: 5px; + border-right-width: 5px; + border-left-width: 5px; +} + +.btn-mini .caret, +.btn-small .caret { + margin-top: 8px; +} + +.dropup .btn-large .caret { + border-bottom-width: 5px; +} + +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; +} + +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.btn-group-vertical > .btn + .btn { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.btn-group-vertical > .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.btn-group-vertical > .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.btn-group-vertical > .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 20px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.alert, +.alert h4 { + color: #c09853; +} + +.alert h4 { + margin: 0; +} + +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; +} + +.alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success h4 { + color: #468847; +} + +.alert-danger, +.alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} + +.alert-danger h4, +.alert-error h4 { + color: #b94a48; +} + +.alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info h4 { + color: #3a87ad; +} + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} + +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} + +.alert-block p + p { + margin-top: 5px; +} + +.nav { + margin-bottom: 20px; + margin-left: 0; + list-style: none; +} + +.nav > li > a { + display: block; +} + +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} + +.nav > li > a > img { + max-width: none; +} + +.nav > .pull-right { + float: right; +} + +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 20px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} + +.nav li + .nav-header { + margin-top: 9px; +} + +.nav-list { + padding-right: 15px; + padding-left: 15px; + margin-bottom: 0; +} + +.nav-list > li > a, +.nav-list .nav-header { + margin-right: -15px; + margin-left: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} + +.nav-list > li > a { + padding: 3px 15px; +} + +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} + +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} + +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} + +.nav-tabs, +.nav-pills { + *zoom: 1; +} + +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + line-height: 0; + content: ""; +} + +.nav-tabs:after, +.nav-pills:after { + clear: both; +} + +.nav-tabs > li, +.nav-pills > li { + float: left; +} + +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} + +.nav-tabs { + border-bottom: 1px solid #ddd; +} + +.nav-tabs > li { + margin-bottom: -1px; +} + +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 20px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover, +.nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #dddddd; +} + +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: #555555; + cursor: default; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} + +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} + +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: #ffffff; + background-color: #0088cc; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li > a { + margin-right: 0; +} + +.nav-tabs.nav-stacked { + border-bottom: 0; +} + +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-topleft: 4px; +} + +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -moz-border-radius-bottomright: 4px; + -moz-border-radius-bottomleft: 4px; +} + +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + z-index: 2; + border-color: #ddd; +} + +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} + +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} + +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} + +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.nav .dropdown-toggle .caret { + margin-top: 6px; + border-top-color: #0088cc; + border-bottom-color: #0088cc; +} + +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} + +/* move down carets for tabs */ + +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} + +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} + +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} + +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} + +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} + +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: #999999; +} + +.tabbable { + *zoom: 1; +} + +.tabbable:before, +.tabbable:after { + display: table; + line-height: 0; + content: ""; +} + +.tabbable:after { + clear: both; +} + +.tab-content { + overflow: auto; +} + +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} + +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} + +.tab-content > .active, +.pill-content > .active { + display: block; +} + +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} + +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} + +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.tabs-below > .nav-tabs > li > a:hover, +.tabs-below > .nav-tabs > li > a:focus { + border-top-color: #ddd; + border-bottom-color: transparent; +} + +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} + +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} + +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} + +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} + +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} + +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} + +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} + +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} + +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} + +.nav > .disabled > a { + color: #999999; +} + +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + cursor: default; + background-color: transparent; +} + +.navbar { + *position: relative; + *z-index: 2; + margin-bottom: 20px; + overflow: visible; +} + +.navbar-inner { + min-height: 40px; + padding-right: 20px; + padding-left: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + *zoom: 1; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); +} + +.navbar-inner:before, +.navbar-inner:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-inner:after { + clear: both; +} + +.navbar .container { + width: auto; +} + +.nav-collapse.collapse { + height: auto; + overflow: visible; +} + +.navbar .brand { + display: block; + float: left; + padding: 10px 20px 10px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .brand:hover, +.navbar .brand:focus { + text-decoration: none; +} + +.navbar-text { + margin-bottom: 0; + line-height: 40px; + color: #777777; +} + +.navbar-link { + color: #777777; +} + +.navbar-link:hover, +.navbar-link:focus { + color: #333333; +} + +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-right: 1px solid #ffffff; + border-left: 1px solid #f2f2f2; +} + +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} + +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn, +.navbar .input-prepend .btn-group, +.navbar .input-append .btn-group { + margin-top: 0; +} + +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} + +.navbar-form:before, +.navbar-form:after { + display: table; + line-height: 0; + content: ""; +} + +.navbar-form:after { + clear: both; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} + +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} + +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} + +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 5px; + white-space: nowrap; +} + +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} + +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} + +.navbar-search .search-query { + padding: 4px 14px; + margin-bottom: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.navbar-static-top { + position: static; + margin-bottom: 0; +} + +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} + +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-right: 0; + padding-left: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} + +.navbar-fixed-top { + top: 0; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar-fixed-bottom { + bottom: 0; +} + +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); +} + +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} + +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} + +.navbar .nav > li { + float: left; +} + +.navbar .nav > li > a { + float: none; + padding: 10px 15px 10px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} + +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} + +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + color: #333333; + text-decoration: none; + background-color: transparent; +} + +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} + +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-right: 5px; + margin-left: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + *background-color: #e5e5e5; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} + +.navbar .btn-navbar:hover, +.navbar .btn-navbar:focus, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} + +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} + +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} + +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} + +.navbar .nav > li > .dropdown-menu:before { + position: absolute; + top: -7px; + left: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.navbar .nav > li > .dropdown-menu:after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + border-left: 6px solid transparent; + content: ''; +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + top: auto; + bottom: -7px; + border-top: 7px solid #ccc; + border-bottom: 0; + border-top-color: rgba(0, 0, 0, 0.2); +} + +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + top: auto; + bottom: -6px; + border-top: 6px solid #ffffff; + border-bottom: 0; +} + +.navbar .nav li.dropdown > a:hover .caret, +.navbar .nav li.dropdown > a:focus .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + color: #555555; + background-color: #e5e5e5; +} + +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} + +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} + +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + right: 12px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + right: 13px; + left: auto; +} + +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + right: 100%; + left: auto; + margin-right: -1px; + margin-left: 0; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} + +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + border-color: #252525; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); +} + +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover, +.navbar-inverse .brand:focus, +.navbar-inverse .nav > li > a:focus { + color: #ffffff; +} + +.navbar-inverse .brand { + color: #999999; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + color: #ffffff; + background-color: transparent; +} + +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover, +.navbar-inverse .navbar-link:focus { + color: #ffffff; +} + +.navbar-inverse .divider-vertical { + border-right-color: #222222; + border-left-color: #111111; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + color: #ffffff; + background-color: #111111; +} + +.navbar-inverse .nav li.dropdown > a:hover .caret, +.navbar-inverse .nav li.dropdown > a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} + +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} + +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} + +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + outline: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); +} + +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + *background-color: #040404; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:focus, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} + +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} + +.breadcrumb { + padding: 8px 15px; + margin: 0 0 20px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; + *display: inline; + text-shadow: 0 1px 0 #ffffff; + *zoom: 1; +} + +.breadcrumb > li > .divider { + padding: 0 5px; + color: #ccc; +} + +.breadcrumb > .active { + color: #999999; +} + +.pagination { + margin: 20px 0; +} + +.pagination ul { + display: inline-block; + *display: inline; + margin-bottom: 0; + margin-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.pagination ul > li { + display: inline; +} + +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 20px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} + +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} + +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} + +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: #999999; + cursor: default; + background-color: transparent; +} + +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-topleft: 4px; +} + +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; +} + +.pagination-centered { + text-align: center; +} + +.pagination-right { + text-align: right; +} + +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 17.5px; +} + +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-bottom-left-radius: 6px; + border-bottom-left-radius: 6px; + -webkit-border-top-left-radius: 6px; + border-top-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + -moz-border-radius-topleft: 6px; +} + +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; +} + +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-bottom-left-radius: 3px; + border-bottom-left-radius: 3px; + -webkit-border-top-left-radius: 3px; + border-top-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-topleft: 3px; +} + +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + border-bottom-right-radius: 3px; + -moz-border-radius-topright: 3px; + -moz-border-radius-bottomright: 3px; +} + +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 11.9px; +} + +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 0 6px; + font-size: 10.5px; +} + +.pager { + margin: 20px 0; + text-align: center; + list-style: none; + *zoom: 1; +} + +.pager:before, +.pager:after { + display: table; + line-height: 0; + content: ""; +} + +.pager:after { + clear: both; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} + +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #f5f5f5; +} + +.pager .next > a, +.pager .next > span { + float: right; +} + +.pager .previous > a, +.pager .previous > span { + float: left; +} + +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + cursor: default; + background-color: #fff; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: 1050; + width: 560px; + margin-left: -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + outline: none; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} + +.modal.fade { + top: -25%; + -webkit-transition: opacity 0.3s linear, top 0.3s ease-out; + -moz-transition: opacity 0.3s linear, top 0.3s ease-out; + -o-transition: opacity 0.3s linear, top 0.3s ease-out; + transition: opacity 0.3s linear, top 0.3s ease-out; +} + +.modal.fade.in { + top: 10%; +} + +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} + +.modal-header .close { + margin-top: 2px; +} + +.modal-header h3 { + margin: 0; + line-height: 30px; +} + +.modal-body { + position: relative; + max-height: 400px; + padding: 15px; + overflow-y: auto; +} + +.modal-form { + margin-bottom: 0; +} + +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + *zoom: 1; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} + +.modal-footer:before, +.modal-footer:after { + display: table; + line-height: 0; + content: ""; +} + +.modal-footer:after { + clear: both; +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} + +.tooltip { + position: absolute; + z-index: 1030; + display: block; + font-size: 11px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); + visibility: visible; +} + +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} + +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} + +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} + +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} + +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #000000; + border-width: 5px 5px 0; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #000000; + border-width: 5px 5px 5px 0; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #000000; + border-width: 5px 0 5px 5px; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #000000; + border-width: 0 5px 5px; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} + +.popover-title:empty { + display: none; +} + +.popover-content { + padding: 9px 14px; +} + +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.popover .arrow { + border-width: 11px; +} + +.popover .arrow:after { + border-width: 10px; + content: ""; +} + +.popover.top .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + border-bottom-width: 0; +} + +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-top-color: #ffffff; + border-bottom-width: 0; +} + +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); + border-left-width: 0; +} + +.popover.right .arrow:after { + bottom: -10px; + left: 1px; + border-right-color: #ffffff; + border-left-width: 0; +} + +.popover.bottom .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + border-top-width: 0; +} + +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-bottom-color: #ffffff; + border-top-width: 0; +} + +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); + border-right-width: 0; +} + +.popover.left .arrow:after { + right: 1px; + bottom: -10px; + border-left-color: #ffffff; + border-right-width: 0; +} + +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} + +.thumbnails:before, +.thumbnails:after { + display: table; + line-height: 0; + content: ""; +} + +.thumbnails:after { + clear: both; +} + +.row-fluid .thumbnails { + margin-left: 0; +} + +.thumbnails > li { + float: left; + margin-bottom: 20px; + margin-left: 20px; +} + +.thumbnail { + display: block; + padding: 4px; + line-height: 20px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +a.thumbnail:hover, +a.thumbnail:focus { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} + +.thumbnail > img { + display: block; + max-width: 100%; + margin-right: auto; + margin-left: auto; +} + +.thumbnail .caption { + padding: 9px; + color: #555555; +} + +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} + +.media, +.media .media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media-object { + display: block; +} + +.media-heading { + margin: 0 0 5px; +} + +.media > .pull-left { + margin-right: 10px; +} + +.media > .pull-right { + margin-left: 10px; +} + +.media-list { + margin-left: 0; + list-style: none; +} + +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 11.844px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + white-space: nowrap; + vertical-align: baseline; + background-color: #999999; +} + +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} + +.badge { + padding-right: 9px; + padding-left: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} + +.label:empty, +.badge:empty { + display: none; +} + +a.label:hover, +a.label:focus, +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} + +.label-important, +.badge-important { + background-color: #b94a48; +} + +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} + +.label-warning, +.badge-warning { + background-color: #f89406; +} + +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} + +.label-success, +.badge-success { + background-color: #468847; +} + +.label-success[href], +.badge-success[href] { + background-color: #356635; +} + +.label-info, +.badge-info { + background-color: #3a87ad; +} + +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} + +.label-inverse, +.badge-inverse { + background-color: #333333; +} + +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} + +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} + +.btn-mini .label, +.btn-mini .badge { + top: 0; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.progress .bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + color: #ffffff; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15); +} + +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} + +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} + +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} + +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} + +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} + +.accordion { + margin-bottom: 20px; +} + +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.accordion-heading { + border-bottom: 0; +} + +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} + +.accordion-toggle { + cursor: pointer; +} + +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} + +.carousel { + position: relative; + margin-bottom: 20px; + line-height: 1; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} + +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + line-height: 1; +} + +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} + +.carousel-inner > .active { + left: 0; +} + +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel-inner > .next { + left: 100%; +} + +.carousel-inner > .prev { + left: -100%; +} + +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} + +.carousel-inner > .active.left { + left: -100%; +} + +.carousel-inner > .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} + +.carousel-control.right { + right: 15px; + left: auto; +} + +.carousel-control:hover, +.carousel-control:focus { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} + +.carousel-indicators { + position: absolute; + top: 15px; + right: 15px; + z-index: 5; + margin: 0; + list-style: none; +} + +.carousel-indicators li { + display: block; + float: left; + width: 10px; + height: 10px; + margin-left: 5px; + text-indent: -999px; + background-color: #ccc; + background-color: rgba(255, 255, 255, 0.25); + border-radius: 5px; +} + +.carousel-indicators .active { + background-color: #fff; +} + +.carousel-caption { + position: absolute; + right: 0; + bottom: 0; + left: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} + +.carousel-caption h4, +.carousel-caption p { + line-height: 20px; + color: #ffffff; +} + +.carousel-caption h4 { + margin: 0 0 5px; +} + +.carousel-caption p { + margin-bottom: 0; +} + +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 30px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + letter-spacing: -1px; + color: inherit; +} + +.hero-unit li { + line-height: 30px; +} + +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.hide { + display: none; +} + +.show { + display: block; +} + +.invisible { + visibility: hidden; +} + +.affix { + position: fixed; +} diff --git a/docs/_static/bootstrap-2.3.2/css/bootstrap.min.css b/docs/_static/bootstrap-2.3.2/css/bootstrap.min.css new file mode 100644 index 00000000..b6428e69 --- /dev/null +++ b/docs/_static/bootstrap-2.3.2/css/bootstrap.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap v2.3.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#333;background-color:#fff}a{color:#08c;text-decoration:none}a:hover,a:focus{color:#005580;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#999}a.muted:hover,a.muted:focus{color:#808080}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:bold;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #eee}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #eee;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}form{margin:0 0 20px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#333;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#999}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#999;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#999}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#999}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#999}.radio,.checkbox{min-height:20px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#595959}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#eee;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:middle;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#dff0d8}.table tbody tr.error>td{background-color:#f2dede}.table tbody tr.warning>td{background-color:#fcf8e3}.table tbody tr.info>td{background-color:#d9edf7}.table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.table-hover tbody tr.error:hover>td{background-color:#ebcccc}.table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.table-hover tbody tr.info:hover>td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#333;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#006dcc;*background-color:#04c;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.btn-primary:active,.btn-primary.active{background-color:#039 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#08c;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#005580;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#333;text-decoration:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#04c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:20px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#999}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:-moz-linear-gradient(top,#fff,#f2f2f2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f2f2f2));background-image:-webkit-linear-gradient(top,#fff,#f2f2f2);background-image:-o-linear-gradient(top,#fff,#f2f2f2);background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#333}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:-moz-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#e5e5e5));background-image:-webkit-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:-o-linear-gradient(top,#f2f2f2,#e5e5e5);background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#e5e5e5;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:-moz-linear-gradient(top,#222,#111);background-image:-webkit-gradient(linear,0 0,0 100%,from(#222),to(#111));background-image:-webkit-linear-gradient(top,#222,#111);background-image:-o-linear-gradient(top,#222,#111);background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#999;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:-moz-linear-gradient(top,#151515,#040404);background-image:-webkit-gradient(linear,0 0,0 100%,from(#151515),to(#040404));background-image:-webkit-linear-gradient(top,#151515,#040404);background-image:-o-linear-gradient(top,#151515,#040404);background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;background-color:#040404;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#999;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#999;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed} diff --git a/docs/_static/bootstrap-2.3.2/img/glyphicons-halflings-white.png b/docs/_static/bootstrap-2.3.2/img/glyphicons-halflings-white.png new file mode 100644 index 00000000..3bf6484a Binary files /dev/null and b/docs/_static/bootstrap-2.3.2/img/glyphicons-halflings-white.png differ diff --git a/docs/_static/bootstrap-2.3.2/img/glyphicons-halflings.png b/docs/_static/bootstrap-2.3.2/img/glyphicons-halflings.png new file mode 100644 index 00000000..a9969993 Binary files /dev/null and b/docs/_static/bootstrap-2.3.2/img/glyphicons-halflings.png differ diff --git a/docs/_static/bootstrap-2.3.2/js/bootstrap.js b/docs/_static/bootstrap-2.3.2/js/bootstrap.js new file mode 100644 index 00000000..638bb187 --- /dev/null +++ b/docs/_static/bootstrap-2.3.2/js/bootstrap.js @@ -0,0 +1,2287 @@ +/* =================================================== + * bootstrap-transition.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#transitions + * =================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) + * ======================================================= */ + + $(function () { + + $.support.transition = (function () { + + var transitionEnd = (function () { + + var el = document.createElement('bootstrap') + , transEndEventNames = { + 'WebkitTransition' : 'webkitTransitionEnd' + , 'MozTransition' : 'transitionend' + , 'OTransition' : 'oTransitionEnd otransitionend' + , 'transition' : 'transitionend' + } + , name + + for (name in transEndEventNames){ + if (el.style[name] !== undefined) { + return transEndEventNames[name] + } + } + + }()) + + return transitionEnd && { + end: transitionEnd + } + + })() + + }) + +}(window.$jqTheme || window.jQuery); +/* ========================================================== + * bootstrap-alert.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#alerts + * ========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* ALERT CLASS DEFINITION + * ====================== */ + + var dismiss = '[data-dismiss="alert"]' + , Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype.close = function (e) { + var $this = $(this) + , selector = $this.attr('data-target') + , $parent + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + $parent = $(selector) + + e && e.preventDefault() + + $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) + + $parent.trigger(e = $.Event('close')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + $parent + .trigger('closed') + .remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent.on($.support.transition.end, removeElement) : + removeElement() + } + + + /* ALERT PLUGIN DEFINITION + * ======================= */ + + var old = $.fn.alert + + $.fn.alert = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('alert') + if (!data) $this.data('alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + + /* ALERT NO CONFLICT + * ================= */ + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + /* ALERT DATA-API + * ============== */ + + $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) + +}(window.$jqTheme || window.jQuery); +/* ============================================================ + * bootstrap-button.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#buttons + * ============================================================ + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* BUTTON PUBLIC CLASS DEFINITION + * ============================== */ + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, $.fn.button.defaults, options) + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + , $el = this.$element + , data = $el.data() + , val = $el.is('input') ? 'val' : 'html' + + state = state + 'Text' + data.resetText || $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d) + }, 0) + } + + Button.prototype.toggle = function () { + var $parent = this.$element.closest('[data-toggle="buttons-radio"]') + + $parent && $parent + .find('.active') + .removeClass('active') + + this.$element.toggleClass('active') + } + + + /* BUTTON PLUGIN DEFINITION + * ======================== */ + + var old = $.fn.button + + $.fn.button = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('button') + , options = typeof option == 'object' && option + if (!data) $this.data('button', (data = new Button(this, options))) + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.defaults = { + loadingText: 'loading...' + } + + $.fn.button.Constructor = Button + + + /* BUTTON NO CONFLICT + * ================== */ + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + /* BUTTON DATA-API + * =============== */ + + $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + }) + +}(window.$jqTheme || window.jQuery); +/* ========================================================== + * bootstrap-carousel.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#carousel + * ========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* CAROUSEL CLASS DEFINITION + * ========================= */ + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.options.pause == 'hover' && this.$element + .on('mouseenter', $.proxy(this.pause, this)) + .on('mouseleave', $.proxy(this.cycle, this)) + } + + Carousel.prototype = { + + cycle: function (e) { + if (!e) this.paused = false + if (this.interval) clearInterval(this.interval); + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + return this + } + + , getActiveIndex: function () { + this.$active = this.$element.find('.item.active') + this.$items = this.$active.parent().children() + return this.$items.index(this.$active) + } + + , to: function (pos) { + var activeIndex = this.getActiveIndex() + , that = this + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) { + return this.$element.one('slid', function () { + that.to(pos) + }) + } + + if (activeIndex == pos) { + return this.pause().cycle() + } + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + , pause: function (e) { + if (!e) this.paused = true + if (this.$element.find('.next, .prev').length && $.support.transition.end) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + clearInterval(this.interval) + this.interval = null + return this + } + + , next: function () { + if (this.sliding) return + return this.slide('next') + } + + , prev: function () { + if (this.sliding) return + return this.slide('prev') + } + + , slide: function (type, next) { + var $active = this.$element.find('.item.active') + , $next = next || $active[type]() + , isCycling = this.interval + , direction = type == 'next' ? 'left' : 'right' + , fallback = type == 'next' ? 'first' : 'last' + , that = this + , e + + this.sliding = true + + isCycling && this.pause() + + $next = $next.length ? $next : this.$element.find('.item')[fallback]() + + e = $.Event('slide', { + relatedTarget: $next[0] + , direction: direction + }) + + if ($next.hasClass('active')) return + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + this.$element.one('slid', function () { + var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) + $nextIndicator && $nextIndicator.addClass('active') + }) + } + + if ($.support.transition && this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + this.$element.one($.support.transition.end, function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { that.$element.trigger('slid') }, 0) + }) + } else { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid') + } + + isCycling && this.cycle() + + return this + } + + } + + + /* CAROUSEL PLUGIN DEFINITION + * ========================== */ + + var old = $.fn.carousel + + $.fn.carousel = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('carousel') + , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) + , action = typeof option == 'string' ? option : options.slide + if (!data) $this.data('carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + $.fn.carousel.defaults = { + interval: 5000 + , pause: 'hover' + } + + $.fn.carousel.Constructor = Carousel + + + /* CAROUSEL NO CONFLICT + * ==================== */ + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + /* CAROUSEL DATA-API + * ================= */ + + $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var $this = $(this), href + , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + , options = $.extend({}, $target.data(), $this.data()) + , slideIndex + + $target.carousel(options) + + if (slideIndex = $this.attr('data-slide-to')) { + $target.data('carousel').pause().to(slideIndex).cycle() + } + + e.preventDefault() + }) + +}(window.$jqTheme || window.jQuery); +/* ============================================================= + * bootstrap-collapse.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#collapse + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* COLLAPSE PUBLIC CLASS DEFINITION + * ================================ */ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, $.fn.collapse.defaults, options) + + if (this.options.parent) { + this.$parent = $(this.options.parent) + } + + this.options.toggle && this.toggle() + } + + Collapse.prototype = { + + constructor: Collapse + + , dimension: function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + , show: function () { + var dimension + , scroll + , actives + , hasData + + if (this.transitioning || this.$element.hasClass('in')) return + + dimension = this.dimension() + scroll = $.camelCase(['scroll', dimension].join('-')) + actives = this.$parent && this.$parent.find('> .accordion-group > .in') + + if (actives && actives.length) { + hasData = actives.data('collapse') + if (hasData && hasData.transitioning) return + actives.collapse('hide') + hasData || actives.data('collapse', null) + } + + this.$element[dimension](0) + this.transition('addClass', $.Event('show'), 'shown') + $.support.transition && this.$element[dimension](this.$element[0][scroll]) + } + + , hide: function () { + var dimension + if (this.transitioning || !this.$element.hasClass('in')) return + dimension = this.dimension() + this.reset(this.$element[dimension]()) + this.transition('removeClass', $.Event('hide'), 'hidden') + this.$element[dimension](0) + } + + , reset: function (size) { + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + [dimension](size || 'auto') + [0].offsetWidth + + this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') + + return this + } + + , transition: function (method, startEvent, completeEvent) { + var that = this + , complete = function () { + if (startEvent.type == 'show') that.reset() + that.transitioning = 0 + that.$element.trigger(completeEvent) + } + + this.$element.trigger(startEvent) + + if (startEvent.isDefaultPrevented()) return + + this.transitioning = 1 + + this.$element[method]('in') + + $.support.transition && this.$element.hasClass('collapse') ? + this.$element.one($.support.transition.end, complete) : + complete() + } + + , toggle: function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + } + + + /* COLLAPSE PLUGIN DEFINITION + * ========================== */ + + var old = $.fn.collapse + + $.fn.collapse = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('collapse') + , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option) + if (!data) $this.data('collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.defaults = { + toggle: true + } + + $.fn.collapse.Constructor = Collapse + + + /* COLLAPSE NO CONFLICT + * ==================== */ + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + /* COLLAPSE DATA-API + * ================= */ + + $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { + var $this = $(this), href + , target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + , option = $(target).data('collapse') ? 'toggle' : $this.data() + $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + $(target).collapse(option) + }) + +}(window.$jqTheme || window.jQuery); +/* ============================================================ + * bootstrap-dropdown.js v2.3.2 + * http://twitter.github.com/bootstrap/javascript.html#dropdowns + * ============================================================ + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* DROPDOWN CLASS DEFINITION + * ========================= */ + + var toggle = '[data-toggle=dropdown]' + , Dropdown = function (element) { + var $el = $(element).on('click.dropdown.data-api', this.toggle) + $('html').on('click.dropdown.data-api', function () { + $el.parent().removeClass('open') + }) + } + + Dropdown.prototype = { + + constructor: Dropdown + + , toggle: function (e) { + var $this = $(this) + , $parent + , isActive + + if ($this.is('.disabled, :disabled')) return + + $parent = getParent($this) + + isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement) { + // if mobile we we use a backdrop because click events don't delegate + $('