def split_edges_prior_to_central_dilation(G='G_standard.pkl', edges=[3], savename='G_standard_split.pkl',cf=0.8): """Splits specific edges in two end and one central parts, in order to prepare them for central dilation / constriction. WARNING: For newly added edges the source is always the smaller vertex index and the target the higher vertex index. This needs to be considered in post-processing (e.g. flow direction, position of RBCs) INPUT: G: VascularGraph edges: Edges to be split savename: Name of VascularGraph with split-edges to be saved to disk. cf: Center fraction. E.g. cf=2/3 would split the edge into 1/6, 4/6, 1/6. HOWEVER, the resulting splitting position depends on the number of available points and the spacing between the points. OUTPUT: None, VascularGraph written to disk. """ savename2=savename edges2=edges G = vgm.read_pkl(G) G.es(edges2)['split'] = [True for e in edges] G.add_points(1.) while len(G.es(split_ne=None)) > 0: eindex = G.es(split_ne=None).indices[0] dfactor = 1. vi, ei, dilated_ei = G.central_dilation(eindex, dfactor, cf) G.es[ei]['split'] = [None for e in ei] del G.es['split'] stdout.write("\rDilation Step \n") vgm.write_pkl(G, savename)
def split_and_steady_state_noRBCs(G='G_standard',edges=[3],fdilation=[1.0],cf=0.8): """Test effect of central dilation at a bifurcation of equal-flow daughter vessels. WARNING: For newly added edges the source is always the smaller vertex index and the target the higher vertex index. This needs to be considered in post-processing (e.g. flow direction, position of RBCs) INPUT: G: Input Graph as pkl-file (name without the ending .pkl) edges: list of edges which are dilated fdilation: list of factors by which the edge diameter is changed cf: Center fraction. E.g. cf=2/3 would split the edge into 1/6, 4/6, 1/6. HOWEVER, the resulting splitting position depends on the number of available points and the spacing between the points. OUTPUT: None, results written to disk. """ filename=G+'.pkl' savename='G_split.pkl' G = vgm.read_pkl(filename) G.add_points(1.) G.es['dfactor'] = [None for e in G.es] G.es[edges]['dfactor'] = fdilation while len(G.es(dfactor_ne=None)) > 0: eindex = G.es(dfactor_ne=None).indices[0] dfactor = G.es[eindex]['dfactor'] vi, ei, dilated_ei = G.central_dilation(eindex, dfactor, cf) G.es[ei]['dfactor'] = [None for e in ei] vgm.write_pkl(G, savename) LSd = vgm.LinearSystem(G) LSd.solve(method='direct')
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')
r.append(np.array([x[0], coord1, coord2])) elif case == 2: r.append(np.array([coord1, y[0], coord2])) elif case == 3: r.append(np.array([coord1, coord2, z[0]])) valuesGridList = [] for j in valuesGrid: for k in j: valuesGridList.append(k) planeG = vgm.VascularGraph(len(r)) planeG.vs['r'] = r planeG.vs[attribute] = valuesGridList vgm.write_vtp(planeG, filename + '.vtp', False) vgm.write_pkl(planeG, filename + '.pkl') return grid_d1, grid_d2, valuesGrid, case #------------------------------------------------------------------------------ def make_axis_labels(minVal, maxVal, factor=1, considerLimits=1): """ Creates the labels for an axis. based on the min and max value provided. INPUT: minVal: minimum Value of the data maxVal: maximum Value of the date factor: factor between the actual values and the strings provided (e.g factor = 0.001 --> value 1000 --> string '1.0') considerLimits: bool if the labels should be trimmed at the lower/upper bound OUTPUT: labels,labelsString: list with the location of the labels and the according strings limits: lower and upper limit for the axis """
def add_to_db(G, rootVertices, dThreshold, dbDirectory, esr=True, eMin=1, **kwargs): """Adds specific vascular trees of a given VascularGraph to a database. Performs advanced tree extraction checking for multiple roots and taking only those vertices 'closest' to the provided rootVertices. Several information properties are also added to the tree: distanceToBorder - the minimal distance of the tree's attachment vertex to the srXTM sample border. sampleName - the name of the srXTM sample from which the tree is extracted. avZOffset - the distance in z-direction between the attachment vertex and the vertex with minimal cortical depth. INPUT: G: VascularGraph. rootVertices: The root vertices of the vascular tree to be added. dThreshold: The diameter threshold below or equal to which edges are to be ignored (required for tree extraction). dbDirectory: The directory of the database as string. If this directory does not exist, it is created. esr: Enforce that all trees have a single root only? (Boolean.) eMin: The minimum number of edges that a tree needs to consist of, in order to be admitted to the database. **kwargs: vtpDir: The name of the directory in which to save a paraview vtp-file representation of the tree. amDir: The name of the directory in which to save an amira am-file representation of the tree. sampleName: The name of the srXTM sample from which the tree is taken. (Added to the tree's dictionary.) OUTPUT: None (pickled VascularGraphs written to disk.) """ # Create database directory, if nonexistant: if not os.path.exists(dbDirectory): os.mkdir(dbDirectory) # Determine number of vascular trees already present in the database: dbCount = len(glob.glob1(dbDirectory, '*.pkl')) # Loop through the rootVertices, extract the corresponding trees and add # them to the database: discardedTreeCount = 0 for i, vertex in enumerate(rootVertices): vascularTree = vascular_tree_subgraph(G, vertex, dThreshold, esr) # Reject tree, if it consists of less than eMin edges: if vascularTree.ecount() < eMin: discardedTreeCount += 1 continue # Add sample name to the tree's dictionary: if kwargs.has_key('sampleName'): vascularTree['sampleName'] = kwargs['sampleName'] # Add the distance attachmentVertex - sample border to the tree's dict: shape = vascularTree.shape() av = vascularTree.vs[vascularTree['attachmentVertex']]['r'][:2] if shape == 'cylinder': radius, center = G.radius_and_center() vascularTree['distanceToBorder'] = np.linalg.norm(av - center) elif shape == 'cuboid': minXY = np.min(G.vs['r'], 0)[:2] maxXY = np.max(G.vs['r'], 0)[:2] vascularTree['distanceToBorder'] = np.min([np.min(av - minXY), np.min(maxXY - av)]) # Align order zero element with z-axis: # strahler.modified_strahler_order(vascularTree, 0.99) # TreeEl = strahler.assign_elements(vascularTree) # av = vascularTree['attachmentVertex'] # avElms = [] # edgeIndices = [] # for elm in xrange(TreeEl.vcount()): # if av in TreeEl.vs[elm]['vertices']: # avElms.append((TreeEl.vs[elm]['order'], TreeEl.vs[elm]['edges'])) # edgeIndices = sorted(avElms, reverse=True)[0][1] # points = np.concatenate([[vascularTree.vs[e.source]['r'], # vascularTree.vs[e.target]['r']] # for e in vascularTree.es[edgeIndices]]) # just node points # z = points[:,2] # orderOfIndices = np.argsort(z) # increasing z values # direction1 = vgm.g_math.pca(points[orderOfIndices]) # if np.rad2deg(np.arccos(np.dot(direction1, np.array([0,0,1]))/np.linalg.norm(direction1))) > 90: # direction1 = -direction1 # direction2 = [0.,0.,1.] # vgm.rotate_using_two_vectors(vascularTree, direction1, direction2) # Add z_{attachmentVertex} - z_{minimum} to the tree's dict: vascularTree['avZOffset'] = vascularTree.vs[vascularTree['attachmentVertex']]['r'][2] - \ np.min(vascularTree.vs['r'], 0)[2] # Add vascularTree to database: vgm.write_pkl(vascularTree, os.path.join(dbDirectory, str(dbCount + i - discardedTreeCount) + '.pkl')) # Write vtp and / or am files if required: for kw, ke, fn in zip(['vtpDir', 'amDir'], ['.vtp', '.am'], [vgm.write_vtp, vgm.write_amira_mesh_ascii]): if kwargs.has_key(kw): if not os.path.exists(kwargs[kw]): os.mkdir(kwargs[kw]) fn(vascularTree, os.path.join(kwargs[kw], str(dbCount + i - discardedTreeCount) + ke))
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 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 split_and_evolve_to_steady_state(G='G_standard',edges=[3],fdilation=[1.0], time=4.0e3, ht0=0.4, Greference=None, plotPrms=[0., 4.0e3, 5e1, True], samplePrms=[0., 4.0e3, 2, True],cf=0.8,**kwargs): """Test effect of central dilation at a bifurcation of equal-flow daughter vessels. WARNING: For newly added edges the source is always the smaller vertex index and the target the higher vertex index. This needs to be considered in post-processing (e.g. flow direction, position of RBCs) INPUT: G: Input Graph as pkl-file (name without the ending .pkl) edges: list of edges which are dilated fdilation: list of factors by which the edge diameter is changed time: time periode which is evolved ht0: initial tube hematocrit value throughout the VascularGraph (this is ignored if Greference is defined) Greference: VascularGraph from which the initial RBC distribution is to be taken. (If set to None, RBCs will be distributed randomly, respecting the ht0 value supplied) The indices od edges and vertices as well as the direction must exactly the same. Otherwise there will be differences in the positions of RBCs plotPrms: plot parameters of RBC position plots (start, stop, step, overwrite) samplePrms: sample parameters (start, stop, step, overwrite) cf: Center fraction. E.g. cf=2/3 would split the edge into 1/6, 4/6, 1/6. HOWEVER, the resulting splitting position depends on the number of available points and the spacing between the points. **kwargs: httBC: tube hematocrit boundary condition at inflow SampleDetailed:Boolean whether every step should be samplede(True) or if the sampling is done by the given samplePrms(False) OUTPUT: None, results written to disk. """ SampleDetailed=False if 'SampleDetailed' in kwargs.keys(): SampleDetailed=kwargs['SampleDetailed'] filename=G+'.pkl' savename='G_split.pkl' G = vgm.read_pkl(filename) if kwargs.has_key('httBC'): for vi in G['av']: for ei in G.adjacent(vi): G.es[ei]['httBC']=kwargs['httBC'] G.add_points(1.) G.es['dfactor'] = [None for e in G.es] G.es[edges]['dfactor'] = fdilation while len(G.es(dfactor_ne=None)) > 0: eindex = G.es(dfactor_ne=None).indices[0] dfactor = G.es[eindex]['dfactor'] vi, ei, dilated_ei = G.central_dilation(eindex, dfactor, cf) G.es[ei]['dfactor'] = [None for e in ei] vgm.write_pkl(G, savename) if Greference is not None: Gr = vgm.read_pkl(Greference) G.es['rRBC'] = copy.deepcopy(Gr.es['rRBC']) LSd = vgm.LinearSystemHtd(G, dThreshold=10.0, ht0='current') else: LSd = vgm.LinearSystemHtd(G, dThreshold=10.0, ht0=ht0) if SampleDetailed: LSd.evolve(time=time, method='direct', plotPrms=plotPrms, samplePrms=samplePrms,SampleDetailed=True) else: LSd.evolve(time=time, method='direct', plotPrms=plotPrms, samplePrms=samplePrms)
def path_between_a_and_v_for_vertexList(G,v,direction='out'): """Finds all posible paths from a given vertex that have a specific length. The paths can be directed or not directed, depending on the type of graph. INPUT: G: Vascular graph in iGraph format. v: Vertex List from which the search is to be started. direction: Way to traverse a directed graph. Can be either 'out', 'in', 'out' = from a to v and 'in' = from v to a OUTPUT: paths: The possible paths from v. """ pathDict={} if direction == 'out': stopKind='v' else: stopKind='a' stopPaths=1e5 stopPaths2=1e6 indexHalf=np.floor(len(v)/9.) #for j in range(9): for j in range(1): pathDict={} print('ROUND') print(j) stdout.flush() if j == 9: indexStart=indexHalf*j indexEnd=len(v) else: indexStart=indexHalf*j indexEnd=indexHalf*(j+1) print(indexStart) print(indexEnd) #for i in v[int(indexStart):int(indexEnd)]: for i in v: print('') print(i) paths=[[i]] pathsEdges=[[]] boolContinue = 1 newPaths = [] newPathsEdges = [] countLoop=0 stdout.flush() while boolContinue: print(len(paths)) stdout.flush() countDone=0 countLoop += 1 if countLoop > stopPaths: print('Path length is getting too long') break if len(paths) > stopPaths2: print('Number of Path is getting too large') break for path,pathEdges in zip(paths,pathsEdges): if G.neighbors(path[-1],type=direction) == []: newPaths.append(path) newPathsEdges.append(pathEdges) countDone += 1 else: for neighbor,adjacent in zip(G.neighbors(path[-1],type=direction),G.adjacent(path[-1],type=direction)): if neighbor in path: print('WARNING already in path') if G.vs[neighbor]['kind'] == 'c': newPaths.append(path + [neighbor]) newPathsEdges.append(pathEdges + [adjacent]) elif G.vs[neighbor]['kind'] == stopKind: newPaths.append(path) newPathsEdges.append(pathEdges) countDone += 1 else: pass paths = newPaths pathsEdges = newPathsEdges newPaths = [] newPathsEdges = [] if countDone == len(paths): print('Add final vertex') for path in paths: if G.neighbors(path[-1],type=direction) == []: newPaths.append(path) newPathsEdges.append(pathEdges) else: for neighbor,adjacent in zip(G.neighbors(path[-1],type=direction),G.adjacent(path[-1],type=direction)): newPaths.append(path + [neighbor]) newPathsEdges.append(pathEdges + [adjacent]) paths = newPaths pathsEdges = newPathsEdges boolContinue = 0 if countLoop > stopPaths: dictDummy={} dictDummy['vertices']=[] dictDummy['edges']=[] dictDummy['finished']=0.0 elif len(paths) > stopPaths2: dictDummy={} dictDummy['vertices']=[] dictDummy['edges']=[] dictDummy['finished']=0.5 else: dictDummy={} dictDummy['vertices']=paths dictDummy['edges']=pathsEdges dictDummy['finished']=1.0 pathDict[i]=dictDummy vgm.write_pkl(pathDict,'pathDict'+str(j+1)+'.pkl')
def construct_cortical_network(G, gXtm, zLim=None, originXtm=None, insertXtm=False, invivo=True,BC=[60,10,0.2]): """Builds a VascularGraph that includes the pial vessels, penetrating arterioles and draining veins, as well as the capillary bed. INPUT: G: VascularGraph of the pial network. This should be created using the function construct_pial_network(). gXTM: VascularGraph of srXTM data. This is implanted at location originXtm of the cortical network. zLim: Limits of capillary grid in z-direction as tuple (zMin, zMax). If not supplied, it will be determined from the artificial network consisting of pial and penetrating vessels, as well as the SRXTM sample. originXtm: The origin (x,y) of the cylindrical srXTM in the cortical network, i.e. the location of its rotational axis. This will be the network's center of mass, if not provided. sf: Scaling Factor by which to multiply vertex positions and diameters (e.g. to convert units of pixels to microns). epperc: Percentage of offshoots that should be en-passent. These are chosen randomly. BC: list with pBC at inlet, pBC at outlet, inflow tube hematocrit OUTPUT: G: VascularGraph. Note that this function relies on input from control.py """ # Get settings from control file: basedir = control.basedir treeDB = control.treeDB eps = finfo(float).eps * 1e4 # Add offshoots and remove parts with z<0: print('Adding offshoots (%i arterial and %i venous)...' % (len(G['apv']), len(G['vpv']))) t0 = time.time() vcount = G.vcount() G.vs['apv']=[0]*G.vcount() G.vs['vpv']=[0]*G.vcount() for i in G['apv']: G.vs[i]['apv']=1 for i in G['vpv']: G.vs[i]['vpv']=1 x=[] y=[] for i in G.vs['r']: x.append(i[0]) y.append(i[1]) xMin=np.min(x) xMax=np.max(x) yMin=np.min(y) yMax=np.max(y) stdout.flush() print('CHECK DEGREE 1') print(max(G.degree())) #All deg1 vertices are in apv or vpv or they are in/outlets #add arterial penetratiing trees G.vs['degree'] = G.degree() print('Number of Components: Only pial') print(len(G.components())) print('Should be KEPT!') components=len(G.components()) #extend_from_db(G, G['apv'], os.path.join(treeDB, 'arteryDB')) extend_from_db(G, G['apv'], os.path.join(treeDB, 'arteryDBmouse')) print('Number of Components: Artery trees added') print(len(G.components())) print('CHECK DEGREE 2') print(max(G.degree())) if len(G.components()) > components: print('ERROR') print('More components than expected') #Assign kind G.vs[range(vcount, G.vcount())]['kind'] = ['a' for v in xrange(vcount, G.vcount())] G.vs['degree']=G.degree() vcount = G.vcount() #add venous penetrating trees #extend_from_db(G, G['vpv'], os.path.join(treeDB, 'veinDB')) extend_from_db(G, G['vpv'], os.path.join(treeDB, 'veinDBmouse')) print('Number of Components: Vein trees added') print(len(G.components())) print('CHECK DEGREE 3') print(max(G.degree())) if len(G.components()) > components: print('ERROR') print('More components than expected') #Assign kind G.vs[range(vcount, G.vcount())]['kind'] = ['v' for v in xrange(vcount, G.vcount())] G.vs['degree']=G.degree() #adding trees lead to some new dead ends at the z=0 level, in contrast to the penetrating vessels their kind #is either 'a' or 'v' but not 'pa' and not 'pv' #Remove capillaries (d < 7 mum) in pial vessels and penetrating vessels print(str(len(G.es(diameter_le=7)))+' Edges have a diameter which is smaller than 7mum and are therefor removed') G.delete_edges(G.es(diameter_le=7)) G.vs['degree']=G.degree() print('Number of Components: Capillaries deleted') print(len(G.components())) if len(G.components()) > components: print('ERROR') print('More components than expected') print(components) print(len(G.components())) for i in len(G.components()): print(len(G.components()[i])) #Remove vertices where z < 0 (would be abolve pial surface) G.vs['z'] = [r[2] for r in G.vs['r']] deleteList=G.vs(z_lt=0).indices print('Number of vertices where z<0') print(len(deleteList)) deleteList.sort(reverse=True) for i in range(len(deleteList)): G.delete_vertices(deleteList[i]) print('Number of components: z lt 0 deleted') print(len(G.components())) if len(G.components()) > components: print('ERROR') print('More components than expected') G.vs['degree']=G.degree() G.delete_vertices(G.vs(degree_eq=0)) print('Number of components: degree 0 deleted') print(len(G.components())) if len(G.components()) > components: print('ERROR') print('More components than expected') #Delete len=0 edges G.es['length'] = [np.linalg.norm(G.vs[e.source]['r'] - G.vs[e.target]['r']) for e in G.es] G.delete_edges(G.es(length_eq=0).indices) print('...done. Time taken: %.1f min' % ((time.time()-t0)/60.)) G.vs['degree']=G.degree() #Delete double Edges doubleVertices=[] print('CHECK for doule Edges (Edges connecting two similar vertices)') #TODO could be changed that only really similiar edges are deleted (see preprocessing SRCTM) for i in range(G.vcount()): if len(G.neighbors(i)) != len(np.unique(G.neighbors(i))): print('') print(G.neighbors(i)) print(np.unique(G.neighbors(i))) doubleVertices.append(i) print('Number of possible double Edges') print(len(doubleVertices)) while len(doubleVertices) > 0: for i in doubleVertices: neighbors=[] print('') print(G.neighbors(i)) print(G.adjacent(i)) adjacents=G.adjacent(i) for k,j in enumerate(G.neighbors(i)): print('') print(k) if j in neighbors: G.delete_edges(adjacents[k]) else: neighbors.append(j) doubleVertices=[] print('len double vertices') for i in range(G.vcount()): if len(G.neighbors(i)) != len(np.unique(G.neighbors(i))): doubleVertices.append(i) print(len(doubleVertices)) #Assign nkind to vertex for i in range(G.vcount()): if G.vs['kind'][i]=='pa': G.vs[i]['nkind']=0 elif G.vs['kind'][i]=='a': G.vs[i]['nkind']=2 elif G.vs['kind'][i]=='pv': G.vs[i]['nkind']=1 elif G.vs['kind'][i]=='v': G.vs[i]['nkind']=3 vgm.write_pkl(G, 'stage1.pkl') vgm.write_pkl(G, 'stage1.vtp') print('number of capillaries') print(len(G.vs(kind_eq='c'))) if not zLim is None: if max(G.vs['z']) > zLim[1]: print('Deleting all vessels below z=%.1f' % (zLim[1])) t0 = time.time() G.delete_vertices(G.vs(z_gt=zLim[1])) print('...done. Time taken: %.1f min' % ((time.time()-t0)/60.)) vgm.write_pkl(G, 'stage1b.pkl') stdout.flush() # Add capillary bed as honeycomb network: # Add volume and conductance before adding capillary grid print('Adding capillary grid...') #all vertices of the network containing the pial vessels and the numNCVertices = G.vcount() vcount=G.vcount() ecount=G.ecount() t0 = time.time() rMin = np.min(G.vs['r'], axis=0) rMax = np.max((np.max(G.vs['r'], axis=0), (np.max(gXtm.vs['r'], axis=0)-np.min(gXtm.vs['r'], axis=0))), axis=0) if not zLim is None: rMin[2] = zLim[0] rMax[2] = zLim[1] print(rMin) print(rMax) #honeycomb = capillary_bed.randomized_honeycomb(gXtm, (rMin[0], rMax[0]), (rMin[1], rMax[1]), (rMin[2], rMax[2])) honeycomb = capillary_bed.honeycomb((rMin[0], rMax[0]), (rMin[1], rMax[1]), (rMin[2], rMax[2]),60) print('CHECK DEGREE HONEYCOMB') print(max(honeycomb.degree())) print(honeycomb.es.attributes()) print('Edges with 0 length in honeycomb') print(len(honeycomb.es(length_eq=0))) honeycomb.vs['degree']=honeycomb.degree() print('Degree 1 vertices in honeycomb') print(len(honeycomb.vs(degree_eq=1))) #Honeycomb network is moved such that it finishes at the same level as the pial+penetrating-network rMinHC= np.min(honeycomb.vs['r'], axis=0) offset=np.array([0.,0.,rMinHC[2]*(-1)]) print('') print('Honeycomb Network is shifted in z-direction by') print(offset) print('such that zmin of Honeycomb Network = 0') vgm.shift(honeycomb,offset) print('number of capillaries') print(len(G.vs(kind_eq='c'))) #honeycomb network and pial+penetrating network are put together G.disjoint_union_attribute_preserving(honeycomb, 'omit', False) print('number of capillaries') print(len(G.vs(kind_eq='c'))) print(len(xrange(vcount, G.vcount()))) G.vs[range(vcount, G.vcount())]['kind'] = ['c' for v in xrange(vcount, G.vcount())] G.vs[range(vcount, G.vcount())]['nkind'] = [5 for v in xrange(vcount, G.vcount())] G.vs['capGrid']=np.zeros(G.vcount()) G.vs[range(vcount,G.vcount())]['capGrid'] = [1]*(G.vcount()-vcount) G.es['capGrid']=np.zeros(G.ecount()) G.es[range(ecount,G.ecount())]['capGrid'] = [1]*(G.ecount()-ecount) print('number of capillaries') print(len(G.vs(kind_eq='c'))) print('number of honeycomb vertices') print(honeycomb.vcount()) vcount = G.vcount() print(G.es.attributes()) print('Edges with length 0') print(len(G.es(length_eq=0))) #print('...done. Time taken: %.1f min' % ((time.time()-t0)/60.)) vgm.write_pkl(G, 'stage2.pkl') stdout.flush() # Connect offshoots to capillary grid to penetrating vessels: print('Connecting offshoots to capillary grid...') print('number of edges where lenght = 0') print(len(G.es(length_eq=0))) t0 = time.time() G.vs['degree'] = G.degree() G.vs['z'] = [r[2] for r in G.vs['r']] #vertices with only one adjacent edge #cv edges of penetrating trees with dead end --> to be connected to capillary bed cv = G.vs(z_ge=0.0, degree_eq=1,capGrid_eq=0).indices print('Number of pials and trees') print(numNCVertices) print('in cv vertices') print(max(cv)) #In and outlet of pial vessels should be preserved for i in G['vv']: if i in cv: cv.remove(i) else: print(G.vs[i]['r']) for i in G['av']: if i in cv: cv.remove(i) else: print(G.vs[i]['r']) print(len(G.es(length_eq=0))) #Capillary bed should not be attached to pial vessels for i in cv: if G.vs[i]['kind'] == 'pa': print('ERROR') if G.vs[i]['kind'] == 'pv': print('ERROR') Kdt = kdtree.KDTree(honeycomb.vs['r'], leafsize=10) print('Dead Ends to connect') print(len(cv)) stdout.flush() posConnections=[] G.vs['degree']=G.degree() print('Are there degree=0 vertices') print(len(G.vs(degree_eq=0))) #Compute possible Connections for vertices. Maximum degree = 4 G.vs['posConnections']=[3]*G.vcount() deg4=G.vs(degree_ge=4).indices G.vs[deg4]['posConnections']=[0]*len(deg4) deg3=G.vs(degree_eq=3).indices G.vs[deg3]['posConnections']=[1]*len(deg3) deg2=G.vs(degree_eq=2).indices G.vs[deg2]['posConnections']=[2]*len(deg2) print('posConnections assigned') stdout.flush() print('Possible number of connections') posConnections=np.sum(G.vs[numNCVertices:G.vcount()-1]['posConnections']) print(posConnections) stdout.flush() #Start connection process for v in cv: diameter = G.es[G.adjacent(v)[0]]['diameter'] newVertexFound=0 count= 1 while newVertexFound != 1: #start with 5 possible nearest neighbors nearestN=Kdt.query(G.vs[v]['r'],k=10*count) for i in range((count-1)*10,count*10): newVertex=int(nearestN[1][i]) if G.vs['posConnections'][newVertex+numNCVertices] == 0: print('No connection possible') else: G.vs[newVertex+numNCVertices]['posConnections'] = int(np.round(G.vs[newVertex+numNCVertices]['posConnections'] - 1)) newVertexFound = 1 break count += 1 print('CONNECTION FOUND') print(v) stdout.flush() nn = newVertex #Distance between analyzed endppoint and closest point in honeycomb network #Maximum length is set to 5 mum, TODO why is this done? why is not the acrual length used? #length = min(5., np.linalg.norm(G.vs[v]['r'] - G.vs[numNCVertices + nn]['r'])) length = np.linalg.norm(G.vs[v]['r'] - G.vs[numNCVertices + nn]['r']) #inewEdges.append((v, numNCVertices+nn)) #add edge between the points G.add_edges((v, numNCVertices + nn)) G.es[G.ecount()-1]['diameter'] = diameter G.es[G.ecount()-1]['length'] = length print('...done. Time taken: %.1f min' % ((time.time()-t0)/60.)) G.vs['degree'] = G.degree() print('CHECK DEGREE 4') print(max(G.degree())) # Remove edges with length 0 and with degree 1: # Deg1 vertices of artificial capillary bed, should be located at the borders of the artificial capillary bed print('Number of edges where length is equal to 0') print(len(G.es(length_eq=0))) G.delete_edges(G.es(length_eq=0).indices) print('Number of degree 1 vertices') G.vs['degree']=G.degree() print(len(G.vs(degree_eq=1))) deg1=G.vs(degree_eq=1).indices G.vs['av']=[0]*G.vcount() G.vs['vv']=[0]*G.vcount() for i in G['av']: deg1.remove(i) G.vs[i]['av']=1 for i in G['vv']: deg1.remove(i) G.vs[i]['vv']=1 G.delete_vertices(deg1) G['av']=G.vs(av_eq=1).indices G['vv']=G.vs(vv_eq=1).indices print('Number of degree 1 vertices') G.vs['degree']=G.degree() print(len(G.vs(degree_eq=1))) deg1=G.vs(degree_eq=1).indices for i in G['av']: deg1.remove(i) for i in G['vv']: deg1.remove(i) G.delete_vertices(deg1) print('Number of degree 1 vertices') G.vs['degree']=G.degree() G['av']=G.vs(av_eq=1).indices G['vv']=G.vs(vv_eq=1).indices print('Number of degree 1 vertices') deg1=G.vs(degree_eq=1).indices print(len(G.vs(degree_eq=1))) for i in G['av']: deg1.remove(i) for i in G['vv']: deg1.remove(i) G.delete_vertices(deg1) print('Number of degree 1 vertices') G.vs['degree']=G.degree() G['av']=G.vs(av_eq=1).indices G['vv']=G.vs(vv_eq=1).indices print('Number of degree 1 vertices') deg1=G.vs(degree_eq=1).indices print(len(G.vs(degree_eq=1))) vgm.write_vtp(G, 'stage3.vtp', False) vgm.write_pkl(G, 'stage3.pkl') stdout.flush() # Set conductance of all vessels: print('Adding conductance...') t0 = time.time() aindices = G.vs(kind='pa').indices aindices.extend(G.vs(kind='a').indices) vindices = G.vs(kind='pv').indices vindices.extend(G.vs(kind='v').indices) cindices = G.vs(kind='c').indices vgm.add_conductance(G, 'a', invivo,edges=G.get_vertex_edges(aindices)) vgm.add_conductance(G, 'v', invivo,edges=G.get_vertex_edges(vindices)) vgm.add_conductance(G, 'a', invivo,edges=G.get_vertex_edges(cindices)) print('...done. Time taken: %.1f min' % ((time.time()-t0)/60.)) stdout.flush() # Insert srXTM sample at originXtm: print('Embedding SRXTM...') t0 = time.time() if insertXtm: G = implant_srxtm.implant_srxtm(G, gXtm, originXtm) print('...done. Time taken: %.1f min' % ((time.time()-t0)/60.)) G['av']=G.vs(av_eq=1).indices G['vv']=G.vs(vv_eq=1).indices vgm.write_vtp(G, 'stage4.vtp', False) vgm.write_pkl(G, 'stage4.pkl') print('stage4 written') stdout.flush() # Delete obsolete graph properties: for vProperty in ['z', 'degree', 'nkind', 'epID', 'maxD']: if vProperty in G.vs.attribute_names(): del G.vs[vProperty] for eProperty in ['diameter_orig', 'diameter_change', 'cost','depth']: if eProperty in G.es.attribute_names(): del G.es[eProperty] for gPropery in ['attachmentVertex', 'sampleName', 'distanceToBorder', 'avZOffset']: if gPropery in G.attributes(): del G[gPropery] # Add a numerical version of vessel kind to facilitate paraview display: nkind = {'pa':0, 'pv':1, 'a':2, 'v':3, 'c':4, 'n':5} for v in G.vs: v['nkind'] = nkind[v['kind']] G.vs['degree']=G.degree() print('CHECK DEGREE 7') print(max(G.vs['degree'])) #1. If effective Diameter exists it is assigned as diameter if 'effDiam' in G.es.attribute_names(): diamEff=G.es(effDiam_ne=None).indices print('effective diameter available') print(len(diamEff)) for i in diamEff: G.es[i]['diameter']=G.es[i]['effDiam'] #2. check if there are degree 0 vertices G.vs['degree']=G.degree() deg0=G.vs(degree_eq=0).indices print('Degree 0 vertices?') print(len(deg0)) G.delete_vertices(deg0) G.vs['degree']=G.degree() #Reassign av and vv and apv and vpv G['av']=G.vs(av_eq=1).indices G['vv']=G.vs(vv_eq=1).indices G['apv']=G.vs(apv_eq=1).indices G['vpv']=G.vs(vpv_eq=1).indices #Eliminate small diameters in capGrid diam0capGrid=G.es(diameter_lt=1.0,capGrid_eq=1.).indices G.es[diam0capGrid]['diameter']=[1]*len(diam0capGrid) #Recheck smalles diameter diam0=G.es(diameter_lt=1.0).indices if len(diam0) > 0: print('ERROR no small diameters should exist') #Recheck for loops at deadEnd vertices print('check for loops') G.es['length2'] = [np.linalg.norm(G.vs[e.source]['r'] -G.vs[e.target]['r']) for e in G.es] len0=G.es(length2_eq=0).indices print('Len0 Edges are deleted') print(len(len0)) G['infoDeadEndLoops']=len(len0) for i in len0: e=G.es[i] if e.tuple[0] != e.tuple[1]: print('WARNING') G.delete_edges(len0) G.vs['degree']=G.degree() del(G.es['length2']) #Delete new deg1s print('Deleting the loops can lead to more degree 1 vertices') G.vs['degree']=G.degree() deg1=G.vs(degree_eq=1).indices print(len(deg1)) while len(deg1) > len(G['av'])+len(G['vv']): print('') print(len(deg1)) av=G.vs(av_eq=1).indices vv=G.vs(vv_eq=1).indices for i in av: if i in deg1: deg1.remove(i) print(len(deg1)) for i in vv: if i in deg1: deg1.remove(i) print(len(deg1)) G.delete_vertices(deg1) G.vs['degree']=G.degree() deg1=G.vs(degree_eq=1).indices stdout.flush() print('All newly created Dead Ends have ben elimanted') G.vs['degree']=G.degree() print(len(G.vs(degree_eq=1))) #Recheck the degrees of the graph G.vs['degree']=G.degree() deg1=G.vs(degree_eq=1).indices print('Degree 1 vertices') print(len(deg1)) for i in deg1: if i not in G['av'] and i not in G['vv']: print('ERROR deg1 vertex not in av and neither in vv') #Recheck max and min degree print('min degree') print(min(G.degree())) if min(G.degree()) < 1: print('ERROR in min degree') if np.max(G.vs['degree']) > 4: print('ERROR in Maximum degree') print(np.max(G.vs['degree'])) #Reassign av and vv and apv and vpv G['av']=G.vs(av_eq=1).indices G['vv']=G.vs(vv_eq=1).indices G['apv']=G.vs(apv_eq=1).indices G['vpv']=G.vs(vpv_eq=1).indices # 6. Diameter Srxtm vessels is adjusted such, that at least one RBC fits in # every vessel if 'isSrxtm' in G.es.attribute_names(): P=vgm.Physiology() vrbc=P.rbc_volume('mouse') G.es['minDist'] = [vrbc / (np.pi * e['diameter']**2 / 4) for e in G.es] maxMinDist=max(G.es['minDist']) countDiamChanged=0 diamNew=[] edge=[] probEdges=G.es(length_lt=1*maxMinDist,isSrxtm_eq=1).indices count=0 for i in probEdges: if 1*G.es['minDist'][i] > G.es['length'][i]: G.es[i]['diameter']=np.ceil(100*np.sqrt(4*2*vrbc/(np.pi*G.es['length'][i])))/100. countDiamChanged += 1 diamNew.append(np.ceil(100*np.sqrt(4*2*vrbc/(np.pi*G.es['length'][i])))/100.) edge.append(i) count += 1 print('vessel volume has been increased in') print(countDiamChanged) G.es['minDist'] = [vrbc / (np.pi * e['diameter']**2 / 4) for e in G.es] stdout.flush() # 7. Length is added to edges which do not have a length yet noLength=G.es(length_eq=None).indices print('Edges with no length') print(len(noLength)) for i in noLength: e=G.es[i] G.es[i]['length']=np.linalg.norm(G.vs[e.source]['r']-G.vs[e.target]['r']) noLength=G.es(length_eq=None).indices print('Edges with no length') print(len(noLength)) # 3. Assign pressure BCs print('Assign pressure BCs') for i in G['av']: G.vs[i]['pBC']=BC[0] print(G.vs[i]['pBC']) for i in G['vv']: G.vs[i]['pBC']=BC[1] print(G.vs[i]['pBC']) print('Pressure BCs assigned') # 4. Assign tube hematocrit boundary conditions print('assign inlet tube hematocrits') for i in G['av']: G.es[G.adjacent(i)[0]]['httBC']=BC[2] #Recheck BCs (pBC,rBC, httBC) for i in G['av']: print('') print(G.vs['pBC'][i]) print(G.vs['rBC'][i]) print(G.es[G.adjacent(i)]['httBC']) if G.vs['pBC'][i] == None and G.vs['rBC'][i] == None: print('ERROR in av boundary condition') if G.es[G.adjacent(i)]['httBC'] == None: print('ERROR in av boundary condition: httBC') for i in G['vv']: print('') print(G.vs['pBC'][i]) print(G.vs['rBC'][i]) if G.vs['pBC'][i] == None and G.vs['rBC'][i] == None: print('ERROR in vv boundary condition') stdout.flush() vgm.write_pkl(G, 'stage5.pkl') stdout.flush() return G
def construct_pial_network(sf=5, epperc=15, epmode='distributed'): """Builds a VascularGraph representing the pial network. INPUT: sf: Scaling Factor by which to multiply vertex positions and diameters (e.g. to convert units of pixels to microns). epperc: Percentage of offshoots that should be en-passent. These are either chosen randomly or such, that the penetrating vessels are distributed as homogeneously as possible. epmode: Mode by which en-passent vessels are chosen. This can be either 'random' or 'distributed' OUTPUT: G: VascularGraph. Note that this function is specifically tuned to the csv files and would need to change if the csv files change. Moreover, it relies on input from control.py """ # Get settings from control file: basedir = control.basedir pialDB = control.pialDB av = control.av vv = control.vv # Read csv files and scale from mm to micron: G = vgm.read_csv(os.path.join(pialDB, 'vertices.csv'), os.path.join(pialDB, 'edges.csv')) del G.vs['nkind'] G.vs['r'] = [r * sf for r in G.vs['r']] G.es['diameter'] = [d * sf for d in G.es['diameter']] G.es['length'] = [np.linalg.norm(G.vs[e.source]['r'] - G.vs[e.target]['r']) for e in G.es] G.vs['isAv'] = [0] * G.vcount() G.vs['isVv'] = [0] * G.vcount() # add 'av' and 'vv' from control dict G.vs(av)['isAv'] = [1] * len(av) G.vs(vv)['isVv'] = [1] * len(vv) G.delete_selfloops() G.add_points(100) G.delete_order_two_vertices() minnp = 3 if epmode == 'distributed': # Find arterial and venous components: #Find clusters = group of connected nodes co = G.components(mode='weak') #all vertices belonging to arterlial network and venous network, respectively aComponent = [] vComponent = [] for c in co: #everything which is not in av => vComponent if any(map(lambda x: x in c, av)): aComponent.extend(c) else: vComponent.extend(c) G.vs['degree'] = G.degree() G.es['nPoints'] = [len(x) for x in G.es['points']] G.es['midpoint'] = [np.mean(G.vs[e.tuple]['r'], axis=0) for e in G.es] epVertices = [] for component, avvv in zip((aComponent, vComponent), (av, vv)): #All edges belong to the component edges = G.get_vertex_edges(G.vs(component).indices, 'all', True) #edges where nPoints >= minnp edges = G.es(edges, nPoints_ge=minnp).indices #vertices of the component with only one adjacent edge dovertices = G.vs(component, degree_eq=1).indices # Compute number of en-passent offshoots: nep = int(epperc / (100 - epperc) * (len(dovertices) - len(avvv))) rlist = G.vs(dovertices)['r'] epEdges = [] for i in xrange(nep): Kdt = kdtree.KDTree(rlist, leafsize=10) distances = [(Kdt.query(G.es[e]['midpoint'])[0], e) for e in edges] #edge which is farest away epEdge = sorted(distances, reverse=True)[0][1] edges.remove(epEdge) epEdges.append(epEdge) rlist.append(G.es[epEdge]['midpoint']) newVertices = range(G.vcount(), G.vcount()+len(epEdges)) component.extend(newVertices) epVertices.extend(newVertices) for edge in epEdges: G.split_edge(edge, int(G.es[edge]['nPoints']/2.), False) G.delete_edges(epEdges) #Prevent new vertices from belonging to in and outlet vertices G.vs(newVertices)['isVv']=[0]*len(newVertices) G.vs(newVertices)['isAv']=[0]*len(newVertices) del G.es['midpoint'] elif epmode == 'random': # Compute number of en-passent offshoots: nep = epperc / (1 - epperc) * \ (len(np.nonzero(np.array(G.degree()) == 1)[0]) - len(av) - len(vv)) G.es['nPoints'] = [len(x) for x in G.es['points']] epEdges = np.random.permutation(G.es(nPoints_gt=minnp).indices)[:nep].tolist() epVertices = range(G.vcount(), G.vcount()+len(epEdges)) for edge in epEdges: G.split_edge(edge, int(G.es[edge]['nPoints']/2.), False) G.delete_edges(epEdges) # Find arterial and venous components: co = G.components(mode='weak') aComponent = [] vComponent = [] for c in co: if any(map(lambda x: x in c, av)): aComponent.extend(c) else: vComponent.extend(c) G['av'] = G.vs(isAv_eq=1).indices G['vv'] = G.vs(isVv_eq=1).indices del G.vs['isAv'] del G.vs['isVv'] del G.es['nPoints'] #all penetrating vessels pv = G.vs(_degree_eq=1).indices #if in or outflow -> remove from penetrating vessels for prop in ['av', 'vv']: for x in G[prop]: try: pv.remove(x) except: pass #add en passent penetrating vessels pv.extend(epVertices) G.vs['degree']=G.degree() remove=[] for i in pv: if G.vs['degree'][i] > 2: remove.append(i) print('Need to be removed') print(remove) for i in remove: pv.remove(i) #List of penetratiting arteries (apv) and venules (vpv) G['apv'] = [v for v in pv if v in aComponent] G['vpv'] = [v for v in pv if v not in aComponent] G.vs['degree'] = G.degree() #vertex characteristic pial arterty or pial venule G.vs[aComponent]['kind'] = ['pa' for v in aComponent] G.vs[vComponent]['kind'] = ['pv' for v in vComponent] G.vs['nkind'] = [0] * G.vcount() #0 = pial artery, 1 = pial venule, 2 = en passent penetrating G.vs[aComponent]['nkind'] = [0 for v in aComponent] G.vs[vComponent]['nkind'] = [1 for v in vComponent] G.vs[epVertices]['nkind'] = [2 for v in epVertices] # Set in- and outflow pressure boundary conditions. # Arterial pressure 60 mmHg, venous pressure 10 mmHg, according to a # Hypertesion paper by Harper and Bohlen 1984. G.vs[G['av'][0]]['pBC'] = 60 * vgm.scaling_factor_du('mmHg', G['defaultUnits']) for v in G['vv']: G.vs[v]['pBC'] = 10 * vgm.scaling_factor_du('mmHg', G['defaultUnits']) vgm.write_pkl(G,'pial.pkl') vgm.write_vtp(G,'pial.vtp',False) return G
def planePlots_paraview(G,edges,intersectionCoords,attribute,filename,interpMethod='linear',gridpoints=100): """ Interpolates values on a grid based on the intersection of edges with a plane. It outputs a .vtp and a .pkl file to be read into paraview. It returns the grid and the interpolated values at those locations (those can be processed to produce a contour plot in matplotlib) INPUT: G: main Graph edges: edges which intersect with the plane of interst intersectionCoords: coordinates intersection points attribute: that should be plotted filename: filename (including path) of the .vtp and .pkl file which will be saved (without file type extension) interpMethod: the interpolation method of scipy.interpolate.griddata common choices: 'nearest','linear' (default),'cubic' gridpoints: number of gridpoints (default = 100) OUTPUT: .pkl and .vtp file is written grid_d1,grid_d2: grid values valuesGrid: interpolated attribute values for the grid case: integer to define if the normal vector is in x- (case=1), y- (case=2) or in z-direction (case=3) """ values=G.es[edges][attribute] x=[] y=[] z=[] for coord in coords: x.append(coord[0]) y.append(coord[1]) z.append(coord[2]) if len(np.unique(x)) < len(np.unique(y)) and len(np.unique(x)) < len(np.unique(z)): case=1 d1Min=np.min(y) d1Max=np.max(y) d2Min=np.min(z) d2Max=np.max(z) d1=y d2=z elif len(np.unique(y)) < len(np.unique(x)) and len(np.unique(y)) < len(np.unique(z)): case=2 d1Min=np.min(x) d1Max=np.max(x) d2Min=np.min(z) d2Max=np.max(z) d1=x d2=z elif len(np.unique(z)) < len(np.unique(y)) and len(np.unique(z)) < len(np.unique(x)): case=3 d1Min=np.min(x) d1Max=np.max(x) d2Min=np.min(y) d2Max=np.max(y) d1=x d2=y grid_d1,grid_d2=np.mgrid[d1Min:d1Max:(d1Max-d1Min)/100,d2Min:d2Max:(d2Max-d2Min)/100] valuesGrid=griddata(zip(d1,d2),values,(grid_d1,grid_d2),method=interpMethod) r=[] for coord1 in np.arange(d1Min,d1Max,(d1Max-d1Min)/100): for coord2 in np.arange(d2Min,d2Max,(d2Max-d2Min)/100): if case == 1: r.append(np.array([x[0],coord1,coord2])) elif case == 2: r.append(np.array([coord1,y[0],coord2])) elif case == 3: r.append(np.array([coord1,coord2,z[0]])) valuesGridList=[] for j in valuesGrid: for k in j: valuesGridList.append(k) planeG=vgm.VascularGraph(len(r)) planeG.vs['r']=r planeG.vs[attribute]=valuesGridList vgm.write_vtp(planeG,filename+'.vtp',False) vgm.write_pkl(planeG,filename+'.pkl') return grid_d1,grid_d2,valuesGrid,case
def path_between_a_and_v_for_vertexList_2(G,v,direction='out'): """Finds all posible paths from a given vertex that have a specific length. The paths can be directed or not directed, depending on the type of graph. INPUT: G: Vascular graph in iGraph format. v: Vertex List from which the search is to be started. direction: Way to traverse a directed graph. Can be either 'out', 'in', 'out' = from a to v and 'in' = from v to a OUTPUT: paths: The possible paths from v. """ pathDict={} if direction == 'out': stopKind='v' else: stopKind='a' stopPaths=1e7 stopPaths2=1e8 indexHalf=np.floor(len(v)/9.) timeProbs=[] lengthsProbs=[] for j in range(9): pathDict={} print('ROUND') print(j) stdout.flush() if j == 8: indexStart=indexHalf*j indexEnd=len(v) else: indexStart=indexHalf*j indexEnd=indexHalf*(j+1) for i in v[indexStart:indexEnd]: paths={} pathsEdges={} reconnection={} reconnected={} stopReached={} paths[0]=[i] pathsEdges[0]=[] reconnected[0]=0 reconnection[0]=[] stopReached[0]=0 boolContinue = 1 countLoop=0 countReconnected = 0 stdout.flush() allVerts=[] boolInfo1=1 boolInfo2=1 boolInfo3=1 boolInfo4=1 tstart=ttime.time() tstep=ttime.time() while boolContinue: tdiff = tstep-tstart if tdiff > 43200: print('stoppend because running longer than 14h') timeProbs.append(i) vgm.write_pkl(timeProbs,'timeProbs.pkl') break else: countDone=0 countLoop += 1 pathsCount=len(paths.keys()) if countLoop > 1e4 and boolInfo1 == 1: print('Path length > 1e4') boolInfo1 = 0 if countLoop > 1e5 and boolInfo2 == 1: print('Path length > 1e5') boolInfo2 = 0 if countLoop > 1e6 and boolInfo3 == 1: print('Path length > 1e6') boolInfo3 = 0 if countLoop > 5e6 and boolInfo4 == 1: print('Path length > 5e6') boolInfo4 = 0 if countLoop > stopPaths: print('Path length is getting too long') lengthsProbs.append([i,0]) break if pathsCount - countReconnected > stopPaths2: print('Number of Path is getting too large') lengthsProbs.append([i,1]) break countReconnected = 0 for k in paths.keys(): if G.neighbors(paths[k][-1],type=direction) == [] or reconnected[k]== 1: if G.neighbors(paths[k][-1],type=direction) == []: countDone += 1 elif reconnected[k]== 1: countReconnected += 1 else: countNeighbor = 0 pathsOld=paths[k] pathsEdgesOld=pathsEdges[k] neighbors2=[] adjacents2=[] for neighbor,adjacent in zip(G.neighbors(paths[k][-1],type=direction),G.adjacent(paths[k][-1],type=direction)): if neighbor not in neighbors2: neighbors2.append(neighbor) adjacents2.append(adjacent) for neighbor,adjacent in zip(neighbors2,adjacents2): if neighbor in paths[k]: print('WARNING already in path') if G.vs[neighbor]['kind'] == 'c': if countNeighbor == 0: if neighbor in allVerts: for l in paths.keys(): if neighbor in paths[l]: reconnection[k] = [l,paths[l].index(neighbor)] reconnected[k] = 1 countReconnected += 1 break else: reconnected[k] = 0 paths[k]=pathsOld + [neighbor] pathsEdges[k]=pathsEdgesOld + [adjacent] else: if neighbor in allVerts: for l in paths.keys(): if neighbor in paths[l]: reconnection[pathsCount] = [l,paths[l].index(neighbor)] reconnected[pathsCount] = 1 countReconnected += 1 break else: reconnected[pathsCount] = 0 paths[pathsCount] = pathsOld + [neighbor] pathsEdges[pathsCount]=pathsEdgesOld + [adjacent] reconnected[pathsCount]=0 stopReached[pathsCount]=0 reconnection[pathsCount]=[] pathsCount = len(paths.keys()) allVerts.append(neighbor) elif G.vs[neighbor]['kind'] == stopKind: countDone += 1 stopReached[k]=1 else: pass countNeighbor = 1 allVerts = np.unique(allVerts) allVerts = allVerts.tolist() if countDone + countReconnected == pathsCount: print('Add final vertex') for k in paths.keys(): pathsOld=paths[k] pathsEdgesOld=pathsEdges[k] if reconnected[k] != 1: if G.neighbors(paths[k][-1],type=direction) == []: pass else: countNeighbor = 0 neighbors2=[] adjacents2=[] for neighbor,adjacent in zip(G.neighbors(paths[k][-1],type=direction),G.adjacent(paths[k][-1],type=direction)): if neighbor not in neighbors2: neighbors2.append(neighbor) adjacents2.append(adjacent) for neighbor,adjacent in zip(neighbors2,adjacents2): if countNeighbor == 0: paths[k]=paths[k] + [neighbor] pathsEdges[k]=pathsEdges[k] + [adjacent] else: paths[pathsCount] = pathsOld + [neighbor] pathsEdges[pathsCount]=pathsEdgesOld + [adjacent] pathsCount = len(paths.keys()) reconnected[pathsCount]=0 stopReached[pathsCount]=0 reconnection[pathsCount]=[] countNeighbor = 1 boolContinue = 0 tstep=ttime.time() if countLoop > stopPaths: dictDummy={} dictDummy['vertices']=[] dictDummy['edges']=[] dictDummy['finished']=0.0 dictDummy['reconnectionPath']=[] dictDummy['reconnectionPos']=[] elif len(paths) > stopPaths2: dictDummy={} dictDummy['vertices']=[] dictDummy['edges']=[] dictDummy['finished']=0.5 dictDummy['reconnectionPath']=[] dictDummy['reconnectionPos']=[] else: dictDummy={} dictDummy['vertices']=paths dictDummy['edges']=pathsEdges reconnectedList=[] reconnectionPath=[] reconnectionPos=[] stopReachedList=[] keys = reconnected.keys() keys.sort() for k in keys: reconnectedList.append(reconnected[k]) stopReachedList.append(stopReached[k]) if len(reconnection[k]) > 0: reconnectionPath.append(reconnection[k][0]) reconnectionPos.append(reconnection[k][1]) else: reconnectionPath.append(None) reconnectionPos.append(None) dictDummy['reconnected']=reconnected dictDummy['stopReached']=stopReachedList dictDummy['reconnectionPath']=reconnectionPath dictDummy['reconnectionPos']=reconnectionPos dictDummy['finished']=[1]*len(keys) pathDict[i]=dictDummy vgm.write_pkl(pathDict,'pathDict'+str(j+1)+'.pkl')
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 transient_dilation(G='G_standard',edges=[240, 243, 246, 249], fdilation=[1.1, 1.1, 0.9, 0.9], ttotal=900,ht0=0.4, ttransient=[400, 100, 10], plotstep=2, samplestep=2,**kwargs): """Load VascularGraph from disk. Run RBC-transport simulation, where a vessel is dilated at some point during the simulation. The dilation occurs in several steps. Note that there are some hard-coded variables which need to be adjusted if use-case changes! INPUT: G: Input Graph as pkl-file (name without the ending .pkl) edges: List of edges to dilate fdilation: List of dilation-factors ttotal: total time ht0: initial tube hematocrit value throughout the VascularGraph ttransient: [tinitial, tduration, steps]= [start of the dilation, time period till dilation is finished, number of steps for dilation] plotstep: timestep for plotting samplestep: timestep for sampling **kwargs: httBC: tube hematocrit boundary condition at inflow bigger: 0 = 2 Vessel network, 1= 2in2 Vessel network 2=Honeycomb wholeBr: Boolean whether the whole Branch is dilated (TRUE) or only center of the Branch is dilated (FALSE) OUTPUT: None, pre- and post-dilation VascularGraph, sample-dictionary, and RBC-plots are written to disk. """ filename=G+'.pkl' G = vgm.read_pkl(filename) if kwargs.has_key('httBC'): for vi in G['av']: for ei in G.adjacent(vi): G.es[ei]['httBC']=kwargs['httBC'] if kwargs.has_key('bigger'): NW=kwargs['bigger'] else: NW=0 if kwargs.has_key('wholeBr'): wholeBr=kwargs['wholeBr'] else: wholeBr=False G.add_points(1.) dilatedList=[] G.es['dfactor'] = [None for e in G.es] G.es[edges]['dfactor'] = 1.0 if wholeBr: dilatedList=edges else: while len(G.es(dfactor_ne=None)) > 0: eindex = G.es(dfactor_ne=None).indices[0] dfactor = G.es[eindex]['dfactor'] vi, ei, dilated_ei = G.central_dilation(eindex, dfactor, 4/5.) G.es[ei]['dfactor'] = [None for e in ei] dilatedList.append(dilated_ei) for i in range(len(dilatedList)): dilatedList[i]=dilatedList[i]-(len(dilatedList)-i-1) Gd = copy.deepcopy(G) LSd = vgm.LinearSystemHtdWCyth(Gd, dThreshold=10.0, ht0=ht0) #Run simulation without dilation till tinitial is reached LSd.evolve(time=ttransient[0], method='direct', plotPrms=[0, ttransient[0], plotstep], samplePrms=[0, ttransient[0], samplestep]) #LSd._plot_sample_average('G_pre_dilation.vtp') #vgm.write_pkl(Gd, 'G_pre_dilation.pkl') tstep = ttransient[1] / ttransient[2] Gd.es['dfactor'] = [None for e in G.es] Gd.es[dilatedList]['dfactor'] = fdilation fsteps = [Gd.es[edge]['diameter'] * (fdil - 1) / ttransient[2] for edge, fdil in zip(dilatedList, fdilation)] for step in xrange(ttransient[2]): for edge, fstep in zip(dilatedList, fsteps): Gd.es[edge]['diameter'] += fstep stdout.write("\rEdge = %g \n" %edge) stdout.write("\rDiameter = %f \n" %Gd.es[edge]['diameter']) #START: Consider Change of minDist during dilation of vessel LSd._update_minDist_and_nMax(esequence=dilatedList) LSd._update_tube_hematocrit(esequence=dilatedList) #END: Consider Change of minDist during dilation of vessel LSd._update_nominal_and_specific_resistance(esequence=dilatedList) LSd._update_eff_resistance_and_LS() LSd.evolve(time=tstep, method='direct', plotPrms=(0.0, tstep, plotstep), samplePrms=(0.0, tstep, samplestep),init=False) filename2='G_transient_dilation_'+str(step)+'.vtp' LSd._plot_sample_average(filename2) vgm.write_pkl(Gd, 'G_transient_dilation_'+str(step)+'.pkl') stdout.write("\rDilation Step = %g \n" %step) #stdout.write("\rDiameter = %f \n" %Gd.es[342]['diameter']) tstep = ttotal - ttransient[0] - ttransient[1] LSd.evolve(time=tstep, method='direct', plotPrms=(0.0, tstep, plotstep), samplePrms=(0.0, tstep, samplestep),init=False) vgm.write_pkl(Gd, 'G_post_dilation.pkl') vgm.write_vtp(Gd, 'G_post_dilation.vtp', False)
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')