Source code for fairyfly_therm.config

"""Fairyfly_therm configurations.

Import this into every module where access configurations are needed.

Usage:

.. code-block:: python

    from fairyfly_therm.config import folders
    print(folders.lbnl_data_path)
    folders.lbnl_data_path = "C:/Users/Person/LBNL"
"""
import os
import json
import pkgutil

import ladybug.config as lb_config


[docs] class Folders(object): """Fairyfly_therm 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: * therm_path * therm_exe * therm_version * therm_version_str * fortran_dll_path * lbnl_data_path * therm_settings_path * therm_lib_path * material_lib_file * gas_lib_file * bc_steady_state_lib_file * user_material_folder * user_gas_folder * user_steady_state_folder * config_file * mute """ THERM_VERSION = (8, 1, 30, 0) LBNL_URL = 'https://windows.lbl.gov/therm-software-downloads' FORTRAN_URL = 'https://windows.lbl.gov/redistributable-packages' 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 therm_path(self): """Get or set the path to Therm installation folder. This is typically the folder within the lbnl program files directory that starts with THERM and contains executables and dlls. """ return self._therm_path @therm_path.setter def therm_path(self, t_path): exe_name = 'THERM{}.exe'.format(self.THERM_VERSION[0]) if not t_path: # check the default installation location t_path = self._find_therm_folder() therm_exe_file = os.path.join(t_path, exe_name) if t_path is not None else None # if the executable exists, set the variables if t_path and os.path.isfile(therm_exe_file): self._therm_path = t_path self._therm_exe = therm_exe_file self._therm_version = self.THERM_VERSION self._therm_version_str = '.'.join(str(i) for i in self.THERM_VERSION) if not self.mute: print("Path to Therm is set to: %s" % self._therm_path) else: if t_path is not None and os.path.isdir(t_path): self._therm_path = t_path else: if t_path is not None: msg = '{} is not a valid path to a Therm installation.'.format(t_path) print(msg) self._therm_path = None self._therm_exe = None self._therm_version = None self._therm_version_str = None @property def therm_exe(self): """Get the path to Therm executable. Will be none if no Therm installation was found. """ return self._therm_exe @property def therm_version(self): """Get a tuple for the version of therm (eg. (8, 1, 30)). This will be None if the version could not be sensed or if no Therm installation was found. """ return self._therm_version @property def therm_version_str(self): """Get text for the full version of therm (eg."8.1.30"). This will be None if the version could not be sensed or if no Therm installation was found. """ return self._therm_version_str @property def fortran_dll_path(self): """Get or set the path to the directory with DLLs for FORTRAN runtime routines. This is typically a folder within the Shared Libraries folder of the common Intel files under Program Files (x86). """ return self._fortran_dll_path @fortran_dll_path.setter def fortran_dll_path(self, f_path): if not f_path: # check the default installation location f_path = self._find_fortran_dll() # if the executable exists, set the variables if f_path and os.path.isdir(f_path): self._fortran_dll_path = f_path if not self.mute: print("Path to Fortran DLL is set to: %s" % self._fortran_dll_path) else: self._fortran_dll_path = None @property def lbnl_data_path(self): """Get or set the path to the folder containing the LBNL data. This folder typically exists under the User profile and contains sub-folders for all installed versions of LBNL THERM and WINDOW. The THERM sub-folder contains a lib sub-folder with XML files for all materials, boundary conditions, etc. """ return self._lbnl_data_path @lbnl_data_path.setter def lbnl_data_path(self, path): if not path: # check the default locations of the template library path = self._find_lbnl_data_folder() # gather all of the sub folders underneath the master folder if path and os.path.isdir(path): self._lbnl_data_path = path self._therm_settings_path = self._check_therm_settings(path) self._material_lib_file = self._check_therm_lib_file(path, 'Materials.xml') self._gas_lib_file = self._check_therm_lib_file(path, 'Gases.xml') self._bc_steady_state_lib_file = self._check_therm_lib_file( path, 'BoundaryConditionsSteadyState.xml') if not self.mute: print('Path to LBNL data is set to: {}'.format(self._lbnl_data_path)) else: if path: msg = '{} is not a valid path to a LBNL data folder.'.format(path) print(msg) self._lbnl_data_path = None self._therm_settings_path = None self._material_lib_file = None self._gas_lib_file = None self._bc_steady_state_lib_file = None @property def therm_settings_path(self): """Get the path to the .ini file with THERM settings. Will be None if no LBNL data folder was found. """ return self._therm_settings_path @property def therm_lib_path(self): """Get or set the path to the folder from which therm materials are loaded. This will be the therm folder within within the user's standards folder if it exists. """ return self._therm_lib_path @therm_lib_path.setter def therm_lib_path(self, path): if not path: # check the default locations of the template library path = self._find_therm_lib() # gather all of the sub folders underneath the master folder if path and os.path.isdir(path): self._therm_lib_path = path mat_dir = os.path.join(path, 'materials') gas_dir = os.path.join(path, 'gases') bc_dir = os.path.join(path, 'conditions') self._user_material_folder = mat_dir if os.path.isdir(mat_dir) else None self._user_gas_folder = gas_dir if os.path.isdir(gas_dir) else None self._user_steady_state_folder = bc_dir if os.path.isdir(bc_dir) else None if not self.mute: print('Path to THERM library is set to: {}'.format(self._lbnl_data_path)) else: if path: msg = '{} is not a valid path to a THERM standards library.'.format(path) print(msg) self._therm_lib_path = None self._user_material_folder = None self._user_gas_folder = None self._user_steady_state_folder = None @property def material_lib_file(self): """Get the path to the material library file.""" return self._material_lib_file @property def gas_lib_file(self): """Get the path to the gas library file.""" return self._gas_lib_file @property def bc_steady_state_lib_file(self): """Get the path to the steady state condition library file.""" return self._bc_steady_state_lib_file @property def user_material_folder(self): """Get the path to the user material library folder.""" return self._user_material_folder @property def user_gas_folder(self): """Get the path to the user gas library folder.""" return self._user_gas_folder @property def user_steady_state_folder(self): """Get the path to the user steady state condition library folder.""" return self._user_steady_state_folder @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 check_therm_version(self): """Raise an exception message about the THERM installation if it is not usable.""" # first, check that we are on the correct operating system if os.name != 'nt': msg = 'LBNL THERM can only run on Windows machines and so it cannot ' \ 'be used while on "{}" operating system.'.format(os.name) raise ValueError(msg) # then, check that THERM is installed and it is the correct version ver_str = '.'.join(str(i) for i in self.THERM_VERSION) dn_msg = 'Download and install THERM version {} from:\n{}'.format( ver_str, self.LBNL_URL) if self.therm_exe is not None: # make sure that the FORTRAN DLL library was installed if self.fortran_dll_path is None: msg = 'A valid THERM installation was found at "{}"\n' \ 'but it does not have the required redistributable packages.\n' \ 'To install these packages, follow the instructions at:\n ' \ '{}'.format(self.therm_path, self.FORTRAN_URL) raise ValueError(msg) return None # everything is good elif self.therm_path is not None: msg = 'A THERM installation was found at "{}" but it is not ' \ 'for version {}.\nFairyfly is currently only compatible with ' \ 'this version of THERM.\n{}.'.format(self.therm_path, ver_str, dn_msg) raise ValueError(msg) else: msg = 'No THERM installation was found on this machine.\n{}'.format(dn_msg) raise ValueError(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 = { "therm_path": r'', "fortran_dll_path": r'', "lbnl_data_path": r'', "therm_lib_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 not key.startswith('__') and p.strip(): default_path[key] = p.strip() # set paths for therm installations self.therm_path = default_path["therm_path"] self.fortran_dll_path = default_path["fortran_dll_path"] self.lbnl_data_path = default_path["lbnl_data_path"] self.therm_lib_path = default_path["therm_lib_path"] @staticmethod def _find_therm_folder(): """Find the Therm installation in its default location.""" # first check if there's a version installed in the ladybug_tools folder # note that this option is not likely to be used because of the THERM license lb_install = lb_config.folders.ladybug_tools_folder thm_path = None if os.path.isdir(lb_install): test_path = os.path.join(lb_install, 'THERM') thm_path = test_path if os.path.isdir(test_path) else None def getversion(therm_path): """Get digits for the version of Version.""" try: ver = ''.join(s for s in therm_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 'THERM' and no version return 0 # then check for the default location where standalone Therm is installed if thm_path is None and os.name == 'nt': # search the C:/ drive on Windows major, minor, _, _ = Folders.THERM_VERSION lbnl_install_dir = 'C:/Program Files (x86)/lbnl' test_path = '{}/THERM{}.{}'.format(lbnl_install_dir, major, minor) if os.path.isdir(test_path): thm_path = test_path elif os.path.isdir(lbnl_install_dir): therm_folders = [] for f in os.listdir(lbnl_install_dir): f_path = os.path.join(lbnl_install_dir, f) if f.lower().startswith('therm') and os.path.isdir(f_path): therm_folders.append(f_path) if len(therm_folders) != 0: thm_path = sorted(therm_folders, key=getversion, reverse=True)[0] return thm_path @staticmethod def _find_fortran_dll(): """Find the folder to the Fortran DLLs in its default location.""" # check for the default location in Program Files (x86) if os.name == 'nt': # search the C:/ drive on Windows dll_dir = 'C:/Program Files (x86)/Common Files/Intel/Shared Libraries/ia32' if os.path.isdir(dll_dir): return dll_dir @staticmethod def _find_lbnl_data_folder(): """Find the LBNL data folder in its default location.""" # then check the default location where the LBNL installer puts it lib_folder = None if os.name == 'nt': # search the C:/ drive on Windows test_path = 'C:/Users/Public/LBNL/' if os.path.isdir(test_path): lib_folder = test_path return lib_folder @staticmethod def _check_therm_settings(path): """Check that a settings file exists within the LBNL data folder.""" if not path: # first check that a path exists return None major, minor, _, _ = Folders.THERM_VERSION settings_dir = os.path.join(path, 'Settings') set_file = os.path.join(settings_dir, 'therm{}.{}.ini'.format(major, minor)) if os.path.isfile(set_file): return set_file @staticmethod def _find_therm_lib(): """Find the user standards folder in its default location.""" # first check if there's a user-defined folder in AppData app_folder = os.getenv('APPDATA') if app_folder is not None: lib_folder = os.path.join(app_folder, 'ladybug_tools', 'standards', 'therm') if os.path.isdir(lib_folder): return lib_folder # then check next to the Python library for finder, name, ispkg in pkgutil.iter_modules(): if name == 'fairyfly_therm_standards': lib_folder = os.path.join(finder.path, name) if os.path.isdir(lib_folder): return lib_folder @staticmethod def _check_therm_lib_file(path, lib_file): """Check that a XML file exists within the LBNL therm library.""" if not path: # first check that a path exists return None if os.name == 'nt': major, minor, _, _ = Folders.THERM_VERSION lib_path = os.path.join(path, 'THERM{}.{}/lib'.format(major, minor), lib_file) if os.path.isfile(lib_path): return lib_path
"""Object possesing all key folders within the configuration.""" folders = Folders(mute=True)