<commit_before>"""
module __LinearSolver__: Algorithm for solving transport processes
========================================================================

.. warning:: The classes of this module should be loaded through the 'Algorithms.__init__.py' file.

"""

import scipy as sp
import scipy.sparse as sprs
import scipy.sparse.linalg as sprslin
from .__GenericAlgorithm__ import GenericAlgorithm


class GenericLinearTransport(GenericAlgorithm):
    r"""
    This class provides essential methods for building and solving matrices in a transport process.
    It will be inherited during Fickian diffusion, Fourier heat conduction, Hagen Poiseuille permeability and electron conduction.

    """

    def __init__(self,**kwargs):
        r'''
        Initializing the class
        '''
        super(GenericLinearTransport,self).__init__(**kwargs)
        
    def setup(self,fluid,conductance,quantity):
        r'''
        This setup provides the initial data for the solver.
        '''
        self._fluid = fluid
        self._conductance = 'throat.'+conductance.split('.')[-1]
        self._quantity = 'pore.'+quantity.split('.')[-1]
        
        #Check health of conductance vector
        if self._fluid.check_data_health(props=self._conductance,quiet=True):
            #If no nans, check for 0's
            ind = sp.nonzero(fluid[self._conductance])[0]
            gmin = sp.amin(self._fluid[self._conductance][ind])
            ind = sp.where(fluid[self._conductance]==0)[0]
            self['throat.conductance'] = self._fluid[self._conductance]
            #To prevent singular matrix
            self['throat.conductance'][ind] = gmin/1000000
        else:
            raise Exception('The provided throat conductance has problems')
            
    def update(self):
        r'''
        Send results of simulation out the the appropriate locations.
        
        This is a basic version of the update that simply sends out the main
        result (quantity). More elaborate updates should be subclassed.
        '''        
        self._fluid[self._quantity] = self[self._quantity]
        self._logger.debug('Results of '+self.name+' algorithm have been added to '+self._fluid.name)

    def set_boundary_conditions(self,bctype='',bcvalue=[],pores=[],mode='merge'):
        r'''
        Apply boundary conditions to specified pores.  This does not support
        throat boundary conditions yet.
        
        Parameters
        ----------
        bctype : string
            Specifies the type of boundary condition to apply.  Can be one of:
            
            - 'Dirichlet' : Specify the quantity in each pore
            - 'Neumann' : Specify the flow rate into each pore
            - 'Neumann_group' : Specify the net flow rate into a group of pores
            
        bcvalue : array_like
            The boundary value to apply, such as concentration or rate
        pores : array_like
            The pores where the boundary conditions should be applied
        mode : string, optional
            Controls how the conditions are applied.  Options are:
            
            - 'merge': Inserts the specified values, leaving existing values elsewhere
            - 'overwrite': Inserts specified values, clearing all other values
            - 'remove': Removes boundary conditions from specified pores
            - 'clear_all': Removes ALL boundary conditions
            
        Notes
        -----
        1. At the moment is it not possible to have multiple boundary conditions 
        in the same pore, so when new conditions are applied any existing ones
        are removed from all other boundary types.
        2. It is also not yet possible to apply boundary conditions to throats.
        '''
        
        BC_default = ['Dirichlet','Neumann','Neumann_group']

        #If mode is 'clear_all' then bypass checks
        if mode == 'clear_all':
            for item in self.labels():
                bcname = item.split('.')[1]
                if bcname in BC_default:
                    self._logger.debug('Removing '+bcname)
                    del self['pore.bcval_'+bcname]
                    del self['pore.'+bcname]
            return

        #Validate bctype
        if bctype.split('.')[-1] not in BC_default:
            raise Exception('Unrecognized bctype') 
            
        #Validate pores
        if pores == []:
            if mode not in ['remove','clear_all']:
                raise Exception('Pores must be specified')
        else:
            pores = sp.array(pores,ndmin=1)
                    
        #Validate bcvalue
        if bcvalue == []:
            if mode not in ['remove','clear_all']:
                raise Exception('bcvalue must be specified')
        else:                        
            bcvalue = sp.array(bcvalue,ndmin=1)
        
        #Check bcvalues are compatible with bctypes
        if bctype == 'Neumann_group':  #Only scalars are acceptable
            if sp.size(bcvalue) != 1: 
                raise Exception('When specifying Neumann_group, bcval should be a scalar')
        else: #Only scalars or Np-long are acceptable
            if sp.size(bcvalue) == 1:
                bcvalue = sp.ones(sp.shape(pores))*bcvalue
            elif sp.size(bcvalue) != sp.size(pores):
                raise Exception('The pore list and bcvalue list are different lengths')
        
        #Confirm that prop and label arrays exist
        if 'pore.bcval_'+bctype not in self.props():
            self['pore.bcval_'+bctype] = sp.ones((self.num_pores(),),dtype=float)*sp.nan
        if 'pore.'+bctype not in self.labels():
            self['pore.'+bctype] = sp.zeros((self.num_pores(),),dtype=bool)
            
        #Remove all BC from specified pores, prior to setting new ones
        for item in self.labels():
            bcname = item.split('.')[-1]
            if bcname in BC_default:
                self['pore.bcval_'+bcname][pores] = sp.nan
                self['pore.'+bcname][pores] = False
        
        #Set boundary conditions based on supplied mode
        if mode == 'merge':
            self['pore.bcval_'+bctype][pores] = bcvalue
            self['pore.'+bctype][pores] = True
        elif mode == 'overwrite':
            self['pore.bcval_'+bctype] = sp.ones((self.num_pores(),),dtype=float)*sp.nan
            self['pore.bcval_'+bctype][pores] = bcvalue
            self['pore.'+bctype] = sp.zeros((self.num_pores(),),dtype=bool)
            self['pore.'+bctype][pores] = True
        elif mode == 'remove':
            if pores == []:
                self._logger.debug('Removing '+bctype)
                del self['pore.bcval_'+bctype]
                del self['pore.'+bctype]
            else:
                self._logger.debug('Removing '+bctype+' from specified pores')
                self['pore.bcval_'+bctype][pores] = sp.nan
                self['pore.'+bctype][pores] = False

    def _build_coefficient_matrix(self):
        r'''
        '''
        # Filling coefficient matrix
        tpore1 = self._net['throat.conns'][:,0]<commit_msg>Fixed apply_boundary_condition so 'remove' and 'clear_all' work better.<commit_after>"""
module __LinearSolver__: Algorithm for solving transport processes
========================================================================

.. warning:: The classes of this module should be loaded through the 'Algorithms.__init__.py' file.

"""

import scipy as sp
import scipy.sparse as sprs
import scipy.sparse.linalg as sprslin
from .__GenericAlgorithm__ import GenericAlgorithm


class GenericLinearTransport(GenericAlgorithm):
    r"""
    This class provides essential methods for building and solving matrices in a transport process.
    It will be inherited during Fickian diffusion, Fourier heat conduction, Hagen Poiseuille permeability and electron conduction.

    """

    def __init__(self,**kwargs):
        r'''
        Initializing the class
        '''
        super(GenericLinearTransport,self).__init__(**kwargs)
        
    def setup(self,fluid,conductance,quantity):
        r'''
        This setup provides the initial data for the solver.
        '''
        self._fluid = fluid
        self._conductance = 'throat.'+conductance.split('.')[-1]
        self._quantity = 'pore.'+quantity.split('.')[-1]
        
        #Check health of conductance vector
        if self._fluid.check_data_health(props=self._conductance,quiet=True):
            #If no nans, check for 0's
            ind = sp.nonzero(fluid[self._conductance])[0]
            gmin = sp.amin(self._fluid[self._conductance][ind])
            ind = sp.where(fluid[self._conductance]==0)[0]
            self['throat.conductance'] = self._fluid[self._conductance]
            #To prevent singular matrix
            self['throat.conductance'][ind] = gmin/1000000
        else:
            raise Exception('The provided throat conductance has problems')
            
    def update(self):
        r'''
        Send results of simulation out the the appropriate locations.
        
        This is a basic version of the update that simply sends out the main
        result (quantity). More elaborate updates should be subclassed.
        '''        
        self._fluid[self._quantity] = self[self._quantity]
        self._logger.debug('Results of '+self.name+' algorithm have been added to '+self._fluid.name)

    def set_boundary_conditions(self,bctype='',bcvalue=[],pores=[],mode='merge'):
        r'''
        Apply boundary conditions to specified pores.  This does not support
        throat boundary conditions yet.
        
        Parameters
        ----------
        bctype : string
            Specifies the type of boundary condition to apply.  Can be one of:
            
            - 'Dirichlet' : Specify the quantity in each pore
            - 'Neumann' : Specify the flow rate into each pore
            - 'Neumann_group' : Specify the net flow rate into a group of pores
            
        bcvalue : array_like
            The boundary value to apply, such as concentration or rate
        pores : array_like
            The pores where the boundary conditions should be applied
        mode : string, optional
            Controls how the conditions are applied.  Options are:
            
            - 'merge': Inserts the specified values, leaving existing values elsewhere
            - 'overwrite': Inserts specified values, clearing all other values
            - 'remove': Removes boundary conditions from specified pores
            - 'clear_all': Removes ALL boundary conditions
            
        Notes
        -----
        1. At the moment is it not possible to have multiple boundary conditions 
        in the same pore, so when new conditions are applied any existing ones
        are removed from all other boundary types.
        2. It is also not yet possible to apply boundary conditions to throats.
        '''
        
        BC_default = ['Dirichlet','Neumann','Neumann_group']

        #If mode is 'clear_all' then bypass checks
        if mode == 'clear_all':
            for item in self.labels():
                bcname = item.split('.')[1]
                if bcname in BC_default:
                    self._logger.debug('Removing '+bcname)
                    del self['pore.bcval_'+bcname]
                    del self['pore.'+bcname]
            return
            
        #If mode is 'remove', also bypass checks
        if mode == 'remove':
            if pores == []:
                self._logger.debug('Cannot remove BC values unless pores are specified')
            else:
                self._logger.debug('Removing '+bctype+' from specified pores')
                self['pore.bcval_'+bctype][pores] = sp.nan
                self['pore.'+bctype][pores] = False
            return

        #Validate bctype
        if bctype.split('.')[-1] not in BC_default:
            raise Exception('Unrecognized bctype') 
            
        #Validate pores
        if pores == []:
            raise Exception('Pores must be specified')
        else:
            pores = sp.array(pores,ndmin=1)
                    
        #Validate bcvalue
        if bcvalue == []:
            raise Exception('bcvalue must be specified')
        else:                        
            bcvalue = sp.array(bcvalue,ndmin=1)
        
        #Check bcvalues are compatible with bctypes
        if bctype == 'Neumann_group':  #Only scalars are acceptable
            if sp.size(bcvalue) != 1: 
                raise Exception('When specifying Neumann_group, bcval should be a scalar')
        else: #Only scalars or Np-long are acceptable
            if sp.size(bcvalue) == 1:
                bcvalue = sp.ones(sp.shape(pores))*bcvalue
            elif sp.size(bcvalue) != sp.size(pores):
                raise Exception('The pore list and bcvalue list are different lengths')
        
        #Confirm that prop and label arrays exist
        if 'pore.bcval_'+bctype not in self.props():
            self['pore.bcval_'+bctype] = sp.ones((self.num_pores(),),dtype=float)*sp.nan
        if 'pore.'+bctype not in self.labels():
            self['pore.'+bctype] = sp.zeros((self.num_pores(),),dtype=bool)
            
        #Remove all BC from specified pores, prior to setting new ones
        for item in self.labels():
            bcname = item.split('.')[-1]
            if bcname in BC_default:
                self['pore.bcval_'+bcname][pores] = sp.nan
                self['pore.'+bcname][pores] = False
        
        #Set boundary conditions based on supplied mode
        if mode == 'merge':
            self['pore.bcval_'+bctype][pores] = bcvalue
            self['pore.'+bctype][pores] = True
        elif mode == 'overwrite':
            self['pore.bcval_'+bctype] = sp.ones((self.num_pores(),),dtype=float)*sp.nan
            self['pore.bcval_'+bctype][pores] = bcvalue
            self['pore.'+bctype] = sp.zeros((self.num_pores(),),dtype=bool)
            self['pore.'+bctype][pores] = True

    def _build_coefficient_matrix(self):
        r'''
        '''
        # Filling coefficient matrix
        tpore1 = self._net['throat.conns'][:,0]
{'fraction_non_alphanumeric': 0.10939374691227327, 'fraction_numerical': 0.0033876773237349143, 'mean_word_length': 2.0460017196904556, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '6432059', 'n_tokens_mistral': 3877, 'n_tokens_neox': 3663, 'n_words': 1138}