Beispiel #1
0
def add_conductance(G, kind, invivo, edges=None):
    """Adds conductance values to the edges of the graph (consult the relevant
    functions in the physiology module for more information.
    INPUT: G: Vascular graph in iGraph format.
           kind: The vessel kind. This can be either 'a' for artery or 'v' for
                 vein.
           invivo: Boolean, whether the physiological blood characteristics 
                   are calculated using the invivo (=True) or invitro (=False)
                   equations
           edges: (Optional.) The indices of the edges to be given a 
                  conductance value. If no indices are supplied, all edges are 
                  considered.
    """
    P = Physiology(G['defaultUnits'])
    if edges is None:
        edgelist = G.es
    else:
        edgelist = G.es(edges)
    #for e in edgelist:
    #print('')
    #print(e['diameter'])
    #print(e['length'])
    #print(P.dynamic_blood_viscosity(e['diameter'],invivo,kind))
    G.es(edgelist.indices)['conductance'] = \
                                   [P.conductance(e['diameter'],e['length'],
                                       P.dynamic_blood_viscosity(e['diameter'],
                                                                 invivo,kind))
                                    for e in edgelist]
Beispiel #2
0
def add_conductance(G,kind,invivo,edges=None):
    """Adds conductance values to the edges of the graph (consult the relevant
    functions in the physiology module for more information.
    INPUT: G: Vascular graph in iGraph format.
           kind: The vessel kind. This can be either 'a' for artery or 'v' for
                 vein.
           invivo: Boolean, whether the physiological blood characteristics 
                   are calculated using the invivo (=True) or invitro (=False)
                   equations
           edges: (Optional.) The indices of the edges to be given a 
                  conductance value. If no indices are supplied, all edges are 
                  considered.
    """
    P = Physiology(G['defaultUnits'])
    if edges is None:
        edgelist = G.es
    else:
        edgelist = G.es(edges)
    #for e in edgelist:
        #print('')
        #print(e['diameter'])
        #print(e['length'])
        #print(P.dynamic_blood_viscosity(e['diameter'],invivo,kind))
    G.es(edgelist.indices)['conductance'] = \
                                   [P.conductance(e['diameter'],e['length'],
                                       P.dynamic_blood_viscosity(e['diameter'],
                                                                 invivo,kind))
                                    for e in edgelist]
Beispiel #3
0
    def __init__(self, G, **kwargs):
        """Constructs the linear system A x = b where the matrix A contains the 
        conductance information of the vascular graph, the vector b specifies 
        the boundary conditions and the vector x holds the pressures at the 
        vertices (for which the system needs to be solved).
        pBC should be given in mmHG and pressure will be output in mmHg

        INPUT: G: Vascular graph in iGraph format.(the pBC should be given in mmHg)
               withRBC: boolean if a fixed distribution of RBCs should be considered
                       if 'htt' is an edge attribute the current distribution is used.
                       Otherwise the value to be assinged for all vessels should be given.
                       For arteries and veins a empirical value which is a function of
                       the diameter is assigned.
               resistanceLength: boolean if diameter is not considered for the restistance
                       and hence the resistance is only a function of the vessel length
        OUTPUT: A: Matrix A of the linear system, holding the conductance 
                   information.
                b: Vector b of the linear system, holding the boundary 
                   conditions.
        """
        self._G = G
        self._P = Physiology(G['defaultUnits'])
        self._muPlasma = self._P.dynamic_plasma_viscosity()
        #Check if a arbirtrary distribution of RBCs should be considered
        if kwargs.has_key('withRBC'):
            if kwargs['withRBC']!=0:
                self._withRBC = kwargs['withRBC']
            else:
                self._withRBC = 0
        else:
            self._withRBC = 0

        if kwargs.has_key('invivo'):
            if kwargs['invivo']!=0:
                self._invivo = kwargs['invivo']
            else:
                self._invivo = 0
        else:
            self._invivo = 0

        if kwargs.has_key('resistanceLength'):
            if kwargs['resistanceLength']==1:
                self._resistanceLength = 1
                print('Diameter not considered for calculation of resistance')
            else:
                self._resistanceLength = 0
        else:
            self._resistanceLength = 0

        if self._withRBC != 0:
            if 'htt' not in G.es.attribute_names():
                G.es['htt']=[self._withRBC]*G.ecount()
                self._withRBC = 1
            httNone = G.es(htt_eq=None).indices
            if len(httNone) > 0:
                G.es[httNone]['htt']=[self._withRBC]*len(httNone)

        self.update(G)
        self._eps = np.finfo(float).eps
Beispiel #4
0
def add_pBCs(G,kind,vertices):
    """Adds pressure boundary conditions to the vascular graph. Pressure values
    are taken from literature (see function 'blood_pressure').
    The pressure boundary vertices recieve a kind tag of either 'a' or 'v' 
    to classify them as arteries or veins respectively.
    INPUT: G: Vascular graph in iGraph format.
           kind: The vertex kind. This can be either 'a' for arterial or 'v' 
                 for venous.
           vertices: The indices of the vertices for which the pressure 
                     boundary conditions are to be set.
    OUTPUT: G is modified in place.
    """    

    P = Physiology(G['defaultUnits'])
    
    for vertex in vertices:
        diameter = max([G.es[x]['diameter'] for x in G.adjacent(vertex,'all')])
        G.vs[vertex]['pBC'] = P.blood_pressure(diameter,kind)
        G.vs[vertex]['kind'] = kind
Beispiel #5
0
def add_pBCs(G, kind, vertices):
    """Adds pressure boundary conditions to the vascular graph. Pressure values
    are taken from literature (see function 'blood_pressure').
    The pressure boundary vertices recieve a kind tag of either 'a' or 'v' 
    to classify them as arteries or veins respectively.
    INPUT: G: Vascular graph in iGraph format.
           kind: The vertex kind. This can be either 'a' for arterial or 'v' 
                 for venous.
           vertices: The indices of the vertices for which the pressure 
                     boundary conditions are to be set.
    OUTPUT: G is modified in place.
    """

    P = Physiology(G['defaultUnits'])

    for vertex in vertices:
        diameter = max(
            [G.es[x]['diameter'] for x in G.adjacent(vertex, 'all')])
        G.vs[vertex]['pBC'] = P.blood_pressure(diameter, kind)
        G.vs[vertex]['kind'] = kind
Beispiel #6
0
    def __init__(self, G, withRBC = 0, invivo = 0, dMin_empirical = 3.5, htdMax_empirical = 0.6, verbose = True,
            diameterOverTime=[],**kwargs):
        """
        Computes the flow and pressure field of a vascular graph without RBC tracking.
        It can be chosen between pure plasma flow, constant hematocrit or a given htt/htd
        distribution.
        The pressure boundary conditions (pBC) should be given in mmHG and pressure will be output in mmHg

        INPUT: G: Vascular graph in iGraph format.(the pBC should be given in mmHg)
               invivo: boolean if the invivo or invitro empirical functions are used (default = 0)
               withRBC: = 0: no RBCs, pure plasma Flow (default)
                        0 < withRBC < 1 & 'htt' not in edgeAttributes: the given value is assigned as htt to all edges.
                        0 < withRBC < 1 & 'htt' in edgeAttributes: the given value is assigned as htt to all edges where htt = None.
                        NOTE: Htd will be computed from htt and used to compute the resistance.
                            If htd is already in the edge attributes, it will be overwritten.
                dMin_empiricial: lower limit for the diameter that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 3.5).
                htdMax_empirical: upper limit for htd that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 0.6). Maximum has to be 1.
                verbose: Bool if WARNINGS and setup information (INFO) is printed
                diameterOverTime: list of the diameterChanges over time. The length of the list is the number of time steps with diameter change.
                    each diameter change should be provided as a tuple, e.g. two diameterChanges at the same time, a single diameter change afterwards.
                    [[[edgeIndex1, edgeIndex1],[newDiameter1, newDiameter2]],[[edgeIndex3], [newDiameter3]]]
        OUTPUT: None, the edge properties htt is assgined and the function update is executed (see description for more details)
        """
        self._G = G
        nVertices = G.vcount()
        self._b = np.zeros(nVertices)            
        self._A = lil_matrix((nVertices,nVertices),dtype=float)
        self._eps = np.finfo(float).eps
        self._P = Physiology(G['defaultUnits'])
        self._muPlasma = self._P.dynamic_plasma_viscosity()
        self._withRBC = withRBC
        self._invivo = invivo
        self._verbose = verbose
        self._dMin_empirical = dMin_empirical
        self._htdMax_empirical = htdMax_empirical
        self._diameterOverTime = diameterOverTime
        self._timeSteps = len(diameterOverTime)
        self._scalingFactor = vgm.units.scaling_factor_du('mmHg',G['defaultUnits'])

        if self._verbose:
            print('INFO: The limits for the compuation of the effective viscosity are set to')
            print('Minimum diameter %.2f' %self._dMin_empirical)
            print('Maximum discharge %.2f' %self._htdMax_empirical)

        if self._withRBC != 0:
            if self._withRBC < 1.:
                if 'htt' not in G.es.attribute_names():
                    G.es['htt']=[self._withRBC]*G.ecount()
                else:
                    httNone = G.es(htt_eq=None).indices
                    if len(httNone) > 0:
                        G.es[httNone]['htt']=[self._withRBC]*len(httNone)
                    else:
                        if self._verbose:
                            print('WARNING: htt is already an edge attribute. \n Existing values are not overwritten!'+\
                                    '\n If new values should be assigned htt has to be deleted beforehand!')
            else:
                print('ERROR: 0 < withRBC < 1')

        if 'rBC' not in G.vs.attribute_names():
            G.vs['rBC'] = [None]*G.vcount()

        if 'pBC' not in G.vs.attribute_names():
            G.vs['pBC'] = [None]*G.vcount()

        #Convert 'pBC' ['mmHG'] to default Units
        for v in G.vs(pBC_ne=None):
            v['pBC']=v['pBC']*self._scalingFactor

        if len(G.vs(pBC_ne=None)) > 0:
            if self._verbose:
                print('INFO: Pressure boundary conditions changed from mmHg --> default Units')

        self.update()
Beispiel #7
0
class LinearSystemTimeCourse(object):
    def __init__(self, G, withRBC = 0, invivo = 0, dMin_empirical = 3.5, htdMax_empirical = 0.6, verbose = True,
            diameterOverTime=[],**kwargs):
        """
        Computes the flow and pressure field of a vascular graph without RBC tracking.
        It can be chosen between pure plasma flow, constant hematocrit or a given htt/htd
        distribution.
        The pressure boundary conditions (pBC) should be given in mmHG and pressure will be output in mmHg

        INPUT: G: Vascular graph in iGraph format.(the pBC should be given in mmHg)
               invivo: boolean if the invivo or invitro empirical functions are used (default = 0)
               withRBC: = 0: no RBCs, pure plasma Flow (default)
                        0 < withRBC < 1 & 'htt' not in edgeAttributes: the given value is assigned as htt to all edges.
                        0 < withRBC < 1 & 'htt' in edgeAttributes: the given value is assigned as htt to all edges where htt = None.
                        NOTE: Htd will be computed from htt and used to compute the resistance.
                            If htd is already in the edge attributes, it will be overwritten.
                dMin_empiricial: lower limit for the diameter that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 3.5).
                htdMax_empirical: upper limit for htd that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 0.6). Maximum has to be 1.
                verbose: Bool if WARNINGS and setup information (INFO) is printed
                diameterOverTime: list of the diameterChanges over time. The length of the list is the number of time steps with diameter change.
                    each diameter change should be provided as a tuple, e.g. two diameterChanges at the same time, a single diameter change afterwards.
                    [[[edgeIndex1, edgeIndex1],[newDiameter1, newDiameter2]],[[edgeIndex3], [newDiameter3]]]
        OUTPUT: None, the edge properties htt is assgined and the function update is executed (see description for more details)
        """
        self._G = G
        nVertices = G.vcount()
        self._b = np.zeros(nVertices)            
        self._A = lil_matrix((nVertices,nVertices),dtype=float)
        self._eps = np.finfo(float).eps
        self._P = Physiology(G['defaultUnits'])
        self._muPlasma = self._P.dynamic_plasma_viscosity()
        self._withRBC = withRBC
        self._invivo = invivo
        self._verbose = verbose
        self._dMin_empirical = dMin_empirical
        self._htdMax_empirical = htdMax_empirical
        self._diameterOverTime = diameterOverTime
        self._timeSteps = len(diameterOverTime)
        self._scalingFactor = vgm.units.scaling_factor_du('mmHg',G['defaultUnits'])

        if self._verbose:
            print('INFO: The limits for the compuation of the effective viscosity are set to')
            print('Minimum diameter %.2f' %self._dMin_empirical)
            print('Maximum discharge %.2f' %self._htdMax_empirical)

        if self._withRBC != 0:
            if self._withRBC < 1.:
                if 'htt' not in G.es.attribute_names():
                    G.es['htt']=[self._withRBC]*G.ecount()
                else:
                    httNone = G.es(htt_eq=None).indices
                    if len(httNone) > 0:
                        G.es[httNone]['htt']=[self._withRBC]*len(httNone)
                    else:
                        if self._verbose:
                            print('WARNING: htt is already an edge attribute. \n Existing values are not overwritten!'+\
                                    '\n If new values should be assigned htt has to be deleted beforehand!')
            else:
                print('ERROR: 0 < withRBC < 1')

        if 'rBC' not in G.vs.attribute_names():
            G.vs['rBC'] = [None]*G.vcount()

        if 'pBC' not in G.vs.attribute_names():
            G.vs['pBC'] = [None]*G.vcount()

        #Convert 'pBC' ['mmHG'] to default Units
        for v in G.vs(pBC_ne=None):
            v['pBC']=v['pBC']*self._scalingFactor

        if len(G.vs(pBC_ne=None)) > 0:
            if self._verbose:
                print('INFO: Pressure boundary conditions changed from mmHg --> default Units')

        self.update()
        
    #--------------------------------------------------------------------------    
        
    def update(self,esequence=None):
        """Constructs the linear system A x = b where the matrix A contains the 
        conductance information of the vascular graph, the vector b specifies 
        the boundary conditions and the vector x holds the pressures at the 
        vertices (for which the system needs to be solved). 
        INPUT: esequence: list of edges which need to be updated. Default=None, i.e. all edges will be updated
        OUTPUT: matrix A and vector b
        """
        G = self._G
        A = self._A
        b = self._b
        htt2htd = self._P.tube_to_discharge_hematocrit
        nurel = self._P.relative_apparent_blood_viscosity
        
        # Compute nominal and specific resistance:
        self._update_nominal_and_specific_resistance(esequence=esequence)

        #edge and vertex List that need to be updated
        if esequence is None:
            es = G.es
            vertexList = G.vs
        else:
            es = G.es(esequence)
            vertexList = []
            for edge in esequence:
                e = G.es[edge]
                vertexList.append(e.source)
                vertexList.append(e.target)
            vertexList = [int(v) for v in np.unique(vertexList)]
            vertexList = G.vs[vertexList]

        #if with RBCs compute effective resistance
        if self._withRBC:
            es['htd'] = [min(htt2htd(htt, d, self._invivo), 1.0) for htt,d in zip(es['htt'],es['diameter'])]
            es['effResistance'] =[ e['resistance'] * nurel(max(self._dMin_empirical,e['diameter']),\
                    min(e['htd'],self._htdMax_empirical),self._invivo) for e in es]
            es['conductance']=1/np.array(es['effResistance'])
        else:
            es['conductance'] = [1/e['resistance'] for e in es]
       
        for vertex in vertexList:        
            i = vertex.index
            A.data[i] = []
            A.rows[i] = []
            b[i] = 0.0
            if vertex['pBC'] is not None:                   
                A[i,i] = 1.0
                b[i] = vertex['pBC']
            else: 
                aDummy=0
                k=0
                neighbors=[]
                for edge in G.incident(i,'all'):
                    if G.is_loop(edge):
                        continue
                    j=G.neighbors(i)[k]
                    k += 1
                    conductance = G.es[edge]['conductance']
                    neighbor = G.vs[j]
                    # +=, -= account for multiedges
                    aDummy += conductance
                    if neighbor['pBC'] is not None:
                        b[i] = b[i] + neighbor['pBC'] * conductance
                    #elif neighbor['rBC'] is not None:
                     #   b[i] = b[i] + neighbor['rBC']
                    else:
                        if j not in neighbors:
                            A[i,j] = - conductance
                        else:
                            A[i,j] = A[i,j] - conductance
                    neighbors.append(j)
                    if vertex['rBC'] is not None:
                        b[i] += vertex['rBC']
                A[i,i]=aDummy
      
        self._A = A
        self._b = b
    
    #--------------------------------------------------------------------------

    def solve(self, method):
        """Solves the linear system A x = b for the vector of unknown pressures
        x, either using a direct solver (obsolete) or an iterative GMRES solver. From the
        pressures, the flow field is computed.
        INPUT: method: This can be either 'direct' or 'iterative2'
        OUTPUT: None - G is modified in place.
                G_final.pkl & G_final.vtp: are save as output
                sampledict.pkl: is saved as output
        """
        b = self._b
        A = self._A
        G = self._G
        htt2htd = self._P.tube_to_discharge_hematocrit
        
        A = self._A.tocsr()
        if method == 'direct':
            linalg.use_solver(useUmfpack=True)
            x = linalg.spsolve(A, b)
        elif method == 'iterative2':
             ml = rootnode_solver(A, smooth=('energy', {'degree':2}), strength='evolution' )
             M = ml.aspreconditioner(cycle='V')
             # Solve pressure system
             x,info = gmres(A, self._b, tol=1000*self._eps, maxiter=200, M=M)
             if info != 0:
                 print('ERROR in Solving the Matrix')
                 print(info)

        G.vs['pressure'] = x
        G.es['flow'] = [abs(G.vs[edge.source]['pressure'] - G.vs[edge.target]['pressure']) *  \
                        edge['conductance'] for edge in G.es]

        #Default Units - mmHg for pressure
        G.vs['pressure'] = [v['pressure']/self._scalingFactor for v in G.vs]

        if self._withRBC:
	        G.es['v']=[e['htd']/e['htt']*e['flow']/(0.25*np.pi*e['diameter']**2) for e in G.es]
        else:
	        G.es['v']=[e['flow']/(0.25*np.pi*e['diameter']**2) for e in G.es]
        
    #--------------------------------------------------------------------------
    def _update_nominal_and_specific_resistance(self, esequence=None):
        """Updates the nominal and specific resistance of a given edge 
        sequence.
        INPUT: esequence: Sequence of edge indices which have to be updated. If not provided, all 
                   edges are updated.
        OUTPUT: None, the edge properties 'resistance' and 'specificResistance'
                are updated (or created).
        """
        G = self._G

        if esequence is None:
            es = G.es
        else:
            es = G.es(esequence)

        es['specificResistance'] = [128 * self._muPlasma / (np.pi * d**4)
                                        for d in es['diameter']]

        es['resistance'] = [l * sr for l, sr in zip(es['length'],
                                                es['specificResistance'])]

    #--------------------------------------------------------------------------

    def evolve(self):
        """ The flow field is recomputed for changing vessel diameters over time. The changing vessel
        diameters have been provided as input in _init_ (diameterOverTime). 
        OUTPUT: None - G is modified in place.
                sampledict.pkl: which saves the pressure for all vertices over time and
                    flow, diameter and RBC velocity for all edges over time.
        """
        G = self._G
        diameterOverTime = self._diameterOverTime
        flow_time_edges=[]
        diameter_time_edges=[]
        v_time_edges=[]
        pressure_time_edges=[]

        #First iteration for initial diameter distribution
        self.solve('iterative2')
        flow_time_edges.append(G.es['flow'])
        diameter_time_edges.append(G.es['diameter'])
        v_time_edges.append(G.es['v'])
        pressure_time_edges.append(G.vs['pressure'])

        for timeStep in range(self._timeSteps):
            edgeSequence = diameterOverTime[timeStep][0]
            G.es[edgeSequence]['diameter'] = diameterOverTime[timeStep][1]
            self.update(esequence=edgeSequence)
            self.solve('iterative2')
            flow_time_edges.append(G.es['flow'])
            diameter_time_edges.append(G.es['diameter'])
            v_time_edges.append(G.es['v'])
            pressure_time_edges.append(G.vs['pressure'])
            vgm.write_pkl(G,'G_'+str(timeStep)+'.pkl')

        flow_edges_time = np.transpose(flow_time_edges)
        diameter_edges_time = np.transpose(diameter_time_edges)
        v_edges_time = np.transpose(v_time_edges)
        pressure_edges_time = np.transpose(pressure_time_edges)

        #Write Output
        sampledict={}
        sampledict['flow']=flow_edges_time
        sampledict['diameter']=diameter_edges_time
        sampledict['v']=v_edges_time
        sampledict['pressure']=pressure_edges_time
        g_output.write_pkl(sampledict, 'sampledict.pkl')

        #Convert 'pBC' from default Units to mmHg
        pBCneNone=G.vs(pBC_ne=None)
        pBCneNone['pBC']=np.array(pBCneNone['pBC'])*(1/self._scalingFactor)
        if len(pBCneNone) > 0:
            if self._verbose:
                print('INFO: Pressure boundary conditions changed from default Units --> mmHg')
Beispiel #8
0
class LinearSystem(object):
    def __init__(self, G, **kwargs):
        """Constructs the linear system A x = b where the matrix A contains the 
        conductance information of the vascular graph, the vector b specifies 
        the boundary conditions and the vector x holds the pressures at the 
        vertices (for which the system needs to be solved).
        pBC should be given in mmHG and pressure will be output in mmHg

        INPUT: G: Vascular graph in iGraph format.(the pBC should be given in mmHg)
               withRBC: boolean if a fixed distribution of RBCs should be considered
                       if 'htt' is an edge attribute the current distribution is used.
                       Otherwise the value to be assinged for all vessels should be given.
                       For arteries and veins a empirical value which is a function of
                       the diameter is assigned.
               resistanceLength: boolean if diameter is not considered for the restistance
                       and hence the resistance is only a function of the vessel length
        OUTPUT: A: Matrix A of the linear system, holding the conductance 
                   information.
                b: Vector b of the linear system, holding the boundary 
                   conditions.
        """
        self._G = G
        self._P = Physiology(G['defaultUnits'])
        self._muPlasma = self._P.dynamic_plasma_viscosity()
        #Check if a arbirtrary distribution of RBCs should be considered
        if kwargs.has_key('withRBC'):
            if kwargs['withRBC']!=0:
                self._withRBC = kwargs['withRBC']
            else:
                self._withRBC = 0
        else:
            self._withRBC = 0

        if kwargs.has_key('invivo'):
            if kwargs['invivo']!=0:
                self._invivo = kwargs['invivo']
            else:
                self._invivo = 0
        else:
            self._invivo = 0

        if kwargs.has_key('resistanceLength'):
            if kwargs['resistanceLength']==1:
                self._resistanceLength = 1
                print('Diameter not considered for calculation of resistance')
            else:
                self._resistanceLength = 0
        else:
            self._resistanceLength = 0

        if self._withRBC != 0:
            if 'htt' not in G.es.attribute_names():
                G.es['htt']=[self._withRBC]*G.ecount()
                self._withRBC = 1
            httNone = G.es(htt_eq=None).indices
            if len(httNone) > 0:
                G.es[httNone]['htt']=[self._withRBC]*len(httNone)

        self.update(G)
        self._eps = np.finfo(float).eps
        
    #--------------------------------------------------------------------------    
        
    def update(self, newGraph=None):
        """Constructs the linear system A x = b where the matrix A contains the 
        conductance information of the vascular graph, the vector b specifies 
        the boundary conditions and the vector x holds the pressures at the 
        vertices (for which the system needs to be solved). x will have the 
        same units of [pressure] as the pBC vertices.
    
        Note that in this approach, A and b contain a mixture of dimensions, 
        i.e. A and b have dimensions of [1.0] and [pressure] in the pBC case,
        [conductance] and [conductance*pressure] otherwise, the latter being 
        rBCs. This has the advantage that no re-indexing is required as the 
        matrices contain all vertices.
        INPUT: newGraph: Vascular graph in iGraph format to replace the 
                         previous self._G. (Optional, default=None.)
        OUTPUT: A: Matrix A of the linear system, holding the conductance 
                   information.
                b: Vector b of the linear system, holding the boundary 
                   conditions.
        """
        htt2htd = self._P.tube_to_discharge_hematocrit
        nurel = self._P.relative_apparent_blood_viscosity
        if newGraph is not None:
            self._G = newGraph
            
        G = self._G
        if not G.vs[0].attributes().has_key('pBC'):
            G.vs[0]['pBC'] = None
        if not G.vs[0].attributes().has_key('rBC'):
            G.vs[0]['rBC'] = None        

        #Convert 'pBC' ['mmHG'] to default Units
        pBCneNone=G.vs(pBC_ne=None).indices
        for i in pBCneNone:
            v=G.vs[i]
            v['pBC']=v['pBC']*vgm.units.scaling_factor_du('mmHg',G['defaultUnits'])
        
        nVertices = G.vcount()
        b = np.zeros(nVertices)            
        A = lil_matrix((nVertices,nVertices),dtype=float)

        # Compute nominal and specific resistance:
        self._update_nominal_and_specific_resistance()

        #if with RBCs compute effective resistance
        if self._withRBC:
            dischargeHt = [min(htt2htd(e, d, self._invivo), 1.0) for e,d in zip(G.es['htt'],G.es['diameter'])]
            G.es['effResistance'] =[ res * nurel(max(4.0,d),min(dHt,0.6),self._invivo) for res,dHt,d in zip(G.es['resistance'], \
                dischargeHt,G.es['diameter'])]
            G.es['conductance']=1/np.array(G.es['effResistance'])
        else: 
	    # Compute conductance
            for e in G.es:
	        e['conductance']=1/e['resistance']
        
            #if not bound_cond is None:
            #    self._conductance = [max(min(c, bound_cond[1]), bound_cond[0])
            #                     for c in G.es['conductance']]
            #else:
            #    self._conductance = G.es['conductance']
        self._conductance = G.es['conductance']

        for vertex in G.vs:        
            i = vertex.index
            A.data[i] = []
            A.rows[i] = []
            b[i] = 0.0
            if vertex['pBC'] is not None:                   
                A[i,i] = 1.0
                b[i] = vertex['pBC']
            else: 
                aDummy=0
                k=0
                neighbors=[]
                for edge in G.adjacent(i,'all'):
                    if G.is_loop(edge):
                        continue
                    j=G.neighbors(i)[k]
                    k += 1
                    conductance = G.es[edge]['conductance']
                    neighbor = G.vs[j]
                    # +=, -= account for multiedges
                    aDummy += conductance
                    if neighbor['pBC'] is not None:
                        b[i] = b[i] + neighbor['pBC'] * conductance
                    #elif neighbor['rBC'] is not None:
                     #   b[i] = b[i] + neighbor['rBC']
                    else:
                        if j not in neighbors:
                            A[i,j] = - conductance
                        else:
                            A[i,j] = A[i,j] - conductance
                    neighbors.append(j)
                    if vertex['rBC'] is not None:
                        b[i] += vertex['rBC']
                A[i,i]=aDummy
      
        self._A = A
        self._b = b
    
    #--------------------------------------------------------------------------

    def solve(self, method, **kwargs):
        """Solves the linear system A x = b for the vector of unknown pressures
        x, either using a direct solver or an iterative AMG solver. From the
        pressures, the flow field is computed.
        INPUT: method: This can be either 'direct' or 'iterative'
               **kwargs 
               precision: The accuracy to which the ls is to be solved. If not 
                          supplied, machine accuracy will be used.
               maxiter: The maximum number of iterations. The default value for
                        the iterative solver is 250.
        OUTPUT: None - G is modified in place.
        """
                
        b = self._b
        G = self._G
        htt2htd = self._P.tube_to_discharge_hematocrit
        
        A = self._A.tocsr()
        if method == 'direct':
            linalg.use_solver(useUmfpack=True)
            x = linalg.spsolve(A, b)
        elif method == 'iterative':
            if kwargs.has_key('precision'):
                eps = kwargs['precision']
            else:
                eps = self._eps
            if kwargs.has_key('maxiter'):
                maxiter = kwargs['maxiter']
            else:
                maxiter = 250
            AA = pyamg.smoothed_aggregation_solver(A, max_levels=10, max_coarse=500)
            x = abs(AA.solve(self._b, x0=None, tol=eps, accel='cg', cycle='V', maxiter=maxiter))
            # abs required, as (small) negative pressures may arise
        elif method == 'iterative2':
         # Set linear solver
             ml = rootnode_solver(A, smooth=('energy', {'degree':2}), strength='evolution' )
             M = ml.aspreconditioner(cycle='V')
             # Solve pressure system
             x,info = gmres(A, self._b, tol=self._eps, maxiter=50, M=M)
             if info != 0:
                 print('ERROR in Solving the Matrix')

        G.vs['pressure'] = x
        self._x = x
        conductance = self._conductance
        G.es['flow'] = [abs(G.vs[edge.source]['pressure'] -   \
                            G.vs[edge.target]['pressure']) *  \
                        conductance[i] for i, edge in enumerate(G.es)]
        for v in G.vs:
            v['pressure']=v['pressure']/vgm.units.scaling_factor_du('mmHg',G['defaultUnits'])
        if self._withRBC:
            for e in G.es:
                dischargeHt = min(htt2htd(e['htt'], e['diameter'], self._invivo), 1.0)
	        e['v']=dischargeHt/e['htt']*e['flow']/(0.25*np.pi*e['diameter']**2)
        else:
            for e in G.es:
	        e['v']=e['flow']/(0.25*np.pi*e['diameter']**2)
        
        #Convert 'pBC' from default Units to mmHg
        pBCneNone=G.vs(pBC_ne=None).indices
        if 'diamCalcEff' in G.es.attribute_names():
            del(G.es['diamCalcEff'])

        if 'effResistance' in G.es.attribute_names():
            del(G.es['effResistance'])

        if 'conductance' in G.es.attribute_names():
            del(G.es['conductance'])

        if 'resistance' in G.es.attribute_names():
            del(G.es['resistance'])

        G.vs[pBCneNone]['pBC']=np.array(G.vs[pBCneNone]['pBC'])*(1/vgm.units.scaling_factor_du('mmHg',G['defaultUnits']))

        vgm.write_pkl(G, 'G_final.pkl')
        vgm.write_vtp(G, 'G_final.vtp',False)

	#Write Output
	sampledict={}
        for eprop in ['flow', 'v']:
            if not eprop in sampledict.keys():
                sampledict[eprop] = []
            sampledict[eprop].append(G.es[eprop])
        for vprop in ['pressure']:
            if not vprop in sampledict.keys():
                sampledict[vprop] = []
            sampledict[vprop].append(G.vs[vprop])

	g_output.write_pkl(sampledict, 'sampledict.pkl')
    #--------------------------------------------------------------------------

    def _verify_mass_balance(self):
        """Computes the mass balance, i.e. sum of flows at each node and adds
        the result as a vertex property 'flowSum'.
        INPUT: None
        OUTPUT: None (result added as vertex property)
        """
        G = self._G
        G.vs['flowSum'] = [sum([G.es[e]['flow'] * np.sign(G.vs[v]['pressure'] -
                                                    G.vs[n]['pressure'])
                               for e, n in zip(G.adjacent(v), G.neighbors(v))])
                           for v in xrange(G.vcount())]
        for i in range(G.vcount()):
            if G.vs[i]['flowSum'] > self._eps:
                print('')
                print(i)
                print(G.vs['flowSum'][i])
                #print(self._res[i])
                print('ERROR')
                for j in G.adjacent(i):
                    print(G.es['flow'][j])

    #--------------------------------------------------------------------------

    def _verify_p_consistency(self):
        """Checks for local pressure maxima at non-pBC vertices.
        INPUT: None.
        OUTPUT: A list of local pressure maxima vertices and the maximum 
                pressure difference to their respective neighbors."""
        G = self._G
        localMaxima = []
        for i, v in enumerate(G.vs):
            if v['pBC'] is None:
                pdiff = [v['pressure'] - n['pressure']
                         for n in G.vs[G.neighbors(i)]]
                if min(pdiff) > 0:
                    localMaxima.append((i, max(pdiff)))         
        return localMaxima

    #--------------------------------------------------------------------------
    
    def _residual_norm(self):
        """Computes the norm of the current residual.
        """
        return np.linalg.norm(self._A * self._x - self._b)

    #--------------------------------------------------------------------------

    def _update_nominal_and_specific_resistance(self, esequence=None):
        """Updates the nominal and specific resistance of a given edge 
        sequence.
        INPUT: es: Sequence of edge indices as tuple. If not provided, all 
                   edges are updated.
        OUTPUT: None, the edge properties 'resistance' and 'specificResistance'
                are updated (or created).
        """
        G = self._G

        if esequence is None:
            es = G.es
        else:
            es = G.es(esequence)

        if self._resistanceLength:
            G.es['specificResistance'] = [1]*G.ecount()
        else:
            G.es['specificResistance'] = [128 * self._muPlasma / (np.pi * d**4)
                                        for d in G.es['diameter']]

        G.es['resistance'] = [l * sr for l, sr in zip(G.es['length'],
                                                G.es['specificResistance'])]
Beispiel #9
0
    def __init__(self,
                 G,
                 invivo=True,
                 assert_pBCs=True,
                 resetHtd=True,
                 **kwargs):
        """Initializes a LinearSystemPries instance.
        INPUT: G: Vascular graph in iGraph format.
               invivo: Boolean, whether the physiological blood characteristics 
                       are calculated using the invivo (=True) or invitro (=False)
                       equations
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
               resetHtd: (Optional, default=True.) Boolean whether or not to
                         reset the discharge hematocrit of the VascularGraph at
                         initialization. It may be useful to preserve the
                         original htd-distribution, if only a minor change from
                         the current state is to be expected (faster to obtain
                         the solution).
	       **kwargs:
		    httBC: tube hematocrit boundary condition at inflow (edge)
		    htdBC: discharge hematocrit boundary condition at inflow (vertex)
                    plasmaType: if it is not given, the default value is used. option two: --> francesco: plasma value of francescos simulations
                    species: what type of animal we are dealing with --> relevant for the rbc volume that is used, default is rat
		    
        OUTPUT: None
        """
        self._G = G
        self._invivo = invivo
        self._P = Physiology(G['defaultUnits'])
        self._eps = 1e-7  #finfo(float).eps * 1e4
        htt2htd = self._P.tube_to_discharge_hematocrit

        if kwargs.has_key('httBC'):
            for vi in G['av']:
                for ei in G.adjacent(vi):
                    G.es[ei]['httBC'] = kwargs['httBC']
                    htdBC = htt2htd(kwargs['httBC'], G.es[ei]['diameter'],
                                    invivo)
                G.vs[vi]['htdBC'] = htdBC
        if kwargs.has_key('htdBC'):
            for vi in G['av']:
                G.vs[vi]['htdBC'] = kwargs['htdBC']

        if kwargs.has_key('plasmaType'):
            self._plasmaType = kwargs['plasmaType']
        else:
            self._plasmaType = 'default'

        if kwargs.has_key('species'):
            self._species = kwargs['species']
        else:
            self._species = 'rat'

        # Discharge hematocrit boundary conditions:
        if not 'htdBC' in G.vs.attribute_names():
            for vi in G['av']:
                htdlist = []
                for ei in G.adjacent(vi):
                    if 'httBC' in G.es.attribute_names():
                        if G.es[ei]['httBC'] != None:
                            htdBC = htt2htd(G.es[ei]['httBC'],
                                            G.es[ei]['diameter'], invivo)
                            G.vs[vi]['htdBC'] = htdBC
                        else:
                            for ei in G.adjacent(vi):
                                htdlist.append(
                                    self._P.discharge_hematocrit(
                                        G.es[ei]['diameter'], 'a'))
                                G.vs[vi]['htdBC'] = np.mean(htdlist)
                    else:
                        for ei in G.adjacent(vi):
                            htdlist.append(
                                self._P.discharge_hematocrit(
                                    G.es[ei]['diameter'], 'a'))
                            G.vs[vi]['htdBC'] = np.mean(htdlist)

        #Convert 'pBC' ['mmHg'] to default Units
        for v in G.vs:
            if v['pBC'] != None:
                v['pBC'] = v['pBC'] * vgm.units.scaling_factor_du(
                    'mmHg', G['defaultUnits'])

        # Initial RBC flow, hematocrit, conductance, pressure and flow:
        G.vs['pressure'] = [0.0 for v in G.vs]
        G.es['rbcFlow'] = [0.0 for e in G.es]
        if resetHtd:
            G.es['htd'] = [0.0 for e in G.es]
        if not G.vs[0].attributes().has_key('pBC'):
            G.vs[0]['pBC'] = None
        if not G.vs[0].attributes().has_key('rBC'):
            G.vs[0]['rBC'] = None
        nVertices = G.vcount()
        self._b = zeros(nVertices)
        self._A = lil_matrix((nVertices, nVertices), dtype=float)
        self._update_conductance_and_LS(G, assert_pBCs)
        self._linear_analysis('iterative2')
        self._rheological_analysis(None, True, 1.0)
        self._linear_analysis('iterative2')
Beispiel #10
0
class LinearSystemPries(object):
    """Solves a VascularGraph for pressure and flow. The influence of red blood
    cells is taken explicitly into account.
    This is an iterative method consisting of two main parts: the linear
    analysis and the rheological analysis. In the linear analysis part (which
    gives this class its name), a linear system Ax = b is constructed from
    current vessel conduction values and solved to yield pressure and flow.
    In the rheological analysis part, a hematocrit redistribution is performed
    based on current flow values and the empirical relations found by Pries et
    al. (1990).
    Note that the method as a whole is non-linear.
    """
    def __init__(self,
                 G,
                 invivo=True,
                 assert_pBCs=True,
                 resetHtd=True,
                 **kwargs):
        """Initializes a LinearSystemPries instance.
        INPUT: G: Vascular graph in iGraph format.
               invivo: Boolean, whether the physiological blood characteristics 
                       are calculated using the invivo (=True) or invitro (=False)
                       equations
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
               resetHtd: (Optional, default=True.) Boolean whether or not to
                         reset the discharge hematocrit of the VascularGraph at
                         initialization. It may be useful to preserve the
                         original htd-distribution, if only a minor change from
                         the current state is to be expected (faster to obtain
                         the solution).
	       **kwargs:
		    httBC: tube hematocrit boundary condition at inflow (edge)
		    htdBC: discharge hematocrit boundary condition at inflow (vertex)
                    plasmaType: if it is not given, the default value is used. option two: --> francesco: plasma value of francescos simulations
                    species: what type of animal we are dealing with --> relevant for the rbc volume that is used, default is rat
		    
        OUTPUT: None
        """
        self._G = G
        self._invivo = invivo
        self._P = Physiology(G['defaultUnits'])
        self._eps = 1e-7  #finfo(float).eps * 1e4
        htt2htd = self._P.tube_to_discharge_hematocrit

        if kwargs.has_key('httBC'):
            for vi in G['av']:
                for ei in G.adjacent(vi):
                    G.es[ei]['httBC'] = kwargs['httBC']
                    htdBC = htt2htd(kwargs['httBC'], G.es[ei]['diameter'],
                                    invivo)
                G.vs[vi]['htdBC'] = htdBC
        if kwargs.has_key('htdBC'):
            for vi in G['av']:
                G.vs[vi]['htdBC'] = kwargs['htdBC']

        if kwargs.has_key('plasmaType'):
            self._plasmaType = kwargs['plasmaType']
        else:
            self._plasmaType = 'default'

        if kwargs.has_key('species'):
            self._species = kwargs['species']
        else:
            self._species = 'rat'

        # Discharge hematocrit boundary conditions:
        if not 'htdBC' in G.vs.attribute_names():
            for vi in G['av']:
                htdlist = []
                for ei in G.adjacent(vi):
                    if 'httBC' in G.es.attribute_names():
                        if G.es[ei]['httBC'] != None:
                            htdBC = htt2htd(G.es[ei]['httBC'],
                                            G.es[ei]['diameter'], invivo)
                            G.vs[vi]['htdBC'] = htdBC
                        else:
                            for ei in G.adjacent(vi):
                                htdlist.append(
                                    self._P.discharge_hematocrit(
                                        G.es[ei]['diameter'], 'a'))
                                G.vs[vi]['htdBC'] = np.mean(htdlist)
                    else:
                        for ei in G.adjacent(vi):
                            htdlist.append(
                                self._P.discharge_hematocrit(
                                    G.es[ei]['diameter'], 'a'))
                            G.vs[vi]['htdBC'] = np.mean(htdlist)

        #Convert 'pBC' ['mmHg'] to default Units
        for v in G.vs:
            if v['pBC'] != None:
                v['pBC'] = v['pBC'] * vgm.units.scaling_factor_du(
                    'mmHg', G['defaultUnits'])

        # Initial RBC flow, hematocrit, conductance, pressure and flow:
        G.vs['pressure'] = [0.0 for v in G.vs]
        G.es['rbcFlow'] = [0.0 for e in G.es]
        if resetHtd:
            G.es['htd'] = [0.0 for e in G.es]
        if not G.vs[0].attributes().has_key('pBC'):
            G.vs[0]['pBC'] = None
        if not G.vs[0].attributes().has_key('rBC'):
            G.vs[0]['rBC'] = None
        nVertices = G.vcount()
        self._b = zeros(nVertices)
        self._A = lil_matrix((nVertices, nVertices), dtype=float)
        self._update_conductance_and_LS(G, assert_pBCs)
        self._linear_analysis('iterative2')
        self._rheological_analysis(None, True, 1.0)
        self._linear_analysis('iterative2')

    #--------------------------------------------------------------------------

    def _update_conductance_and_LS(self, newGraph=None, assert_pBCs=True):
        """Constructs the linear system A x = b where the matrix A contains the
        conductance information of the vascular graph, the vector b specifies
        the boundary conditions and the vector x holds the pressures at the
        vertices (for which the system needs to be solved). x will have the
        same units of [pressure] as the pBC vertices.

        Note that in this approach, A and b contain a mixture of dimensions,
        i.e. A and b have dimensions of [1.0] and [pressure] in the pBC case,
        [conductance] and [conductance*pressure] otherwise, the latter being
        rBCs. This has the advantage that no re-indexing is required as the
        matrices contain all vertices.
        INPUT: newGraph: Vascular graph in iGraph format to replace the
                         previous self._G. (Optional, default=None.)
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
        OUTPUT: A: Matrix A of the linear system, holding the conductance
                   information.
                b: Vector b of the linear system, holding the boundary
                   conditions.
        """

        if newGraph is not None:
            self._G = newGraph

        G = self._G
        P = self._P
        invivo = self._invivo
        cond = P.conductance
        nublood = P.dynamic_blood_viscosity

        G.es['conductance'] = [
            cond(
                e['diameter'], e['length'],
                nublood(e['diameter'],
                        invivo,
                        discharge_ht=e['htd'],
                        plasmaType=self._plasmaType)) for e in G.es
        ]
        G.es['conductance'] = [
            max(min(c, 1e5), 1e-5) for c in G.es['conductance']
        ]

        A = self._A
        b = self._b

        if assert_pBCs:
            # Ensure that the problem is well posed in terms of BCs.
            # This takes care of unconnected nodes as well as connected
            # components of the graph that have not been assigned a minimum of
            # one pressure boundary condition:
            for component in G.components():
                if all(map(lambda x: x is None, G.vs(component)['pBC'])):
                    i = component[0]
                    G.vs[i]['pBC'] = 0.0

        for vertex in G.vs:
            i = vertex.index
            A.data[i] = []
            A.rows[i] = []
            b[i] = 0.0
            if vertex['pBC'] is not None:
                A[i, i] = 1.0
                b[i] = vertex['pBC']
            else:
                aDummy = 0
                k = 0
                neighbors = []
                for edge in G.adjacent(i, 'all'):
                    if G.is_loop(edge):
                        continue
                    j = G.neighbors(i)[k]
                    k += 1
                    conductance = G.es[edge]['conductance']
                    neighbor = G.vs[j]
                    # +=, -= account for multiedges
                    aDummy += conductance
                    if neighbor['pBC'] is not None:
                        b[i] = b[i] + neighbor['pBC'] * conductance
                    #elif neighbor['rBC'] is not None:
                    #   b[i] = b[i] + neighbor['rBC']
                    else:
                        if j not in neighbors:
                            A[i, j] = -conductance
                        else:
                            A[i, j] = A[i, j] - conductance
                    neighbors.append(j)
                    if vertex['rBC'] is not None:
                        b[i] += vertex['rBC']
                A[i, i] = aDummy

        self._A = A
        self._b = b
        self._G = G

    #--------------------------------------------------------------------------

    def _update_rbc_flow(self, limiter=0.5):
        """Traverses all vertices ordered by pressure from high to low.
        Distributes the red cell flow of the mother vessel to the daughter
        vessels according to an empirical relation.
        Note that plasma and RBC flow are conserved at bifurcations, however,
        discharge hematocrit is not a conservative quantity.
        INPUT: limiter: Limits change from one iteration level to the next, if
                        < 1.0, at limiter == 1.0 the change is unmodified.
        OUTPUT: None, edge properties 'rbcFlow' and 'htd' are modified
                in-place.
        """
        # This limit ensures that the hematocrit stays within the physically
        # possible bounds:
        htdLimit = 0.99

        # Short notation:
        G = self._G
        eps = self._eps
        pse = self._P.phase_separation_effect
        #pse = self._P.phase_separation_effect_step

        # Copy current htd:
        oldRbcFlow = copy.deepcopy(G.es['rbcFlow'])
        G.es['rbcFlow'] = [0.0 for e in G.es]
        G.es['htd'] = [0.0 for e in G.es]

        # Vertices sorted by pressure:
        pSortedVertices = sorted([(v['pressure'], v.index) for v in G.vs],
                                 reverse=True)

        # Loop through vertices, distributing discharge hematocrit:
        for vertexPressure, vertex in pSortedVertices:
            # Determine in- and outflow edges:
            outEdges = []
            inEdges = []
            for neighbor, edge in zip(G.neighbors(vertex, 'all'),
                                      G.adjacent(vertex, 'all')):
                if G.vs[neighbor]['pressure'] < vertexPressure - eps:
                    outEdges.append(edge)
                elif G.vs[neighbor]['pressure'] > vertexPressure + eps:
                    inEdges.append(edge)

            # The rbc flow is computed from htdBC and the red cells entering
            # from the mother vessels (if any exist). In case of multiple
            # mother vessels, each is distributed according to the empirical
            # relation. If there are more than two daughter edges, the
            # emperical relation is applied to all possible pairings and the
            # final fractional flow to each daughter is computed in a
            # hierarchical fashion (see below).
            trimmedOutEdges = copy.deepcopy(outEdges)
            for outEdge in outEdges:
                if G.es[outEdge]['flow'] <= eps:
                    trimmedOutEdges.remove(outEdge)
                elif not (G.vs[vertex]['htdBC'] is None):
                    G.es[outEdge]['rbcFlow'] = G.vs[vertex]['htdBC'] * \
                                               G.es[outEdge]['flow']
                    G.es[outEdge]['htd'] = G.vs[vertex]['htdBC']
                    trimmedOutEdges.remove(outEdge)

            # Only edges without hematocrit BC are included in the distribution
            # algorithm:
            outEdges = copy.deepcopy(trimmedOutEdges)
            NoOutEdges = len(outEdges)

            if len(outEdges) == 0:
                continue
            elif len(outEdges) == 1:
                outEdge = outEdges[0]
                rbcFlowIn = 0.0
                FlowIn = 0.0
                for inEdge in inEdges:
                    rbcFlowIn += G.es[inEdge]['rbcFlow']
                    FlowIn += G.es[inEdge]['flow']
                if len(inEdges) == 0:
                    G.es[outEdge]['htd'] = G.es[outEdge]['htdBC']
                    G.es[outEdge]['rbcFlow'] = G.es[outEdge]['flow'] * G.es[
                        outEdge]['htd']
                else:
                    G.es[outEdge]['rbcFlow'] = rbcFlowIn
                    G.es[outEdge]['htd'] = min(rbcFlowIn / FlowIn, htdLimit)
                if G.es[outEdge]['htd'] < 0:
                    print('ERROR 1 htd smaller than 0')
            else:
                rbcFlowIn = 0.0
                edgepairs = list(itertools.combinations(outEdges, 2))
                for inEdge in inEdges:
                    df = G.es[inEdge]['diameter']
                    htdIn = G.es[inEdge]['htd']
                    rbcFlowIn += G.es[inEdge]['rbcFlow']
                    outFractions = dict(zip(outEdges, [[] for e in outEdges]))
                    for edgepair in edgepairs:
                        oe0, oe1 = G.es[edgepair]
                        flowSum = sum(G.es[edgepair]['flow'])
                        if flowSum > 0.0:
                            relativeValue = oe0['flow'] / flowSum
                            #stdout.write("\r oe0/flowSum = %f        \n" % relativeValue)
                            #if oe0['flow']/flowSum > 0.49 and oe0['flow']/flowSum < 0.51:
                            #stdout.write("\r NOW        \n")
                            f0 = pse(oe0['flow'] / flowSum, oe0['diameter'],
                                     oe1['diameter'], df, htdIn)
                            #f0 = pse(oe0['flow'] / flowSum,
                            #         0.7, 0.7, 0.7, 0.64)

                            f1 = 1 - f0
                        else:
                            f0 = 0
                            f1 = 0
                        outFractions[oe0.index].append(f0)
                        outFractions[oe1.index].append(f1)
                    # Sort out-edges from highest to lowest out fraction and
                    # distribute RBC flow accordingly:
                    sortedOutEdges = sorted(zip(
                        map(sum, outFractions.values()), outFractions.keys()),
                                            reverse=True)
                    remainingFraction = 1.0
                    for i, soe in enumerate(sortedOutEdges[:-1]):
                        outEdge = soe[1]
                        outFractions[outEdge] = remainingFraction * \
                                               sorted(outFractions[outEdge])[i]
                        remainingFraction -= outFractions[outEdge]
                        remainingFraction = max(remainingFraction, 0.0)

                    outFractions[sortedOutEdges[-1][1]] = remainingFraction

                    #Outflow in second outEdge is calculated by the difference of inFlow and OutFlow first
                    #outEdge
                    count = 0
                    for outEdge in outEdges:
                        G.es[outEdge]['rbcFlow'] += G.es[inEdge]['rbcFlow'] * \
                                                    outFractions[outEdge]
                        #stdout.write("\r outEdge = %g        \n" %outEdge)
                        #stdout.write("\r G.es[outEdge]['rbcFlow'] = %f        \n" %G.es[outEdge]['rbcFlow'])

                        if count == 0:
                            G.es[outEdge]['rbcFlow'] = oldRbcFlow[outEdge] + \
                                (G.es[outEdge]['rbcFlow'] - oldRbcFlow[outEdge]) * limiter
                        elif count == 1:
                            G.es[outEdge]['rbcFlow'] = G.es[inEdge][
                                'rbcFlow'] - G.es[outEdges[0]]['rbcFlow']
                        #stdout.write("\r LIMITED: G.es[outEdge]['rbcFlow'] = %f        \n" %G.es[outEdge]['rbcFlow'])
                        count += 1

                # Limit change between iteration levels for numerical
                # stability. Note that this is only applied to the diverging
                # bifurcations:
                for outEdge in outEdges:
                    if G.es[outEdge]['flow'] > eps:
                        G.es[outEdge]['htd'] = min(G.es[outEdge]['rbcFlow'] / \
                                                G.es[outEdge]['flow'], htdLimit)

                        if G.es[outEdge]['htd'] < 0:
                            print('ERROR 2 htd smaller than 0')
                        #stdout.write("\r outEdge = %g        \n" %outEdge)
                        #stdout.write("\r outFractions[outEdge] = %f        \n" %outFractions[outEdge])
                        #stdout.write("\r G.es[outEdge]['flow'] = %f        \n" %G.es[outEdge]['flow'])
                        #stdout.write("\r G.es[outEdge]['htd'] = %f        \n" %G.es[outEdge]['htd'])

    #--------------------------------------------------------------------------

    def _linear_analysis(self, method, **kwargs):
        """Performs the linear analysis, in which the pressure and flow fields
        are computed.
        INPUT: method: This can be either 'direct' or 'iterative'
               **kwargs
               precision: The accuracy to which the ls is to be solved. If not
                          supplied, machine accuracy will be used. (This only
                          applies to the iterative solver)
        OUTPUT: The maximum, mean, and median pressure change. Moreover,
                pressure and flow are modified in-place.
        """

        G = self._G
        A = self._A.tocsr()
        if method == 'direct':
            linalg.use_solver(useUmfpack=True)
            x = linalg.spsolve(A, self._b)
        elif method == 'iterative':
            if kwargs.has_key('precision'):
                eps = kwargs['precision']
            else:
                eps = self._eps
            AA = smoothed_aggregation_solver(A, max_levels=10, max_coarse=500)
            x = abs(
                AA.solve(self._b,
                         x0=None,
                         tol=eps,
                         accel='cg',
                         cycle='V',
                         maxiter=150))
            # abs required, as (small) negative pressures may arise
        elif method == 'iterative2':
            # Set linear solver
            ml = rootnode_solver(A,
                                 smooth=('energy', {
                                     'degree': 2
                                 }),
                                 strength='evolution')
            M = ml.aspreconditioner(cycle='V')
            # Solve pressure system
            #x,info = gmres(A, self._b, tol=self._eps, maxiter=50, M=M, x0=self._x)
            #x,info = gmres(A, self._b, tol=self._eps/10000000000000, maxiter=50, M=M)
            x, info = gmres(A, self._b, tol=self._eps / 10000, maxiter=50, M=M)
            if info != 0:
                print('SOLVEERROR in Solving the Matrix')

        pdiff = map(abs, [(p - xx) / p if p > 0 else 0.0
                          for p, xx in zip(G.vs['pressure'], x)])
        maxPDiff = max(pdiff)
        meanPDiff = np.mean(pdiff)
        medianPDiff = np.median(pdiff)
        log.debug(np.nonzero(np.array(pdiff) == maxPDiff)[0])

        G.vs['pressure'] = x
        G.es['flow'] = [abs(G.vs[edge.source]['pressure'] -   \
                            G.vs[edge.target]['pressure']) *  \
                        edge['conductance'] for edge in G.es]

        self._maxPDiff = maxPDiff
        self._meanPDiff = meanPDiff
        self._medianPDiff = medianPDiff

        return maxPDiff, meanPDiff, medianPDiff

    #--------------------------------------------------------------------------

    def _rheological_analysis(self,
                              newGraph=None,
                              assert_pBCs=True,
                              limiter=0.5):
        """Performs the rheological analysis, in which the discharge hematocrit
        and apparent viscosity (and thus the new conductances of the vessels)
        are computed.
        INPUT: newGraph: Vascular graph in iGraph format to replace the
                         previous self._G. (Optional, default=None.)
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
               limiter: Limits change from one iteration level to the next, if
                        > 1.0, at limiter == 1.0 the change is unmodified.
        OUTPUT: None, edge properties 'htd' and 'conductance' are modified
                in-place.
        """
        self._update_rbc_flow(limiter)
        self._update_conductance_and_LS(newGraph, assert_pBCs)

    #--------------------------------------------------------------------------

    def solve(self,
              method,
              precision=None,
              maxIterations=1e4,
              limiter=0.5,
              **kwargs):
        """Solve for pressure, flow, hematocrit and conductance by iterating
        over linear and rheological analysis. Stop when either the desired
        accuracy has been achieved or the maximum number of iterations have
        been performed.
        INPUT: method: This can be either 'direct' or 'iterative'
               precision: Desired precision, measured in the maximum amount by
                          which the pressure may change from one iteration
                          level to the next. If the maximum change is below
                          threshold, the iteration is aborted.
               maxIterations: The maximum number of iterations to perform.
               limiter: Limits change from one iteration level to the next, if
                        > 1.0, at limiter == 1.0 the change is unmodified.
               **kwargs
               precisionLS: The accuracy to which the ls is to be solved. If
                            not supplied, machine accuracy will be used. (This
                            only applies to the iterative solver)
        OUTPUT: None, the vascular graph is modified in-place.
        """
        G = self._G
        P = self._P
        invivo = self._invivo
        if precision is None: precision = self._eps
        iterationCount = 0
        maxPDiff = 1e200
        filename = 'iter_' + str(iterationCount) + '.vtp'
        g_output.write_vtp(G, filename, False)
        filenames = [filename]

        convergenceHistory = []
        while iterationCount < maxIterations and maxPDiff > precision:
            stdout.write("\rITERATION %g \n" % iterationCount)
            self._rheological_analysis(None, False, limiter=0.5)
            maxPDiff, meanPDiff, medianPDiff = self._linear_analysis(
                method, **kwargs)
            #maxPDiff=self._maxPDiff
            #meanPDiff=self._meanPDiff
            #medianPDiff=self._medianPDiff
            log.info('Iteration %i of %i' %
                     (iterationCount + 1, maxIterations))
            log.info('maximum pressure change: %.2e' % maxPDiff)
            log.info('mean pressure change: %.2e' % meanPDiff)
            log.info('median pressure change: %.2e\n' % medianPDiff)
            convergenceHistory.append((maxPDiff, meanPDiff, medianPDiff))
            iterationCount += 1
            G.es['htt'] = [
                P.discharge_to_tube_hematocrit(e['htd'], e['diameter'], invivo)
                for e in G.es
            ]
            vrbc = P.rbc_volume(self._species)
            G.es['nMax'] = [
                np.pi * e['diameter']**2 / 4 * e['length'] / vrbc for e in G.es
            ]
            G.es['minDist'] = [e['length'] / e['nMax'] for e in G.es]
            G.es['nRBC'] = [
                e['htt'] * e['length'] / e['minDist'] for e in G.es
            ]
            filename = 'iter_' + str(iterationCount) + '.vtp'
            g_output.write_vtp(G, filename, False)
            filenames.append(filename)
        if iterationCount >= maxIterations:
            stdout.write("\rMaximum number of iterations reached\n")
        elif maxPDiff <= precision:
            stdout.write("\rPrecision limit is reached\n")
        self.integrity_check()
        G.es['htt'] = [
            P.discharge_to_tube_hematocrit(e['htd'], e['diameter'], invivo)
            for e in G.es
        ]
        vrbc = P.rbc_volume(self._species)
        G.es['nMax'] = [
            np.pi * e['diameter']**2 / 4 * e['length'] / vrbc for e in G.es
        ]
        G.es['minDist'] = [e['length'] / e['nMax'] for e in G.es]
        G.es['nRBC'] = [e['htt'] * e['length'] / e['minDist'] for e in G.es]
        G.es['v'] = [
            4 * e['flow'] *
            P.velocity_factor(e['diameter'], invivo, tube_ht=e['htt']) /
            (np.pi * e['diameter']**2) if e['htt'] > 0 else 4 * e['flow'] /
            (np.pi * e['diameter']**2) for e in G.es
        ]

        for v in G.vs:
            v['pressure'] = v['pressure'] / vgm.units.scaling_factor_du(
                'mmHg', G['defaultUnits'])
        filename = 'iter_final.vtp'
        g_output.write_vtp(G, filename, False)
        filenames.append(filename)
        g_output.write_pvd_time_series('sequence.pvd', filenames)
        vgm.write_pkl(G, 'G_final.pkl')

        stdout.flush()
        return convergenceHistory
        return G

    #--------------------------------------------------------------------------

    def integrity_check(self):
        """Asserts that mass conservation is honored by writing the sum of in-
        and outflow as well as the sum of the in- and outgoing discharge
        hematocrit to the graph vertices as 'flowSum' and 'htdSum'
        INPUT: None
        OUTPUT: None, flow and htd sums added as vertex properties in-place.
        """
        G = self._G
        eps = self._eps
        G.es['plasmaFlow'] = [(1 - e['htd']) * e['flow'] for e in G.es]
        for v in G.vs:
            vertex = v.index
            vertexPressure = v['pressure']
            outEdges = []
            inEdges = []
            for neighbor, edge in zip(G.neighbors(vertex, 'all'),
                                      G.adjacent(vertex, 'all')):
                if G.vs[neighbor]['pressure'] < vertexPressure:
                    outEdges.append(edge)
                elif G.vs[neighbor]['pressure'] >= vertexPressure:
                    inEdges.append(edge)
            v['flowSum'] = sum(G.es[outEdges]['flow']) - \
                           sum(G.es[inEdges]['flow'])
            v['rbcFlowSum'] = sum(G.es[outEdges]['rbcFlow']) - \
                              sum(G.es[inEdges]['rbcFlow'])
            v['plasmaFlowSum'] = sum(G.es[outEdges]['plasmaFlow']) - \
                              sum(G.es[inEdges]['plasmaFlow'])
        for e in G.es:
            e['errorHtd'] = abs(e['rbcFlow'] / e['flow'] - e['htd']) \
                            if e['flow'] > eps else 0.0
        log.info('Max error htd: %.1g' % max(G.es['errorHtd']))
Beispiel #11
0
    def __init__(self, G, invivo=True,assert_pBCs=True, resetHtd=True,**kwargs):
        """Initializes a LinearSystemPries instance.
        INPUT: G: Vascular graph in iGraph format.
               invivo: Boolean, whether the physiological blood characteristics 
                       are calculated using the invivo (=True) or invitro (=False)
                       equations
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
               resetHtd: (Optional, default=True.) Boolean whether or not to
                         reset the discharge hematocrit of the VascularGraph at
                         initialization. It may be useful to preserve the
                         original htd-distribution, if only a minor change from
                         the current state is to be expected (faster to obtain
                         the solution).
	       **kwargs:
		    httBC: tube hematocrit boundary condition at inflow (edge)
		    htdBC: discharge hematocrit boundary condition at inflow (vertex)
                    plasmaType: if it is not given, the default value is used. option two: --> francesco: plasma value of francescos simulations
                    species: what type of animal we are dealing with --> relevant for the rbc volume that is used, default is rat
		    
        OUTPUT: None
        """
        self._G = G
        self._invivo=invivo
        self._P = Physiology(G['defaultUnits'])
        self._eps = 1e-7 #finfo(float).eps * 1e4
	htt2htd=self._P.tube_to_discharge_hematocrit

        if kwargs.has_key('httBC'):
            for vi in G['av']:
                for ei in G.adjacent(vi):
                    G.es[ei]['httBC']=kwargs['httBC']
		    htdBC = htt2htd(kwargs['httBC'],G.es[ei]['diameter'],invivo)
	        G.vs[vi]['htdBC']=htdBC
	if kwargs.has_key('htdBC'):
	    for vi in G['av']:
		G.vs[vi]['htdBC']=kwargs['htdBC']	

	if kwargs.has_key('plasmaType'):
            self._plasmaType=kwargs['plasmaType']
        else:
            self._plasmaType='default'

	if kwargs.has_key('species'):
            self._species=kwargs['species']
        else:
            self._species='rat'

        # Discharge hematocrit boundary conditions:
        if not 'htdBC' in G.vs.attribute_names():
            for vi in G['av']:
                htdlist = []
                for ei in G.adjacent(vi):
                    if 'httBC' in G.es.attribute_names():
                        if G.es[ei]['httBC'] != None:
		            htdBC = htt2htd(G.es[ei]['httBC'],G.es[ei]['diameter'],invivo)
	                    G.vs[vi]['htdBC']=htdBC
                        else:
                            for ei in G.adjacent(vi):
                                htdlist.append(self._P.discharge_hematocrit(
                                                       G.es[ei]['diameter'], 'a'))
                                G.vs[vi]['htdBC'] = np.mean(htdlist)
                    else:
                        for ei in G.adjacent(vi):
                            htdlist.append(self._P.discharge_hematocrit(
                                                   G.es[ei]['diameter'], 'a'))
                            G.vs[vi]['htdBC'] = np.mean(htdlist)

        #Convert 'pBC' ['mmHg'] to default Units
        for v in G.vs:
            if v['pBC'] != None:
                v['pBC']=v['pBC']*vgm.units.scaling_factor_du('mmHg',G['defaultUnits'])

        # Initial RBC flow, hematocrit, conductance, pressure and flow:
        G.vs['pressure'] = [0.0 for v in G.vs]
        G.es['rbcFlow'] = [0.0 for e in G.es]
        if resetHtd:
            G.es['htd'] = [0.0 for e in G.es]
        if not G.vs[0].attributes().has_key('pBC'):
            G.vs[0]['pBC'] = None
        if not G.vs[0].attributes().has_key('rBC'):
            G.vs[0]['rBC'] = None
        nVertices = G.vcount()
        self._b = zeros(nVertices)
        self._A = lil_matrix((nVertices,nVertices),dtype=float)
        self._update_conductance_and_LS(G, assert_pBCs)
        self._linear_analysis('iterative2')
        self._rheological_analysis(None, True, 1.0)
        self._linear_analysis('iterative2')
Beispiel #12
0
class LinearSystemPries(object):
    """Solves a VascularGraph for pressure and flow. The influence of red blood
    cells is taken explicitly into account.
    This is an iterative method consisting of two main parts: the linear
    analysis and the rheological analysis. In the linear analysis part (which
    gives this class its name), a linear system Ax = b is constructed from
    current vessel conduction values and solved to yield pressure and flow.
    In the rheological analysis part, a hematocrit redistribution is performed
    based on current flow values and the empirical relations found by Pries et
    al. (1990).
    Note that the method as a whole is non-linear.
    """
    def __init__(self, G, invivo=True,assert_pBCs=True, resetHtd=True,**kwargs):
        """Initializes a LinearSystemPries instance.
        INPUT: G: Vascular graph in iGraph format.
               invivo: Boolean, whether the physiological blood characteristics 
                       are calculated using the invivo (=True) or invitro (=False)
                       equations
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
               resetHtd: (Optional, default=True.) Boolean whether or not to
                         reset the discharge hematocrit of the VascularGraph at
                         initialization. It may be useful to preserve the
                         original htd-distribution, if only a minor change from
                         the current state is to be expected (faster to obtain
                         the solution).
	       **kwargs:
		    httBC: tube hematocrit boundary condition at inflow (edge)
		    htdBC: discharge hematocrit boundary condition at inflow (vertex)
                    plasmaType: if it is not given, the default value is used. option two: --> francesco: plasma value of francescos simulations
                    species: what type of animal we are dealing with --> relevant for the rbc volume that is used, default is rat
		    
        OUTPUT: None
        """
        self._G = G
        self._invivo=invivo
        self._P = Physiology(G['defaultUnits'])
        self._eps = 1e-7 #finfo(float).eps * 1e4
	htt2htd=self._P.tube_to_discharge_hematocrit

        if kwargs.has_key('httBC'):
            for vi in G['av']:
                for ei in G.adjacent(vi):
                    G.es[ei]['httBC']=kwargs['httBC']
		    htdBC = htt2htd(kwargs['httBC'],G.es[ei]['diameter'],invivo)
	        G.vs[vi]['htdBC']=htdBC
	if kwargs.has_key('htdBC'):
	    for vi in G['av']:
		G.vs[vi]['htdBC']=kwargs['htdBC']	

	if kwargs.has_key('plasmaType'):
            self._plasmaType=kwargs['plasmaType']
        else:
            self._plasmaType='default'

	if kwargs.has_key('species'):
            self._species=kwargs['species']
        else:
            self._species='rat'

        # Discharge hematocrit boundary conditions:
        if not 'htdBC' in G.vs.attribute_names():
            for vi in G['av']:
                htdlist = []
                for ei in G.adjacent(vi):
                    if 'httBC' in G.es.attribute_names():
                        if G.es[ei]['httBC'] != None:
		            htdBC = htt2htd(G.es[ei]['httBC'],G.es[ei]['diameter'],invivo)
	                    G.vs[vi]['htdBC']=htdBC
                        else:
                            for ei in G.adjacent(vi):
                                htdlist.append(self._P.discharge_hematocrit(
                                                       G.es[ei]['diameter'], 'a'))
                                G.vs[vi]['htdBC'] = np.mean(htdlist)
                    else:
                        for ei in G.adjacent(vi):
                            htdlist.append(self._P.discharge_hematocrit(
                                                   G.es[ei]['diameter'], 'a'))
                            G.vs[vi]['htdBC'] = np.mean(htdlist)

        #Convert 'pBC' ['mmHg'] to default Units
        for v in G.vs:
            if v['pBC'] != None:
                v['pBC']=v['pBC']*vgm.units.scaling_factor_du('mmHg',G['defaultUnits'])

        # Initial RBC flow, hematocrit, conductance, pressure and flow:
        G.vs['pressure'] = [0.0 for v in G.vs]
        G.es['rbcFlow'] = [0.0 for e in G.es]
        if resetHtd:
            G.es['htd'] = [0.0 for e in G.es]
        if not G.vs[0].attributes().has_key('pBC'):
            G.vs[0]['pBC'] = None
        if not G.vs[0].attributes().has_key('rBC'):
            G.vs[0]['rBC'] = None
        nVertices = G.vcount()
        self._b = zeros(nVertices)
        self._A = lil_matrix((nVertices,nVertices),dtype=float)
        self._update_conductance_and_LS(G, assert_pBCs)
        self._linear_analysis('iterative2')
        self._rheological_analysis(None, True, 1.0)
        self._linear_analysis('iterative2')

    #--------------------------------------------------------------------------

    def _update_conductance_and_LS(self, newGraph=None, assert_pBCs=True):
        """Constructs the linear system A x = b where the matrix A contains the
        conductance information of the vascular graph, the vector b specifies
        the boundary conditions and the vector x holds the pressures at the
        vertices (for which the system needs to be solved). x will have the
        same units of [pressure] as the pBC vertices.

        Note that in this approach, A and b contain a mixture of dimensions,
        i.e. A and b have dimensions of [1.0] and [pressure] in the pBC case,
        [conductance] and [conductance*pressure] otherwise, the latter being
        rBCs. This has the advantage that no re-indexing is required as the
        matrices contain all vertices.
        INPUT: newGraph: Vascular graph in iGraph format to replace the
                         previous self._G. (Optional, default=None.)
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
        OUTPUT: A: Matrix A of the linear system, holding the conductance
                   information.
                b: Vector b of the linear system, holding the boundary
                   conditions.
        """

        if newGraph is not None:
            self._G = newGraph

        G = self._G
        P = self._P
        invivo=self._invivo
        cond = P.conductance
        nublood = P.dynamic_blood_viscosity

        G.es['conductance'] = [cond(e['diameter'], e['length'],
                               nublood(e['diameter'], invivo,discharge_ht=e['htd'],plasmaType=self._plasmaType))
                               for e in G.es]
        G.es['conductance'] = [max(min(c, 1e5), 1e-5)
                               for c in G.es['conductance']]


        A = self._A
        b = self._b

        if assert_pBCs:
            # Ensure that the problem is well posed in terms of BCs.
            # This takes care of unconnected nodes as well as connected
            # components of the graph that have not been assigned a minimum of
           # one pressure boundary condition:
            for component in G.components():
                if all(map(lambda x: x is None, G.vs(component)['pBC'])):
                    i = component[0]
                    G.vs[i]['pBC'] = 0.0

        for vertex in G.vs:
            i = vertex.index
            A.data[i] = []
            A.rows[i] = []
            b[i] = 0.0
            if vertex['pBC'] is not None:
                A[i,i] = 1.0
                b[i] = vertex['pBC']
            else:
                aDummy=0
                k=0
                neighbors=[]
                for edge in G.adjacent(i,'all'):
                    if G.is_loop(edge):
                        continue
                    j=G.neighbors(i)[k]
                    k += 1
                    conductance = G.es[edge]['conductance']
                    neighbor = G.vs[j]
                    # +=, -= account for multiedges
                    aDummy += conductance
                    if neighbor['pBC'] is not None:
                        b[i] = b[i] + neighbor['pBC'] * conductance
                    #elif neighbor['rBC'] is not None:
                     #   b[i] = b[i] + neighbor['rBC']
                    else:
                        if j not in neighbors:
                            A[i,j] = - conductance
                        else:
                            A[i,j] = A[i,j] - conductance
                    neighbors.append(j)
                    if vertex['rBC'] is not None:
                        b[i] += vertex['rBC']
                A[i,i]=aDummy


        self._A = A
        self._b = b
	self._G = G

    #--------------------------------------------------------------------------

    def _update_rbc_flow(self, limiter=0.5):
        """Traverses all vertices ordered by pressure from high to low.
        Distributes the red cell flow of the mother vessel to the daughter
        vessels according to an empirical relation.
        Note that plasma and RBC flow are conserved at bifurcations, however,
        discharge hematocrit is not a conservative quantity.
        INPUT: limiter: Limits change from one iteration level to the next, if
                        < 1.0, at limiter == 1.0 the change is unmodified.
        OUTPUT: None, edge properties 'rbcFlow' and 'htd' are modified
                in-place.
        """
        # This limit ensures that the hematocrit stays within the physically
        # possible bounds:
        htdLimit = 0.99

        # Short notation:
        G = self._G
        eps = self._eps
        pse = self._P.phase_separation_effect
        #pse = self._P.phase_separation_effect_step

        # Copy current htd:
	oldRbcFlow = copy.deepcopy(G.es['rbcFlow'])
        G.es['rbcFlow'] = [0.0 for e in G.es]
        G.es['htd'] = [0.0 for e in G.es]

        # Vertices sorted by pressure:
        pSortedVertices = sorted([(v['pressure'], v.index) for v in G.vs],
                                 reverse=True)

        # Loop through vertices, distributing discharge hematocrit:
        for vertexPressure, vertex in pSortedVertices:
            # Determine in- and outflow edges:
            outEdges = []
            inEdges = []
            for neighbor, edge in zip(G.neighbors(vertex, 'all'), G.adjacent(vertex, 'all')):
                if G.vs[neighbor]['pressure'] < vertexPressure - eps:
                    outEdges.append(edge)
                elif G.vs[neighbor]['pressure'] > vertexPressure + eps:
                    inEdges.append(edge)

            # The rbc flow is computed from htdBC and the red cells entering
            # from the mother vessels (if any exist). In case of multiple
            # mother vessels, each is distributed according to the empirical
            # relation. If there are more than two daughter edges, the
            # emperical relation is applied to all possible pairings and the
            # final fractional flow to each daughter is computed in a
            # hierarchical fashion (see below).
            trimmedOutEdges = copy.deepcopy(outEdges)
            for outEdge in outEdges:
                if G.es[outEdge]['flow'] <= eps:
                    trimmedOutEdges.remove(outEdge)
                elif not (G.vs[vertex]['htdBC'] is None):
                    G.es[outEdge]['rbcFlow'] = G.vs[vertex]['htdBC'] * \
                                               G.es[outEdge]['flow']
                    G.es[outEdge]['htd'] = G.vs[vertex]['htdBC']
                    trimmedOutEdges.remove(outEdge)

            # Only edges without hematocrit BC are included in the distribution
            # algorithm:
            outEdges = copy.deepcopy(trimmedOutEdges)
	    NoOutEdges=len(outEdges)

            if len(outEdges) == 0:
                continue
            elif len(outEdges) == 1:
                outEdge = outEdges[0]
                rbcFlowIn = 0.0
		FlowIn = 0.0
                for inEdge in inEdges:
                    rbcFlowIn += G.es[inEdge]['rbcFlow']
		    FlowIn += G.es[inEdge]['flow']
		if len(inEdges) == 0:
		    G.es[outEdge]['htd']=G.es[outEdge]['htdBC']
		    G.es[outEdge]['rbcFlow']=G.es[outEdge]['flow']*G.es[outEdge]['htd']
		else:
                    G.es[outEdge]['rbcFlow'] = rbcFlowIn
		    G.es[outEdge]['htd'] = min(rbcFlowIn/FlowIn,htdLimit)
                if G.es[outEdge]['htd'] < 0:
                    print('ERROR 1 htd smaller than 0')
            else:
                rbcFlowIn = 0.0
                edgepairs = list(itertools.combinations(outEdges, 2))
                for inEdge in inEdges:
                    df = G.es[inEdge]['diameter']
                    htdIn = G.es[inEdge]['htd']
                    rbcFlowIn += G.es[inEdge]['rbcFlow']
                    outFractions = dict(zip(outEdges, [[] for e in outEdges]))
                    for edgepair in edgepairs:
                        oe0, oe1 = G.es[edgepair]
                        flowSum = sum(G.es[edgepair]['flow'])
                        if flowSum > 0.0:
			    relativeValue=oe0['flow']/flowSum
			    #stdout.write("\r oe0/flowSum = %f        \n" % relativeValue)
			    #if oe0['flow']/flowSum > 0.49 and oe0['flow']/flowSum < 0.51:
		                #stdout.write("\r NOW        \n")
                            f0 = pse(oe0['flow'] / flowSum,
                                     oe0['diameter'], oe1['diameter'],
                                    df, htdIn)
                            #f0 = pse(oe0['flow'] / flowSum,
                            #         0.7, 0.7, 0.7, 0.64)

                            f1 = 1 - f0
                        else:
                            f0 = 0
                            f1 = 0
                        outFractions[oe0.index].append(f0)
                        outFractions[oe1.index].append(f1)
                    # Sort out-edges from highest to lowest out fraction and
                    # distribute RBC flow accordingly:
                    sortedOutEdges = sorted(zip(map(sum, outFractions.values()),
                                                outFractions.keys()),
                                            reverse=True)
                    remainingFraction = 1.0
                    for i, soe in enumerate(sortedOutEdges[:-1]):
                        outEdge = soe[1]
                        outFractions[outEdge] = remainingFraction * \
                                               sorted(outFractions[outEdge])[i]
                        remainingFraction -= outFractions[outEdge]
                        remainingFraction = max(remainingFraction, 0.0)

                    outFractions[sortedOutEdges[-1][1]] = remainingFraction

		    #Outflow in second outEdge is calculated by the difference of inFlow and OutFlow first
		    #outEdge
		    count = 0 
                    for outEdge in outEdges:
                        G.es[outEdge]['rbcFlow'] += G.es[inEdge]['rbcFlow'] * \
                                                    outFractions[outEdge]
                        #stdout.write("\r outEdge = %g        \n" %outEdge)
                        #stdout.write("\r G.es[outEdge]['rbcFlow'] = %f        \n" %G.es[outEdge]['rbcFlow'])

		        if count == 0:
                            G.es[outEdge]['rbcFlow'] = oldRbcFlow[outEdge] + \
                                (G.es[outEdge]['rbcFlow'] - oldRbcFlow[outEdge]) * limiter
		        elif count == 1:
		            G.es[outEdge]['rbcFlow']=G.es[inEdge]['rbcFlow']-G.es[outEdges[0]]['rbcFlow']
                        #stdout.write("\r LIMITED: G.es[outEdge]['rbcFlow'] = %f        \n" %G.es[outEdge]['rbcFlow'])
                        count += 1


                # Limit change between iteration levels for numerical
                # stability. Note that this is only applied to the diverging
                # bifurcations:
                for outEdge in outEdges:
                    if G.es[outEdge]['flow'] > eps:
                        G.es[outEdge]['htd'] = min(G.es[outEdge]['rbcFlow'] / \
                                                G.es[outEdge]['flow'], htdLimit)

                        if G.es[outEdge]['htd'] < 0:
                            print('ERROR 2 htd smaller than 0')
                        #stdout.write("\r outEdge = %g        \n" %outEdge)
                        #stdout.write("\r outFractions[outEdge] = %f        \n" %outFractions[outEdge])
                        #stdout.write("\r G.es[outEdge]['flow'] = %f        \n" %G.es[outEdge]['flow'])
                        #stdout.write("\r G.es[outEdge]['htd'] = %f        \n" %G.es[outEdge]['htd'])

    #--------------------------------------------------------------------------

    def _linear_analysis(self, method, **kwargs):
        """Performs the linear analysis, in which the pressure and flow fields
        are computed.
        INPUT: method: This can be either 'direct' or 'iterative'
               **kwargs
               precision: The accuracy to which the ls is to be solved. If not
                          supplied, machine accuracy will be used. (This only
                          applies to the iterative solver)
        OUTPUT: The maximum, mean, and median pressure change. Moreover,
                pressure and flow are modified in-place.
        """

        G = self._G
        A = self._A.tocsr()
        if method == 'direct':
            linalg.use_solver(useUmfpack=True)
            x = linalg.spsolve(A, self._b)
        elif method == 'iterative':
            if kwargs.has_key('precision'):
                eps = kwargs['precision']
            else:
                eps = self._eps
            AA = smoothed_aggregation_solver(A, max_levels=10, max_coarse=500)
            x = abs(AA.solve(self._b, x0=None, tol=eps, accel='cg', cycle='V', maxiter=150))
            # abs required, as (small) negative pressures may arise
        elif method == 'iterative2':
         # Set linear solver
             ml = rootnode_solver(A, smooth=('energy', {'degree':2}), strength='evolution' )
             M = ml.aspreconditioner(cycle='V')
             # Solve pressure system
             #x,info = gmres(A, self._b, tol=self._eps, maxiter=50, M=M, x0=self._x)
             #x,info = gmres(A, self._b, tol=self._eps/10000000000000, maxiter=50, M=M)
             x,info = gmres(A, self._b, tol=self._eps/10000, maxiter=50, M=M)
             if info != 0:
                 print('SOLVEERROR in Solving the Matrix')

        pdiff = map(abs, [(p - xx) / p if p > 0 else 0.0
                          for p, xx in zip(G.vs['pressure'], x)])
        maxPDiff = max(pdiff)
        meanPDiff = np.mean(pdiff)
        medianPDiff = np.median(pdiff)
        log.debug(np.nonzero(np.array(pdiff) == maxPDiff)[0])

        G.vs['pressure'] = x
        G.es['flow'] = [abs(G.vs[edge.source]['pressure'] -   \
                            G.vs[edge.target]['pressure']) *  \
                        edge['conductance'] for edge in G.es]

	self._maxPDiff=maxPDiff
        self._meanPDiff=meanPDiff
        self._medianPDiff=medianPDiff

        return maxPDiff, meanPDiff, medianPDiff

    #--------------------------------------------------------------------------

    def _rheological_analysis(self, newGraph=None, assert_pBCs=True,
                              limiter=0.5):
        """Performs the rheological analysis, in which the discharge hematocrit
        and apparent viscosity (and thus the new conductances of the vessels)
        are computed.
        INPUT: newGraph: Vascular graph in iGraph format to replace the
                         previous self._G. (Optional, default=None.)
               assert_pBCs: (Optional, default=True.) Boolean whether or not to
                            check components for correct pressure boundary
                            conditions.
               limiter: Limits change from one iteration level to the next, if
                        > 1.0, at limiter == 1.0 the change is unmodified.
        OUTPUT: None, edge properties 'htd' and 'conductance' are modified
                in-place.
        """
        self._update_rbc_flow(limiter)
        self._update_conductance_and_LS(newGraph, assert_pBCs)

    #--------------------------------------------------------------------------

    def solve(self, method, precision=None, maxIterations=1e4, limiter=0.5,
              **kwargs):
        """Solve for pressure, flow, hematocrit and conductance by iterating
        over linear and rheological analysis. Stop when either the desired
        accuracy has been achieved or the maximum number of iterations have
        been performed.
        INPUT: method: This can be either 'direct' or 'iterative'
               precision: Desired precision, measured in the maximum amount by
                          which the pressure may change from one iteration
                          level to the next. If the maximum change is below
                          threshold, the iteration is aborted.
               maxIterations: The maximum number of iterations to perform.
               limiter: Limits change from one iteration level to the next, if
                        > 1.0, at limiter == 1.0 the change is unmodified.
               **kwargs
               precisionLS: The accuracy to which the ls is to be solved. If
                            not supplied, machine accuracy will be used. (This
                            only applies to the iterative solver)
        OUTPUT: None, the vascular graph is modified in-place.
        """
        G = self._G
        P = self._P
	invivo=self._invivo
        if precision is None: precision = self._eps
        iterationCount = 0
        maxPDiff = 1e200
        filename = 'iter_'+str(iterationCount)+'.vtp'
        g_output.write_vtp(G, filename, False)
        filenames = [filename]

        convergenceHistory = []
        while iterationCount < maxIterations and maxPDiff > precision:
            stdout.write("\rITERATION %g \n" %iterationCount)
            self._rheological_analysis(None, False, limiter=0.5)
            maxPDiff, meanPDiff, medianPDiff = self._linear_analysis(method, **kwargs)
	    #maxPDiff=self._maxPDiff
	    #meanPDiff=self._meanPDiff
	    #medianPDiff=self._medianPDiff
            log.info('Iteration %i of %i' % (iterationCount+1, maxIterations))
            log.info('maximum pressure change: %.2e' % maxPDiff)
            log.info('mean pressure change: %.2e' % meanPDiff)
            log.info('median pressure change: %.2e\n' % medianPDiff)
            convergenceHistory.append((maxPDiff, meanPDiff, medianPDiff))
            iterationCount += 1
            G.es['htt'] = [P.discharge_to_tube_hematocrit(e['htd'], e['diameter'],invivo)
                           for e in G.es]
            vrbc = P.rbc_volume(self._species)
            G.es['nMax'] = [np.pi * e['diameter']**2 / 4 * e['length'] / vrbc
                            for e in G.es]
            G.es['minDist'] = [e['length'] / e['nMax'] for e in G.es]
            G.es['nRBC'] =[e['htt']*e['length']/e['minDist'] for e in G.es]
            filename = 'iter_'+str(iterationCount)+'.vtp'
            g_output.write_vtp(G, filename, False)
            filenames.append(filename)
        if iterationCount >= maxIterations:
            stdout.write("\rMaximum number of iterations reached\n")
        elif maxPDiff <= precision :
            stdout.write("\rPrecision limit is reached\n")
        self.integrity_check()
        G.es['htt'] = [P.discharge_to_tube_hematocrit(e['htd'], e['diameter'],invivo)
                       for e in G.es]
        vrbc = P.rbc_volume(self._species)
        G.es['nMax'] = [np.pi * e['diameter']**2 / 4 * e['length'] / vrbc
                        for e in G.es]
        G.es['minDist'] = [e['length'] / e['nMax'] for e in G.es]
	G.es['nRBC'] =[e['htt']*e['length']/e['minDist'] for e in G.es]
	G.es['v']=[4 * e['flow'] * P.velocity_factor(e['diameter'], invivo, tube_ht=e['htt']) /
                     (np.pi * e['diameter']**2) if e['htt'] > 0 else
                     4 * e['flow'] / (np.pi * e['diameter']**2)
                     for e in G.es]

        for v in G.vs:
            v['pressure']=v['pressure']/vgm.units.scaling_factor_du('mmHg',G['defaultUnits'])
        filename = 'iter_final.vtp'
        g_output.write_vtp(G, filename, False)
        filenames.append(filename)
        g_output.write_pvd_time_series('sequence.pvd', filenames)
        vgm.write_pkl(G, 'G_final.pkl')

	stdout.flush()
        return convergenceHistory
        return G

    #--------------------------------------------------------------------------

    def integrity_check(self):
        """Asserts that mass conservation is honored by writing the sum of in-
        and outflow as well as the sum of the in- and outgoing discharge
        hematocrit to the graph vertices as 'flowSum' and 'htdSum'
        INPUT: None
        OUTPUT: None, flow and htd sums added as vertex properties in-place.
        """
        G = self._G
        eps = self._eps
        G.es['plasmaFlow'] = [(1 - e['htd']) * e['flow'] for e in G.es]
        for v in G.vs:
            vertex = v.index
            vertexPressure = v['pressure']
            outEdges = []
            inEdges = []
            for neighbor, edge in zip(G.neighbors(vertex, 'all'),
                                      G.adjacent(vertex, 'all')):
                if G.vs[neighbor]['pressure'] < vertexPressure:
                    outEdges.append(edge)
                elif G.vs[neighbor]['pressure'] >= vertexPressure:
                    inEdges.append(edge)
            v['flowSum'] = sum(G.es[outEdges]['flow']) - \
                           sum(G.es[inEdges]['flow'])
            v['rbcFlowSum'] = sum(G.es[outEdges]['rbcFlow']) - \
                              sum(G.es[inEdges]['rbcFlow'])
            v['plasmaFlowSum'] = sum(G.es[outEdges]['plasmaFlow']) - \
                              sum(G.es[inEdges]['plasmaFlow'])
        for e in G.es:
            e['errorHtd'] = abs(e['rbcFlow'] / e['flow'] - e['htd']) \
                            if e['flow'] > eps else 0.0
        log.info('Max error htd: %.1g' % max(G.es['errorHtd']))
Beispiel #13
0
    def __init__(self,
                 G,
                 withRBC=0,
                 invivo=0,
                 dMin_empirical=3.5,
                 htdMax_empirical=0.6,
                 verbose=True,
                 **kwargs):
        """
        Computes the flow and pressure field of a vascular graph without RBC tracking.
        It can be chosen between pure plasma flow, constant hematocrit or a given htt/htd
        distribution.
        The pressure boundary conditions (pBC) should be given in mmHG and pressure will be output in mmHg

        INPUT: G: Vascular graph in iGraph format.(the pBC should be given in mmHg)
               invivo: boolean if the invivo or invitro empirical functions are used (default = 0)
               withRBC: = 0: no RBCs, pure plasma Flow (default)
                        0 < withRBC < 1 & 'htt' not in edgeAttributes: the given value is assigned as htt to all edges.
                        0 < withRBC < 1 & 'htt' in edgeAttributes: the given value is assigned as htt to all edges where htt = None.
                        NOTE: If htd is not in the edge attributes, Htd will be computed from htt and used to compute the resistance.
                        If htd is already in the edge attributes, it won't be recomputed but the current htd values will be used.
                dMin_empiricial: lower limit for the diameter that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 3.5).
                htdMax_empirical: upper limit for htd that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 0.6). Maximum has to be 1.
                verbose: Bool if WARNINGS and setup information is printed
        OUTPUT: None, the edge properties htt is assgined and the function update is executed (see description for more details)
        """
        self._G = G
        self._eps = np.finfo(float).eps
        self._P = Physiology(G['defaultUnits'])
        self._muPlasma = self._P.dynamic_plasma_viscosity()
        self._withRBC = withRBC
        self._invivo = invivo
        self._verbose = verbose
        self._dMin_empirical = dMin_empirical
        self._htdMax_empirical = htdMax_empirical

        if self._verbose:
            print(
                'INFO: The limits for the compuation of the effective viscosity are set to'
            )
            print('Minimum diameter %.2f' % self._dMin_empirical)
            print('Maximum discharge %.2f' % self._htdMax_empirical)

        if self._withRBC != 0:
            if self._withRBC < 1.:
                if 'htt' not in G.es.attribute_names():
                    G.es['htt'] = [self._withRBC] * G.ecount()
                else:
                    httNone = G.es(htt_eq=None).indices
                    if len(httNone) > 0:
                        G.es[httNone]['htt'] = [self._withRBC] * len(httNone)
                    else:
                        if self._verbose:
                            print('WARNING: htt is already an edge attribute. \n Existing values are not overwritten!'+\
                                    '\n If new values should be assigned htt has to be deleted beforehand!')
            else:
                print('ERROR: 0 < withRBC < 1')

        if 'rBC' not in G.vs.attribute_names():
            G.vs['rBC'] = [None] * G.vcount()

        if 'pBC' not in G.vs.attribute_names():
            G.vs['pBC'] = [None] * G.vcount()

        self.update()
Beispiel #14
0
class LinearSystem(object):
    def __init__(self,
                 G,
                 withRBC=0,
                 invivo=0,
                 dMin_empirical=3.5,
                 htdMax_empirical=0.6,
                 verbose=True,
                 **kwargs):
        """
        Computes the flow and pressure field of a vascular graph without RBC tracking.
        It can be chosen between pure plasma flow, constant hematocrit or a given htt/htd
        distribution.
        The pressure boundary conditions (pBC) should be given in mmHG and pressure will be output in mmHg

        INPUT: G: Vascular graph in iGraph format.(the pBC should be given in mmHg)
               invivo: boolean if the invivo or invitro empirical functions are used (default = 0)
               withRBC: = 0: no RBCs, pure plasma Flow (default)
                        0 < withRBC < 1 & 'htt' not in edgeAttributes: the given value is assigned as htt to all edges.
                        0 < withRBC < 1 & 'htt' in edgeAttributes: the given value is assigned as htt to all edges where htt = None.
                        NOTE: If htd is not in the edge attributes, Htd will be computed from htt and used to compute the resistance.
                        If htd is already in the edge attributes, it won't be recomputed but the current htd values will be used.
                dMin_empiricial: lower limit for the diameter that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 3.5).
                htdMax_empirical: upper limit for htd that is used to compute nurel (effective viscosity). The aim of the limit
                        is to avoid using the empirical equations in a range where no data exists (default = 0.6). Maximum has to be 1.
                verbose: Bool if WARNINGS and setup information is printed
        OUTPUT: None, the edge properties htt is assgined and the function update is executed (see description for more details)
        """
        self._G = G
        self._eps = np.finfo(float).eps
        self._P = Physiology(G['defaultUnits'])
        self._muPlasma = self._P.dynamic_plasma_viscosity()
        self._withRBC = withRBC
        self._invivo = invivo
        self._verbose = verbose
        self._dMin_empirical = dMin_empirical
        self._htdMax_empirical = htdMax_empirical

        if self._verbose:
            print(
                'INFO: The limits for the compuation of the effective viscosity are set to'
            )
            print('Minimum diameter %.2f' % self._dMin_empirical)
            print('Maximum discharge %.2f' % self._htdMax_empirical)

        if self._withRBC != 0:
            if self._withRBC < 1.:
                if 'htt' not in G.es.attribute_names():
                    G.es['htt'] = [self._withRBC] * G.ecount()
                else:
                    httNone = G.es(htt_eq=None).indices
                    if len(httNone) > 0:
                        G.es[httNone]['htt'] = [self._withRBC] * len(httNone)
                    else:
                        if self._verbose:
                            print('WARNING: htt is already an edge attribute. \n Existing values are not overwritten!'+\
                                    '\n If new values should be assigned htt has to be deleted beforehand!')
            else:
                print('ERROR: 0 < withRBC < 1')

        if 'rBC' not in G.vs.attribute_names():
            G.vs['rBC'] = [None] * G.vcount()

        if 'pBC' not in G.vs.attribute_names():
            G.vs['pBC'] = [None] * G.vcount()

        self.update()

    #--------------------------------------------------------------------------

    def update(self):
        """Constructs the linear system A x = b where the matrix A contains the 
        conductance information of the vascular graph, the vector b specifies 
        the boundary conditions and the vector x holds the pressures at the 
        vertices (for which the system needs to be solved). 
    
        OUTPUT: matrix A and vector b
        """
        htt2htd = self._P.tube_to_discharge_hematocrit
        nurel = self._P.relative_apparent_blood_viscosity
        G = self._G

        #Convert 'pBC' ['mmHG'] to default Units
        for v in G.vs(pBC_ne=None):
            v['pBC'] = v['pBC'] * vgm.units.scaling_factor_du(
                'mmHg', G['defaultUnits'])

        nVertices = G.vcount()
        b = np.zeros(nVertices)
        A = lil_matrix((nVertices, nVertices), dtype=float)

        # Compute nominal and specific resistance:
        self._update_nominal_and_specific_resistance()

        #if with RBCs compute effective resistance
        if self._withRBC:
            if 'htd' not in G.es.attribute_names():
                dischargeHt = [
                    min(htt2htd(htt, d, self._invivo), 1.0)
                    for htt, d in zip(G.es['htt'], G.es['diameter'])
                ]
                G.es['htd'] = dischargeHt
            else:
                dischargeHt = G.es['htd']
                if self._verbose:
                    print('WARNING: htd is already an edge attribute. \n Existing values are not overwritten!'+\
                        '\n If new values should be assigned htd has to be deleted beforehand!')
            G.es['effResistance'] =[ res * nurel(max(self._dMin_empirical,d),min(dHt,self._htdMax_empirical),self._invivo) \
                    for res,dHt,d in zip(G.es['resistance'], dischargeHt,G.es['diameter'])]
            G.es['conductance'] = 1 / np.array(G.es['effResistance'])
        else:
            G.es['conductance'] = [1 / e['resistance'] for e in G.es]

        self._conductance = G.es['conductance']

        for vertex in G.vs:
            i = vertex.index
            A.data[i] = []
            A.rows[i] = []
            b[i] = 0.0
            if vertex['pBC'] is not None:
                A[i, i] = 1.0
                b[i] = vertex['pBC']
            else:
                aDummy = 0
                k = 0
                neighbors = []
                for edge in G.incident(i, 'all'):
                    if G.is_loop(edge):
                        continue
                    j = G.neighbors(i)[k]
                    k += 1
                    conductance = G.es[edge]['conductance']
                    neighbor = G.vs[j]
                    # +=, -= account for multiedges
                    aDummy += conductance
                    if neighbor['pBC'] is not None:
                        b[i] = b[i] + neighbor['pBC'] * conductance
                    #elif neighbor['rBC'] is not None:
                    #   b[i] = b[i] + neighbor['rBC']
                    else:
                        if j not in neighbors:
                            A[i, j] = -conductance
                        else:
                            A[i, j] = A[i, j] - conductance
                    neighbors.append(j)
                    if vertex['rBC'] is not None:
                        b[i] += vertex['rBC']
                A[i, i] = aDummy

        self._A = A
        self._b = b

    #--------------------------------------------------------------------------

    def solve(self, method, **kwargs):
        """Solves the linear system A x = b for the vector of unknown pressures
        x, either using a direct solver (obsolete) or an iterative GMRES solver. From the
        pressures, the flow field is computed.
        INPUT: method: This can be either 'direct' or 'iterative2'
        OUTPUT: None - G is modified in place.
                G_final.pkl & G_final.vtp: are save as output
                sampledict.pkl: is saved as output
        """
        b = self._b
        G = self._G
        htt2htd = self._P.tube_to_discharge_hematocrit

        A = self._A.tocsr()
        if method == 'direct':
            linalg.use_solver(useUmfpack=True)
            x = linalg.spsolve(A, b)
        elif method == 'iterative2':
            ml = rootnode_solver(A,
                                 smooth=('energy', {
                                     'degree': 2
                                 }),
                                 strength='evolution')
            M = ml.aspreconditioner(cycle='V')
            # Solve pressure system
            #x,info = gmres(A, self._b, tol=self._eps, maxiter=1000, M=M)
            x, info = gmres(A, self._b, tol=10 * self._eps, M=M)
            if info != 0:
                print('ERROR in Solving the Matrix')
                print(info)

        G.vs['pressure'] = x
        self._x = x
        conductance = self._conductance
        G.es['flow'] = [abs(G.vs[edge.source]['pressure'] - G.vs[edge.target]['pressure']) *  \
                        conductance[i] for i, edge in enumerate(G.es)]

        #Default Units - mmHg for pressure
        for v in G.vs:
            v['pressure'] = v['pressure'] / vgm.units.scaling_factor_du(
                'mmHg', G['defaultUnits'])

        if self._withRBC:
            G.es['v'] = [
                e['htd'] / e['htt'] * e['flow'] /
                (0.25 * np.pi * e['diameter']**2) for e in G.es
            ]
        else:
            G.es['v'] = [
                e['flow'] / (0.25 * np.pi * e['diameter']**2) for e in G.es
            ]

        #Convert 'pBC' from default Units to mmHg
        pBCneNone = G.vs(pBC_ne=None).indices
        G.vs[pBCneNone]['pBC'] = np.array(G.vs[pBCneNone]['pBC']) * (
            1 / vgm.units.scaling_factor_du('mmHg', G['defaultUnits']))

        vgm.write_pkl(G, 'G_final.pkl')
        vgm.write_vtp(G, 'G_final.vtp', False)

        #Write Output
        sampledict = {}
        for eprop in ['flow', 'v']:
            if not eprop in sampledict.keys():
                sampledict[eprop] = []
            sampledict[eprop].append(G.es[eprop])
        for vprop in ['pressure']:
            if not vprop in sampledict.keys():
                sampledict[vprop] = []
            sampledict[vprop].append(G.vs[vprop])

            g_output.write_pkl(sampledict, 'sampledict.pkl')

    #--------------------------------------------------------------------------
    def _update_nominal_and_specific_resistance(self, esequence=None):
        """Updates the nominal and specific resistance of a given edge 
        sequence.
        INPUT: es: Sequence of edge indices as tuple. If not provided, all 
                   edges are updated.
        OUTPUT: None, the edge properties 'resistance' and 'specificResistance'
                are updated (or created).
        """
        G = self._G

        if esequence is None:
            es = G.es
        else:
            es = G.es(esequence)

        G.es['specificResistance'] = [
            128 * self._muPlasma / (np.pi * d**4) for d in G.es['diameter']
        ]

        G.es['resistance'] = [
            l * sr for l, sr in zip(G.es['length'], G.es['specificResistance'])
        ]