<commit_before>'''
###############################################################################
Controller:  Overall controller class
###############################################################################
'''
import pickle as _pickle
import copy as _copy
import time, random, string
import OpenPNM
from OpenPNM.Base import logging
logger = logging.getLogger()

class Controller(dict):
    r"""

    """
    # The following __instance__ class variable and subclassed __new__ method
    # makes the Controller class a 'Singleton'.  This way, the _ctrl attribute
    # of every OpenPNM object is the same, AND if you create a ctrl on the
    # command line (ctrl = OpenPNM.Base.Controller()) it will be the same ctrl!
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if Controller.__instance__ is None:
            Controller.__instance__ = dict.__new__(cls)
        return Controller.__instance__

    def __init__(self):
        r'''
        '''
        self.comments = 'Using OpenPNM ' + OpenPNM.__version__

    def __str__(self):
        header = ('-'*60)
        print(header)
        print("{c:<10s} {a:<25s} {b:<25s}".format(a='Class', b='Name', c='Type'))
        print(header)
        for item in self.networks():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Network'))
        print(header)
        for item in self.geometries():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Geometry'))
        print(header)
        for item in self.phases():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Phase'))
        print(header)
        for item in self.physics():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Physics'))
        print(header)
        for item in self.algorithms():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Algorithm'))
        print(header)
        return ''
        
    def _setloglevel(self,level):
        logger.setLevel(level)
        print('Log level has been changed to -->',logger.level)
    
    def _getloglevel(self):
        print('Log level is currently set to -->',logger.level)
        
    loglevel = property(fget=_getloglevel,fset=_setloglevel)

    def show_tree(self):
        r'''
        Prints a heirarchical list of object associations
        '''
        for net in self.networks():
            header = ('-'*60)
            print(header)
            print('Network: '+net.name)
            for geom in net.geometries():
                print('+ '+'Geometry: '+geom)
            for phase in net.phases():
                if len(net.phases(phase)[0].phases())==0:
                    print('+ '+'Pure Phase: '+phase)
                if len(net.phases(phase)[0].phases())>1:
                    print('+ '+'Mixture Phase: '+phase)
                    comps = net.phases(phase).phases()
                    for compname in comps:
                        print('+++ '+'Component Phase: '+compname)
                for phys in net.phases(phase)[0].physics():
                    print('++ '+'Physics: '+phys)

    def networks(self):
        r'''
        Returns a list of all Network objects
        '''
        return self._get_objects(obj_type='GenericNetwork')

    def geometries(self):
        r'''
        Returns a list of all Geometry objects
        '''
        return self._get_objects(obj_type='GenericGeometry')

    def phases(self):
        r'''
        Returns a list of all Phase objects
        '''
        return self._get_objects(obj_type='GenericPhase')

    def physics(self):
        r'''
        Returns a list of all Physics objects
        '''
        return self._get_objects(obj_type='GenericPhysics')

    def algorithms(self):
        r'''
        Returns a list of all Algorithm objects
        '''
        return self._get_objects(obj_type='GenericAlgorithm')

    def _get_objects(self,obj_type):
        temp = []
        for obj in self.keys():
            mro = [item.__name__ for item in self[obj].__class__.__mro__]
            if obj_type in mro:
                temp.append(self[obj])
        return temp

    def clear(self):
        r'''
        This is an overloaded version of the standard dict's ``clear`` method.
        This completely clears the Controller object's dict as expected, but
        also removes links to the Controller object in all objects.

        '''
        for item in self.keys():
            self[item]._ctrl = {}
        self.__dict__ = {}
        super(Controller,self).clear()
        
    def update(self,arg):
        r'''
        This is a subclassed version of the standard dict's ``update`` method.  
        It can accept a dictionary as usual, but also an OpenPNM Network 
        object, from which all the associated objects are extracted and added 
        to the Controller.
        
        Notes
        -----
        The Network (and other Core objects) do not store Algorithms, so this
        update will not add any Algorithm objects to the Controller.  This may 
        '''
        if arg.__class__ == dict:
            super(Controller,self).update(arg)
        else:
            mro = [item.__name__ for item in arg.__class__.__mro__]
            if 'GenericNetwork' in mro:
                net = arg
                self[net.name] = net
                net._ctrl = self
                for item in net._geometries + net._physics + net._phases:
                    self[item.name] = item
                    item._ctrl = self

    def purge_object(self,obj,mode='single'):
        r'''
        Remove an object, including all traces of it in its associated objects

        Parameters
        ----------
        obj : OpenPNM Object
            The object to be removed.  This method removes all traces of the 
            object from everywhere, including all the object tracking lists and 
            label dictionaries of every object.
        mode : string
            Dicates the type of purge to be performed.  Options are:
            
            - 'single': Only purges the specified object
            - 'complete': Purges the specified object AND all of its associated objects

        Notes
        -----<commit_msg>Controller.update now accepts a dict of OpenPNM objects or single Network instance.  In both cases it add the objects to the Controller, and vice-versa.<commit_after>'''
###############################################################################
Controller:  Overall controller class
###############################################################################
'''
import pickle as _pickle
import copy as _copy
import time, random, string
import OpenPNM
from OpenPNM.Base import logging
logger = logging.getLogger()

class Controller(dict):
    r"""

    """
    # The following __instance__ class variable and subclassed __new__ method
    # makes the Controller class a 'Singleton'.  This way, the _ctrl attribute
    # of every OpenPNM object is the same, AND if you create a ctrl on the
    # command line (ctrl = OpenPNM.Base.Controller()) it will be the same ctrl!
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if Controller.__instance__ is None:
            Controller.__instance__ = dict.__new__(cls)
        return Controller.__instance__

    def __init__(self):
        r'''
        '''
        self.comments = 'Using OpenPNM ' + OpenPNM.__version__

    def __str__(self):
        header = ('-'*60)
        print(header)
        print("{c:<10s} {a:<25s} {b:<25s}".format(a='Class', b='Name', c='Type'))
        print(header)
        for item in self.networks():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Network'))
        print(header)
        for item in self.geometries():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Geometry'))
        print(header)
        for item in self.phases():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Phase'))
        print(header)
        for item in self.physics():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Physics'))
        print(header)
        for item in self.algorithms():
            print("{c:<10s} {a:<25s} {b:<25s}".format(a=item.__class__.__name__, b=item.name, c='Algorithm'))
        print(header)
        return ''
        
    def _setloglevel(self,level):
        logger.setLevel(level)
        print('Log level has been changed to -->',logger.level)
    
    def _getloglevel(self):
        print('Log level is currently set to -->',logger.level)
        
    loglevel = property(fget=_getloglevel,fset=_setloglevel)

    def show_tree(self):
        r'''
        Prints a heirarchical list of object associations
        '''
        for net in self.networks():
            header = ('-'*60)
            print(header)
            print('Network: '+net.name)
            for geom in net.geometries():
                print('+ '+'Geometry: '+geom)
            for phase in net.phases():
                if len(net.phases(phase)[0].phases())==0:
                    print('+ '+'Pure Phase: '+phase)
                if len(net.phases(phase)[0].phases())>1:
                    print('+ '+'Mixture Phase: '+phase)
                    comps = net.phases(phase).phases()
                    for compname in comps:
                        print('+++ '+'Component Phase: '+compname)
                for phys in net.phases(phase)[0].physics():
                    print('++ '+'Physics: '+phys)

    def networks(self):
        r'''
        Returns a list of all Network objects
        '''
        return self._get_objects(obj_type='GenericNetwork')

    def geometries(self):
        r'''
        Returns a list of all Geometry objects
        '''
        return self._get_objects(obj_type='GenericGeometry')

    def phases(self):
        r'''
        Returns a list of all Phase objects
        '''
        return self._get_objects(obj_type='GenericPhase')

    def physics(self):
        r'''
        Returns a list of all Physics objects
        '''
        return self._get_objects(obj_type='GenericPhysics')

    def algorithms(self):
        r'''
        Returns a list of all Algorithm objects
        '''
        return self._get_objects(obj_type='GenericAlgorithm')

    def _get_objects(self,obj_type):
        temp = []
        for obj in self.keys():
            mro = [item.__name__ for item in self[obj].__class__.__mro__]
            if obj_type in mro:
                temp.append(self[obj])
        return temp

    def clear(self):
        r'''
        This is an overloaded version of the standard dict's ``clear`` method.
        This completely clears the Controller object's dict as expected, but
        also removes links to the Controller object in all objects.

        '''
        for item in self.keys():
            self[item]._ctrl = {}
        self.__dict__ = {}
        super(Controller,self).clear()
        
    def update(self,arg):
        r'''
        This is a subclassed version of the standard dict's ``update`` method.  
        It can accept a dictionary of OpenPNM Core objects in which case it 
        adds the objects to the Controller.  It can also accept an OpenPNM 
        Network object in which case it extracts all associated objects and 
        adds them to the Controller.  In both cases it adds the Controller to
        all object's ``controller`` attribute.
        
        Notes
        -----
        The Network (and other Core objects) do not store Algorithms, so this
        update will not add any Algorithm objects to the Controller.  This may
        change.  
        '''
        if arg.__class__ == dict:
            for item in arg.keys():
                self[item] = arg[item]
                arg[item]._ctrl = self
        else:
            mro = [item.__name__ for item in arg.__class__.__mro__]
            if 'GenericNetwork' in mro:
                net = arg
                self[net.name] = net
                net._ctrl = self
                for item in net._geometries + net._physics + net._phases:
                    self[item.name] = item
                    item._ctrl = self

    def purge_object(self,obj,mode='single'):
        r'''
        Remove an object, including all traces of it in its associated objects

        Parameters
        ----------
        obj : OpenPNM Object
            The object to be removed.  This method removes all traces of the 
            object from everywhere, including all the object tracking lists and 
            label dictionaries of every object.
        mode : string
            Dicates the type of purge to be performed.  Options are:
            
            - 'single': Only purges the specified object
            - 'complete': Purges the specified object AND all of its associated objects

        Notes
        -----
{'fraction_non_alphanumeric': 0.13359768186670734, 'fraction_numerical': 0.006862894616440445, 'mean_word_length': 2.064252336448598, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7033297', 'n_tokens_mistral': 3705, 'n_tokens_neox': 3532, 'n_words': 1114}