def accessible_surface(self, radius, nump=100, superradius=200, include_edges=True, view_mesh=False, savefig=None, write_cmm_file=None, verbose=False, chimera_bin='chimera'): """ Calculates a mesh surface around the model (distance equal to input **radius**) and checks if each point of this mesh could be replaced by an object (i.e. a protein) of a given **radius** Outer part of the model can be excluded from the estimation of accessible surface, as the occupancy outside the model is unknown (see superradius option). :param radius: radius of the object we want to fit in the model. :param None write_cmm_file: path to file in which to write cmm with the colored meshed (red inaccessible points, green accessible points) :param 100 nump: number of points to draw around a given particle. This number also sets the number of points drawn around edges, as each point occupies a given surface (see maths below). *Note that this number is considerably lowered by the occupancy of edges, depending of the angle formed by the edges surrounding a given particle, only 10% to 50% of the ``nump`` will be drawn in fact.* :param True include_edges: if False, edges will not be included in the calculation of the accessible surface, only particles. Note that statistics on particles (like last item returned) will not change, and computation time will be significantly decreased. :param False view_mesh: launches chimera to display the mesh around the model :param None savefig: path where to save chimera image :param 'chimera' chimera_bin: path to chimera binary to use :param False verbose: prints stats about the surface :param 200 superradius: radius of an object used to exclude outer surface of the model. Superradius must be higher than radius. This function will first define a mesh around the chromatin, representing all possible position of the center of the object we want to fit. This mesh will be at a distance of *radius* from the chromatin strand. All dots in the mesh represents an equal area (*a*), the whole surface of the chromatin strand being: :math:`A=n \\times a` (*n* being the total number of dots in the mesh). The mesh consists of spheres around particles of the model, and cylinders around edges joining particles (no overlap is allowed between sphere and cylinders or cylinder and cylinder when they are consecutive). If we want that all dots of the mesh representing the surface of the chromatin, corresponds to an equal area (:math:`a`) .. math:: a = \\frac{4\pi r^2}{s} = \\frac{2\pi r N_{(d)}}{c} with: * :math:`r` radius of the object to fit (as the input parameter **radius**) * :math:`s` number of points in sphere * :math:`c` number of points in circle (as the input parameter **nump**) * :math:`N_{(d)}` number of circles in an edge of length :math:`d` According to this, when the distance between two particles is equal to :math:`2r` (:math:`N=2r`), we would have :math:`s=c`. As : .. math:: 2\pi r = \sqrt{4\pi r^2} \\times \sqrt{\pi} It is fair to state the number of dots represented along a circle as: .. math:: c = \sqrt{s} \\times \sqrt{\pi} Thus the number of circles in an edge of length :math:`d` must be: .. math:: N_{(d)}=\\frac{s}{\sqrt{s}\sqrt{\pi}}\\times\\frac{d}{2r} :returns: a list of *1-* the number of dots in the mesh that could be occupied by an object of the given radius *2-* the total number of dots in the mesh *3-* the estimated area of the mesh (in square micrometers) *4-* the area of the mesh of a virtually straight strand of chromatin defined as :math:`contour\\times 2\pi r + 4\pi r^2` (also in micrometers) *5-* a list of number of (accessibles, inaccessible) for each particle (percentage burried can be infered afterwards by accessible/(accessible+inaccessible) ) """ points, dots, superdots, points2dots = build_mesh( self['x'], self['y'], self['z'], len(self), nump, radius, superradius, include_edges) # calculates the number of inaccessible peaces of surface if superradius: radius2 = (superradius - 4)**2 outdot = [] for x2, y2, z2 in superdots: for j, (x1, y1, z1) in enumerate(points): if fast_square_distance(x1, y1, z1, x2, y2, z2) < radius2: outdot.append(False) break else: outdot.append(True) continue points.insert(0, points.pop(j)) else: outdot = [False] * len(superdots) # calculates the number of inaccessible peaces of surface radius2 = (radius - 2)**2 grey = (0.6, 0.6, 0.6) red = (1, 0, 0) green = (0, 1, 0) colors = [] for i, (x2, y2, z2) in enumerate(dots): if outdot[i]: colors.append(grey) continue for j, (x1, y1, z1) in enumerate(points): if fast_square_distance(x1, y1, z1, x2, y2, z2) < radius2: colors.append(red) break else: colors.append(green) continue points.insert(0, points.pop(j)) possibles = colors.count(green) acc_parts = [] for p in sorted(points2dots.keys()): acc = 0 ina = 0 for dot in points2dots[p]: if colors[dot] == green: acc += 1 elif colors[dot] == red: ina += 1 acc_parts.append((p + 1, acc, ina)) # some stats dot_area = 4 * pi * (float(radius) / 1000)**2 / nump area = (possibles * dot_area) total = (self.contour() / 1000 * 2 * pi * float(radius) / 1000 + 4 * pi * (float(radius) / 1000)**2) if verbose: print((' Accessible surface: %s micrometers^2' + '(%s accessible times %s micrometers)') % (round(area, 2), possibles, dot_area)) print(' (%s accessible dots of %s total times %s micrometers)' % (possibles, outdot.count(False), round(dot_area, 5))) print(' - %s%% of the contour mesh' % (round( (float(possibles) / outdot.count(False)) * 100, 2))) print(' - %s%% of a virtual straight chromatin (%s microm^2)' % (round((area / total) * 100, 2), round(total, 2))) # write cmm file if savefig: view_mesh = True if write_cmm_file or view_mesh: out = '<marker_set name=\"2\">\n' form = ('<marker id=\"%s\" x=\"%s\" y=\"%s\" z=\"%s\"' + ' r=\"%s\" g=\"%s\" b=\"%s\" ' + 'radius=\"7\"/>\n') for k_2, thing in enumerate(dots): out += form % (1 + k_2, thing[0], thing[1], thing[2], colors[k_2][0], colors[k_2][1], colors[k_2][2]) if superradius: for k_3, thing in enumerate(superdots): out += form % (1 + k_3 + k_2 + 1, thing[0], thing[1], thing[2], 0.1, 0.1, 0.1) out += '</marker_set>\n' if view_mesh: out_f = open('/tmp/tmp_mesh.cmm', 'w') out_f.write(out) out_f.close() if write_cmm_file: out_f = open(write_cmm_file, 'w') out_f.write(out) out_f.close() if view_mesh: chimera_cmd = [ 'focus', 'bonddisplay never #1', 'shape tube #1 radius 15 bandLength 300 segmentSubdivisions 1 followBonds on', '~show #1', 'set bg_color white', 'windowsize 800 600', 'clip yon -500', 'set subdivision 1', 'set depth_cue', 'set dc_color black', 'set dc_start 0.5', 'set dc_end 1', 'scale 0.8' ] if savefig: if savefig.endswith('.png'): chimera_cmd += ['copy file %s png' % (savefig)] elif savefig[-4:] in ('.mov', 'webm'): chimera_cmd += [ 'movie record supersample 1', 'turn y 3 120', 'wait 120', 'movie stop', 'movie encode output %s' % savefig ] self.write_cmm('/tmp/') chimera_view([ '/tmp/tmp_mesh.cmm', '/tmp/model.%s.cmm' % (self['rand_init']) ], chimera_bin=chimera_bin, align=False, savefig=savefig, chimera_cmd=chimera_cmd) return (possibles, outdot.count(False), area, total, acc_parts)
def accessible_surface(self, radius, nump=100, superradius=200, include_edges=True, view_mesh=False, savefig=None, write_cmm_file=None, verbose=False, chimera_bin='chimera'): """ Calculates a mesh surface around the model (distance equal to input **radius**) and checks if each point of this mesh could be replaced by an object (i.e. a protein) of a given **radius** Outer part of the model can be excluded from the estimation of accessible surface, as the occupancy outside the model is unkown (see superradius option). :param radius: radius of the object we want to fit in the model. :param None write_cmm_file: path to file in which to write cmm with the colored meshed (red inaccessible points, green accessible points) :param 100 nump: number of points to draw around a given particle. This number also sets the number of points drawn around edges, as each point occupies a given surface (see maths below). *Note that this number is considerably lowered by the occupancy of edges, depending of the angle formed by the edges surrounding a given particle, only 10% to 50% of the ``nump`` will be drawn in fact.* :param True include_edges: if False, edges will not be included in the calculation of the accessible surface, only particles. Note that statistics on particles (like last item returned) will not change, and computation time will be significantly decreased. :param False view_mesh: launches chimera to display the mesh around the model :param None savefig: path where to save chimera image :param 'chimera' chimera_bin: path to chimera binary to use :param False verbose: prints stats about the surface :param 200 superradius: radius of an object used to exclude outer surface of the model. Superradius must be higher than radius. This function will first define a mesh around the chromatin, representing all possible position of the center of the object we want to fit. This mesh will be at a distance of *radius* from the chromatin strand. All dots in the mesh represents an equal area (*a*), the whole surface of the chromatin strand being: :math:`A=n \\times a` (*n* being the total number of dots in the mesh). The mesh consists of spheres around particles of the model, and cylinders around edges joining particles (no overlap is allowed between sphere and cylinders or cylinder and cylinder when they are consecutive). If we want that all dots of the mesh representing the surface of the chromatin, corresponds to an equal area (:math:`a`) .. math:: a = \\frac{4\pi r^2}{s} = \\frac{2\pi r N_{(d)}}{c} with: * :math:`r` radius of the object to fit (as the input parameter **radius**) * :math:`s` number of points in sphere * :math:`c` number of points in circle (as the input parameter **nump**) * :math:`N_{(d)}` number of circles in an edge of length :math:`d` According to this, when the distance between two particles is equal to :math:`2r` (:math:`N=2r`), we would have :math:`s=c`. As : .. math:: 2\pi r = \sqrt{4\pi r^2} \\times \sqrt{\pi} It is fair to state the number of dots represented along a circle as: .. math:: c = \sqrt{s} \\times \sqrt{\pi} Thus the number of circles in an edge of length :math:`d` must be: .. math:: N_{(d)}=\\frac{s}{\sqrt{s}\sqrt{\pi}}\\times\\frac{d}{2r} :returns: a list of *1-* the number of dots in the mesh that could be occupied by an object of the given radius *2-* the total number of dots in the mesh *3-* the estimated area of the mesh (in square micrometers) *4-* the area of the mesh of a virtually straight strand of chromatin defined as :math:`contour\\times 2\pi r + 4\pi r^2` (also in micrometers) *5-* a list of number of (accessibles, inaccessible) for each particle (percentage burried can be infered afterwards by accessible/(accessible+inaccessible) ) """ points, dots, superdots, points2dots = build_mesh( self['x'], self['y'], self['z'], len(self), nump, radius, superradius, include_edges) # calculates the number of inaccessible peaces of surface if superradius: radius2 = (superradius - 4)**2 outdot = [] for x2, y2, z2 in superdots: for j, (x1, y1, z1) in enumerate(points): if fast_square_distance(x1, y1, z1, x2, y2, z2) < radius2: outdot.append(False) break else: outdot.append(True) continue points.insert(0, points.pop(j)) else: outdot = [False] * len(superdots) # calculates the number of inaccessible peaces of surface radius2 = (radius - 2)**2 grey = (0.6, 0.6, 0.6) red = (1, 0, 0) green = (0, 1, 0) colors = [] for i, (x2, y2, z2) in enumerate(dots): if outdot[i]: colors.append(grey) continue for j, (x1, y1, z1) in enumerate(points): if fast_square_distance(x1, y1, z1, x2, y2, z2) < radius2: colors.append(red) break else: colors.append(green) continue points.insert(0, points.pop(j)) possibles = colors.count(green) acc_parts = [] for p in sorted(points2dots.keys()): acc = 0 ina = 0 for dot in points2dots[p]: if colors[dot]==green: acc += 1 elif colors[dot]==red: ina += 1 acc_parts.append((p + 1, acc, ina)) # some stats dot_area = 4 * pi * (float(radius) / 1000)**2 / nump area = (possibles * dot_area) total = (self.contour() / 1000 * 2 * pi * float(radius) / 1000 + 4 * pi * (float(radius) / 1000)**2) if verbose: print (' Accessible surface: %s micrometers^2' + '(%s accessible times %s micrometers)') % ( round(area, 2), possibles, dot_area) print ' (%s accessible dots of %s total times %s micrometers)' % ( possibles, outdot.count(False), round(dot_area, 5)) print ' - %s%% of the contour mesh' % ( round((float(possibles)/outdot.count(False))*100, 2)) print ' - %s%% of a virtual straight chromatin (%s microm^2)' % ( round((area/total)*100, 2), round(total, 2)) # write cmm file if savefig: view_mesh = True if write_cmm_file or view_mesh: out = '<marker_set name=\"2\">\n' form = ('<marker id=\"%s\" x=\"%s\" y=\"%s\" z=\"%s\"' + ' r=\"%s\" g=\"%s\" b=\"%s\" ' + 'radius=\"7\"/>\n') for k_2, thing in enumerate(dots): out += form % (1 + k_2, thing[0], thing[1], thing[2], colors[k_2][0], colors[k_2][1], colors[k_2][2]) if superradius: for k_3, thing in enumerate(superdots): out += form % (1 + k_3 + k_2 + 1, thing[0], thing[1], thing[2], 0.1, 0.1, 0.1) out += '</marker_set>\n' if view_mesh: out_f = open('/tmp/tmp_mesh.cmm', 'w') out_f.write(out) out_f.close() if write_cmm_file: out_f = open(write_cmm_file, 'w') out_f.write(out) out_f.close() if view_mesh: chimera_cmd = [ 'focus', 'bonddisplay never #1', 'shape tube #1 radius 15 bandLength 300 segmentSubdivisions 1 followBonds on', '~show #1', 'set bg_color white', 'windowsize 800 600', 'clip yon -500', 'set subdivision 1', 'set depth_cue', 'set dc_color black', 'set dc_start 0.5', 'set dc_end 1', 'scale 0.8'] if savefig: if savefig.endswith('.png'): chimera_cmd += ['copy file %s png' % (savefig)] elif savefig[-4:] in ('.mov', 'webm'): chimera_cmd += [ 'movie record supersample 1', 'turn y 3 120', 'wait 120', 'movie stop', 'movie encode output %s' % savefig] self.write_cmm('/tmp/') chimera_view(['/tmp/tmp_mesh.cmm', '/tmp/model.%s.cmm' % (self['rand_init'])], chimera_bin=chimera_bin, align=False, savefig=savefig, chimera_cmd=chimera_cmd) return (possibles, outdot.count(False), area, total, acc_parts)