Source code for exo_k.settings

# -*- coding: utf-8 -*-
"""
@author: jeremy leconte

A class based on a singleton to store global options only once for every instance.
"""
import os.path
import re
from glob import glob
from string import Template
from .util.singleton import Singleton

[docs] class Settings(Singleton): """A class based on a singleton to store global options only once for every instance. So all the following methods can be called using the following syntax: >>> exo_k.Settings().method_name(agrs) In gerneal, they will change internal global attributes that change the global behavior of some routines in the library. """
[docs] def init(self, *args, **kwds): self.reset_search_path() self._log_interp = True self._log_interp_aerosol = False self._convert_to_mks = False self._delimiter = '_' self.set_delimiters(r"_.\-") self._case_sensitive = False
[docs] def reset_search_path(self, path_type = 'all', no_path = False): """Set default search path. Parameters ---------- path_type: str What type of path to reset. Possibilities are 'all' (default), 'kdata', 'cia', and 'aerosol' no_path: bool (optional) If False (default), the search path is reset to '.'. If True, empties the search path. """ if no_path: local_path=[] else: local_path=[os.path.abspath('.')] if path_type in ('kdata', 'ktable', 'all'): self._ktable_search_path = local_path.copy() if path_type in ('kdata', 'xtable', 'all'): self._xtable_search_path = local_path.copy() if path_type in ('cia', 'all'): self._cia_search_path = local_path.copy() if path_type in ('aerosol', 'all'): self._aerosol_search_path = local_path.copy()
[docs] def add_search_path(self, *search_paths, path_type = 'kdata'): """Add path(s) to the list of paths that will be searched for various files. Parameters ---------- search_path : string or list of strings Search path(s) to look for opacities. path_type: str What type of path to change. Possibilities are: * 'kdata' (default): global xsec and corr-k search path * 'ktable' : only corr-k files * 'xtable' : only cross section files * 'cia': search path for CIA files * 'aerosol': search path for Aerosol optical property files Examples -------- >>> exo_k.Settings().add_search_path('data/xsec','data/corrk') >>> exo_k.Settings().search_path() ['/your/path/to/exo_k', '/your/path/to/exo_k/data/xsec', '/your/path/to/exo_k/data/corrk'] """ path_to_change=[] if path_type == 'cia': path_to_change.append(self._cia_search_path) elif path_type == 'aerosol': path_to_change.append(self._aerosol_search_path) else: if path_type in ('kdata', 'ktable'): path_to_change.append(self._ktable_search_path) if path_type in ('kdata', 'xtable'): path_to_change.append(self._xtable_search_path) if path_to_change == []: raise RuntimeError('I did not understand the path type provided.') for path in search_paths: if not os.path.isdir(path): raise NotADirectoryError("The search_path you provided "\ f"does not exist or is not a directory ({path})") else: for to_change in path_to_change: if os.path.abspath(path) not in to_change: to_change.append(os.path.abspath(path))
[docs] def add_cia_search_path(self, *search_paths): """Add path(s) to the list of paths that will be searched for cia files. Parameters ---------- search_path : string or list of strings Search path(s) to look for opacities. """ self.add_search_path(*search_paths, path_type = 'cia')
[docs] def add_aerosol_search_path(self, *search_paths): """Add path(s) to the list of paths that will be searched for aerosol files. Parameters ---------- search_path : string or list of strings Search path(s) to look for opacities. """ self.add_search_path(*search_paths, path_type = 'aerosol')
[docs] def set_search_path(self, *search_paths, path_type = 'kdata'): """Like :func:~`exo_k.settings.Settings.add_search_path` except for the fact that the path is reset first. """ self.reset_search_path(path_type = path_type, no_path = True) self.add_search_path(*search_paths, path_type = path_type)
[docs] def set_cia_search_path(self, *search_paths): """Like :func:~`exo_k.settings.Settings.add_cia_search_path` except for the fact that the path is reset first. """ self.set_search_path(*search_paths, path_type = 'cia')
[docs] def set_aerosol_search_path(self, *search_paths): """Like :func:~`exo_k.settings.Settings.add_aerosol_search_path` except for the fact that the path is reset first. """ self.set_search_path(*search_paths, path_type = 'aerosol')
@property def search_path(self): """Returns the current value of the global search path (_search_path) """ return {'ktable': self._ktable_search_path, 'xtable': self._xtable_search_path, 'cia': self._cia_search_path, 'aerosol': self._aerosol_search_path}
[docs] def cia_search_path(self): """Returns the current value of the cia search path (_cia_search_path) """ raise RuntimeError('Deprecated after v0.0.5. Use xk.Settings().search_path["cia"] instead.')
[docs] def aerosol_search_path(self): """Returns the current value of the aerosol search path (_aerosol_search_path) """ raise RuntimeError( 'Deprecated after v0.0.5. Use xk.Settings().search_path["aerosol"] instead.')
[docs] def set_delimiter(self, newdelimiter): """Sets the delimiter string used to separate molecule names in filenames. Parameters ---------- newdelimiter: string New delimiter to use. Default is '_'. Example ------- If I have a file named 'H2O.R10000_xsec.hdf5' that I want to load in a `Kdatabase`, the default settings will result in an error: >>> database=xk.Kdatabase(['H2O'],'R10000') No file was found with these filters: ('H2O_', 'R1000') in the following directories: ['/home/falco/xsec/xsec_sampled_R10000_0.3-15'] Using >>> xk.Settings().set_delimiter('.') >>> database=xk.Kdatabase(['H2O'],'R10000') finds the file. """ self._delimiter = newdelimiter
[docs] def set_delimiters(self, newdelimiters): """Sets the delimiter string used to separate molecule names in filenames. If you want to include the '-' character, you must put a '\\' before to avoid it being interpreted as a special character by the `re` module. Parameters ---------- newdelimiters: string New delimiters to use. Default is '_.\\-'. """ self._delimiters = newdelimiters self._search_mol_template = Template('(?=^$mol['+self._delimiters+ \ ']|['+self._delimiters+']$mol['+self._delimiters+'])') self._search_cia_template = Template('(?=^$mol1\\-$mol2['+self._delimiters+ \ ']|^$mol2\\-$mol1['+self._delimiters+'])')
[docs] def set_log_interp(self, log_interp): """Sets the default interpolation mode for kdata. Default is Log. Parameters ---------- log_interp: boolean If True, log interpolation. Linear if False. """ self._log_interp = log_interp
[docs] def set_log_interp_aerosol(self, log_interp): """Sets the default interpolation mode for aerosols. Default is Linear. Parameters ---------- log_interp: boolean If True, log interpolation. Linear if False. """ self._log_interp_aerosol = log_interp
[docs] def set_case_sensitive(self, case_sensitive): """Set whether name matching is case sensitive. Default is False. Parameters ---------- case_sensitive: boolean If True, name matching is case sensitive. """ self._case_sensitive = case_sensitive
[docs] def set_mks(self, set_mks): """Forces conversion to mks system. Parameters ---------- set_mks: boolean If True, all datasets are converted to mks upon loading. """ self._convert_to_mks = set_mks
[docs] def list_files(self, *str_filters, molecule = None, only_one = False, search_path = None, path_type = 'kdata'): """A routine that provides a list of all filenames containing a set of string filters in one of the global _search_path or a local one. Whether the search is case sensitive is specified through the Settings.set_case_sensitive() method. .. warning:: The pattern matching with the `str_filters` is done using regular expressions. If you want to match special characters (like a dot in a filename), do not forget to put a backslash in front of it. Parameters ---------- *str_filters: str A set of strings that need to be contained in the name of the file molecule: str The name of a molecule to be looked for in the filename. It must be followed by one of the characters in self._delimiters and either at the begining of the name or just after one of the characters in self._delimiters. only_one: boolean, optional If true, only one filename is returned (the first one). If false, a list is returned. Default is False. search_path: str, optional If search_path is provided, it locally overrides the global _search_path settings and only files in search_path are returned. Returns ------- list of strings List of filenames corresponding to all the str_filters """ if search_path is not None: local_search_path=[search_path] else: if path_type == 'ktable': local_search_path=self._ktable_search_path elif path_type == 'xtable': local_search_path=self._xtable_search_path elif path_type == 'aerosol': local_search_path=self._aerosol_search_path filenames = [f for path in local_search_path for f in glob(os.path.join(path,'*'))] finalnames=filenames[:] if molecule is not None: if self._case_sensitive: template=self._search_mol_template.substitute(mol=molecule) else: template=self._search_mol_template.substitute(mol=molecule.lower()) else: template='^' for filt in str_filters: if self._case_sensitive: template+='(?=.*'+filt+')' else: template+='(?=.*'+filt.lower()+')' #print(template) for filename in filenames: if os.path.isdir(filename): finalnames.remove(filename) continue if self._case_sensitive: basename=os.path.basename(filename) else: basename=os.path.basename(filename).lower() if re.search(template, basename) is None: finalnames.remove(filename) continue if len(finalnames)>1 and only_one: print("""be careful: {filt} String filters not specific enough, several corresponding files have been found. We will use the first one: {file} Other files are: {other_files}""".format(filt=str_filters,file=finalnames[0], \ other_files=finalnames[1:])) finalnames=[finalnames[0]] if not finalnames: raise NoFileFoundError("""No file found to match this regular expression (based on your filters): {filt} in the following directories: {path} """.format(filt=template,path=local_search_path)) # an empty sequence yields False in a conditional statement ! return finalnames
[docs] def list_cia_files(self, *str_filters, molecule_pair = None, only_one = False, search_path = None): """A routine that provides a list of all filenames containing a set of string filters in the global _search_path or a local one. Whether the search is case sensitive is specified through the Settings.set_case_sensitive() method. .. warning:: The pattern matching with the `str_filters` is done using regular expressions. If you want to match special characters (like a dot in a filename), do not forget to put a backslash in front of it. Parameters ---------- *str_filters: str A set of strings that need to be contained in the name of the file molecule_pair: list of 2 str The name of the 2 molecules in the cia pair. only_one: boolean, optional If true, only one filename is returned (the first one). If false, a list is returned. Default is False. search_path: str, optional If search_path is provided, it locally overrides the global _search_path settings and only files in search_path are returned. Returns ------- list of strings List of filenames corresponding to all the str_filters """ if search_path is not None: local_search_path=[search_path] else: local_search_path=self._cia_search_path filenames = [f for path in local_search_path for f in glob(os.path.join(path,'*'))] finalnames=filenames[:] if molecule_pair is not None: if self._case_sensitive: template=self._search_cia_template.substitute(mol1=molecule_pair[0], mol2=molecule_pair[1]) else: template=self._search_cia_template.substitute(mol1=molecule_pair[0].lower(), mol2=molecule_pair[1].lower()) else: template='^' for filt in str_filters: if self._case_sensitive: template+='(?=.*'+filt+')' else: template+='(?=.*'+filt.lower()+')' #print(template) for filename in filenames: if os.path.isdir(filename): finalnames.remove(filename) continue if self._case_sensitive: basename=os.path.basename(filename) else: basename=os.path.basename(filename).lower() if re.search(template, basename) is None: finalnames.remove(filename) continue if len(finalnames)>1 and only_one: print("""be careful: {filt} String filters not specific enough, several corresponding files have been found. We will use the first one: {file} Other files are: {other_files}""".format(filt=str_filters,file=finalnames[0], \ other_files=finalnames[1:])) finalnames=[finalnames[0]] if not finalnames: raise NoFileFoundError("""No file found to match this regular expression (based on your filters): {filt} in the following directories: {path} """.format(filt=template,path=local_search_path)) # an empty sequence yields False in a conditional statement ! return finalnames
[docs] class NoFileFoundError(Exception): """Error when no file is found """