class cif(DictMixin): def __init__(self, blocks=None): if blocks is not None: self.blocks = OrderedDict(blocks) else: self.blocks = OrderedDict() self.keys_lower = dict([(key.lower(), key) for key in self.blocks.keys()]) def __setitem__(self, key, value): assert isinstance(value, block) if not re.match(tag_re, '_' + key): raise Sorry("%s is not a valid data block name" % key) self.blocks[key] = value self.keys_lower[key.lower()] = key def get(self, key, default=None): key_lower = self.keys_lower.get(key.lower()) if (key_lower is None): return default return self.blocks.get(key_lower, default) def __getitem__(self, key): result = self.get(key) if (result is None): raise KeyError('Unknown CIF data block name: "%s"' % key) return result def __delitem__(self, key): del self.blocks[self.keys_lower[key.lower()]] del self.keys_lower[key.lower()] def keys(self): return self.blocks.keys() def __repr__(self): return repr(OrderedDict(self.iteritems())) def __copy__(self): return cif(self.blocks.copy()) copy = __copy__ def __deepcopy__(self, memo): return cif(copy.deepcopy(self.blocks, memo)) def deepcopy(self): return copy.deepcopy(self) def show(self, out=None, indent=" ", indent_row=None, data_name_field_width=34, loop_format_strings=None, align_columns=True): if out is None: out = sys.stdout for name, block in self.items(): print >> out, "data_%s" % name block.show(out=out, indent=indent, indent_row=indent_row, data_name_field_width=data_name_field_width, loop_format_strings=loop_format_strings, align_columns=align_columns) def __str__(self): s = StringIO() self.show(out=s) return s.getvalue() def validate(self, dictionary, show_warnings=True, error_handler=None, out=None): if out is None: out = sys.stdout from iotbx.cif import validation errors = {} if error_handler is None: error_handler = validation.ErrorHandler() for key, block in self.blocks.iteritems(): error_handler = error_handler.__class__() dictionary.set_error_handler(error_handler) block.validate(dictionary) errors.setdefault(key, error_handler) if error_handler.error_count or error_handler.warning_count: error_handler.show(show_warnings=show_warnings, out=out) return error_handler def sort(self, recursive=False, key=None, reverse=False): self.blocks = OrderedDict( sorted(self.blocks.items(), key=key, reverse=reverse)) if recursive: for b in self.blocks.values(): b.sort(recursive=recursive, reverse=reverse)
class cif(DictMixin): def __init__(self, blocks=None): if blocks is not None: self.blocks = OrderedDict(blocks) else: self.blocks = OrderedDict() self.keys_lower = dict([(key.lower(), key) for key in self.blocks.keys()]) def __setitem__(self, key, value): assert isinstance(value, block) if not re.match(tag_re, '_'+key): raise Sorry("%s is not a valid data block name" %key) self.blocks[key] = value self.keys_lower[key.lower()] = key def get(self, key, default=None): key_lower = self.keys_lower.get(key.lower()) if (key_lower is None): return default return self.blocks.get(key_lower, default) def __getitem__(self, key): result = self.get(key) if (result is None): raise KeyError('Unknown CIF data block name: "%s"' % key) return result def __delitem__(self, key): del self.blocks[self.keys_lower[key.lower()]] del self.keys_lower[key.lower()] def keys(self): return self.blocks.keys() def __repr__(self): return repr(OrderedDict(self.iteritems())) def __copy__(self): return cif(self.blocks.copy()) copy = __copy__ def __deepcopy__(self, memo): return cif(copy.deepcopy(self.blocks, memo)) def deepcopy(self): return copy.deepcopy(self) def show(self, out=None, indent=" ", indent_row=None, data_name_field_width=34, loop_format_strings=None): if out is None: out = sys.stdout for name, block in self.items(): print >> out, "data_%s" %name block.show( out=out, indent=indent, indent_row=indent_row, data_name_field_width=data_name_field_width, loop_format_strings=loop_format_strings) def __str__(self): s = StringIO() self.show(out=s) return s.getvalue() def validate(self, dictionary, show_warnings=True, error_handler=None, out=None): if out is None: out = sys.stdout from iotbx.cif import validation errors = {} if error_handler is None: error_handler = validation.ErrorHandler() for key, block in self.blocks.iteritems(): error_handler = error_handler.__class__() dictionary.set_error_handler(error_handler) block.validate(dictionary) errors.setdefault(key, error_handler) if error_handler.error_count or error_handler.warning_count: error_handler.show(show_warnings=show_warnings, out=out) return error_handler def sort(self, recursive=False, key=None, reverse=False): self.blocks = OrderedDict(sorted(self.blocks.items(), key=key, reverse=reverse)) if recursive: for b in self.blocks.values(): b.sort(recursive=recursive, reverse=reverse)
class align_crystal(object): vector_names = { a.elems: 'a', b.elems: 'b', c.elems: 'c', } def __init__(self, experiment, vectors, frame='reciprocal', mode='main'): from libtbx.utils import Sorry self.experiment = experiment self.vectors = vectors self.frame = frame self.mode = mode gonio = experiment.goniometer scan = experiment.scan self.s0 = matrix.col(self.experiment.beam.get_s0()) self.rotation_axis = matrix.col(gonio.get_rotation_axis()) from dxtbx.model import MultiAxisGoniometer if not isinstance(gonio, MultiAxisGoniometer): raise Sorry('Only MultiAxisGoniometer models supported') axes = gonio.get_axes() if len(axes) != 3: raise Sorry('Only 3-axis goniometers supported') e1, e2, e3 = (matrix.col(e) for e in reversed(axes)) fixed_rotation = matrix.sqr(gonio.get_fixed_rotation()) setting_rotation = matrix.sqr(gonio.get_setting_rotation()) rotation_axis = matrix.col(gonio.get_rotation_axis_datum()) rotation_matrix = rotation_axis.axis_and_angle_as_r3_rotation_matrix( experiment.scan.get_oscillation()[0], deg=True) from dials.algorithms.refinement import rotation_decomposition results = OrderedDict() # from https://github.com/legrandp/xdsme/blob/master/XOalign/XOalign.py#L427 # referential_permutations sign permutations for four permutations of # parallel/antiparallel (rotation axis & beam) # y1 // e1, y2 // beamVector; y1 anti// e1, y2 // beamVector # y1 // e1, y2 anti// beamVector; y1 anti// e1, y2 anti// beamVector ex = matrix.col((1, 0, 0)) ey = matrix.col((0, 1, 0)) ez = matrix.col((0, 0, 1)) referential_permutations = ([ ex, ey, ez], [-ex, -ey, ez], [ ex, -ey, -ez], [-ex, ey, -ez]) for (v1_, v2_) in self.vectors: results[(v1_, v2_)] = OrderedDict() space_group = self.experiment.crystal.get_space_group() for smx in list(space_group.smx())[:]: results[(v1_, v2_)][smx] = [] crystal = copy.deepcopy(self.experiment.crystal) cb_op = sgtbx.change_of_basis_op(smx) crystal = crystal.change_basis(cb_op) # Goniometer datum setting [D] at which the orientation was determined D = (setting_rotation * rotation_matrix * fixed_rotation).inverse() # The setting matrix [U] will vary with the datum setting according to # [U] = [D] [U0] U = matrix.sqr(crystal.get_U()) # XXX In DIALS recorded U is equivalent to U0 - D is applied to U inside # prediction U0 = U B = matrix.sqr(crystal.get_B()) if self.frame == 'direct': B = B.inverse().transpose() v1_0 = U0 * B * v1_ v2_0 = U0 * B * v2_ #c (b) The laboratory frame vectors l1 & l2 are normally specified with the #c MODE command: MODE MAIN (the default) sets l1 (along which v1 will be #c placed) along the principle goniostat axis e1 (Omega), and l2 along #c the beam s0. This allows rotation for instance around a principle axis. #c The other mode is MODE CUSP, which puts l1 (v1) perpendicular to the #c beam (s0) and the e1 (Omega) axis, and l2 (v2) in the plane containing #c l1 & e1 (ie l1 = e1 x s0, l2 = e1). if self.mode == 'cusp': l1 = self.rotation_axis.cross(self.s0) l2 = self.rotation_axis else: l1 = self.rotation_axis.normalize() l3 = l1.cross(self.s0).normalize() l2 = l1.cross(l3) for perm in referential_permutations: S = matrix.sqr(perm[0].elems + perm[1].elems + perm[2].elems) from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(v1_0, S * l1, v2_0, S * l2) solutions = rotation_decomposition.solve_r3_rotation_for_angles_given_axes( R, e1, e2, e3, return_both_solutions=True, deg=True) if solutions is None: continue results[(v1_, v2_)][smx].extend(solutions) self.all_solutions = results self.unique_solutions = OrderedDict() for (v1, v2), result in results.iteritems(): for solutions in result.itervalues(): for solution in solutions: k = tuple(round(a, 3) for a in solution[1:]) self.unique_solutions.setdefault(k, OrderedSet()) self.unique_solutions[k].add((v1, v2)) def _vector_as_str(self, v): v = v.elems if v in self.vector_names: vstr = self.vector_names[v] if self.frame == 'reciprocal': vstr += '*' else: vstr = str(v) return vstr def show(self): from libtbx import table_utils self.info() rows = [] names = self.experiment.goniometer.get_names() space_group = self.experiment.crystal.get_space_group() reciprocal = self.frame == 'reciprocal' for angles, vector_pairs in self.unique_solutions.iteritems(): v1, v2 = list(vector_pairs)[0] rows.append(( describe(v1, space_group, reciprocal=reciprocal), describe(v2, space_group, reciprocal=reciprocal), '% 7.3f' %angles[0], '% 7.3f' %angles[1], )) rows = [('Primary axis', 'Secondary axis', names[1], names[0])] + \ sorted(rows) print 'Independent solutions:' print table_utils.format(rows=rows, has_header=True) def as_json(self, filename=None): names = self.experiment.goniometer.get_names() solutions = [] space_group = self.experiment.crystal.get_space_group() reciprocal = self.frame == 'reciprocal' for angles, solns in self.unique_solutions.iteritems(): solutions.append({ 'primary_axis': [self._vector_as_str(v1) for v1, v2 in solns], 'secondary_axis': [self._vector_as_str(v2) for v1, v2 in solns], 'primary_axis_type': [axis_type(v1, space_group) for v1, v2 in solns], 'secondary_axis_type': [axis_type(v2, space_group) for v1, v2 in solns], names[1]: angles[0], names[0]: angles[1] }) d = {'solutions': solutions, 'goniometer': self.experiment.goniometer.to_dict()} import json if filename is not None: return json.dump(d, open(filename, 'wb'), indent=2) else: return json.dumps(d, indent=2) def info(self): from libtbx import table_utils U = matrix.sqr(self.experiment.crystal.get_U()) B = matrix.sqr(self.experiment.crystal.get_B()) a_star_ = U * B * a_star b_star_ = U * B * b_star c_star_ = U * B * c_star Binvt = B.inverse().transpose() a_ = U * Binvt * a b_ = U * Binvt * b c_ = U * Binvt * c names = self.experiment.goniometer.get_names() axes = self.experiment.goniometer.get_axes() rows = [['Experimental axis', 'a*', 'b*', 'c*']] rows.append([names[0]] + [ '%.3f' %smallest_angle(axis.angle(matrix.col(axes[0]), deg=True)) for axis in (a_star_, b_star_, c_star_)]) rows.append(['Beam'] + [ '%.3f' %smallest_angle(axis.angle(self.s0, deg=True)) for axis in (a_star_, b_star_, c_star_)]) rows.append([names[2]] + [ '%.3f' %smallest_angle(axis.angle(matrix.col(axes[2]), deg=True)) for axis in (a_star_, b_star_, c_star_)]) print 'Angles between reciprocal cell axes and principal experimental axes:' print table_utils.format(rows=rows, has_header=True) print rows = [['Experimental axis', 'a', 'b', 'c']] rows.append([names[0]] + [ '%.3f' %smallest_angle(axis.angle(matrix.col(axes[0]), deg=True)) for axis in (a_, b_, c_)]) rows.append(['Beam'] + [ '%.3f' %smallest_angle(axis.angle(self.s0, deg=True)) for axis in (a_, b_, c_)]) rows.append([names[2]] + [ '%.3f' %smallest_angle(axis.angle(matrix.col(axes[2]), deg=True)) for axis in (a_, b_, c_)]) print 'Angles between unit cell axes and principal experimental axes:' print table_utils.format(rows=rows, has_header=True) print
def __init__(self, experiment, vectors, frame='reciprocal', mode='main'): from libtbx.utils import Sorry self.experiment = experiment self.vectors = vectors self.frame = frame self.mode = mode gonio = experiment.goniometer scan = experiment.scan self.s0 = matrix.col(self.experiment.beam.get_s0()) self.rotation_axis = matrix.col(gonio.get_rotation_axis()) from dxtbx.model import MultiAxisGoniometer if not isinstance(gonio, MultiAxisGoniometer): raise Sorry('Only MultiAxisGoniometer models supported') axes = gonio.get_axes() if len(axes) != 3: raise Sorry('Only 3-axis goniometers supported') e1, e2, e3 = (matrix.col(e) for e in reversed(axes)) fixed_rotation = matrix.sqr(gonio.get_fixed_rotation()) setting_rotation = matrix.sqr(gonio.get_setting_rotation()) rotation_axis = matrix.col(gonio.get_rotation_axis_datum()) rotation_matrix = rotation_axis.axis_and_angle_as_r3_rotation_matrix( experiment.scan.get_oscillation()[0], deg=True) from dials.algorithms.refinement import rotation_decomposition results = OrderedDict() # from https://github.com/legrandp/xdsme/blob/master/XOalign/XOalign.py#L427 # referential_permutations sign permutations for four permutations of # parallel/antiparallel (rotation axis & beam) # y1 // e1, y2 // beamVector; y1 anti// e1, y2 // beamVector # y1 // e1, y2 anti// beamVector; y1 anti// e1, y2 anti// beamVector ex = matrix.col((1, 0, 0)) ey = matrix.col((0, 1, 0)) ez = matrix.col((0, 0, 1)) referential_permutations = ([ ex, ey, ez], [-ex, -ey, ez], [ ex, -ey, -ez], [-ex, ey, -ez]) for (v1_, v2_) in self.vectors: results[(v1_, v2_)] = OrderedDict() space_group = self.experiment.crystal.get_space_group() for smx in list(space_group.smx())[:]: results[(v1_, v2_)][smx] = [] crystal = copy.deepcopy(self.experiment.crystal) cb_op = sgtbx.change_of_basis_op(smx) crystal = crystal.change_basis(cb_op) # Goniometer datum setting [D] at which the orientation was determined D = (setting_rotation * rotation_matrix * fixed_rotation).inverse() # The setting matrix [U] will vary with the datum setting according to # [U] = [D] [U0] U = matrix.sqr(crystal.get_U()) # XXX In DIALS recorded U is equivalent to U0 - D is applied to U inside # prediction U0 = U B = matrix.sqr(crystal.get_B()) if self.frame == 'direct': B = B.inverse().transpose() v1_0 = U0 * B * v1_ v2_0 = U0 * B * v2_ #c (b) The laboratory frame vectors l1 & l2 are normally specified with the #c MODE command: MODE MAIN (the default) sets l1 (along which v1 will be #c placed) along the principle goniostat axis e1 (Omega), and l2 along #c the beam s0. This allows rotation for instance around a principle axis. #c The other mode is MODE CUSP, which puts l1 (v1) perpendicular to the #c beam (s0) and the e1 (Omega) axis, and l2 (v2) in the plane containing #c l1 & e1 (ie l1 = e1 x s0, l2 = e1). if self.mode == 'cusp': l1 = self.rotation_axis.cross(self.s0) l2 = self.rotation_axis else: l1 = self.rotation_axis.normalize() l3 = l1.cross(self.s0).normalize() l2 = l1.cross(l3) for perm in referential_permutations: S = matrix.sqr(perm[0].elems + perm[1].elems + perm[2].elems) from rstbx.cftbx.coordinate_frame_helpers import align_reference_frame R = align_reference_frame(v1_0, S * l1, v2_0, S * l2) solutions = rotation_decomposition.solve_r3_rotation_for_angles_given_axes( R, e1, e2, e3, return_both_solutions=True, deg=True) if solutions is None: continue results[(v1_, v2_)][smx].extend(solutions) self.all_solutions = results self.unique_solutions = OrderedDict() for (v1, v2), result in results.iteritems(): for solutions in result.itervalues(): for solution in solutions: k = tuple(round(a, 3) for a in solution[1:]) self.unique_solutions.setdefault(k, OrderedSet()) self.unique_solutions[k].add((v1, v2))
def __init__(self, pdb_hierarchy, sequences, alignment_params=None, crystal_symmetry=None, coordinate_precision=5, occupancy_precision=3, b_iso_precision=5, u_aniso_precision=5): pdb_hierarchy_as_cif_block.__init__( self, pdb_hierarchy, crystal_symmetry=crystal_symmetry, coordinate_precision=coordinate_precision, occupancy_precision=occupancy_precision, b_iso_precision=b_iso_precision, u_aniso_precision=u_aniso_precision) import mmtbx.validation.sequence validation = mmtbx.validation.sequence.validation( pdb_hierarchy=pdb_hierarchy, sequences=sequences, params=alignment_params, extract_residue_groups=True, log=null_out(), # silence output ) entity_loop = iotbx.cif.model.loop(header=( '_entity.id', '_entity.type', #'_entity.src_method', #'_entity.pdbx_description', '_entity.formula_weight', '_entity.pdbx_number_of_molecules', #'_entity.details', #'_entity.pdbx_mutation', #'_entity.pdbx_fragment', #'_entity.pdbx_ec' )) entity_poly_loop = iotbx.cif.model.loop(header=( '_entity_poly.entity_id', '_entity_poly.type', '_entity_poly.nstd_chirality', '_entity_poly.nstd_linkage', '_entity_poly.nstd_monomer', '_entity_poly.pdbx_seq_one_letter_code', '_entity_poly.pdbx_seq_one_letter_code_can', '_entity_poly.pdbx_strand_id', '_entity_poly.type_details' )) entity_poly_seq_loop = iotbx.cif.model.loop(header=( '_entity_poly_seq.entity_id', '_entity_poly_seq.num', '_entity_poly_seq.mon_id', '_entity_poly_seq.hetero', )) sequence_counts = OrderedDict() sequence_to_chain_ids = {} entity_id = 0 sequence_to_entity_id = {} chain_id_to_entity_id = {} sequence_to_chains = {} residue_group_to_seq_num_mapping = {} aligned_pdb_chains = OrderedSet() non_polymer_counts = dict_with_default_0() non_polymer_resname_to_entity_id = OrderedDict() for chain in validation.chains: sequence = chain.alignment.b if sequence not in sequence_to_entity_id: entity_id += 1 sequence_to_entity_id[sequence] = entity_id sequence_counts.setdefault(sequence, 0) sequence_counts[sequence] += 1 sequence_to_chain_ids.setdefault(sequence, []) sequence_to_chain_ids[sequence].append(chain.chain_id) sequence_to_chains.setdefault(sequence, []) sequence_to_chains[sequence].append(chain) chain_id_to_entity_id[chain.chain_id] = sequence_to_entity_id[sequence] aligned_pdb_chains.add(chain.residue_groups[0].parent()) unaligned_pdb_chains = OrderedSet(pdb_hierarchy.chains()) - aligned_pdb_chains assert len(chain.residue_groups) + chain.n_missing_start + chain.n_missing_end == len(sequence) residue_groups = [None] * chain.n_missing_start + chain.residue_groups + [None] * chain.n_missing_end i = chain.n_missing_start seq_num = 0 for i, residue_group in enumerate(residue_groups): if residue_group is None and chain.alignment.b[i] == '-': # a deletion continue seq_num += 1 if residue_group is not None: residue_group_to_seq_num_mapping[ residue_group] = seq_num for pdb_chain in unaligned_pdb_chains: for residue_group in pdb_chain.residue_groups(): for resname in residue_group.unique_resnames(): if resname not in non_polymer_resname_to_entity_id: entity_id += 1 non_polymer_resname_to_entity_id[resname] = entity_id non_polymer_counts[resname] += 1 for sequence, count in sequence_counts.iteritems(): entity_poly_seq_num = 0 entity_id = sequence_to_entity_id[sequence] entity_loop.add_row(( entity_id, 'polymer', #polymer/non-polymer/macrolide/water #'?', #src_method #'?', # pdbx_description '?', # formula_weight len(sequence_to_chains[sequence]), # pdbx_number_of_molecules #'?', # details #'?', # pdbx_mutation #'?', # pdbx_fragment #'?' # pdbx_ec )) # The definition of the cif item _entity_poly.pdbx_seq_one_letter_code # says that modifications and non-standard amino acids should be encoded # as 'X', however in practice the PDB seem to encode them as the three-letter # code in parentheses. pdbx_seq_one_letter_code = [] pdbx_seq_one_letter_code_can = [] chains = sequence_to_chains[sequence] from iotbx.pdb import amino_acid_codes chain = chains[0] matches = chain.alignment.matches() for i, one_letter_code in enumerate(sequence): #Data items in the ENTITY_POLY_SEQ category specify the sequence #of monomers in a polymer. Allowance is made for the possibility #of microheterogeneity in a sample by allowing a given sequence #number to be correlated with more than one monomer ID. The #corresponding ATOM_SITE entries should reflect this #heterogeneity. monomer_id = None if i >= chain.n_missing_start and i < (len(sequence) - chain.n_missing_end): monomer_id = chain.resnames[i-chain.n_missing_start] if monomer_id is None and one_letter_code == '-': continue pdbx_seq_one_letter_code_can.append(one_letter_code) if monomer_id is None: if sequence_to_chains[sequence][0].chain_type == mmtbx.validation.sequence.PROTEIN: monomer_id = amino_acid_codes.three_letter_given_one_letter.get( one_letter_code, "UNK") # XXX else: monomer_id = one_letter_code else: if sequence_to_chains[sequence][0].chain_type == mmtbx.validation.sequence.PROTEIN: one_letter_code = amino_acid_codes.one_letter_given_three_letter.get( monomer_id, "(%s)" %monomer_id) pdbx_seq_one_letter_code.append(one_letter_code) entity_poly_seq_num += 1 entity_poly_seq_loop.add_row(( entity_id, entity_poly_seq_num, monomer_id, 'no', #XXX )) entity_poly_type = '?' entity_nstd_chirality = 'n' # we should probably determine the chirality more correctly by examining # the chirality of the backbone chain rather than relying on the residue # names to be correct if chain.chain_type == mmtbx.validation.sequence.PROTEIN: n_d_peptides = 0 n_l_peptides = 0 n_achiral_peptides = 0 n_unknown = 0 for resname in chain.resnames: if resname == "GLY": n_achiral_peptides += 1 elif resname in iotbx.pdb.common_residue_names_amino_acid: n_l_peptides += 1 elif resname in amino_acid_codes.three_letter_l_given_three_letter_d: n_d_peptides += 1 else: n_unknown += 1 n_total = sum([n_d_peptides, n_l_peptides, n_achiral_peptides, n_unknown]) if (n_l_peptides + n_achiral_peptides)/n_total > 0.5: entity_poly_type = 'polypeptide(L)' if n_d_peptides > 0: entity_nstd_chirality = 'y' elif (n_d_peptides + n_achiral_peptides)/n_total > 0.5: entity_poly_type = 'polypeptide(D)' if n_l_peptides > 0: entity_nstd_chirality = 'y' elif chain.chain_type == mmtbx.validation.sequence.NUCLEIC_ACID: n_dna = 0 n_rna = 0 n_unknown = 0 for resname in chain.resnames: if resname is not None and resname.strip().upper() in ( 'AD', 'CD', 'GD', 'TD', 'DA', 'DC', 'DG', 'DT'): n_dna += 1 elif resname is not None and resname.strip().upper() in ( 'A', 'C', 'G', 'T', '+A', '+C', '+G', '+T'): n_rna += 1 else: n_unknown += 1 n_total = sum([n_dna + n_rna + n_unknown]) if n_dna/n_total > 0.5 and n_rna == 0: entity_poly_type = 'polydeoxyribonucleotide' elif n_rna/n_total > 0.5 and n_dna == 0: entity_poly_type = 'polyribonucleotide' elif (n_rna + n_dna)/n_total > 0.5: entity_poly_type = 'polydeoxyribonucleotide/polyribonucleotide hybrid' entity_poly_loop.add_row(( entity_id, entity_poly_type, entity_nstd_chirality, 'no', 'no', wrap_always("".join(pdbx_seq_one_letter_code), width=80).strip(), wrap_always("".join(pdbx_seq_one_letter_code_can), width=80).strip(), ','.join(sequence_to_chain_ids[sequence]), '?' )) for resname, entity_id in non_polymer_resname_to_entity_id.iteritems(): entity_type = "non-polymer" if resname == "HOH": entity_type = "water" # XXX entity_loop.add_row(( entity_id, entity_type, #polymer/non-polymer/macrolide/water #'?', #src_method #'?', # pdbx_description '?', # formula_weight non_polymer_counts[resname], # pdbx_number_of_molecules #'?', # details #'?', # pdbx_mutation #'?', # pdbx_fragment #'?' # pdbx_ec )) self.cif_block.add_loop(entity_loop) self.cif_block.add_loop(entity_poly_loop) self.cif_block.add_loop(entity_poly_seq_loop) self.cif_block.update(pdb_hierarchy.as_cif_block()) label_entity_id = self.cif_block['_atom_site.label_entity_id'] auth_seq_id = self.cif_block['_atom_site.auth_seq_id'] ins_code = self.cif_block['_atom_site.pdbx_PDB_ins_code'] auth_asym_id = self.cif_block['_atom_site.auth_asym_id'] label_seq_id = flex.std_string(auth_seq_id.size(), '.') ins_code = ins_code.deep_copy() ins_code.set_selected(ins_code == '?', '') for residue_group, seq_num in residue_group_to_seq_num_mapping.iteritems(): sel = ((auth_asym_id == residue_group.parent().id) & (ins_code == residue_group.icode.strip()) & (auth_seq_id == residue_group.resseq.strip())) label_seq_id.set_selected(sel, str(seq_num)) label_entity_id.set_selected( sel, str(chain_id_to_entity_id[residue_group.parent().id])) for pdb_chain in unaligned_pdb_chains: for residue_group in pdb_chain.residue_groups(): sel = ((auth_asym_id == residue_group.parent().id) & (ins_code == residue_group.icode.strip()) & (auth_seq_id == residue_group.resseq.strip())) label_entity_id.set_selected( sel, str(non_polymer_resname_to_entity_id[residue_group.unique_resnames()[0]])) self.cif_block['_atom_site.label_seq_id'] = label_seq_id # reorder the loops atom_site_loop = self.cif_block['_atom_site'] atom_site_aniso_loop = self.cif_block.get('_atom_site_anisotrop') del self.cif_block['_atom_site'] self.cif_block.add_loop(atom_site_loop) if atom_site_aniso_loop is not None: del self.cif_block['_atom_site_anisotrop'] self.cif_block.add_loop(atom_site_aniso_loop)