Module bioiain.base
Sub-modules
bioiain.base.atombioiain.base.chainbioiain.base.entitybioiain.base.ligandbioiain.base.mmcifbioiain.base.residuebioiain.base.structure
Functions
def downloadPDBlist(data_dir: str,
list_name: str,
pdb_list: list = None,
file_path: str = None,
file_format='cif',
overwrite: bool = False) ‑> str-
Expand source code
def downloadPDBlist(data_dir:str, list_name:str, pdb_list:list=None, file_path:str = None, file_format="cif", overwrite:bool=False) -> str: """ Downloads a list of PDB files into a folder of given name within the data_dir. Creates a file containing all the file in the data_dir :param data_dir: Directory to create the download folder :param list_name: Name of the folder to download files to :param pdb_list: List of PDB codes to download :param file_path: (optional) file with PDB codes, separated by comma or new lines, extends pdb_list :param file_format: PDB / CIF, extension of downloaded files :param overwrite: True to download existing pdb files and overwrite :return: Path to folder containing downloaded files """ log("header", "Downloading PDB files...") file_format = file_format.lower() assert file_format in ["pdb", "cif"] if pdb_list is None: pdb_list = [] if file_path is not None: with open(file_path) as f: for line in f: new = string_to_list(line, delimiter=",") for n in new: n = clean_string(n) pdb_list.append(n) pdb_list = sorted(list(set([p.upper() for p in pdb_list]))) if len(pdb_list) <= 10: log(1, "N codes:", pdb_list) else: log(1, "N codes:", len(pdb_list)) os.makedirs(data_dir, exist_ok=True) list_folder = os.path.join(data_dir, list_name) os.makedirs(list_folder, exist_ok=True) link_file = "{}_{}.link.list".format(list_name, file_format) with open(os.path.join(data_dir, link_file) , "w") as f: for pdb in pdb_list: if file_format == "pdb": f.write("https://files.rcsb.org/download/{}.pdb\n".format(pdb)) elif file_format == "cif": f.write("https://files.rcsb.org/download/{}.cif\n".format(pdb)) # log("debug", "Generated links at: {}".format(os.path.join(data_dir, link_file) )) with open(os.path.join(data_dir, link_file)) as f: counter = 0 failed_counter = 0 skipped_counter = 0 for line in f: line = line.replace("\n", "") f_name = line.split("/")[-1] if os.path.exists(os.path.join(list_folder, f_name)) and not overwrite: skipped_counter += 1 continue url = line log("debug", "...Downloading {}".format(url), end="\r") response = requests.get(url) if response.status_code != 200: log("Error", "Failed to download from:", line) failed_counter += 1 else: with open(os.path.join(list_folder, f_name), "w") as f: f.write(response.text) counter += 1 log(1, "{} files downloaded, {} failed, {} skipped".format(counter, failed_counter, skipped_counter)) return list_folderDownloads a list of PDB files into a folder of given name within the data_dir. Creates a file containing all the file in the data_dir :param data_dir: Directory to create the download folder :param list_name: Name of the folder to download files to :param pdb_list: List of PDB codes to download :param file_path: (optional) file with PDB codes, separated by comma or new lines, extends pdb_list :param file_format: PDB / CIF, extension of downloaded files :param overwrite: True to download existing pdb files and overwrite :return: Path to folder containing downloaded files
def read_mmcif(file_path,
output_folder=None,
subset: list | str = None,
exclude: list | str = None,
as_dict=False) ‑> MMCIF-
Expand source code
def read_mmcif(file_path, output_folder=None, subset:list|str=None, exclude:list|str=None ,as_dict=False) -> MMCIF: from ..utilities.strings import str_to_list_with_literals data = {} name = os.path.basename(file_path).split(".")[0] if type(subset) is str: subset = [subset] if type(exclude) is str: subset = [exclude] if output_folder is not None: os.makedirs(output_folder, exist_ok=True) with open(file_path, "r") as f: n = -1 in_loop = False group_key = None loop_keys = None loop_values = None next_line = None eof = False multi_line = False multi_cached = None multi_delimiters = [";"] looping = False while not eof: n+=1 line = next_line next_line = next(f, None) if line is None: continue if next_line is None: eof = True next_line = "#" if line.startswith("#"): in_loop = False looping = False group_key = None multi_cached = None continue if line.startswith("loop_"): in_loop = True loop_keys = [] loop_values = [] group_key = [] #print("LOOP START") continue #print("\nLINE:") #print(repr(line)) #print("group_key:", repr(group_key)) #print("multi:", multi_line) #print("loop:", in_loop) if multi_line: if multi_cached is None: multi_cached = "" if line.replace("\n", "").strip() == "": multi_cached += line continue if type(multi_cached) is list: line_list, open_lit = str_to_list_with_literals(line, check_open_literal=True ) if line.replace("\n", "").strip()[-1] in multi_delimiters: line = line.replace("\n", "").strip()[:-2] multi_line = False open_lit = False multi_delimiter = None #print("MULTI LINE END") if not in_loop: v.append(multi_cached) multi_cached = None continue else: loop_values[-1].append(multi_cached) else: if type(multi_cached) is str: if line[0] in multi_delimiters: multi_cached += line[1:] else: multi_cached += line elif type(multi_cached) is list: if open_lit: multi_cached[-1] += line_list[0] multi_cached.extend(line_list[1:]) else: multi_cached.extend(line_list) else: raise MMCIFTypeError("Bioiain mmcif Parser error") if not in_loop: if not multi_line: line_list, open_lit = str_to_list_with_literals(line, check_open_literal=True) if len(line_list) == 0: continue group_key = line_list[0].split(".")[0].replace("\n", "").strip() try: k = line_list[0].split(".")[1] except IndexError: k = group_key group_key = None if len(line_list) == 1: v = [] else: v = line_list[1:] if open_lit: #print(multi_cached) #print(line_list) #print("MULTI_LINE START (OPEN-LIT)") multi_line = True multi_cache = line_list[-1] multi_delimiter = line_list[-1][0] if next_line[0] in multi_delimiters and not multi_line: #print("MULTI_LINE START") multi_line = True multi_delimiter = next_line[0] continue if group_key is None: #print("multi line", multi_line) #print("line",repr(line)) #print("line_list",line_list) #print(n) if n != 1: #log("warning", f"No key-value structure found in line {n}:", repr(line), f"\n (In file: {file_path})") pass else: #log("debug", "Parsing:", line.replace("\n", "").strip()) pass if not multi_line and group_key is not None: # print(group_key) # print(subset) # print([group_key == s if not s.endswith("*") else group_key.startswith(s[:-1]) for s in # subset]) if exclude is not None: # print(group_key) # print(exclude) # print([group_key == s if not s.endswith("*") else group_key.startswith(s[:-1]) for s in # exclude]) if any([group_key == s if not s.endswith("*") else group_key.startswith(s[:-1]) for s in exclude]): continue if subset is not None: if not any([group_key == s if not s.endswith("*") else group_key.startswith(s[:-1]) for s in subset]): continue # if group_key not in subset: # continue if group_key not in data.keys(): data[group_key] = [{}] #print(f"{group_key}.{k} ->", " ".join(v)) data[group_key][0][k] = interpret(" ".join(v)) else: # IN LOOP #print("MULTI:", multi_line) if not multi_line: if line.startswith("_") and not looping: group_key.append(line.split(".")[0].replace("\n", "").strip()) loop_keys.append(line.split(" ")[0].split(".")[1].replace("\n", "").strip()) continue else: looping = True if multi_cached is None: if len(loop_values) > 0: if len(loop_values[-1]) < len(loop_keys): loop_values[-1].extend(str_to_list_with_literals(line)) else: loop_values.append(str_to_list_with_literals(line)) else: loop_values.append(str_to_list_with_literals(line)) else: multi_cached = None if (next_line[0] in multi_delimiters) and (not multi_line): multi_line = True multi_delimiter = next_line[0] multi_cache = [] continue if next_line[0] == "#": try: assert len(set(group_key)) == 1 except AssertionError: print(group_key) log("error", f"Multiple keys structure found in line {n}:", repr(line)) raise MMCIFError(f"Multiple keys structure found") exit() continue group_key = group_key[0] if exclude is not None: if group_key in exclude: continue if subset is not None: if group_key not in subset: continue if group_key not in data.keys(): data[group_key] = [] for i, l in enumerate(loop_values): try: assert len(l) == len(loop_keys) except AssertionError: log("warning", f"Missmatch on key-value numbers in group {group_key}, element {i}") continue d = {k:interpret(v) for k, v in zip(loop_keys, l)} data[group_key].append(d) #print(f"{group_key} ->", f"list of length: {len(loop_values)}") if not as_dict: mmcif = MMCIF(data, cif_path=file_path) if output_folder is not None: save_path = os.path.join(output_folder, f"{name}.header.json") log("debug","Headers saved to:", os.path.abspath(save_path)) mmcif.save(save_path) return mmcif else: return data def write_atoms(atoms,
file_path,
name=None,
include_misc=True,
preserve_ids=False,
mode='w',
key='_atom_site') ‑> str-
Expand source code
def write_atoms(atoms, file_path, name=None, include_misc=True, preserve_ids=False, mode="w", key="_atom_site") -> str: if len(atoms) == 0: return None labels = atoms[0]._mmcif_dict( include_misc=include_misc).keys() if not file_path.endswith(".cif"): file_path += ".cif" os.makedirs(os.path.dirname(file_path), exist_ok=True) if name is None: name = os.path.basename(file_path).split(".")[0] try: with open(file_path, mode) as f: if mode == "w": f.write(f"data_{name}\n") f.write("#\n") f.write("loop_\n") for l in labels: f.write(f"{key}.{l}\n") n = 1 for a in atoms: d = a._mmcif_dict(include_misc=include_misc) if not preserve_ids: d["id"] = f"{n:4d}" if len(d.values()) != len(labels): log("Warning", f"Atom with inconsistent ({d.values()}/{len(labels)}) labels") continue f.write(" ".join(d.values()) + "\n") n+=1 return file_path except Exception as e: log("error", f"Atom write interrupted, deleting corrupted file: {file_path}") os.remove(file_path) log("error", "File deleted successfully!") raise e
Classes
class BIAtom (data)-
Expand source code
class BIAtom(PseudoAtom): def __init__(self, data): if len(data) == 1: data = data[0] for k, v in data.items(): if v == "." or v == "?" or v=="None": #print(k, "is empty for:", data["id"]) #print(data) data[k] = None essential_labs = [ "group_PDB", "id", "type_symbol", "label_alt_id", "label_atom_id", "label_comp_id", "label_seq_id", "label_asym_id", "label_entity_id", "auth_atom_id", "auth_comp_id", "auth_seq_id", "auth_asym_id", "Cartn_x", "Cartn_y", "Cartn_z", "occupancy", "B_iso_or_equiv", "pdbx_PDB_model_num", "pdbx_PDB_ins_code", ] self.misc = {} for k, v in data.items(): if not (k in essential_labs): self.misc[k] = v #ATOM self.atomnum = int(data["id"]) self.type = data["group_PDB"] # ATOM / HETATM self.element = data["type_symbol"].strip() #C self.name = data["label_atom_id"].strip() # CA (Auto) self.name2 = data["auth_atom_id"].strip() # CA (Given) self.prime = False if "'" in self.name: self.prime = True self.name = self.name.replace("'", "").strip() self.name2 = self.name2.replace("'", "").strip() #RES self.resname = data["label_comp_id"] # Auto self.resname2 = data["auth_comp_id"] # Given self.resseq = data["label_seq_id"] # Auto if self.resseq is not None: self.resseq = int(self.resseq) self.resnum = data["auth_seq_id"] # Given if self.resnum is not None: self.resnum = int(self.resnum) #CHAIN self.chain = data["label_asym_id"] # Auto self.complex = data["auth_asym_id"] # Given self.entity = int(data["label_entity_id"]) self.model = int(data["pdbx_PDB_model_num"]) #PROPERTIES self.id = (self.name, self.resnum, self.complex) # Ambiguous (author) self.id2 = (self.name, self.resseq, self.chain, self.entity) # Not ambiguous (label) self.id3 = (self.atomnum, self.type, self.element, self.name, self.resseq, self.chain, self.model) # Unique x = float(data["Cartn_x"]) y = float(data["Cartn_y"]) z = float(data["Cartn_z"]) coord = (x, y, z) super().__init__(coord) self.occupancy = float(data["occupancy"]) self.b = float(data["B_iso_or_equiv"]) self.ins_code = data.get("pdbx_PDB_ins_code", None) #DISORDER if "label_alt_id" not in data: data["label_alt_id"] = "." self.alt_id = data["label_alt_id"] if self.alt_id is None or self.alt_id == "None": self.alt_id = "." if self.alt_id == ".": self.disordered = False self.doppelgangers = None self.favourite = None else: self.disordered = True self.doppelgangers = [] self.favourite = True def print_full(self): if self.disordered: if self.favourite: r = "\n<bi.{} id={}.{} b={} occupancy={} (disordred)>".format(self.__class__.__name__, self.id, self.alt_id, self.b, self.occupancy) for datm in self.doppelgangers: r += "\n - <bi.{} id={}.{} b={} occupancy={} (disordered)>".format(self.__class__.__name__, datm.id, datm.alt_id, datm.b, datm.occupancy) return r else: return "<bi.{} id={}.{} b={} occupancy={} (disordred/not-favourite)>".format(self.__class__.__name__, self.id, self.alt_id, self.b, self.occupancy) else: return "<bi.{} id={} b={}>".format(self.__class__.__name__, self.id, self.b) def __repr__(self): if self.disordered: if self.favourite: return "<bi.{} id={}.{} b={} occupancy={} (disordred)>".format(self.__class__.__name__, self.id, self.alt_id, self.b, self.occupancy) else: return "<bi.{} id={}.{} b={} occupancy={} (disordred/not-favourite)>".format(self.__class__.__name__, self.id, self.alt_id, self.b, self.occupancy) else: return "<bi.{} id={} b={}>".format(self.__class__.__name__, self.id, self.b) def __iter__(self): if not self.disordered or self.doppelgangers is None: self.i = 0 #raise AtomDisorderException("Trying to iterate over a non-disordered atom") return self else: self.i = 0 return self def __next__(self): if not self.disordered or self.doppelgangers is None: if self.i == 0: self.i += 1 return self else: self.i = None raise StopIteration if self.i > len(self.doppelgangers): self.i = None raise StopIteration else: if self.i == 0: self.i += 1 return self else: self.i += 1 #print(len(self.doppelgangers), self.i, self.i-2) return self.doppelgangers[self.i - 2] def set_bfactor(self, bfactor): for t in self: t.b = float(bfactor) return self.b def set_coord(self, coord): for t in self: t.coord = float(coord) return self.b def set_misc(self, label, value): for t in self: t.misc[label] = value return self.misc[label] def get_misc(self, label, keyerror="undefined"): if label in self.misc: return self.misc[label] else: if keyerror == "undefined": raise KeyError else: return keyerror def pdb_string(self, new_id=None): record_name = f"{self.type:<6s}"[-6:] if new_id is None: atom_serial_number = f"{self.atomnum:5d}"[-5:] else: atom_serial_number = f"{new_id:5d}"[-5:] if len(self.element) >= 2 or len(self.name) >= 4: atom_name = f"{self.name:<4s}"[-4:] else: atom_name = f" {self.name:<3s}"[-4:] if self.alt_id is None or self.alt_id==".": alternate_location_indicator = " " else: alternate_location_indicator = f"{self.alt_id:1s}"[0] residue_name = f"{self.resname:>3s}"[:3] chain_identifier = f"{self.chain:1s}"[0] residue_sequence_number = f"{self.resnum:>4d}"[-4:] code_for_insertions_of_residues = " " # ?? xcoord = f"{self.x:>8.3f}"[:8] ycoord = f"{self.y:>8.3f}"[:8] zcoord = f"{self.z:>8.3f}"[:8] occupancy = f"{self.occupancy:>6.2f}"[-6:] temperature_factor = f"{self.b:>6.2f}"[-6:] segment_identifier = f" " # ?? element_symbol = f"{self.element:>2s}"[:2] charge = " " # ?? string = "" string+=record_name # 1-6 string+=atom_serial_number # 7-11 string+=" " # 12 string+=atom_name # 13-16 string+=alternate_location_indicator # 17 string+=residue_name # 18-20 string+=" " # 21 string+=chain_identifier # 22 string+=residue_sequence_number # 23-26 string+=code_for_insertions_of_residues # 26 string+=" " # 28-30 string+=xcoord # 31-38 string+=ycoord # 39-46 string+=zcoord # 47-54 string+=occupancy # 55-60 string+=temperature_factor # 61-66 string+= " " # 67-72 string+=segment_identifier # 73-76 string+=element_symbol # 77-78 string+=charge # 79-80 return string @staticmethod def _none_point(val): if val is None: return "." if type(val) == float: return f"{val:7.3f}" elif type(val) == int: return f"{val:>3d}" elif type(val) in [list, tuple, dict]: log("warning", "Data type not insertable in mmcif:", type(val)) return "." else: return f"{str(val):<3s}" def _mmcif_dict(self, include_misc=True): try: data = {} data["group_PDB"] = f"{self.type:6s}" data["id"] = f"{self.atomnum:4d}" data["label_alt_id"] = f"{self._none_point(self.alt_id):>1s}" data["type_symbol"] = f"{self._none_point(self.element):<2s}" if self.prime and not self.name.endswith("'"): data["label_atom_id"] = f"\"{self.name:>2s}'\"" else: data["label_atom_id"] = f"{self._none_point(self.name):<5s}" data["label_comp_id"] =f"{self._none_point(self.resname):<3s}" data["label_seq_id"] = f"{self._none_point(self.resseq):>3s}" data["label_asym_id"] = f"{self._none_point(self.chain):1s}" data["label_entity_id"] = f"{self._none_point(self.entity):1s}" if self.prime and not self.name2.endswith("'"): data["auth_atom_id"] = f"\"{self._none_point(self.name2):>2s}'\"" else: data["auth_atom_id"] = f"{self._none_point(self.name2):<5s}" data["auth_comp_id"] =f"{self._none_point(self.resname2):<3s}" data["auth_seq_id"] = f"{self._none_point(self.resnum):>3s}" data["auth_asym_id"] = f"{self._none_point(self.complex):1s}" data["pdbx_PDB_model_num"] = f"{self._none_point(self.model):>2s}" data["Cartn_x"] = f"{self.x:7.3f}" data["Cartn_y"] = f"{self.y:7.3f}" data["Cartn_z"] = f"{self.z:7.3f}" data["occupancy"] = f"{self.occupancy:6.3f}" data["B_iso_or_equiv"] = f"{self.b:6.2f}" data["pdbx_PDB_ins_code"] = f"{self._none_point(self.ins_code):>1s}" except Exception as e: log("ERROR", "Generating atom mmcif dict") print(self) print(self.__dict__) raise e if include_misc: for k, v in self.misc.items(): data[k] = self._none_point(v) return dataAncestors
Methods
def get_misc(self, label, keyerror='undefined')-
Expand source code
def get_misc(self, label, keyerror="undefined"): if label in self.misc: return self.misc[label] else: if keyerror == "undefined": raise KeyError else: return keyerror def pdb_string(self, new_id=None)-
Expand source code
def pdb_string(self, new_id=None): record_name = f"{self.type:<6s}"[-6:] if new_id is None: atom_serial_number = f"{self.atomnum:5d}"[-5:] else: atom_serial_number = f"{new_id:5d}"[-5:] if len(self.element) >= 2 or len(self.name) >= 4: atom_name = f"{self.name:<4s}"[-4:] else: atom_name = f" {self.name:<3s}"[-4:] if self.alt_id is None or self.alt_id==".": alternate_location_indicator = " " else: alternate_location_indicator = f"{self.alt_id:1s}"[0] residue_name = f"{self.resname:>3s}"[:3] chain_identifier = f"{self.chain:1s}"[0] residue_sequence_number = f"{self.resnum:>4d}"[-4:] code_for_insertions_of_residues = " " # ?? xcoord = f"{self.x:>8.3f}"[:8] ycoord = f"{self.y:>8.3f}"[:8] zcoord = f"{self.z:>8.3f}"[:8] occupancy = f"{self.occupancy:>6.2f}"[-6:] temperature_factor = f"{self.b:>6.2f}"[-6:] segment_identifier = f" " # ?? element_symbol = f"{self.element:>2s}"[:2] charge = " " # ?? string = "" string+=record_name # 1-6 string+=atom_serial_number # 7-11 string+=" " # 12 string+=atom_name # 13-16 string+=alternate_location_indicator # 17 string+=residue_name # 18-20 string+=" " # 21 string+=chain_identifier # 22 string+=residue_sequence_number # 23-26 string+=code_for_insertions_of_residues # 26 string+=" " # 28-30 string+=xcoord # 31-38 string+=ycoord # 39-46 string+=zcoord # 47-54 string+=occupancy # 55-60 string+=temperature_factor # 61-66 string+= " " # 67-72 string+=segment_identifier # 73-76 string+=element_symbol # 77-78 string+=charge # 79-80 return string def print_full(self)-
Expand source code
def print_full(self): if self.disordered: if self.favourite: r = "\n<bi.{} id={}.{} b={} occupancy={} (disordred)>".format(self.__class__.__name__, self.id, self.alt_id, self.b, self.occupancy) for datm in self.doppelgangers: r += "\n - <bi.{} id={}.{} b={} occupancy={} (disordered)>".format(self.__class__.__name__, datm.id, datm.alt_id, datm.b, datm.occupancy) return r else: return "<bi.{} id={}.{} b={} occupancy={} (disordred/not-favourite)>".format(self.__class__.__name__, self.id, self.alt_id, self.b, self.occupancy) else: return "<bi.{} id={} b={}>".format(self.__class__.__name__, self.id, self.b) def set_bfactor(self, bfactor)-
Expand source code
def set_bfactor(self, bfactor): for t in self: t.b = float(bfactor) return self.b def set_coord(self, coord)-
Expand source code
def set_coord(self, coord): for t in self: t.coord = float(coord) return self.b def set_misc(self, label, value)-
Expand source code
def set_misc(self, label, value): for t in self: t.misc[label] = value return self.misc[label]
Inherited members
class BIChain (*args, **kwargs)-
Expand source code
class BIChain(BIEntity): child_class = BIResidue extension = "chain" level = "chain" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.paths["sub_folder"] = "chains" def find_id(self, method="first"): if method == "first": chain_id = self.atoms()[0].chain else: chain_id = method return chain_id def id(self): return self.data["info"]["chain_id"] def set_chain_id(self, chain_id=None, complex=False): if chain_id is None: chain_id = self.find_id() chain_id = str(chain_id) if len(chain_id) != 1: chain_id = chain_id.replace("_", "-") log(f"warning", "CHAIN ID is longer than 1: {chain_id}") self.set_flag("unconventional_chain_id", True) self.data["info"]["chain_id"] = chain_id if self.has_flag("has_chain_id", True): old_name = self.name().split("_") for n, o in enumerate(old_name): if o == self.id(): old_name[n] = self.id() new_name = "_".join(old_name) self.set_name(new_name, append=False) else: self.set_name(chain_id, append=True) self.set_flag("has_chain_id", True) for a in self.all_atoms(): a.chain = chain_id if complex: a.complex = chain_id return self.id() @classmethod def from_atoms(cls, atoms, code=None, chain_id=None, overwrite_complex=False, **kwargs): self = super().from_atoms(atoms, code, **kwargs) self._atoms = atoms self.set_chain_id(chain_id, complex=overwrite_complex) self.sequence() return self @classmethod def from_file(cls,*args, chain_id=None, overwrite_complex=False, **kwargs): self = super().from_file(*args, **kwargs) self._atoms = self.atoms(chain=chain_id, hetatm=True, disordered=True) self.set_chain_id(chain_id, complex=overwrite_complex) self.sequence() return selfAncestors
Subclasses
Static methods
def from_atoms(atoms, code=None, chain_id=None, overwrite_complex=False, **kwargs)def from_file(*args, chain_id=None, overwrite_complex=False, **kwargs)
Methods
def find_id(self, method='first')-
Expand source code
def find_id(self, method="first"): if method == "first": chain_id = self.atoms()[0].chain else: chain_id = method return chain_id def id(self)-
Expand source code
def id(self): return self.data["info"]["chain_id"] def set_chain_id(self, chain_id=None, complex=False)-
Expand source code
def set_chain_id(self, chain_id=None, complex=False): if chain_id is None: chain_id = self.find_id() chain_id = str(chain_id) if len(chain_id) != 1: chain_id = chain_id.replace("_", "-") log(f"warning", "CHAIN ID is longer than 1: {chain_id}") self.set_flag("unconventional_chain_id", True) self.data["info"]["chain_id"] = chain_id if self.has_flag("has_chain_id", True): old_name = self.name().split("_") for n, o in enumerate(old_name): if o == self.id(): old_name[n] = self.id() new_name = "_".join(old_name) self.set_name(new_name, append=False) else: self.set_name(chain_id, append=True) self.set_flag("has_chain_id", True) for a in self.all_atoms(): a.chain = chain_id if complex: a.complex = chain_id return self.id()
Inherited members
class BIEntity (export_folder=None, parent=None, use_tmp=False, **kwargs)-
Expand source code
class BIEntity(object): child_class = None extension = "structure" level = "structure" tmp_folder = "/tmp" excluded_from_headers = ["_bi_*", "_atom_site", "_aleph_*","_cell", "_symmetry"] def __init__(self, export_folder=None, parent=None, use_tmp=False, **kwargs): if export_folder is None: export_folder = os.path.join(SUBDIR_NAME, "exports") self.children = [] self.paths = { "self": None, # This entity cif path "source": None, "minimal": None, # This but only nice atoms (no headers or data) "parent": None, # Parent entity cif path "export_folder": export_folder, # Folder with all exports (default: "bioiain/exports") "top_folder": None, # Highest related folder "sub_folder": "", # Path of self under top_folder } self.data = { "info": { "code": None, # The code of this structure, if any "name": None, # The name of this structure (used mainly for file naming) "class": self.__class__.__name__, }, "sequences": { "aa": None, }, "symmetry": {}, # Machine "embeddings": {}, # Tools "SASA": {}, "PISA":{}, "DSSP":{}, } self.headers = { "entry":{ "id":None } } self.flags = { "loaded": False, } self.exporting = ["data", "paths", "flags"] #Properties self._com = None self._kdtree = None #CVectors self._cvectors = None # Children self._chains = None self._residues = None self._atoms = None self._mates = None # Crystal self._card = None self._parameters = None self._operations = None if parent is not None: self.paths["export_folder"] = parent.paths["export_folder"] self.paths["parent"] = parent.paths["self"] self.data["info"]["parent"] = repr(parent) self.headers = parent.headers if use_tmp: self.paths["export_folder"] = os.path.join(self.tmp_folder, self.paths["export_folder"] ) def clear_cahces(self): self._com = None self._kdtree = None self._chains = None self._residues = None self._atoms = None self._mates = None self._cvectors = None def __repr__(self): if self.is_symmetry(): return "<{}:{} id={} op={}>".format(self.__class__.__name__, self.code(), self.id(), self.op()) return "<{}:{} id={} (len:{})>".format(self.__class__.__name__, self.code(), self.id(), len(self)) def __str__(self): return repr(self) def __len__(self): return len(self.residues()) def name(self): return str(self.data["info"]["name"]) def set_name(self, name, append=False): if append and self.name() is not None: self.data["info"]["name"] = self.name() + f"_{name}" else: self.data["info"]["name"] = str(name) def path(self, minimal=False): if not minimal: if self.paths.get("self", None) is None: self.export() return self.paths["self"] else: if self.paths.get("minimal", None) is None: self.export(minimal=True) return self.paths["minimal"] def folder(self): folders = [] if self.paths["export_folder"] is not None: folders.append(self.paths["export_folder"]) if self.paths["top_folder"] is not None: folders.append(self.paths["top_folder"]) if self.paths["sub_folder"] is not None: folders.append(self.paths["sub_folder"]) return os.path.join(*folders) def code(self): return str(self.data["info"]["code"]) def id(self): return str(self.data["info"]["code"]) def get_sequence(self, name="aa"): return self.data["sequences"][name] def set_sequence(self, name, seq): self.data["sequences"][name] = seq return self.data["sequences"][name] def has_flag(self, flag, value=None): if value is None: return flag in self.flags return self.flags.get(flag) == value def set_flag(self, flag, value=True): self.flags[flag] = value def sequence(self, force=False): if self.get_sequence() is None or force: seq = "".join([r.rn1 for r in self.residues()]) self.data["sequences"]["aa"] = seq return self.get_sequence() def structure(self, code=None): from .structure import BIStructure if code is None: code = str(self.data["info"]["code"]) return BIStructure.from_atoms(self._atoms, code, parent=self) def chains(self): return self.atoms(as_chains=True, hetatm=True) def residues(self): return self.atoms(ca_only=False, residues=True) def waters(self): return self.atoms(ca_only=False, water=True) def dna(self): return self.atoms(ca_only=False, dna=True) def ligands(self, relevant_only=True): return [l for l in self.atoms(ca_only=False, ligands=True) if l.relevant] def cvectors(self, vc_mode=None): if self._cvectors is None or vc_mode != getattr(self, "_vc_mode", None): self._calculate_cvectors(vc_mode=vc_mode) else: log(1, f"Using previously saved CVectors ({vc_mode})...") return self._cvectors def _calculate_cvectors(self, vc_mode=None): log(1, "Calculating CVectors for:", self.name(), f"({vc_mode})") from ..aleph.vectors import CVector residues = self.residues() n_res = len(residues) cvector_list = [] for n, res in enumerate(residues): if n == 0 or n == n_res -1: continue print(f"{n:4d}/{len(residues)-2:4d}", end="\r") cvector = CVector(residues[n-1], res, residues[n+1], params=self.params(), symops=self.symops(), entity_centre=self.com(), vc_mode=vc_mode) if cvector.trash: continue cvector_list.append(cvector) log(2, f"n CVectors: {len(cvector_list)}") self._cvectors = cvector_list self._vc_mode = vc_mode return cvector_list def atoms(self, ca_only=False, hetatm=False, ligands=False, residues=False, dna=False, water=False, hydrogens=False, force=False, group_by_residue=False, disordered=False, as_residues=False, chain=None, group_by_chain=False, as_chains=False, **kwargs): from .atom import _fix_disordered from .residue import build_res target_entities = [] if as_residues: residues = True if residues: target_entities.append("residue") if dna: target_entities.append("dna") if water or ligands: hetatm = True ca_only = False if water: target_entities.append("water") if ligands: target_entities.append("ligand") atoms = self.all_atoms() if not disordered: atoms = _fix_disordered(atoms) if not hetatm: atoms = [a for a in atoms if a.type == "ATOM"] if not hydrogens: atoms = [a for a in atoms if a.element != "H"] if ca_only: atoms = [a for a in atoms if a.name == "CA"] if not water: atoms = [a for a in atoms if a.name != "HOH"] if chain is not None: atoms = [a for a in atoms if a.chain == chain] if as_chains or group_by_chain: from .chain import BIChain chain_list = {} for atom in atoms: if atom.complex in chain_list.keys(): chain_list[atom.complex].append(atom) else: chain_list[atom.complex] = [atom] if as_chains: for ch, atms in chain_list.items(): chain_list[ch] = BIChain().from_atoms(atms, self.code(), ch, parent=self) chain_list = [ch for ch in chain_list.values()] return chain_list if group_by_residue or len(target_entities) > 0: atoms_by_res = {} for atom in atoms: if atom.id2[1:] in atoms_by_res: atoms_by_res[atom.id2[1:]].append(atom) else: atoms_by_res[atom.id2[1:]] = [atom] if len(target_entities) > 0: entities = [] for resatms in atoms_by_res.values(): e = build_res(resatms, parent=self) if getattr(e, "type", None) in target_entities: entities.append(e) return entities return atoms_by_res return atoms def remove_atom(self, atom): try: print(len(self._atoms)) self._atoms.remove(atom) print(len(self._atoms)) log(f"warning", f"Atom {atom} removed") except: log("warning", f"Atom {atom} not found, not removed") return False return True def set_symmetry(self): pass @classmethod def from_atoms(cls, atoms, code=None, share=True, **kwargs): self = cls(**kwargs) if share: self._atoms = atoms else: self._atoms = [a.copy() for a in atoms] if code is None and kwargs.get("parent", None) is not None: code = kwargs["parent"].code() if code is not None: self.data["info"]["code"] = clean_string(code).upper() #self.data["info"]["code"] = str(code) self.set_name(self.code()) self.paths["top_folder"] = self.code() return self @classmethod def from_file(cls, filepath, code="auto", file_format="auto", force=False, check_existing=True, source=None, **kwargs): log(1, "Loading from file:", filepath) if not os.path.exists(filepath): raise FileNotFoundError(filepath) self = cls(**kwargs) if file_format == "auto": file_format = filepath.split(".")[-1] if file_format not in ["cif", "pdb"]: raise UnknownFormat(file_format) try: self._all_atoms(filepath=filepath, force=True, is_pdb=file_format == "pdb", **kwargs) except (StructureLoadException, CrystalError) as e: log("Error", f"Structure not loaded: {filepath}", e) raise e return None if code == "auto": code = str(read_mmcif(filepath, subset="_entry")["_entry.id"]) if code == "file" or code is None: code = filepath.split(".")[0] self.data["info"]["code"] = clean_string(code).upper() self.headers["entry"]["id"] = self.code() self.paths["top_folder"] = self.code() self.set_name(self.code()) self.set_flag("fractional", False) log(2, "Reading CIF data...") data = read_mmcif(filepath, subset="_bi_*") if len(data) > 0: #print(data) for k in data.keys(): kk = k.split("_")[2] ss = "_".join(k.split("_")[3:]) old_data = getattr(self, kk, {}) new_data = old_data if ss is None or ss == "": new_data = new_data | data[k] else: new_data[ss] = new_data.get(ss, {}) | data[k] setattr(self, kk, new_data) self.set_flag("data_recovered", True) else: self.set_flag("data_recovered", False) if self.paths["self"] != filepath: self.paths["source"] = filepath elif source is not None: self.paths["source"] = source if check_existing and not force: prev_path = self.export(dry=True) if os.path.exists(prev_path): log("warning", "Recovering previously exported file:", prev_path) try: recovered_self = cls.from_file(prev_path, check_existing=False, source=filepath, **kwargs) if recovered_self is None: raise StructureRecoverException() return recovered_self except Exception as e: log("warning", e.__class__.__name__, e) log("Error", "Recovery failed (returning new)") raise self.recover_cvectors() self.set_flag("loaded", True) self.export() return self def recover_cvectors(self): pass def from_biopython(self, entity): pass def com(self, force=False): from ..utilities.maths import find_com if self._com is None or force: self._com = find_com(self) return self._com def all_atoms(self): if self._atoms is None: self._all_atoms() return self._atoms def _all_atoms(self, filepath=None, force=False, require_crystal=True, **kwargs): from .atom import BIAtom if filepath is None: filepath = self.paths.get("self", None) if not hasattr(self, "_atoms"): force = True elif self._atoms is None: force = True if force: if filepath is None: self.export() if not os.path.exists(filepath): self.export() log(1, "Reading atoms from CIF:", filepath) mmcif= read_mmcif(filepath, subset=["_atom_site", "_cell", "_symmetry"]) atoms=mmcif("_atom_site") if atoms is None: raise StructureLoadException("No atoms") self.headers["cell"] = mmcif("_cell") self.headers["symmetry"] = mmcif("_symmetry") self.data["symmetry"]["in_asu"] = True try: self._calculate_crystal() except CrystalError as e: if require_crystal: raise e atoms = [BIAtom(a) for a in atoms] self._atoms = atoms return self._atoms def fix_headers(self): if self.headers["symmetry"].get("space_group_name_H-M", None) is not None: self.headers["symmetry"]["space_group_name_H-M"] = f"\'{self.headers["symmetry"]["space_group_name_H-M"]}\'" def export(self, minimal=False, cleanup=False, as_pdb=False, target_folder=None, sufix=None, dry=False, all_headers=True): custom_folder = False if target_folder is None: target_folder = self.paths["export_folder"] else: custom_folder = True fname = str(self.name()) if sufix is not None: custom_folder = True if sufix[0] in [".", "-", "_", "(",]: fname += sufix else: fname += "_"+sufix fname += f".{self.extension}" if minimal: fname += ".minimal" try: base_folder = os.path.join(target_folder, self.paths.get("top_folder", self.code()), self.paths["sub_folder"].strip() ) except TypeError: print(self.paths) raise os.makedirs(base_folder, exist_ok=True) base_path = os.path.join(base_folder, fname) if dry: return base_path+".cif" log(2, f"Exporting: {self} to {base_path}") if self.has_flag("is_fractional", True): log("Warning", "A fractional entity was about to be exported!") log("Warning", "An orthogonal copy was made for you and exported instead! (only for atoms)") orth = self.copy()._to_orthogonal() else: orth = self if minimal: minimal_path= orth._export_structure(base_path, headers=False, all_headers=False, misc_fields=True, cleanup=True, as_pdb=as_pdb) if not custom_folder: self.paths["minimal"] = minimal_path return minimal_path else: path = orth._export_structure(base_path, headers=True, all_headers=all_headers, misc_fields=True, cleanup=cleanup, as_pdb=as_pdb) if not custom_folder: self.paths["self"] = path if not as_pdb: self.set_flag("exported", True) self.paths["data"] = self._export_data(base_path) return path def _export_data(self, filepath, mode="w") -> str: if filepath.endswith(".cif"): filepath = filepath.replace(".cif", ".json") elif not filepath.endswith(".json"): filepath += ".json" exp = {e: self.__getattribute__(e) for e in self.exporting if hasattr(self, e)} with open(filepath, mode) as f: f.write(json.dumps(exp, indent=4)) return filepath def _export_structure(self, filepath:str, atoms:list=None, headers:bool=None, misc_fields:bool=True, cleanup=True, as_pdb=False, all_headers=True, cvectors=True, cvmatrix=True) -> str: log(2,"Exporting structure...") mode = "w" if atoms is None: if cleanup: atoms = self.atoms() else: #log("Warning", "Exporting all atoms and misc fields might corrupt the file (cleanup=True recommended)") atoms = self._all_atoms() if as_pdb: return write_pdb_atoms(atoms, filepath, mode=mode, end=True) else: full_headers = {} if all_headers and headers and self.paths.get("source", None) is not None: full_headers = read_mmcif(self.paths.get("source", None), exclude=self.excluded_from_headers).dict() #print(full_headers.keys()) if headers: for e in self.exporting: d = getattr(self, e) if e == "data": for k, v in d.items(): write_dict(v, file_path=filepath, label=f"bi_{e}_{k}", mode=mode, name=self.name()) mode = "a" else: write_dict(d, file_path=filepath, label=f"bi_{e}", mode=mode, name=self.name()) mode = "a" # print(type(self.headers)) # print(self.headers) full_headers = full_headers | self.headers for k, d in full_headers.items(): if type(d) is list and len(d) > 1: write_dict_list(d, file_path=filepath, label=k, mode=mode, name=self.name()) mode = "a" else: if type(d) is list: d = d[0] write_dict(d, file_path=filepath, label=k, mode=mode, name=self.name()) mode = "a" #print(self) #log(3, "CVECTORS", cvectors, self._cvectors is not None) #print(self._cvectors) #log(3,"CVMATRIX", cvmatrix, getattr(self, "_cvmatrix", None) is not None) #print(getattr(self, "_cvmatrix", None)) if cvectors and (self._cvectors is not None): log(3, "Exporting cvectors...") write_dict_list(self._cvectors, file_path=filepath, label="aleph_cvectors", mode=mode, name=self.name()) mode = "a" if cvmatrix and (getattr(self, "_cvmatrix", None) is not None): log(3, "Exporting cvmatrix...") write_dict_list([v.closest_vp for v in self._cvmatrix.vectors], file_path=filepath, label="aleph_cvmatrix", mode=mode, name=self.name()) mode = "a" log(3, "Exporting atoms...") return write_atoms(atoms, filepath, name=self.name(), include_misc=misc_fields, mode=mode) @classmethod def recover_from_id(cls, code, endswith=None, full_name=None, **kwargs): placeholder = cls(**kwargs) path = os.path.join(placeholder.paths["export_folder"], code, placeholder.paths["sub_folder"]) if full_name is not None: path = os.path.join(path, full_name) else: if not os.path.exists(path): raise StructureNotFound(path) for file in os.listdir(path): ext = file.split(".")[-1] if ext != "json": continue extension= file.split(".")[-2] if extension != placeholder.extension: continue if endswith is not None: if not file.split(".")[0].endswith(endswith): continue path = os.path.join(path, file) return cls.recover_from_path(path, **kwargs) raise StructureNotFound(path) # @classmethod # def recover_from_path(cls, cif_path, **kwargs): # # # if path.endswith(".json"): # # data_path = path # # cif_path = data_path.replace(".json", ".cif") # # elif path.endswith(".cif"): # # cif_path = path # # data_path = cif_path.replace(".cif", ".json") # # else: # # cif_path = path+".cif" # # data_path = path+".json" # # # # if os.path.exists(cif_path): # # self = cls.from_file(cif_path, check_existing=False, **kwargs) # # if self.has_flag("data_recovered", True): # # log(3, "Recovering data from CIF...") # # else: # # raise FileNotFoundError(path, cif_path) # # elif os.path.exists(data_path): # # raw = json.load(open(path, "r")) # # self = cls.from_file(raw["paths"]["self"], check_existing=False, **kwargs) # # else: # # raise FileNotFoundError(path, cif_path, data_path) # # # # if os.path.exists(data_path) and raw is None: # # log(3, "Recovering data from json...") # # raw = json.load(open(path, "r")) # # for k, v in raw.items(): # # setattr(self, k, v) # # elif raw is None: # # log("Warning", "Data file not found for:", path) # # return self def _calculate_crystal(self): try: self._get_crystal_card() self._get_operations() except Exception as e: self.set_flag("crystal_error", True) raise CrystalError() def _get_crystal_card(self): cell = self.headers["cell"] a = float(cell["length_a"]) b = float(cell["length_b"]) c = float(cell["length_c"]) alpha = float(cell["angle_alpha"]) beta = float(cell["angle_beta"]) gamma = float(cell["angle_gamma"]) Z = float(cell["Z_PDB"]) card = dict(a=a, b=b, c=c, alpha=alpha, beta=beta, gamma=gamma, Z=Z) self._card = card return self._card def _get_operations(self): from ..utilities.space_groups import dictio_space_groups space_group_key = self.headers["symmetry"].get("Int_Tables_number") space_group_key = int(space_group_key) self._operations = dictio_space_groups[space_group_key] return self._operations def operations(self): if self._operations is None: self._get_operations() return self._operations def symops(self, n=None): if self._operations is None: self._get_operations() if n is None: return self._operations["symops"] else: return self._operations["symops"][n] def params(self): if self._parameters is None: self._calculate_parameters() return self._parameters def _calculate_parameters(self) -> dict: if self._card is None: self._get_crystal_card() card = self._card parameters = {} parameters["A"] = A = float(card["a"]) parameters["B"] = B = float(card["b"]) parameters["C"] = C = float(card["c"]) parameters["alphaDeg"] = alphaDeg = float(card["alpha"]) parameters["betaDeg"] = betaDeg = float(card["beta"]) parameters["gammaDeg"] = gammaDeg = float(card["gamma"]) parameters["alpha"] = alpha = (alphaDeg * 2 * np.pi) / 360 parameters["beta"] = beta = (betaDeg * 2 * np.pi) / 360 parameters["gamma"] = gamma = (gammaDeg * 2 * np.pi) / 360 parameters["c_a"] = c_a = np.cos(alpha) parameters["c_b"] = c_b = np.cos(beta) parameters["c_g"] = c_g = np.cos(gamma) parameters["s_g"] = s_g = np.sin(gamma) parameters["q"] = q = np.sqrt(1 + 2 * c_a * c_b * c_g - c_a ** 2 - c_b ** 2 - c_g ** 2) parameters["uu"] = uu = s_g / (q * C) parameters["vv"] = vv = (c_b * c_g - c_a) / (q * B * s_g) parameters["uuy"] = uuy = 1 / (B * s_g) parameters["vvz"] = vvz = -1 * (c_g / (A * s_g)) parameters["uuz"] = uuz = (c_a * c_g - c_b) / (q * A * s_g) parameters["vvy"] = vvy = 1 / A self._parameters = parameters return self._parameters def fragment(self, in_place=False, force=False): from ..aleph.fragments import FragmentedStructure if isinstance(self, FragmentedStructure): if not in_place: frag = self.copy() else: frag = self else: frag = FragmentedStructure.from_atoms(self.all_atoms(), parent=self, share=in_place, export_folder=self.paths["export_folder"]) frag.fragments(force=force) return frag def copy(self): from copy import deepcopy new = self.__class__.from_atoms(self.all_atoms(), parent=self, share=False) new.data = deepcopy(self.data) new.flags = deepcopy(self.flags) new.set_flag("is_copy", True) new.set_flag("exported", False) return new def displace(self, distance:float|int|list[float|int]|tuple[float|int], inplace=True): if not inplace: self = self.copy() for a in self.all_atoms(): a + distance return self def _to_fractional(self): if self.has_flag("is_fractional", True): raise AlreadyFractional(self) for a in self.all_atoms(): a.to_frac(self.params()) self.set_flag("is_fractional", True) def _to_orthogonal(self): if self.has_flag("is_fractional", False): raise AlreadyOrthogonal(self) for a in self.all_atoms(): a.to_orth(self.params()) self.set_flag("is_fractional", False) def _symmetry_operation(self, symop): was_orth = False if self.has_flag("is_fractional", False): was_orth = True self._to_fractional() for a in self.all_atoms(): a.symop(self.symops(symop), self.params()) if was_orth: self._to_orthogonal() return self def is_symmetry(self): return self.has_flag("is_symmetry", True) def op(self): return self.data["symmetry"].get("symop", None) def symmetry(self, symop, in_place=False): if not in_place: self = self.copy() self._symmetry_operation(symop) self.set_flag("is_symmetry", True) self.data["symmetry"]["in_asu"] = False self.data["symmetry"]["symop"] = symop return self def mates(self): mates = [] for symop in self.symops().keys(): mates.append(self.symmetry(symop)) self._mates = mates return self._mates def show(self, execute=True, script=None): if script is None: from ..visualisation.pymol import PymolScript script = PymolScript(self.name()) script.load(self.export(), self.name()) script.spectrum(self.name()) script.orient() script.write_script() if execute: script.execute() return script def av_sasa(self, force=False, **kwargs): if force or self.data["SASA"].get("average", None) is not None: av = 0 total = 0 sasas = self.sasa_list(**kwargs) for s in sasas: if s is not None: av += s total += 1 if total == 0: av = None else: av /= total self.data["SASA"]["average"] = av return self.data["SASA"]["average"] def sasa_list(self, **kwargs): self.calculate_sasa(**kwargs) sasas = [] for a in self.atoms(**kwargs): sasas.append(a.get_misc("SASA", None)) return sasas def calculate_sasa(self, force=False, **kwargs): if not self.has_flag("sasa_calculated") or force: from ..tools.SASA import SASA sasa = SASA(**kwargs) sasa.compute(self, **kwargs) return self def assembly_level(self): pass def _calculate_pisa(self, **kwargs): from ..tools.PISA import PISA pisa = PISA(pisa_id=self.name(), **kwargs)Subclasses
Class variables
var child_class-
The type of the None singleton.
var excluded_from_headers-
The type of the None singleton.
var extension-
The type of the None singleton.
var level-
The type of the None singleton.
var tmp_folder-
The type of the None singleton.
Static methods
def from_atoms(atoms, code=None, share=True, **kwargs)def from_file(filepath,
code='auto',
file_format='auto',
force=False,
check_existing=True,
source=None,
**kwargs)def recover_from_id(code, endswith=None, full_name=None, **kwargs)
Methods
def all_atoms(self)-
Expand source code
def all_atoms(self): if self._atoms is None: self._all_atoms() return self._atoms def assembly_level(self)-
Expand source code
def assembly_level(self): pass def atoms(self,
ca_only=False,
hetatm=False,
ligands=False,
residues=False,
dna=False,
water=False,
hydrogens=False,
force=False,
group_by_residue=False,
disordered=False,
as_residues=False,
chain=None,
group_by_chain=False,
as_chains=False,
**kwargs)-
Expand source code
def atoms(self, ca_only=False, hetatm=False, ligands=False, residues=False, dna=False, water=False, hydrogens=False, force=False, group_by_residue=False, disordered=False, as_residues=False, chain=None, group_by_chain=False, as_chains=False, **kwargs): from .atom import _fix_disordered from .residue import build_res target_entities = [] if as_residues: residues = True if residues: target_entities.append("residue") if dna: target_entities.append("dna") if water or ligands: hetatm = True ca_only = False if water: target_entities.append("water") if ligands: target_entities.append("ligand") atoms = self.all_atoms() if not disordered: atoms = _fix_disordered(atoms) if not hetatm: atoms = [a for a in atoms if a.type == "ATOM"] if not hydrogens: atoms = [a for a in atoms if a.element != "H"] if ca_only: atoms = [a for a in atoms if a.name == "CA"] if not water: atoms = [a for a in atoms if a.name != "HOH"] if chain is not None: atoms = [a for a in atoms if a.chain == chain] if as_chains or group_by_chain: from .chain import BIChain chain_list = {} for atom in atoms: if atom.complex in chain_list.keys(): chain_list[atom.complex].append(atom) else: chain_list[atom.complex] = [atom] if as_chains: for ch, atms in chain_list.items(): chain_list[ch] = BIChain().from_atoms(atms, self.code(), ch, parent=self) chain_list = [ch for ch in chain_list.values()] return chain_list if group_by_residue or len(target_entities) > 0: atoms_by_res = {} for atom in atoms: if atom.id2[1:] in atoms_by_res: atoms_by_res[atom.id2[1:]].append(atom) else: atoms_by_res[atom.id2[1:]] = [atom] if len(target_entities) > 0: entities = [] for resatms in atoms_by_res.values(): e = build_res(resatms, parent=self) if getattr(e, "type", None) in target_entities: entities.append(e) return entities return atoms_by_res return atoms def av_sasa(self, force=False, **kwargs)-
Expand source code
def av_sasa(self, force=False, **kwargs): if force or self.data["SASA"].get("average", None) is not None: av = 0 total = 0 sasas = self.sasa_list(**kwargs) for s in sasas: if s is not None: av += s total += 1 if total == 0: av = None else: av /= total self.data["SASA"]["average"] = av return self.data["SASA"]["average"] def calculate_sasa(self, force=False, **kwargs)-
Expand source code
def calculate_sasa(self, force=False, **kwargs): if not self.has_flag("sasa_calculated") or force: from ..tools.SASA import SASA sasa = SASA(**kwargs) sasa.compute(self, **kwargs) return self def chains(self)-
Expand source code
def chains(self): return self.atoms(as_chains=True, hetatm=True) def clear_cahces(self)-
Expand source code
def clear_cahces(self): self._com = None self._kdtree = None self._chains = None self._residues = None self._atoms = None self._mates = None self._cvectors = None def code(self)-
Expand source code
def code(self): return str(self.data["info"]["code"]) def com(self, force=False)-
Expand source code
def com(self, force=False): from ..utilities.maths import find_com if self._com is None or force: self._com = find_com(self) return self._com def copy(self)-
Expand source code
def copy(self): from copy import deepcopy new = self.__class__.from_atoms(self.all_atoms(), parent=self, share=False) new.data = deepcopy(self.data) new.flags = deepcopy(self.flags) new.set_flag("is_copy", True) new.set_flag("exported", False) return new def cvectors(self, vc_mode=None)-
Expand source code
def cvectors(self, vc_mode=None): if self._cvectors is None or vc_mode != getattr(self, "_vc_mode", None): self._calculate_cvectors(vc_mode=vc_mode) else: log(1, f"Using previously saved CVectors ({vc_mode})...") return self._cvectors def displace(self,
distance: float | int | list[float | int] | tuple[float | int],
inplace=True)-
Expand source code
def displace(self, distance:float|int|list[float|int]|tuple[float|int], inplace=True): if not inplace: self = self.copy() for a in self.all_atoms(): a + distance return self def dna(self)-
Expand source code
def dna(self): return self.atoms(ca_only=False, dna=True) def export(self,
minimal=False,
cleanup=False,
as_pdb=False,
target_folder=None,
sufix=None,
dry=False,
all_headers=True)-
Expand source code
def export(self, minimal=False, cleanup=False, as_pdb=False, target_folder=None, sufix=None, dry=False, all_headers=True): custom_folder = False if target_folder is None: target_folder = self.paths["export_folder"] else: custom_folder = True fname = str(self.name()) if sufix is not None: custom_folder = True if sufix[0] in [".", "-", "_", "(",]: fname += sufix else: fname += "_"+sufix fname += f".{self.extension}" if minimal: fname += ".minimal" try: base_folder = os.path.join(target_folder, self.paths.get("top_folder", self.code()), self.paths["sub_folder"].strip() ) except TypeError: print(self.paths) raise os.makedirs(base_folder, exist_ok=True) base_path = os.path.join(base_folder, fname) if dry: return base_path+".cif" log(2, f"Exporting: {self} to {base_path}") if self.has_flag("is_fractional", True): log("Warning", "A fractional entity was about to be exported!") log("Warning", "An orthogonal copy was made for you and exported instead! (only for atoms)") orth = self.copy()._to_orthogonal() else: orth = self if minimal: minimal_path= orth._export_structure(base_path, headers=False, all_headers=False, misc_fields=True, cleanup=True, as_pdb=as_pdb) if not custom_folder: self.paths["minimal"] = minimal_path return minimal_path else: path = orth._export_structure(base_path, headers=True, all_headers=all_headers, misc_fields=True, cleanup=cleanup, as_pdb=as_pdb) if not custom_folder: self.paths["self"] = path if not as_pdb: self.set_flag("exported", True) self.paths["data"] = self._export_data(base_path) return path def fix_headers(self)-
Expand source code
def fix_headers(self): if self.headers["symmetry"].get("space_group_name_H-M", None) is not None: self.headers["symmetry"]["space_group_name_H-M"] = f"\'{self.headers["symmetry"]["space_group_name_H-M"]}\'" def folder(self)-
Expand source code
def folder(self): folders = [] if self.paths["export_folder"] is not None: folders.append(self.paths["export_folder"]) if self.paths["top_folder"] is not None: folders.append(self.paths["top_folder"]) if self.paths["sub_folder"] is not None: folders.append(self.paths["sub_folder"]) return os.path.join(*folders) def fragment(self, in_place=False, force=False)-
Expand source code
def fragment(self, in_place=False, force=False): from ..aleph.fragments import FragmentedStructure if isinstance(self, FragmentedStructure): if not in_place: frag = self.copy() else: frag = self else: frag = FragmentedStructure.from_atoms(self.all_atoms(), parent=self, share=in_place, export_folder=self.paths["export_folder"]) frag.fragments(force=force) return frag def from_biopython(self, entity)-
Expand source code
def from_biopython(self, entity): pass def get_sequence(self, name='aa')-
Expand source code
def get_sequence(self, name="aa"): return self.data["sequences"][name] def has_flag(self, flag, value=None)-
Expand source code
def has_flag(self, flag, value=None): if value is None: return flag in self.flags return self.flags.get(flag) == value def id(self)-
Expand source code
def id(self): return str(self.data["info"]["code"]) def is_symmetry(self)-
Expand source code
def is_symmetry(self): return self.has_flag("is_symmetry", True) def ligands(self, relevant_only=True)-
Expand source code
def ligands(self, relevant_only=True): return [l for l in self.atoms(ca_only=False, ligands=True) if l.relevant] def mates(self)-
Expand source code
def mates(self): mates = [] for symop in self.symops().keys(): mates.append(self.symmetry(symop)) self._mates = mates return self._mates def name(self)-
Expand source code
def name(self): return str(self.data["info"]["name"]) def op(self)-
Expand source code
def op(self): return self.data["symmetry"].get("symop", None) def operations(self)-
Expand source code
def operations(self): if self._operations is None: self._get_operations() return self._operations def params(self)-
Expand source code
def params(self): if self._parameters is None: self._calculate_parameters() return self._parameters def path(self, minimal=False)-
Expand source code
def path(self, minimal=False): if not minimal: if self.paths.get("self", None) is None: self.export() return self.paths["self"] else: if self.paths.get("minimal", None) is None: self.export(minimal=True) return self.paths["minimal"] def recover_cvectors(self)-
Expand source code
def recover_cvectors(self): pass def remove_atom(self, atom)-
Expand source code
def remove_atom(self, atom): try: print(len(self._atoms)) self._atoms.remove(atom) print(len(self._atoms)) log(f"warning", f"Atom {atom} removed") except: log("warning", f"Atom {atom} not found, not removed") return False return True def residues(self)-
Expand source code
def residues(self): return self.atoms(ca_only=False, residues=True) def sasa_list(self, **kwargs)-
Expand source code
def sasa_list(self, **kwargs): self.calculate_sasa(**kwargs) sasas = [] for a in self.atoms(**kwargs): sasas.append(a.get_misc("SASA", None)) return sasas def sequence(self, force=False)-
Expand source code
def sequence(self, force=False): if self.get_sequence() is None or force: seq = "".join([r.rn1 for r in self.residues()]) self.data["sequences"]["aa"] = seq return self.get_sequence() def set_flag(self, flag, value=True)-
Expand source code
def set_flag(self, flag, value=True): self.flags[flag] = value def set_name(self, name, append=False)-
Expand source code
def set_name(self, name, append=False): if append and self.name() is not None: self.data["info"]["name"] = self.name() + f"_{name}" else: self.data["info"]["name"] = str(name) def set_sequence(self, name, seq)-
Expand source code
def set_sequence(self, name, seq): self.data["sequences"][name] = seq return self.data["sequences"][name] def set_symmetry(self)-
Expand source code
def set_symmetry(self): pass def show(self, execute=True, script=None)-
Expand source code
def show(self, execute=True, script=None): if script is None: from ..visualisation.pymol import PymolScript script = PymolScript(self.name()) script.load(self.export(), self.name()) script.spectrum(self.name()) script.orient() script.write_script() if execute: script.execute() return script def structure(self, code=None)-
Expand source code
def structure(self, code=None): from .structure import BIStructure if code is None: code = str(self.data["info"]["code"]) return BIStructure.from_atoms(self._atoms, code, parent=self) def symmetry(self, symop, in_place=False)-
Expand source code
def symmetry(self, symop, in_place=False): if not in_place: self = self.copy() self._symmetry_operation(symop) self.set_flag("is_symmetry", True) self.data["symmetry"]["in_asu"] = False self.data["symmetry"]["symop"] = symop return self def symops(self, n=None)-
Expand source code
def symops(self, n=None): if self._operations is None: self._get_operations() if n is None: return self._operations["symops"] else: return self._operations["symops"][n] def waters(self)-
Expand source code
def waters(self): return self.atoms(ca_only=False, water=True)
class BIResidue (atoms, require_ca=True, **kwargs)-
Expand source code
class BIResidue(object): child_class = BIAtom type="residue" def __init__(self, atoms, require_ca=True, **kwargs): if type(atoms) == dict: atoms = atoms.values() self.atoms = [a for a in atoms if a.element != "H"] self.ca = None self.cb = None self.c = None self.o = None self.n = None self.resnum = None self.resname = None self.rn1 = None self.resseq = None self.chain = None self.complex = None self.fragment = None self.is_residue = True self.is_disordered = False self._sasa = None self._av_sasa = None self._norm_sasa = None for a in self.atoms: if len(a.resname) == 2: log("Warning", "(DEPRECATED use to initialise a nucleotide. Use build res instead") self.__class__ = BINucleoutide self.__init__(self.atoms) break if a.name == "CA": self.ca = a elif a.name == "CB": self.cb = a elif a.name == "C": self.c = a elif a.name == "O": self.o = a elif a.name == "N": self.n = a self.backbone = [self.ca, self.c, self.o, self.n] if self.is_residue: if len(self.atoms) == 1: log("Warning", "Only one atom given to residue, treating as CA") self.ca = self.atoms[0] if self.ca is None: if require_ca: log("error", "Trying to initialise residue with no CA") print([a.name for a in self.atoms]) raise NoCaFound() self.set_fragment() self.resnum = self.ca.resnum self.resname = self.ca.resname try: self.rn1 = d3to1[self.resname] except: self.rn1 = "X" self.resseq = self.ca.resseq self.chain = self.ca.chain self.entity = self.ca.entity self.complex = self.ca.complex self.is_disordered = not self.ca.ins_code is None if self.is_disordered: raise NotImplementedError() if self.fragment is None: self.id = ( self.resname, self.resnum, self.resseq, self.chain, self.complex , self.entity) else: self.id = ( self.resname, self.resnum, self.resseq, self.chain, self.entity, self.complex, self.fragment) if any([a is None for a in self.backbone]): log("error", "Trying to initialise residue with no backbone") print(self.backbone) print(self) raise NoBackbone() def __repr__(self): return f"<bi.{self.__class__.__name__} id={self.id}>" def name(self): return "_".join([str(v) for v in self.id]) def to_atoms(self, key, value): for atom in self.atoms: atom.set_misc(key, value) def bfactor(self): return self.ca.b def set_bfactor(self, bfactor): for a in self.atoms: a.set_bfactor(bfactor) return self def set_fragment(self, fragment=None, unset=False): if fragment is not None or unset: for a in self.atoms: a.set_misc(fragment) self.fragment = self.ca.get_misc("fragment", None) def sasa(self, normalised=False, average=False, force=False): if normalised: if self._norm_sasa is None or force: self._read_sasa() return self._norm_sasa elif average: if self._av_sasa is None or force: self._read_sasa() return self._av_sasa else: if self._sasa is None or force: self._read_sasa() return self._sasa def _read_sasa(self): from ..tools.SASA import residue_sasas summ = 0 total = 0 for a in self.atoms: s = a.get_misc("SASA") summ += s total += 1 av = summ / total if total != 0 else None self._sasa = summ self._av_sasa = av self._norm_sasa = min(1, summ / residue_sasas[self.resname]) if self.resname in residue_sasas and av is not None else None #print( self._av_sasa, self._sasa, residue_sasas[self.resname], self._norm_sasa) return self._sasaClass variables
var child_class-
The type of the None singleton.
var type-
The type of the None singleton.
Methods
def bfactor(self)-
Expand source code
def bfactor(self): return self.ca.b def name(self)-
Expand source code
def name(self): return "_".join([str(v) for v in self.id]) def sasa(self, normalised=False, average=False, force=False)-
Expand source code
def sasa(self, normalised=False, average=False, force=False): if normalised: if self._norm_sasa is None or force: self._read_sasa() return self._norm_sasa elif average: if self._av_sasa is None or force: self._read_sasa() return self._av_sasa else: if self._sasa is None or force: self._read_sasa() return self._sasa def set_bfactor(self, bfactor)-
Expand source code
def set_bfactor(self, bfactor): for a in self.atoms: a.set_bfactor(bfactor) return self def set_fragment(self, fragment=None, unset=False)-
Expand source code
def set_fragment(self, fragment=None, unset=False): if fragment is not None or unset: for a in self.atoms: a.set_misc(fragment) self.fragment = self.ca.get_misc("fragment", None) def to_atoms(self, key, value)-
Expand source code
def to_atoms(self, key, value): for atom in self.atoms: atom.set_misc(key, value)
class BIStructure (*args, **kwargs)-
Expand source code
class BIStructure(BIEntity): child_class = BIChain extension = "structure" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def structure(self, *args, **kwargs): return selfAncestors
Subclasses
Methods
def structure(self, *args, **kwargs)-
Expand source code
def structure(self, *args, **kwargs): return self
Inherited members
class Ligand (atoms, parent=None, relevance_threshold=15, **kwargs)-
Expand source code
class Ligand(object): child_class = BIAtom type = "ligand" def __init__(self, atoms, parent=None, relevance_threshold=15, **kwargs): self.atoms = atoms self.name = self.atoms[0].resname self.chain = self.atoms[0].chain self.complex = self.atoms[0].complex self.entity = self.atoms[0].entity self.model = self.atoms[0].model self.id = (self.name, self.complex) self.id2 = (self.name, self.chain) self.relevant = True self._com = None if parent is not None: self._determine_relevance(entity=parent, relevance_threshold=relevance_threshold) def _determine_relevance(self, entity=None, relevance_threshold=15): self.relevant = False if entity is None: self.relevant = True else: sa = self._calculate_sasa(entity=entity) if sa < relevance_threshold: self.relevant = True #print("SASA", sa, "LIGAND:", self) return self.relevant def _calculate_sasa(self, entity=None): from ..tools.SASA import SASA sasa = SASA() sasas = sasa.compute(entity=entity, targets=self.atoms, quiet=True) assert len(sasas) == len(self.atoms) av_sasa = sum(sasas) / len(sasas) self.sasa = av_sasa return self.sasa def com(self, force=False): if self._com is None or force: from ..utilities.maths import find_com self._com = find_com(self.atoms) return self._com def __repr__(self): return f"<bi.{self.__class__.__name__} id={self.id2}({self.complex})>"Class variables
var child_class-
The type of the None singleton.
var type-
The type of the None singleton.
Methods
def com(self, force=False)-
Expand source code
def com(self, force=False): if self._com is None or force: from ..utilities.maths import find_com self._com = find_com(self.atoms) return self._com
class MMCIF (data, cif_path=None)-
Expand source code
class MMCIF(object): def __init__(self, data, cif_path=None): self.data = data self.cif_path = cif_path def __repr__(self): return f"<bi.MMCIF: {self.cif_path} keys={self.keys()}>" def save(self, path): json.dump(self.data, open(path, "w"), indent=4) def keys(self): return list(self.data.keys()) def items(self): return self.data.items() def dict(self): return self.data def __len__(self): return len(self.data.keys()) def __getitem__(self, key): index = None subkey = None key = key.split(".") #print(key) if type(key) is str: pass elif len(key) == 1: key = key[0] elif len(key) == 2: key, subkey = key elif len(key) == 3: key, index, subkey = key else: log("warning", "Invalid key {}".format(key)) if not key.startswith("_"): key = "_" + key try: d = self.data[key] if len(d) == 1 and index is None: index = 0 #print(key, index, subkey) if index is None and subkey is None: ret = [v for v in d] elif index is None: #print([v.keys() for n, v in enumerate(d)]) ret = [v[subkey] for v in d] elif subkey is None: ret = d[index] else: ret = d[index][subkey] except KeyError as e: #print(ret) log("warning", f"Key not found: {e} ({key}.{index}.{subkey}) in {self.cif_path}") return None return ret def __call__(self, *args): entry = ".".join([*args]) return self[entry] @classmethod def read_mmcif(cls, *args, **kwargs): self = read_mmcif(*args, as_dict=False, **kwargs) return selfStatic methods
def read_mmcif(*args, **kwargs)
Methods
def dict(self)-
Expand source code
def dict(self): return self.data def items(self)-
Expand source code
def items(self): return self.data.items() def keys(self)-
Expand source code
def keys(self): return list(self.data.keys()) def save(self, path)-
Expand source code
def save(self, path): json.dump(self.data, open(path, "w"), indent=4)
class PseudoAtom (coords, fractional=False)-
Expand source code
class PseudoAtom(object): child_class = None def __init__(self, coords, fractional=False): self.x, self.y, self.z = coords self.coord = (self.x, self.y, self.z) self.is_fractional = fractional self._all_is_frac = False self._last_centre = None self._sym_coords = {} self.element = getattr(self, "element", None) def __repr__(self): return f"<bi.{self.__class__.__name__} at: {self.coord} (frac:{self.is_fractional})>" def __str__(self): return repr(self) def __add__(self, other): if type(other) in (int, float): if len(other) == 1: a = (other, other, other) elif len(other) == 3: a = other else: raise TypeError self.x += a[0] self.y += a[1] self.z += a[2] self.coord = self.x, self.y, self.z return self else: raise NotImplementedError() def __sub__(self, other): if type(other) in (int, float): if len(other) == 1: a = (other, other, other) elif len(other) == 3: a = other else: raise TypeError self.x -= a[0] self.y -= a[1] self.z -= a[2] self.coord = self.x, self.y, self.z return self else: raise NotImplementedError() def copy(self): from copy import deepcopy new = deepcopy(self) return new @staticmethod def _to_frac(coord, params): x, y, z = coord nx = (x * params["vvy"]) + (y * params["vvz"]) + (z * params["uuz"]) ny = (y * params["uuy"]) + (z * params["vv"]) nz = z * params["uu"] return nx, ny, nz @staticmethod def _to_orth(coord, params): t1, t2, t3 = coord tz = t3 / params["uu"] ty = (t2 - tz * params["vv"]) / params["uuy"] tx = (t1 - ty * params["vvz"] - tz * params["uuz"]) / params["vvy"] return tx, ty, tz def to_frac(self, params): if self.is_fractional: log("Warning", f"Atom: {self} is already fractional") raise AlreadyFractional(self) self._orth_coords = self.coord nx, ny, nz = self._to_frac(self.coord, params) self.x = nx self.y = ny self.z = nz self.coord = (self.x, self.y, self.z) self.is_fractional = True return self def to_orth(self, params): if not self.is_fractional: log("Warning", f"Atom: {self} is already orthogonal") raise AlreadyOrthogonal(self) self._frac_coords = self.coord tx, ty, tz = self._to_orth(self.coord, params) self.x = tx self.y = ty self.z = tz self.coord = (self.x, self.y, self.z) self.is_fractional = False return self def closest(self, target, symops, params, centre=None, first_only=True, force=False): if not self.is_fractional: len_fn = length else: len_fn = flength distances_all = {opn:(v,len_fn(vector(v, target), params=params), pos) for opn, (v, pos) in self.all(symops, params, centre, force=force).items()} sorted_all = {k: v for k, v in sorted(distances_all.items(), key=lambda x: x[1][1])} #print([(k, d, pos ) for k, (v, d, pos) in sorted_all.items()]) if first_only: for opn, (v, d, pos) in sorted_all.items(): return v, d, opn, pos return sorted_all def all(self, symops, params, centre=None, force=False): all_coords = {} if self._last_centre == centre and self._all_is_frac == self.is_fractional and not force: for opn, symop in symops.items(): if opn in self._sym_coords.keys(): all_coords[opn] = self._sym_coords[opn] else: all_coords[opn] = self.at(symop, params, centre=centre) else: for opn, symop in symops.items(): all_coords[opn] = self.at(symop, params, centre=centre) self._all_is_frac = self.is_fractional return all_coords def at(self, symop, params, centre=None, centre_is_frac=False): if not self.is_fractional: p2 = np.array(self._to_frac(self.coord, params)) was_orth = True else: was_orth = False p2 = np.array(self.coord) if centre is None: if self.is_fractional: centre = np.array(self.coord) else: centre = np.array(self._to_frac(self.coord, params)) else: if centre_is_frac: centre = np.array(centre) else: centre = np.array(self._to_frac(centre, params)) p2s = np.array(self._symop(p2, symop)) + 99.5 delta = ( ( p2s - np.array(centre) ) % 1 ) - 0.5 new = np.array(centre) + delta position = (new - p2s + 99.5) for p in position: assert p % 1 == 0 position = tuple([int(p) for p in position]) if position == (0,0,0): position = None if was_orth: new = self._to_orth(new, params) return new, position @staticmethod def _symop(coord, symop, position=None): rot = symop["rot"] tra = symop["tra"] x, y, z = coord nx = (rot[0][0] * x) + (rot[0][1] * y) + (rot[0][2] * z) + tra[0] ny = (rot[1][0] * x) + (rot[1][1] * y) + (rot[1][2] * z) + tra[1] nz = (rot[2][0] * x) + (rot[2][1] * y) + (rot[2][2] * z) + tra[2] if position is not None: nx += position[0] ny += position[1] nz += position[2] return nx, ny, nz def symop(self, symop, params, position=None): if not self.is_fractional: self.to_frac(params) was_orth = True else: was_orth = False nx, ny, nz = self._symop(self.coord, symop, position=position) self.x = nx self.y = ny self.z = nz self.coord = (self.x, self.y, self.z) if was_orth: self.to_orth(params) return selfSubclasses
Class variables
var child_class-
The type of the None singleton.
Methods
def all(self, symops, params, centre=None, force=False)-
Expand source code
def all(self, symops, params, centre=None, force=False): all_coords = {} if self._last_centre == centre and self._all_is_frac == self.is_fractional and not force: for opn, symop in symops.items(): if opn in self._sym_coords.keys(): all_coords[opn] = self._sym_coords[opn] else: all_coords[opn] = self.at(symop, params, centre=centre) else: for opn, symop in symops.items(): all_coords[opn] = self.at(symop, params, centre=centre) self._all_is_frac = self.is_fractional return all_coords def at(self, symop, params, centre=None, centre_is_frac=False)-
Expand source code
def at(self, symop, params, centre=None, centre_is_frac=False): if not self.is_fractional: p2 = np.array(self._to_frac(self.coord, params)) was_orth = True else: was_orth = False p2 = np.array(self.coord) if centre is None: if self.is_fractional: centre = np.array(self.coord) else: centre = np.array(self._to_frac(self.coord, params)) else: if centre_is_frac: centre = np.array(centre) else: centre = np.array(self._to_frac(centre, params)) p2s = np.array(self._symop(p2, symop)) + 99.5 delta = ( ( p2s - np.array(centre) ) % 1 ) - 0.5 new = np.array(centre) + delta position = (new - p2s + 99.5) for p in position: assert p % 1 == 0 position = tuple([int(p) for p in position]) if position == (0,0,0): position = None if was_orth: new = self._to_orth(new, params) return new, position def closest(self, target, symops, params, centre=None, first_only=True, force=False)-
Expand source code
def closest(self, target, symops, params, centre=None, first_only=True, force=False): if not self.is_fractional: len_fn = length else: len_fn = flength distances_all = {opn:(v,len_fn(vector(v, target), params=params), pos) for opn, (v, pos) in self.all(symops, params, centre, force=force).items()} sorted_all = {k: v for k, v in sorted(distances_all.items(), key=lambda x: x[1][1])} #print([(k, d, pos ) for k, (v, d, pos) in sorted_all.items()]) if first_only: for opn, (v, d, pos) in sorted_all.items(): return v, d, opn, pos return sorted_all def copy(self)-
Expand source code
def copy(self): from copy import deepcopy new = deepcopy(self) return new def symop(self, symop, params, position=None)-
Expand source code
def symop(self, symop, params, position=None): if not self.is_fractional: self.to_frac(params) was_orth = True else: was_orth = False nx, ny, nz = self._symop(self.coord, symop, position=position) self.x = nx self.y = ny self.z = nz self.coord = (self.x, self.y, self.z) if was_orth: self.to_orth(params) return self def to_frac(self, params)-
Expand source code
def to_frac(self, params): if self.is_fractional: log("Warning", f"Atom: {self} is already fractional") raise AlreadyFractional(self) self._orth_coords = self.coord nx, ny, nz = self._to_frac(self.coord, params) self.x = nx self.y = ny self.z = nz self.coord = (self.x, self.y, self.z) self.is_fractional = True return self def to_orth(self, params)-
Expand source code
def to_orth(self, params): if not self.is_fractional: log("Warning", f"Atom: {self} is already orthogonal") raise AlreadyOrthogonal(self) self._frac_coords = self.coord tx, ty, tz = self._to_orth(self.coord, params) self.x = tx self.y = ty self.z = tz self.coord = (self.x, self.y, self.z) self.is_fractional = False return self
class Water (atoms, **kwargs)-
Expand source code
class Water(object): child_class = BIAtom type = "water" def __init__(self, atoms, **kwargs): self.atoms = atoms for a in self.atoms: if a.name == "O": self.o = a self.resseq = self.o.resseq self.id = self.resseq self.relevant = False def __repr__(self): return f"<bi.{self.__class__.__name__} id={self.id}>"Class variables
var child_class-
The type of the None singleton.
var type-
The type of the None singleton.