6 This module is for calculating surface areas
7 from OpenStructure using the external program naccess
9 How To Use This Module:
10 1. Import it (e.g. as "from ost.bindings import naccess")
11 2. Use it (e.g. as "sasa = naccess.CalculateSurfaceArea(entity)")
23 from ost
import settings
26 def _GetExecutable(naccess_exe):
28 Method to check if naccess executable is present
30 :param naccess: Explicit path to msms executable
31 :returns: Path to the executable
32 :exception: FileNotFound if executable is not found
34 return settings.Locate(
'naccess', explicit_file_name=naccess_exe)
36 def _SetupFiles(entity, selection, scratch_dir, max_number_of_atoms):
38 Setup files for naccess calculation in temporary directory
40 :param entity: EntityHandle or EntityView to calculate surface
41 :param selection: Calculate surface for subset of entity
42 :param scratch_dir: Directory for temporary files (NACCESS is sensitive to "." in directory names
43 :param max_number_of_atoms: Max Number of atoms in the entity (i.e. is limited in the default NACCESS version to 50 000)
44 :returns: array containing temporary directory, input filename for naccess and directory of the input file
45 :exception: RuntimeError if selection is not valid
51 tmp_dir_name=tempfile.mkdtemp(dir=scratch_dir)
53 tmp_dir_name=tempfile.mkdtemp()
56 entity_view=entity.Select(selection)
57 if len(entity_view.atoms) > max_number_of_atoms:
58 raise RuntimeError,
"Too much atoms for NACCESS (> %s)" % max_number_of_atoms
59 if not entity_view.IsValid():
60 raise RuntimeError,
"Could not create view for selection (%s)"%(selection)
63 tmp_file_name=os.path.join(tmp_dir_name,
"entity.pdb")
64 tmp_file_base = os.path.join(tmp_dir_name,
"entity")
65 io.SavePDB(entity_view, tmp_file_name)
66 return (tmp_dir_name, tmp_file_name, tmp_file_base)
69 def _ParseAsaFile(entity, file, asa_atom):
71 Reads Area file (.asa) and attach asa per atom to an entitiy
73 :param entity: EntityHandle or EntityView for attaching sasa on atom level
74 :param file: Filename of area file
75 :param asa_atom: Name of the float property for SASA
79 asa_lines = asa_fh.readlines()
83 if l.startswith(
"ATOM"):
89 atom_name = atom_name.strip()
91 res_number = res_number.strip()
94 m=re.match(
r'(?P<num>-?\d+)(?P<ins>\w)?', res_number)
100 resNum =
mol.ResNum(int(di[
"num"]), di[
"ins"])
102 a = entity.FindAtom(chain_id, resNum, atom_name)
104 a.SetFloatProp(asa_atom, float(asa))
106 print chain_id, resNum, atom_name
109 def _ParseRsaFile(enti,file, asa_abs, asa_rel):
111 Reads Area file (.rsa) and attach asa (absolute + relative) per residue to an entitiy
113 :param entity: EntityHandle or EntityView for attaching sasa on atom level
114 :param file: Filename of .rsa file
115 :param asa_atom: Name of the float property for absolute SASA
116 :param asa_atom: Name of the float property for relative SASA
117 :exception: RuntimeError if residue names are not the same
120 area_lines = area_fh.readlines()
123 area_lines = area_lines[4:]
127 if l.startswith(
"RES"):
128 p = re.compile(
r'\s+')
132 res_name = res_name.strip()
135 res_number= res_number.strip()
137 abs_all, rel_all =l[15:28].strip().split()
138 m=re.match(
r'(?P<num>-?\d+)(?P<ins>\w)?', res_number)
140 if di[
"ins"] ==
None:
143 resNum =
mol.ResNum(int(di[
"num"]), di[
"ins"])
145 res = enti.handle.FindResidue(chain_id, resNum)
148 if res_name == res.name:
149 res.SetFloatProp(asa_rel, float(rel_all) )
150 res.SetFloatProp(asa_abs, float(abs_all) )
152 raise RuntimeError,
"Residue Names are not the same for ResNumb: %s (%s vs %s)" % (res_number, res.name, res_name)
155 def __CleanupFiles(dir_name):
157 Method which recursively deletes a directory
159 :warning: This method removes also non-empty directories without asking, so
163 shutil.rmtree(dir_name)
166 def _RunNACCESS(command, temp_dir):
168 Method to run the MSMS surface calculation
170 This method starts the external MSMS executable and returns the stdout of MSMS
172 :param command: Command to execute
173 :returns: stdout of MSMS
174 :exception: CalledProcessError for non-zero return value
176 proc = subprocess.Popen(command, shell=
True, stdout=subprocess.PIPE, cwd = temp_dir)
177 stdout_value, stderr_value = proc.communicate()
180 if proc.returncode!=0:
181 print "WARNING: naccess error\n", stdout_value
182 raise subprocess.CalledProcessError(proc.returncode, command)
189 include_hydrogens=
False, include_hetatm =
False,
190 include_water =
False, selection=
"",
191 naccess_exe=
None, keep_files=
False , asa_abs=
"asaAbs", asa_rel=
"asaRel", asa_atom=
"asaAtom", scratch_dir =
None, max_number_of_atoms=50000):
193 Calculates analytical the solvent accessible surface area by using the
194 external naccess program
196 This method calculates the molecular surface areas by invoking the external
197 program naccess. First, it is checked if the naccess executable is present, then,
198 the necessary files are prepared in a temporary directory and naccess is
199 executed. The last step is to remove the temporary directory.
202 :param entity: OST entity to calculate surface
203 :param radius: Surface probe radius
204 :param include_hydrogens: Calculate surface including hydrogens
205 :param include_hetatm: Calculate surface including hetatms
206 :param include_water: Calculate surface including water
207 :param selection: Calculate surface for subset of entity
208 :param naccess_exe: naccess executable (full path to executable)
209 :param keep_files: Do not delete temporary files
210 :param asa_abs: Attaches per residue absolute SASA to specified FloatProp on residue level
211 :param asa_rel: Attaches per residue relative SASA to specified FloatProp on residue level
212 :param asa_atom: Attaches per atom SASA to specified FloatProp at atom level
213 :param scratch_dir: Directory for temporary files (NACCESS is sensitive to "." in directory names
214 :param max_number_of_atoms: Max Number of atoms in the entity (i.e. is limited in the default NACCESS version to 50 000)
216 :returns: absolute SASA calculated using asa_atom
222 naccess_executable=_GetExecutable(naccess_exe)
226 (naccess_data_dir, naccess_data_file,naccess_data_base )=_SetupFiles(entity, selection, scratch_dir, max_number_of_atoms)
229 command=
"%s %s -p %f " % \
230 (naccess_executable, naccess_data_file, radius)
231 if include_hydrogens:
232 command =
"%s -w" % command
234 command =
"%s -y" % command
236 command =
"%s -h" % command
238 stdout_value=_RunNACCESS(command, naccess_data_dir)
240 new_asa= os.path.join(naccess_data_dir,
"%s.asa" % naccess_data_base)
241 _ParseAsaFile(entity, new_asa, asa_atom)
243 new_rsa = os.path.join(naccess_data_dir,
"%s.rsa" % naccess_data_base)
244 _ParseRsaFile(entity, new_rsa, asa_abs, asa_rel)
248 for a
in entity.atoms:
249 sasa += a.GetFloatProp(asa_atom, 0.0)
258 __CleanupFiles(naccess_data_dir)