Example #1
0
 def __init__(self, name):
     Logger.__init__(self, name)
     Fittable.__init__(self)
     self._name = name
     self._total_contribution = None
     self._enabled = True
     self.sigma_xsec = None
 def init(self):
     self.opacity_dict = {}
     self._opacity_path = None
     self.log = Logger('OpacityCache')
     self._default_interpolation = 'linear'
     self._memory_mode = True
     self._radis = False
     self._radis_props = (600, 30000, 10000)
Example #3
0
    def __init__(self, name, nlayers):
        Fittable.__init__(self)
        Logger.__init__(self, name)

        if nlayers <= 0:
            self.error('Number of layers: [%s] should be greater than 0',
                       nlayers)
            raise ValueError('Number of layers should be at least 1')

        self._nlayers = int(nlayers)
Example #4
0
    def __init__(self, name):
        Logger.__init__(self, name)
        Fittable.__init__(self)
        self.opacity_dict = {}
        self.cia_dict = {}

        self._native_grid = None

        self._fitting_parameters = {}

        self.contribution_list = []
Example #5
0
    def __init__(self, temperature=5000, radius=1.0, distance=1,
                 magnitudeK=10.0, mass=1.0, metallicity=1.0):

        Logger.__init__(self, self.__class__.__name__)
        Fittable.__init__(self)
        self._temperature = temperature
        self._radius = radius*RSOL
        self._mass = mass*MSOL
        self.debug('Star mass %s', self._mass)
        self.sed = None
        self.distance = distance
        self.magnitudeK = magnitudeK
        self._metallicity = metallicity
Example #6
0
    def __init__(self, name):
        Logger.__init__(self, name)
        Fittable.__init__(self)

        self.mu_profile = None


        if GlobalCache()['opacity_method'] == 'ktables':
            self._avail_active = KTableCache().find_list_of_molecules()
        else:

            self._avail_active = OpacityCache().find_list_of_molecules()
        #self._avail_active = OpacityCache().find_list_of_molecules()
        deactive_list = GlobalCache()['deactive_molecules']
        if deactive_list is not None:
            self._avail_active = [k for k in self._avail_active if k not in deactive_list]
Example #7
0
 def __init__(self,
              planet_mass=1.0,
              planet_radius=1.0,
              planet_distance=1,
              impact_param=0.5,
              orbital_period=2.0,
              albedo=0.3,
              transit_time=3000.0):
     Logger.__init__(self, 'Planet')
     Fittable.__init__(self)
     self._mass = planet_mass * MJUP
     self._radius = planet_radius * RJUP
     self._distance = planet_distance * AU
     self._impact = impact_param
     self._orbit_period = orbital_period
     self._albedo = albedo
     self._transit_time = transit_time
Example #8
0
 def __init__(self,
              planet_mass=1.0,
              planet_radius=1.0,
              planet_sma=None,
              planet_distance=1.0,
              impact_param=0.5,
              orbital_period=2.0,
              albedo=0.3,
              transit_time=3000.0):
     Logger.__init__(self, 'Planet')
     Fittable.__init__(self)
     self.set_planet_mass(planet_mass, 'Mjup')
     self.set_planet_radius(planet_radius, 'Rjup')
     self.set_planet_semimajoraxis(planet_sma or planet_distance)
     self._impact = impact_param
     self._orbit_period = orbital_period
     self._albedo = albedo
     self._transit_time = transit_time
Example #9
0
from taurex.log import Logger
from .classfactory import ClassFactory
log = Logger('Factory')


def get_keywordarg_dict(klass, is_mixin=False):

    import inspect
    init_dicts = {}
    if not is_mixin:
        init_dicts = {}
        args, varargs, varkw, defaults = inspect.getargspec(klass.__init__)
        log.debug('Inpection {} {} {} {}'.format(args, varargs, varkw,
                                                 defaults))
        if defaults is None:
            return init_dicts

        keyword_args = args[-len(defaults):]

        for keyword, value in zip(keyword_args, defaults):
            init_dicts[keyword] = value
    else:
        from taurex.mixin.core import determine_mixin_args
        init_dicts = determine_mixin_args(klass.__bases__)

    return init_dicts


def create_klass(config, klass, is_mixin):
    kwargs = get_keywordarg_dict(klass, is_mixin)
 def init(self):
     self.variable_dict = {}
     self.log = Logger('GlobalCache')
Example #11
0
 def __init__(self):
     Logger.__init__(self, self.__class__.__name__)
Example #12
0
 def __init__(self, name, molecule_name):
     Logger.__init__(self, name)
     Fittable.__init__(self)
     self._molecule_name = molecule_name
     self.mix_profile = None
    def __init__(self, name, nlayers):
        Fittable.__init__(self)
        Logger.__init__(self, name)

        self._nlayers = int(nlayers)
Example #14
0
 def __init__(self, name):
     Logger.__init__(self, name)
     Fittable.__init__(self)
class ClassFactory(Singleton):
    """
    A factory the discovers new
    classes from plugins
    """

    def init(self):
        self.log = Logger('ClassFactory')

        self.extension_paths = []
        self.reload_plugins()

    def set_extension_paths(self, paths=None, reload=True):
        self.extension_paths = paths
        if reload:
            self.reload_plugins()

    def reload_plugins(self):
        self.log.info('Reloading all modules and plugins')
        self.setup_batteries_included()
        self.setup_batteries_included_mixin()
        self.load_plugins()
        self.load_extension_paths()

    def setup_batteries_included_mixin(self):
        """
        Collect all the classes that are built into
        TauREx 3
        """
        from taurex.mixin import mixins

        self._temp_mixin_klasses = set()
        self._chem_mixin_klasses = set()
        self._gas_mixin_klasses = set()
        self._press_mixin_klasses = set()
        self._planet_mixin_klasses = set()
        self._star_mixin_klasses = set()
        self._inst_mixin_klasses = set()
        self._model_mixin_klasses = set()
        self._contrib_mixin_klasses = set()
        self._opt_mixin_klasses = set()
        self._obs_mixin_klasses = set()

        self._temp_mixin_klasses.update(
            self._collect_temperatures_mixin(mixins))
        self._chem_mixin_klasses.update(self._collect_chemistry_mixin(mixins))
        self._gas_mixin_klasses.update(self._collect_gas_mixin(mixins))
        self._press_mixin_klasses.update(self._collect_pressure_mixin(mixins))
        self._planet_mixin_klasses.update(self._collect_planets_mixin(mixins))
        self._star_mixin_klasses.update(self._collect_star_mixin(mixins))
        self._inst_mixin_klasses.update(self._collect_instrument_mixin(mixins))
        self._model_mixin_klasses.update(self._collect_model_mixin(mixins))
        self._obs_mixin_klasses.update(self._collect_observation_mixin(mixins))
        self._contrib_mixin_klasses.update(
            self._collect_contributions_mixin(mixins))

        self._opt_mixin_klasses.update(self._collect_optimizer_mixin(mixins))

    def setup_batteries_included(self):
        """
        Collect all the classes that are built into
        TauREx 3
        """
        from taurex import temperature, chemistry, pressure, planet, \
            stellar, instruments, model, contributions, optimizer, opacity, \
            spectrum
        from taurex.opacity import ktables
        from taurex.core import priors

        self._temp_klasses = set()
        self._chem_klasses = set()
        self._gas_klasses = set()
        self._press_klasses = set()
        self._planet_klasses = set()
        self._star_klasses = set()
        self._inst_klasses = set()
        self._model_klasses = set()
        self._contrib_klasses = set()
        self._opt_klasses = set()
        self._opac_klasses = set()
        self._ktab_klasses = set()
        self._obs_klasses = set()
        self._prior_klasses = set()

        self._temp_klasses.update(self._collect_temperatures(temperature))
        self._chem_klasses.update(self._collect_chemistry(chemistry))
        self._gas_klasses.update(self._collect_gas(chemistry))
        self._press_klasses.update(self._collect_pressure(pressure))
        self._planet_klasses.update(self._collect_planets(planet))
        self._star_klasses.update(self._collect_star(stellar))
        self._inst_klasses.update(self._collect_instrument(instruments))
        self._model_klasses.update(self._collect_model(model))
        self._obs_klasses.update(self._collect_observation(spectrum))
        self._contrib_klasses.update(
            self._collect_contributions(contributions))

        self._opt_klasses.update(self._collect_optimizer(optimizer))
        self._opac_klasses.update(self._collect_opacity(opacity))
        self._ktab_klasses.update(self._collect_ktables(ktables))
        self._prior_klasses.update(self._collect_priors(priors))

    def load_plugin(self, plugin_module):

        self._temp_klasses.update(self._collect_temperatures(plugin_module))
        self._chem_klasses.update(self._collect_chemistry(plugin_module))
        self._gas_klasses.update(self._collect_gas(plugin_module))
        self._press_klasses.update(self._collect_pressure(plugin_module))
        self._planet_klasses.update(self._collect_planets(plugin_module))
        self._star_klasses.update(self._collect_star(plugin_module))
        self._inst_klasses.update(self._collect_instrument(plugin_module))
        self._model_klasses.update(self._collect_model(plugin_module))
        self._obs_klasses.update(self._collect_observation(plugin_module))
        self._contrib_klasses.update(
            self._collect_contributions(plugin_module))
        self._opt_klasses.update(self._collect_optimizer(plugin_module))
        self._opac_klasses.update(self._collect_opacity(plugin_module))
        self._prior_klasses.update(self._collect_priors(plugin_module))
        self._ktab_klasses.update(self._collect_ktables(plugin_module))

        # Load any mixins

        self._temp_mixin_klasses.update(
            self._collect_temperatures_mixin(plugin_module))
        self._chem_mixin_klasses.update(
            self._collect_chemistry_mixin(plugin_module))
        self._gas_mixin_klasses.update(
            self._collect_gas_mixin(plugin_module))
        self._press_mixin_klasses.update(
            self._collect_pressure_mixin(plugin_module))
        self._planet_mixin_klasses.update(
            self._collect_planets_mixin(plugin_module))
        self._star_mixin_klasses.update(
            self._collect_star_mixin(plugin_module))
        self._inst_mixin_klasses.update(
            self._collect_instrument_mixin(plugin_module))
        self._model_mixin_klasses.update(
            self._collect_model_mixin(plugin_module))
        self._obs_mixin_klasses.update(
            self._collect_observation_mixin(plugin_module))
        self._contrib_mixin_klasses.update(
            self._collect_contributions_mixin(plugin_module))

        self._opt_mixin_klasses.update(
            self._collect_optimizer_mixin(plugin_module))

    def discover_plugins(self):
        plugins = {}
        failed_plugins = {}
        for entry_point in pkg_resources.iter_entry_points('taurex.plugins'):

            entry_point_name = entry_point.name

            try:
                module = entry_point.load()
            except Exception as e:
                # For whatever reason do not attempt to load the plugin
                self.log.warning('Could not load plugin %s', entry_point_name)
                self.log.warning('Reason: %s', str(e))
                failed_plugins[entry_point_name] = str(e)
                continue

            plugins[entry_point_name] = module

        return plugins, failed_plugins

    def load_plugins(self):
        plugins, failed_plugins = self.discover_plugins()
        self.log.info('----------Plugin loading---------')
        self.log.debug('Discovered plugins %s', plugins.values())

        for k, v in plugins.items():
            self.log.info('Loading %s', k)
            self.load_plugin(v)

    def load_extension_paths(self):
        import glob
        import os
        import pathlib
        import importlib
        paths = self.extension_paths
        if paths:
            # Make sure they're unique
            all_files = set(sum([glob.glob(
                                os.path.join(os.path.abspath(p), '*.py'))
                             for p in paths], []))

            for f in all_files:
                self.info('Loading extensions from %s', f)
                module_name = pathlib.Path(f).stem
                spec = importlib.util.spec_from_file_location(module_name, f)
                foo = importlib.util.module_from_spec(spec)
                try:
                    spec.loader.exec_module(foo)
                    self.load_plugin(foo)
                except Exception as e:
                    self.log.warning('Could not load extension from file %s',
                                     f)
                    self.log.warning('Reason: %s', str(e))

    def _collect_classes(self, module, base_klass):
        """
        Collects all classes that are a subclass of base
        """
        klasses = []
        clsmembers = inspect.getmembers(module, inspect.isclass)
        for _, c in clsmembers:
            if issubclass(c, base_klass) and (c is not base_klass):
                self.log.debug(f' Found class {c.__name__}')
                klasses.append(c)

        return klasses

    def _collect_temperatures(self, module):
        from taurex.temperature import TemperatureProfile
        return self._collect_classes(module, TemperatureProfile)

    def _collect_chemistry(self, module):
        from taurex.chemistry import Chemistry
        return self._collect_classes(module, Chemistry)

    def _collect_gas(self, module):
        from taurex.chemistry import Gas
        return self._collect_classes(module, Gas)

    def _collect_pressure(self, module):
        from taurex.pressure import PressureProfile
        return self._collect_classes(module, PressureProfile)

    def _collect_planets(self, module):
        from taurex.planet import BasePlanet
        return self._collect_classes(module, BasePlanet)

    def _collect_star(self, module):
        from taurex.stellar import Star
        return self._collect_classes(module, Star)

    def _collect_instrument(self, module):
        from taurex.instruments import Instrument
        return self._collect_classes(module, Instrument)

    def _collect_model(self, module):
        from taurex.model import ForwardModel, SimpleForwardModel
        return [c for c in self._collect_classes(module, ForwardModel)
                if c is not SimpleForwardModel]

    def _collect_contributions(self, module):
        from taurex.contributions import Contribution
        return self._collect_classes(module, Contribution)

    def _collect_optimizer(self, module):
        from taurex.optimizer import Optimizer
        return self._collect_classes(module, Optimizer)

    def _collect_opacity(self, module):
        from taurex.opacity import Opacity, InterpolatingOpacity
        from taurex.opacity.ktables import KTable
        return [c for c in self._collect_classes(module, Opacity)
                if c is not InterpolatingOpacity and not issubclass(c, KTable)]

    def _collect_ktables(self, module):
        from taurex.opacity.ktables import KTable
        return [c for c in self._collect_classes(module, KTable)]

    def _collect_observation(self, module):
        from taurex.spectrum import BaseSpectrum
        return [c for c in self._collect_classes(module, BaseSpectrum)]

    def _collect_priors(self, module):
        from taurex.core.priors import Prior
        return [c for c in self._collect_classes(module, Prior)]

    # Mixins
    def _collect_temperatures_mixin(self, module):
        from taurex.mixin import TemperatureMixin
        return self._collect_classes(module, TemperatureMixin)

    def _collect_chemistry_mixin(self, module):
        from taurex.mixin import ChemistryMixin
        return self._collect_classes(module, ChemistryMixin)

    def _collect_gas_mixin(self, module):
        from taurex.mixin import GasMixin
        return self._collect_classes(module, GasMixin)

    def _collect_pressure_mixin(self, module):
        from taurex.mixin import PressureMixin
        return self._collect_classes(module, PressureMixin)

    def _collect_planets_mixin(self, module):
        from taurex.mixin import PlanetMixin
        return self._collect_classes(module, PlanetMixin)

    def _collect_star_mixin(self, module):
        from taurex.mixin import StarMixin
        return self._collect_classes(module, StarMixin)

    def _collect_instrument_mixin(self, module):
        from taurex.mixin import InstrumentMixin
        return self._collect_classes(module, InstrumentMixin)

    def _collect_model_mixin(self, module):
        from taurex.mixin import ForwardModelMixin
        return self._collect_classes(module, ForwardModelMixin)

    def _collect_contributions_mixin(self, module):
        from taurex.mixin import ContributionMixin
        return self._collect_classes(module, ContributionMixin)

    def _collect_optimizer_mixin(self, module):
        from taurex.mixin import OptimizerMixin
        return self._collect_classes(module, OptimizerMixin)

    def _collect_observation_mixin(self, module):
        from taurex.mixin import ObservationMixin
        return self._collect_classes(module, ObservationMixin)

    def list_from_base(self, klass_type):

        from taurex.temperature import TemperatureProfile
        from taurex.chemistry import Chemistry
        from taurex.chemistry import Gas
        from taurex.pressure import PressureProfile
        from taurex.planet import BasePlanet
        from taurex.stellar import Star
        from taurex.instruments import Instrument
        from taurex.model import ForwardModel
        from taurex.contributions import Contribution
        from taurex.optimizer import Optimizer
        from taurex.opacity import Opacity

        from taurex.opacity.ktables import KTable
        from taurex.spectrum import BaseSpectrum
        from taurex.core.priors import Prior

        klass_dict = {
            TemperatureProfile: self.temperatureKlasses,
            Chemistry: self.chemistryKlasses,
            Gas: self.gasKlasses,
            PressureProfile: self.pressureKlasses,
            BasePlanet: self.planetKlasses,
            Star: self.starKlasses,
            Instrument: self.instrumentKlasses,
            ForwardModel: self.modelKlasses,
            Contribution: self.contributionKlasses,
            Optimizer: self.optimizerKlasses,
            Opacity: self.opacityKlasses,
            KTable: self.ktableKlasses,
            BaseSpectrum: self.observationKlasses,
            Prior: self.priorKlasses,

        }

        return klass_dict[klass_type]

    @property
    def temperatureKlasses(self):
        return self._temp_klasses

    @property
    def chemistryKlasses(self):
        return self._chem_klasses

    @property
    def gasKlasses(self):
        return self._gas_klasses

    @property
    def pressureKlasses(self):
        return self._press_klasses

    @property
    def planetKlasses(self):
        return self._planet_klasses

    @property
    def starKlasses(self):
        return self._star_klasses

    @property
    def instrumentKlasses(self):
        return self._inst_klasses

    @property
    def modelKlasses(self):
        return self._model_klasses

    @property
    def contributionKlasses(self):
        return self._contrib_klasses

    @property
    def optimizerKlasses(self):
        return self._opt_klasses

    @property
    def opacityKlasses(self):
        return self._opac_klasses

    @property
    def ktableKlasses(self):
        return self._ktab_klasses

    @property
    def observationKlasses(self):
        return self._obs_klasses

    @property
    def priorKlasses(self):
        return self._prior_klasses

    # Mixins

    @property
    def temperatureMixinKlasses(self):
        return self._temp_mixin_klasses

    @property
    def chemistryMixinKlasses(self):
        return self._chem_mixin_klasses

    @property
    def gasMixinKlasses(self):
        return self._gas_mixin_klasses

    @property
    def pressureMixinKlasses(self):
        return self._press_mixin_klasses

    @property
    def planetMixinKlasses(self):
        return self._planet_mixin_klasses

    @property
    def starMixinKlasses(self):
        return self._star_mixin_klasses

    @property
    def instrumentMixinKlasses(self):
        return self._inst_mixin_klasses

    @property
    def modelMixinKlasses(self):
        return self._model_mixin_klasses

    @property
    def contributionMixinKlasses(self):
        return self._contrib_mixin_klasses

    @property
    def optimizerMixinKlasses(self):
        return self._opt_mixin_klasses

    @property
    def observationMixinKlasses(self):
        return self._obs_mixin_klasses
Example #16
0
    def __init__(self, name):
        Logger.__init__(self, name)
        Fittable.__init__(self)

        self.mu_profile = None
        self._avail_active = OpacityCache().find_list_of_molecules()
class CIACache(Singleton):
    """
    Implements a lazy load of collisionally induced absorpiton cross-sections
    Supports pickle files and HITRAN cia files. Functionally behaves the same
    as :class:`~taurex.cache.opacitycache.OpacityCache` except the keys are
    now cia pairs
    e.g:

    >>> CIACache()['H2-H2']
    <taurex.cia.picklecia.PickleCIA at 0x107a60be0>

    Pickle ``.db`` and HITRAN ``.cia`` files are supported and automatically
    loaded. with priority given to ``.db`` files



    """
    def init(self):
        self.cia_dict = {}
        self._cia_path = None
        self.log = Logger('CIACache')

    def set_cia_path(self, cia_path):
        """

        Sets the path to search for CIA files

        Parameters
        ----------
        cia_path : str or :obj:`list` of str
            Either a single path or a list of paths that contain CIA files

        """
        self._cia_path = cia_path

    def __getitem__(self, key):
        """
        For a CIA pair, load from the set path and return the
        relevant :class:`~taurex.cia.cia.CIA` object

        Parameter
        ---------
        key : str
            cia pair name

        Returns
        -------
        :class:`~taurex.cia.picklecia.PickleCIA` or :class:`~taurex.cia.hitrancia.HitranCIA`
            Desire CIA object, format depends on what is found
            in the path set by :func:`set_cia_path`

        Raise
        -----
        Exception
            If desired CIA pair could not be loaded

        """

        if key in self.cia_dict:
            return self.cia_dict[key]
        else:
            # Try a load of the cia
            self.load_cia(pair_filter=[key])
            # If we have it after a load then good job boys
            if key in self.cia_dict:
                return self.cia_dict[key]
            else:
                # Otherwise throw an error
                self.log.error('CIA for pair %s could not be loaded', key)
                self.log.error(
                    'It could not be found in the local dictionary '
                    ' %s', list(self.cia_dict.keys()))
                self.log.error('Or paths %s', self._cia_path)
                self.log.error('Try loading it manually/ putting it in a path')
                raise Exception('cia could notn be loaded')

    def add_cia(self, cia, pair_filter=None):
        """

        Adds a :class:`~taurex.cia.cia.CIA` object to the cache to then be
        used by Taurex 3

        Parameters
        ----------
        cia : :class:`~taurex.cia.cia.CIA`
            CIA object to add to the cache

        pair_filter : :obj:`list` of str , optional
            If provided, the cia object will only be included
            if its pairname is in the list. Mostly used by the
            :func:`__getitem__` for filtering

        """

        self.log.info('Loading cia %s into model', cia.pairName)
        if cia.pairName in self.cia_dict:
            self.log.error(
                'cia with name %s already in '
                'opactiy dictionary %s', cia.pairName, self.cia_dict.keys())

            raise Exception('cia for molecule %s already exists')

        if pair_filter is not None:
            if cia.pairName in pair_filter:
                self.log.info('Loading cia %s into model', cia.pairName)
                self.cia_dict[cia.pairName] = cia
        self.cia_dict[cia.pairName] = cia

    def load_cia_from_path(self, path, pair_filter=None):
        """
        Searches path for CIA files, creates and loads them into the cache
        ``.db`` will be loaded as :class:`~taurex.cia.picklecia.PickleCIA` and
        ``.cia`` files will be loaded as
        :class:`~taurex.cia.hitrancia.HitranCIA`

        Parameters
        ----------
        path : str
            Path to search for CIA files

        pair_filter : :obj:`list` of str , optional
            If provided, the cia will only be loaded
            if its pairname is in the list. Mostly used by the
            :func:`__getitem__` for filtering

        """
        from glob import glob
        from pathlib import Path
        import os
        from taurex.cia import PickleCIA

        # Find .db files
        glob_path = os.path.join(path, '*.db')

        file_list = glob(glob_path)
        self.log.debug('Glob list: %s', glob_path)
        self.log.debug('File list FOR CIA %s', file_list)

        for files in file_list:
            pairname = Path(files).stem.split('_')[0]

            self.log.debug('pairname found %s', pairname)

            if pair_filter is not None:
                if pairname not in pair_filter:
                    continue
            op = PickleCIA(files, pairname)
            self.add_cia(op)

        # Find .cia files
        glob_path = os.path.join(path, '*.cia')

        file_list = glob(glob_path)
        self.log.debug('File list %s', file_list)

        for files in file_list:
            from taurex.cia import HitranCIA
            pairname = Path(files).stem.split('_')[0]

            if pair_filter is not None:
                if pairname not in pair_filter:
                    continue
            op = HitranCIA(files)
            self.add_cia(op)

    def load_cia(self, cia_xsec=None, cia_path=None, pair_filter=None):
        """
        Main function to use when loading CIA files. Handles both
        cross sections and paths. Handles lists of either so lists of
        :class:`~taurex.cia.cia.CIA` objects or lists of paths can be used
        to load multiple files/objects


        Parameters
        ----------
        cia_xsec : :class:`~taurex.cia.cia.CIA` or :obj:`list` of :class:`~taurex.cia.cia.CIA` , optional
            Object(s) to include in cache

        cia_path : str or :obj:`list` of str, optional
            search path(s) to look for cias

        pair_filter : :obj:`list` of str , optional
            If provided, the cia will only be loaded
            if its pair name is in this list. Mostly used by the
            :func:`__getitem__` for filtering

        """

        from taurex.cia import CIA
        if cia_path is None:
            cia_path = self._cia_path

        self.log.debug('CIA XSEC, CIA_PATH %s %s', cia_xsec, cia_path)
        if cia_xsec is not None:
            if isinstance(cia_xsec, (list, )):
                self.log.debug('cia passed is list')
                for xsec in cia_xsec:
                    self.add_cia(xsec, pair_filter=pair_filter)
            elif isinstance(cia_xsec, CIA):
                self.add_cia(cia_xsec, pair_filter=pair_filter)
            else:
                self.log.error(
                    'Unknown type %s passed into cia, should be a list, single \
                     cia or None if reading a path', type(xsec))
                raise Exception('Unknown type passed into cia')
        if cia_path is not None:

            if isinstance(cia_path, str):
                self.load_cia_from_path(cia_path, pair_filter=pair_filter)
            elif isinstance(cia_path, (list, )):
                for path in cia_path:
                    self.load_cia_from_path(path, pair_filter=pair_filter)
 def init(self):
     self.cia_dict = {}
     self._cia_path = None
     self.log = Logger('CIACache')
class OpacityCache(Singleton):
    """
    Implements a lazy load of opacities. A singleton that
    loads and caches xsections as they are needed. Calling

    >>> opt = OpacityCache()
    >>> opt2 = OpacityCache()

    Reveals that:

    >>> opt == opt2
    True

    Importantly this class will automatically search directories for cross-sections
    set using the :func:`set_opacity_path` method:

    >>> opt.set_opacity_path('path/to/crossections')
    

    Multiple paths can be set as well

    >>> opt.set_opacity_path(['/path/to/crosssections','/another/path/to/crosssections'])

    To get the cross-section object for a particular molecule use the square bracket operator:

    >>> opt['H2O']
    <taurex.opacity.pickleopacity.PickleOpacity at 0x107a60be0>

    This returns a :class:`~taurex.opacity.opacity.Opacity` object for you to compute H2O cross sections from.
    When called for the first time, a directory search is performed and, if found, the appropriate cross-section is loaded. Subsequent calls will immediately
    return the already loaded object:

    >>> h2o_a = opt['H2O']
    >>> h2o_b = opt['H2O']
    >>> h2o_a == h2o_b
    True

    If you have any plugins that include new opacity formats, the cache
    will automatically detect them. 

    

    Lastly you can manually add an opacity directly for a molecule
    into the cache:

    >>> new_h2o = MyNewOpacityFormat()
    >>> new_h2o.molecule
    H2O
    >>> opt.add_opacity(new_h2o)
    >>> opt['H2O']
    <MyNewOpacityFormat at 0x107a60be0>

    


    Now TauREx3 will use it instead in all calculations!

    """
    def init(self):
        self.opacity_dict = {}
        self._opacity_path = None
        self.log = Logger('OpacityCache')
        self._force_active = []

    def set_opacity_path(self, opacity_path):
        """
        Set the path(s) that will be searched for opacities.
        Opacities in this path must be of supported types:

            - HDF5 opacities
            - ``.pickle`` opacities
            - ExoTransmit opacities.

        Parameters
        ----------

        opacity_path : str or :obj:`list` of str, optional
            search path(s) to look for molecular opacities

        """

        import os

        GlobalCache()['xsec_path'] = opacity_path

        if not os.path.isdir(opacity_path):
            self.log.error('PATH: %s does not exist!!!', opacity_path)
            raise NotADirectoryError
        self.log.debug('Path set to %s', opacity_path)

    def enable_radis(self, enable):
        """
        Enables/Disables use of RADIS to fill in missing molecules
        using HITRAN.

        .. warning::
            This is extremely unstable and crashes frequently.
            It is also very slow as it requires
            the computation of the Voigt profile for every temperature.
            We recommend leaving it as False unless necessary.

        Parameters
        ----------
        enable: bool
            Whether to enable RADIS functionality (default = False)

        """

        GlobalCache()['enable_radis'] = enable

    def set_radis_wavenumber(self, wn_start, wn_end, wn_points):
        GlobalCache()['radius_grid'] = wn_start, wn_end, wn_points

        self.clear_cache()

    def set_memory_mode(self, in_memory):
        """
        If using the HDF5 opacities, whether to stream
        opacities from file (slower, less memory) or load
        them into memory (faster, more memory)

        Parameters
        ----------
        in_memory: bool
            Whether HDF5 files should be streamed (False)
            or loaded into memory (True, default)


        """

        GlobalCache()['xsec_in_memory'] = in_memory
        self.clear_cache()

    def force_active(self, molecules):
        """
        Allows some molecules to be forced as active.
        Useful when using other radiative codes to do the calculation

        Parameters
        ----------
        molecules: obj:`list`
            List of molecules

        """
        self._force_active = molecules

    def set_interpolation(self, interpolation_mode):
        """
        Sets the interpolation mode for all currently loaded (and future loaded) cross-sections

        Can either be ``linear`` for linear interpolation of both temeprature and pressure:

        >>> OpacityCache().set_interpolation('linear')

        or ``exp`` for natural exponential interpolation of temperature
        and linear for pressure

        >>> OpacityCache().set_interpolation('exp')
        

        Parameters
        ----------
        interpolation_mode: str
            Either ``linear`` for bilinear interpolation or ``exp`` for exp-linear interpolation
        

        """
        GlobalCache()['xsec_interpolation'] = interpolation_mode
        self.clear_cache()

    def __getitem__(self, key):
        """
        For a molecule return the relevant :class:`~taurex.opacity.opacity.Opacity` object.


        Parameter
        ---------
        key : str
            molecule name

        Returns
        -------
        :class:`~taurex.opacity.pickleopacity.PickleOpacity`
            Cross-section object desired
        
        Raise
        -----
        Exception
            If molecule could not be loaded/found

        """
        if key in self.opacity_dict:
            return self.opacity_dict[key]
        else:
            #Try a load of the opacity
            self.load_opacity(molecule_filter=[key])
            #If we have it after a load then good job boys
            if key in self.opacity_dict:
                return self.opacity_dict[key]
            else:
                #Otherwise throw an error
                self.log.error('Opacity for molecule %s could not be loaded',
                               key)
                self.log.error(
                    'It could not be found in the local dictionary %s',
                    list(self.opacity_dict.keys()))
                self.log.error('Or paths %s', GlobalCache()['xsec_path'])
                self.log.error('Try loading it manually/ putting it in a path')
                raise Exception('Opacity could not be loaded')

    def add_opacity(self, opacity, molecule_filter=None):
        """

        Adds a :class:`~taurex.opacity.opacity.Opacity` object to the cache to then be
        used by Taurex 3

        Parameters
        ----------
        opacity : :class:`~taurex.opacity.opacity.Opacity`
            Opacity object to add to the cache
        
        molecule_filter : :obj:`list` of str , optional
            If provided, the opacity object will only be included
            if its molecule is in the list. Mostly used by the 
            :func:`__getitem__` for filtering

        """
        self.log.info('Reading opacity %s', opacity.moleculeName)
        if opacity.moleculeName in self.opacity_dict:
            self.log.warning(
                'Opacity with name %s already in opactiy dictionary %s skipping',
                opacity.moleculeName, self.opacity_dict.keys())
            return
        if molecule_filter is not None:
            if opacity.moleculeName in molecule_filter:
                self.log.info('Loading opacity %s into model',
                              opacity.moleculeName)
                self.opacity_dict[opacity.moleculeName] = opacity
        else:
            self.log.info('Loading opacity %s into model',
                          opacity.moleculeName)
            self.opacity_dict[opacity.moleculeName] = opacity

    def find_list_of_molecules(self):
        from glob import glob
        import os
        from taurex.parameter.classfactory import ClassFactory
        opacity_klasses = ClassFactory().opacityKlasses

        molecules = []

        for c in opacity_klasses:
            molecules.extend([x[0] for x in c.discover()])

        forced = self._force_active or []
        return set(molecules + forced + list(self.opacity_dict.keys()))

    def load_opacity_from_path(self, path, molecule_filter=None):
        """
        Searches path for molecular cross-section files, creates and loads them into the cache
        ``.pickle`` will be loaded as :class:`~taurex.opacity.pickleopacity.PickleOpacity`
        

        Parameters
        ----------
        path : str
            Path to search for molecular cross-section files
        
        molecule_filter : :obj:`list` of str , optional
            If provided, the opacity will only be loaded
            if its molecule is in this list. Mostly used by the 
            :func:`__getitem__` for filtering

        """
        from taurex.parameter.classfactory import ClassFactory

        cf = ClassFactory()

        opacity_klass_list = sorted(cf.opacityKlasses,
                                    key=lambda x: x.priority())

        for c in opacity_klass_list:

            for mol, args in c.discover():
                self.log.debug('Klass: %s %s', mol, args)
                op = None
                if mol in molecule_filter and mol not in self.opacity_dict:
                    if not isinstance(args, (
                            list,
                            tuple,
                    )):
                        args = [args]
                    op = c(*args)

                if op is not None and op.moleculeName not in self.opacity_dict:
                    self.add_opacity(op, molecule_filter=molecule_filter)
                op = None  # Ensure garbage collection when run once

    def load_opacity(self,
                     opacities=None,
                     opacity_path=None,
                     molecule_filter=None):
        """
        Main function to use when loading molecular opacities. Handles both 
        cross sections and paths. Handles lists of either so lists of 
        :class:`~taurex.opacity.opacity.Opacity` objects or lists of paths can be used
        to load multiple files/objects


        Parameters
        ----------
        opacities : :class:`~taurex.opacity.opacity.Opacity` or :obj:`list` of :class:`~taurex.opacity.opacity.Opacity` , optional
            Object(s) to include in cache
        
        opacity_path : str or :obj:`list` of str, optional
            search path(s) to look for molecular opacities
        
        molecule_filter : :obj:`list` of str , optional
            If provided, the opacity will only be loaded
            if its molecule is in this list. Mostly used by the 
            :func:`__getitem__` for filtering

        """
        from taurex.opacity import Opacity

        if opacity_path is None:
            opacity_path = GlobalCache()['xsec_path']

        if opacities is not None:
            if isinstance(opacities, (list, )):
                self.log.debug('Opacity passed is list')
                for opacity in opacities:
                    self.add_opacity(opacity, molecule_filter=molecule_filter)
            elif isinstance(opacities, Opacity):
                self.add_opacity(opacities, molecule_filter=molecule_filter)
            else:
                self.log.error(
                    'Unknown type %s passed into opacities, should be a list, single \
                     opacity or None if reading a path', type(opacities))
                raise Exception('Unknown type passed into opacities')
        else:
            self.load_opacity_from_path(opacity_path,
                                        molecule_filter=molecule_filter)
            # if isinstance(opacity_path, str):
            #     self.load_opacity_from_path(opacity_path, molecule_filter=molecule_filter)
            # elif isinstance(opacity_path, (list,)):
            #     for path in opacity_path:
            #         self.load_opacity_from_path(path, molecule_filter=molecule_filter)

    def clear_cache(self):
        """
        Clears all currently loaded cross-sections
        """
        self.opacity_dict = {}
 def init(self):
     self.opacity_dict = {}
     self._opacity_path = None
     self.log = Logger('OpacityCache')
     self._force_active = []
class OpacityCache(Singleton):
    """
    Implements a lazy load of opacities. A singleton that
    loads and caches xsections as they are needed. Calling

    >>> opt = OpacityCache()
    >>> opt2 = OpacityCache()

    Reveals that:

    >>> opt == opt2
    True
    
    Importantly this class will automatically search directories for cross-sections
    set using the :func:`set_opacity_path` method:

    >>> opt.set_opacity_path('path/to/crossections')

    Multiple paths can be set as well

    >>> opt.set_opacity_path(['/path/to/crosssections','/another/path/to/crosssections'])

    Currently only :obj:`.pickle` files are supported.

    To get the cross-section object for a particular molecule use the square bracket operator:

    >>> opt['H2O']
    <taurex.opacity.pickleopacity.PickleOpacity at 0x107a60be0>

    This returns a :class:`~taurex.opacity.pickleopacity.PickleOpacity` object for you to compute H2O cross sections from.
    When called for the first time, a directory search is performed and, if found, the appropriate cross-section is loaded. Subsequent calls will immediately
    return the already loaded object:

    >>> h2o_a = opt['H2O']
    >>> h2o_b = opt['H2O']
    >>> h2o_a == h2o_b
    True

    Lastly if you've got a hot new opacity format, you can try out
    by manually adding it into the cache:

    >>> new_h2o = MyNewOpacityFormat()
    >>> opt.add_opacity(new_h2o)
    >>> opt['H2O]
    <MyNewOpacityFormat at 0x107a60be0>

    Now TauREx3 will use it instead in all calculations!

    """
    def init(self):
        self.opacity_dict = {}
        self._opacity_path = None
        self.log = Logger('OpacityCache')
        self._default_interpolation = 'linear'
        self._memory_mode = True
        self._radis = False
        self._radis_props = (600, 30000, 10000)
    
    def set_opacity_path(self, opacity_path):
        """
        Set the path(s) that will be searched for opacities.
        Opacities in this path must be of supported types:

            - HDF5 opacities
            - ``.pickle`` opacities
            - ExoTransmit opacities.

        Parameters
        ----------

        opacity_path : str or :obj:`list` of str, optional
            search path(s) to look for molecular opacities

        """

        import os
        if not os.path.isdir(opacity_path):
            self.log.error('PATH: %s does not exist!!!', opacity_path)
            raise NotADirectoryError
        self.log.debug('Path set to %s', opacity_path)
        self._opacity_path = opacity_path

    def enable_radis(self, enable):
        """
        Enables/Disables use of RADIS to fill in missing molecules
        using HITRAN.

        .. warning::
            This is extremely unstable and crashes frequently.
            It is also very slow as it requires
            the computation of the Voigt profile for every temperature.
            We recommend leaving it as False unless necessary.

        Parameters
        ----------
        enable: bool
            Whether to enable RADIS functionality (default = False)

        """

        self._radis = enable

    def set_radis_wavenumber(self, wn_start, wn_end, wn_points):
        self._radis_props = (wn_start, wn_end, wn_points)

        

        self.clear_cache()
    
    def set_memory_mode(self,in_memory):
        """
        If using the HDF5 opacities, whether to stream
        opacities from file (slower, less memory) or load
        them into memory (faster, more memory)

        Parameters
        ----------
        in_memory: bool
            Whether HDF5 files should be streamed (False)
            or loaded into memory (True, default)


        """

        self._memory_mode = in_memory
        self.clear_cache()

    def set_interpolation(self,interpolation_mode):
        """
        Sets the interpolation mode for all currently loaded (and future loaded) cross-sections

        Can either be ``linear`` for linear interpolation of both temeprature and pressure:

        >>> OpacityCache().set_interpolation('linear')

        or ``exp`` for natural exponential interpolation of temperature
        and linear for pressure

        >>> OpacityCache().set_interpolation('exp')
        

        Parameters
        ----------
        interpolation_mode: str
            Either ``linear`` for bilinear interpolation or ``exp`` for exp-linear interpolation
        

        """
        return
        self._default_interpolation = interpolation_mode
        for values in self.opacity_dict.values():
            values.set_interpolation_mode(self._default_interpolation)
    
    

    def __getitem__(self,key):
        """
        For a molecule return the relevant :class:`~taurex.opacity.opacity.Opacity` object.


        Parameter
        ---------
        key : str
            molecule name

        Returns
        -------
        :class:`~taurex.opacity.pickleopacity.PickleOpacity`
            Cross-section object desired
        
        Raise
        -----
        Exception
            If molecule could not be loaded/found

        """
        if key in self.opacity_dict:
            return self.opacity_dict[key]
        else:
            #Try a load of the opacity
            self.load_opacity(molecule_filter=[key])
            #If we have it after a load then good job boys
            if key in self.opacity_dict:
                return self.opacity_dict[key]
            else:
                try:
                    if self._radis:
                        return self.create_radis_opacity(key,molecule_filter=[key])
                    else:
                        raise Exception
                except Exception as e:
                    self.log.error('EXception thrown %s',e)
                    #Otherwise throw an error
                    self.log.error('Opacity for molecule %s could not be loaded',key)
                    self.log.error('It could not be found in the local dictionary %s',list(self.opacity_dict.keys()))
                    self.log.error('Or paths %s',self._opacity_path)
                    self.log.error('Try loading it manually/ putting it in a path')
                    raise Exception('Opacity could notn be loaded')

    def create_radis_opacity(self,molecule,molecule_filter=None):
        from taurex.opacity.radisopacity import RadisHITRANOpacity
        if molecule not in self.opacity_dict:
            self.log.info('Creating Opacity from RADIS+HITRAN')
            wn_start,wn_end,wn_points = self._radis_props
            radis = RadisHITRANOpacity(molecule_name=molecule, wn_start=wn_start,wn_end=wn_end,wn_points=wn_points)
            
            

            self.add_opacity(radis,molecule_filter=molecule_filter)
            return radis
        else:
            self.log.info('Opacity %s already exsits',molecule)


    def add_opacity(self,opacity,molecule_filter=None):
        """

        Adds a :class:`~taurex.opacity.opacity.Opacity` object to the cache to then be
        used by Taurex 3

        Parameters
        ----------
        opacity : :class:`~taurex.opacity.opacity.Opacity`
            Opacity object to add to the cache
        
        molecule_filter : :obj:`list` of str , optional
            If provided, the opacity object will only be included
            if its molecule is in the list. Mostly used by the 
            :func:`__getitem__` for filtering

        """
        self.log.info('Reading opacity %s',opacity.moleculeName)
        if opacity.moleculeName in self.opacity_dict:
            self.log.warning('Opacity with name %s already in opactiy dictionary %s skipping',opacity.moleculeName,self.opacity_dict.keys())
            return
        if molecule_filter is not None:
            if opacity.moleculeName in molecule_filter:
                self.log.info('Loading opacity %s into model',opacity.moleculeName)
                self.opacity_dict[opacity.moleculeName] = opacity       
        else:     
            self.log.info('Loading opacity %s into model',opacity.moleculeName)
            self.opacity_dict[opacity.moleculeName] = opacity   


    def search_hdf5_molecules(self):
        """
        Find molecules with HDF5 opacities in set path

        Returns
        -------
        molecules: :obj`list`
            List of molecules with HDF5 opacities
        
        """
        from glob import glob
        import os    
        from taurex.opacity.hdf5opacity import HDF5Opacity
        glob_path = [os.path.join(self._opacity_path,'*.h5'),os.path.join(self._opacity_path,'*.hdf5')]
        file_list = [f for glist in glob_path for f in glob(glist)]
        
        return [HDF5Opacity(f,interpolation_mode=self._default_interpolation,in_memory=False).moleculeName for f in file_list ]

    def search_pickle_molecules(self):
        """
        Find molecules with ``.pickle`` opacities in set path

        Returns
        -------
        molecules: :obj`list`
            List of molecules with ``.pickle`` opacities
        
        """

        from glob import glob
        import os    
        glob_path = os.path.join(self._opacity_path,'*.pickle')
        file_list = [f for f in glob(glob_path)]
        
        return [pathlib.Path(f).stem.split('.')[0] for f in file_list ]

    def search_exotransmit_molecules(self):
        """
        Find molecules with Exo-Transmit opacities in set path

        Returns
        -------
        molecules: :obj`list`
            List of molecules with ExoTransmit opacities
        
        """

        from glob import glob
        import os    
        glob_path = os.path.join(self._opacity_path,'*.dat')
        file_list = [f for f in glob(glob_path)]
        
        return [pathlib.Path(f).stem[4:] for f in file_list ]

    def search_radis_molecules(self):
        """
        Searches for molecules in HITRAN

        Returns
        -------
        molecules: :obj`list`
            List of molecules available in HITRAN, if radis is enabled,
            otherwise an empty list

        """
        trans = { '1':'H2O',    '2':'CO2',   '3':'O3',      '4':'N2O',   '5':'CO',    '6':'CH4',   '7':'O2',     
            '9':'SO2',   '10':'NO2',  '11':'NH3',    '12':'HNO3', '13':'OH',   '14':'HF',   '15':'HCl',   '16':'HBr',
            '17':'HI',    '18':'ClO',  '19':'OCS',    '20':'H2CO', '21':'HOCl',    '23':'HCN',   '24':'CH3Cl',
            '25':'H2O2',  '26':'C2H2', '27':'C2H6',   '28':'PH3',  '29':'COF2', '30':'SF6',  '31':'H2S',   '32':'HCOOH',
            '33':'HO2',   '34':'O',    '35':'ClONO2', '36':'NO+',  '37':'HOBr', '38':'C2H4',  '40':'CH3Br',
            '41':'CH3CN', '42':'CF4',  '43':'C4H2',   '44':'HC3N',   '46':'CS',   '47':'SO3'}
        if self._radis:
            return list(trans.values())
        else:
            return []
    def find_list_of_molecules(self):
        from glob import glob
        import os
        from taurex.opacity import PickleOpacity
        pickles = []
        hedef = []
        exo = []
        if self._opacity_path is not None:
        
            pickles = self.search_pickle_molecules()
            hedef = self.search_hdf5_molecules()
            exo = self.search_exotransmit_molecules()
        return list(set(pickles+hedef+exo+self.search_radis_molecules()))
    def load_opacity_from_path(self,path,molecule_filter=None):
        """
        Searches path for molecular cross-section files, creates and loads them into the cache
        ``.pickle`` will be loaded as :class:`~taurex.opacity.pickleopacity.PickleOpacity`
        

        Parameters
        ----------
        path : str
            Path to search for molecular cross-section files
        
        molecule_filter : :obj:`list` of str , optional
            If provided, the opacity will only be loaded
            if its molecule is in this list. Mostly used by the 
            :func:`__getitem__` for filtering

        """ 
        from glob import glob
        import os
        from taurex.opacity import PickleOpacity
        from taurex.opacity.hdf5opacity import HDF5Opacity
        from taurex.opacity.exotransmit import ExoTransmitOpacity
        glob_path = [os.path.join(path,'*.h5'),os.path.join(path,'*.hdf5'),os.path.join(path,'*.pickle'),os.path.join(path,'*.dat')]
    
        file_list = [f for glist in glob_path for f in glob(glist)]
        self.log.debug('File list %s',file_list)
        for files in file_list:
            op = None
            if files.lower().endswith(('.hdf5', '.h5')):
                op = HDF5Opacity(files,interpolation_mode=self._default_interpolation,in_memory=False)
                
                if molecule_filter is not None:
                        if not op.moleculeName in molecule_filter:
                            continue
                if op.moleculeName in self.opacity_dict.keys():
                    continue
                del op

                op = HDF5Opacity(files,interpolation_mode=self._default_interpolation,in_memory=self._memory_mode)

            elif files.endswith('pickle'):
                splits = pathlib.Path(files).stem.split('.')
                if molecule_filter is not None:
                        if not splits[0] in molecule_filter:
                            continue
                if splits[0] in self.opacity_dict.keys():
                    continue
                op = PickleOpacity(files,interpolation_mode=self._default_interpolation)
                op._molecule_name = splits[0]
            elif files.endswith('dat'):
                mol_name = pathlib.Path(files).stem[4:]
                if molecule_filter is not None:
                        if not mol_name in molecule_filter:
                            continue
                if mol_name in self.opacity_dict.keys():
                    continue
                op = ExoTransmitOpacity(files,interpolation_mode=self._default_interpolation)
            if op is not None:
                self.add_opacity(op,molecule_filter=molecule_filter)

    def load_opacity(self,opacities=None,opacity_path=None,molecule_filter=None):
        """
        Main function to use when loading molecular opacities. Handles both 
        cross sections and paths. Handles lists of either so lists of 
        :class:`~taurex.opacity.opacity.Opacity` objects or lists of paths can be used
        to load multiple files/objects


        Parameters
        ----------
        opacities : :class:`~taurex.opacity.opacity.Opacity` or :obj:`list` of :class:`~taurex.opacity.opacity.Opacity` , optional
            Object(s) to include in cache
        
        opacity_path : str or :obj:`list` of str, optional
            search path(s) to look for molecular opacities
        
        molecule_filter : :obj:`list` of str , optional
            If provided, the opacity will only be loaded
            if its molecule is in this list. Mostly used by the 
            :func:`__getitem__` for filtering

        """ 
        from taurex.opacity import Opacity
        
        if opacity_path is None:
            opacity_path = self._opacity_path

        if opacities is not None:
            if isinstance(opacities,(list,)):
                self.log.debug('Opacity passed is list')
                for opacity in opacities:
                    self.add_opacity(opacity,molecule_filter=molecule_filter)
            elif isinstance(opacities,Opacity):
                self.add_opacity(opacities,molecule_filter=molecule_filter)
            else:
                self.log.error('Unknown type %s passed into opacities, should be a list, single \
                     opacity or None if reading a path',type(opacities))
                raise Exception('Unknown type passed into opacities')
        elif opacity_path is not None:

            if isinstance(opacity_path,str):
                self.load_opacity_from_path(opacity_path,molecule_filter=molecule_filter)
            elif isinstance(opacity_path,(list,)):
                for path in opacity_path:
                    self.load_opacity_from_path(path,molecule_filter=molecule_filter)
    
    def clear_cache(self):
        """
        Clears all currently loaded cross-sections
        """
        self.opacity_dict = {}
    def init(self):
        self.log = Logger('ClassFactory')

        self.extension_paths = []
        self.reload_plugins()