def WriteFrame(self, system, label=None, xyz=None): """Write a single frame.""" # . Check the data. if xyz == None: xyz = system.coordinates3 if (xyz is None) or (not isinstance( xyz, Coordinates3)) or (xyz.rows != len(system.atoms)): raise TextFileWriterError( "Invalid or missing data to write to Jaguar input file.") # . Check the label. if label is None: label = system.label if label is None: label = "Jaguar input file." # . Check the electronic state. if system.electronicState is None: charge = 0 multip = 1 else: charge = system.electronicState.charge multip = system.electronicState.multiplicity # . Header. self.file.write(label + "\n\n") # . General section. self.file.write("&gen\n") self.file.write("molchg={:d}\n".format(charge)) self.file.write("multip={:d}\n".format(multip)) self.file.write("&\n") # . Coordinates. self.file.write("&zmat\n") numbers = system.atoms.GetItemAttributes("atomicNumber") for (i, n) in enumerate(numbers): symbol = PeriodicTable.Symbol(n, index=i + 1) self.file.write("{:<5s}{:25.15f}{:25.15f}{:25.15f}\n".format( symbol, xyz[i, 0], xyz[i, 1], xyz[i, 2])) self.file.write("&\n")
def runTest ( self ): """The test.""" # . Initialization. isOK = True # . Output setup. dataPath = os.path.join ( os.getenv ( "PDYNAMO_PBABEL" ), "data", _Source ) log = self.GetLog ( ) # . Loop over the data. for ( label, isSpherical ) in _EMSLG94Data: # . Get the bases. bases = EMSLG94File_ToGaussianBases ( os.path.join ( dataPath, label + _Extension ), isSpherical = isSpherical ) if log is not None: log.Paragraph ( "Processed bases for basis {:s} = {:d}.".format ( label, len ( bases ) ) ) # . Check for an appropriate outPath. if self.resultPath is not None: outPath = os.path.join ( self.resultPath, _Destination ) if not os.path.exists ( outPath ): os.mkdir ( outPath ) outPath = os.path.join ( outPath, label.lower ( ) ) if not os.path.exists ( outPath ): os.mkdir ( outPath ) # . Save the bases. for basis in bases: symbol = PeriodicTable.Symbol ( basis.atomicNumber ) YAMLPickle ( os.path.join ( outPath, symbol + YAMLPickleFileExtension ), basis ) # . Success/failure. self.assertTrue ( isOK )
def AtomToken(self, atom): """Return a SMILES token for an atom.""" # . Check for a reduced atom representation. # . Basic checks. QREDUCED = (atom.atomicNumber in ELEMENTSORGANIC) and (atom.formalCharge == 0) and (atom.isotope <= 0) # . Check for the expected number of implicit hydrogens (also ensures the atom has standard valence). if QREDUCED: valence = atom.valence - atom.implicithydrogens for v in VALENCIESORGANIC[atom.atomicNumber]: hcount = v - valence if hcount >= 0: break QREDUCED = (atom.implicithydrogens == hcount) # . Check for a reduced chirality representation. if atom.chiralityclass is not None: QCHIRALITYREDUCED = (CHIRALITYDEFAULTCLASSES.get( atom.connections, None) == atom.chiralityclass) and (atom.chiralitynumber <= 4) QREDUCED = QREDUCED and QCHIRALITYREDUCED # . Generate the string. if QCHIRALITYREDUCED: ctoken = atom.chiralitynumber * "@" else: ctoken = "@" + atom.chiralityclass + "{:d}".format( atom.chiralitynumber) else: ctoken = "" # . Encode the string. if atom.isAromatic: atoken = PeriodicTable.Symbol(atom.atomicNumber).lower() else: atoken = PeriodicTable.Symbol(atom.atomicNumber) tokens = [atoken, ctoken] if not QREDUCED: tokens[0:0] = "[" if atom.isotope != 0: tokens[1:1] = "{:d}".format(isotope) if atom.implicithydrogens == 1: tokens.append("H") elif atom.implicithydrogens > 1: tokens.append("H" + "{:d}".format(atom.implicithydrogens)) if atom.formalCharge < 0: tokens.append("{:d}".format(atom.formalCharge)) elif atom.formalCharge > 0: tokens.append("+" + "{:d}".format(atom.formalCharge)) tokens.append("]") return "".join(tokens)
def _GetHardSphereRadii(atoms): """Get the hard-sphere radii for the atoms.""" hsradii = Real1DArray.WithExtent(len(atoms)) hsradii.Set(0.0) atomicNumbers = atoms.GetItemAttributes("atomicNumber") for (i, atomicNumber) in enumerate(atomicNumbers): try: hsradii[i] = _HSRADII[atomicNumber] except: raise KeyError("Hard-sphere radius for element " + PeriodicTable.Symbol(atomicNumber) + " unknown.") return hsradii
def WriteFrame ( self, system, label = None, xyz = None ): """Write a single frame.""" # . Check the data. if xyz == None: xyz = system.coordinates3 if ( xyz is None ) or ( not isinstance ( xyz, Coordinates3 ) ) or ( xyz.rows != len ( system.atoms ) ): raise TextFileWriterError ( "Invalid or missing data to write to XYZ file." ) # . Check the label. if label is None: label = system.label # . Write the frame. self.file.write ( "{:6d}\n".format ( len ( system.atoms ) ) ) if label is None: self.file.write ( "\n" ) else: if len ( label ) > _MaximumLabelLength: label = label[0:_MaximumLabelLength] self.file.write ( label + "\n" ) numbers = system.atoms.GetItemAttributes ( "atomicNumber" ) for i in range ( xyz.rows ): symbol = PeriodicTable.Symbol ( numbers[i] ) self.file.write ( "{:<5s}{:25.15f}{:25.15f}{:25.15f}\n".format ( symbol, xyz[i,0], xyz[i,1], xyz[i,2] ) )
def WriteFrame(self, system, label=None, xyz=None): """Write a single frame.""" # . Get data. natoms = len(system.atoms) nbonds = len(system.connectivity.bonds) # . Check the data. if xyz == None: xyz = system.coordinates3 if (xyz is None) or (not isinstance( xyz, Coordinates3)) or (xyz.rows != natoms): raise TextFileWriterError( "Invalid or missing data to write to MOL2 file.") # . Check the label. if label is None: label = system.label # . Molecule block. self.file.write(_RTISTRING + "MOLECULE\n") if label is None: self.file.write("\n") else: self.file.write( label.strip()[0:min(len(label), _MAXIMUMLINELENGTH)] + "\n") self.file.write("{:d} {:d}\n".format(natoms, nbonds)) self.file.write(_MOLECULETYPE + "\n") self.file.write(_CHARGETYPE + "\n") self.file.write("\n") # . Atom block. self.file.write(_RTISTRING + "ATOM\n") for (i, atom) in enumerate(system.atoms): self.file.write( "{:6d} {:<6s} {:10.4f} {:10.4f} {:10.4f} {:s}\n".format( i + 1, PeriodicTable.Symbol(atom.atomicNumber, index=i), xyz[i, 0], xyz[i, 1], xyz[i, 2], _ATOMTYPE)) self.file.write("\n") # . Bond block. self.file.write(_RTISTRING + "BOND\n") for (i, bond) in enumerate(system.connectivity.bonds): self.file.write("{:6d} {:6d} {:6d} {:<s}\n".format( i + 1, bond.i + 1, bond.j + 1, _MOL2BONDTYPES[bond.type])) self.file.write("\n") # . Symmetry block. if system.symmetryParameters is not None: p = system.symmetryParameters self.file.write(_RTISTRING + "CRYSIN\n") self.file.write( "{:.3f} {:.3f} {:.3f} {:.1f} {:.1f} {:.1f} 1 1\n".format( p.a, p.b, p.c, p.alpha, p.beta, p.gamma))
def CreateElementSequence(system, entityDescription=None, entityLabel="E"): """Create a sequence of a single entity using element symbols for atom names and components and one atom per component.""" # . Initialization. minorSeparator = Sequence.defaultAttributes["fieldSeparator"] sequence = Sequence() entity = SequenceEntity(label=entityLabel) sequence.AddChild(entity) # . Process the atoms. for (index, atom) in enumerate(system.atoms): symbol = PeriodicTable.Symbol(atom.atomicNumber).upper() component = SequenceComponent(genericLabel=symbol, label=symbol + minorSeparator + repr(index + 1)) entity.AddChild(component) component.AddChild(atom) atom.index = index atom.label = symbol # . Finish up. system.__dict__["_sequence"] = sequence
def WriteFrame(self, system, label=None, xyz=None): """Write a single frame.""" # . Check the data. if xyz == None: xyz = system.coordinates3 if (xyz is None) or (not isinstance( xyz, Coordinates3)) or (xyz.rows != len(system.atoms)): raise TextFileWriterError( "Invalid or missing data to write to MOL file.") # . Check the label. if label is None: label = system.label # . Write the header block. if label is None: self.file.write("\n\n\n") else: self.file.write( label.strip()[0:min(len(label), _MAXIMUMLINELENGTH)] + "\n\n\n") # . Counts line. self.file.write("{:3d}{:3d}".format(len(system.atoms), len(system.connectivity.bonds)) + 8 * " 0" + "999 V2000\n") # . Atom block. for (i, atom) in enumerate(system.atoms): self.file.write("{:10.4f}{:10.4f}{:10.4f} {:<2s}".format( xyz[i, 0], xyz[i, 1], xyz[i, 2], PeriodicTable.Symbol(atom.atomicNumber)) + 5 * " 0" + "\n") # . Bond block. for (i, bond) in enumerate(system.connectivity.bonds): self.file.write("{:3d}{:3d}{:3d}".format( bond.i + 1, bond.j + 1, _MOLBONDTYPES[bond.type]) + 3 * " 0" + "\n") # . Properties block. for (i, atom) in enumerate(system.atoms): formalCharge = getattr(atom, "formalCharge", 0) if formalCharge != 0: self.file.write("M CHG 1{:4d}{:4d}\n".format( i + 1, formalCharge)) self.file.write("M END\n")
def JaguarBondOrders(infile, outfile, bondOrdertolerance=_DEFAULTBONDORDERTOLERANCE, chargetolerance=_DEFAULTCHARGETOLERANCE, log=logFile, QCROSS=False): """Calculate bond orders given Jaguar input and output files.""" # . Parse the input file. infile = JaguarInputFileReader(infile) infile.Parse() infile.Summary(log=logFile) # . Parse the output file. outfile = JaguarOutputFileReader(outfile) outfile.Parse() outfile.Summary(log=logFile) # . Get data from the files. # . Input. atomicNumbersi = getattr(infile, "atomicNumbers", None) coordinates3 = getattr(infile, "coordinates3", None) orbitalsets = getattr(infile, "orbitalsets", None) # . Output. atomicNumberso = getattr(outfile, "atomicNumbers", None) nfunctions = getattr(outfile, "nfunctions", None) overlap = getattr(outfile, "overlap", None) # . Check for coordinates. if coordinates3 is None: raise ValueError("The coordinate data is missing from the input file.") # . Check the orbital data. QOK = (orbitalsets is not None) and (len(orbitalsets) > 0) if QOK: if (len(orbitalsets) == 1) and ("" in orbitalsets): spinDensitiesRESTRICTED = True elif (len(orbitalsets) == 2) and ("alpha" in orbitalsets) and ("beta" in orbitalsets): spinDensitiesRESTRICTED = False else: QOK = False if not QOK: raise ValueError("Invalid orbital data on input file.") if spinDensitiesRESTRICTED: nbasisi = infile.orbitalsets[""][1] else: nbasisi = infile.orbitalsets["alpha"][1] # . Check the overlap. if (overlap is None): raise ValueError("The overlap matrix is missing from the output file.") nbasiso = overlap.Dimension() # . Check the array giving the number of functions per atom. # print nfunctions, len ( nfunctions ), len ( atomicNumberso ), sum ( nfunctions ), nbasiso if (nfunctions is None) or (len(nfunctions) != len(atomicNumberso) or (sum(nfunctions) != nbasiso)): raise ValueError( "Basis function data on the output file is missing or invalid.") # . Create the function index array. findices = [] first = 0 last = 0 for f in nfunctions: last += f findices.append((first, last)) first += f # . Check for compatibility between the data. QOK = (atomicNumbersi is not None) and (atomicNumberso is not None) and ( len(atomicNumbersi) == len(atomicNumberso)) and (nbasisi == nbasiso) if QOK: for (i, j) in zip(atomicNumbersi, atomicNumberso): if i != j: QOK = False break if not QOK: raise ValueError( "The systems on the input and output files are incompatible.") # . Set the keys for the calculation. if spinDensitiesRESTRICTED: keys = [""] else: keys = ["alpha", "beta"] # . Get the densities multiplied by the overlap. ps = {} for key in keys: p = _MakeDensity(orbitalsets[key]) # p.Print ( title = "**1**" ) result = Real2DArray.WithExtents(nbasisi, nbasisi) result.Set(999.0) p.PostMultiply(overlap, result) # overlap.Print ( title = "**2**" ) # result.Print ( title = "**3**" ) ps[key] = result # f = STOP # . Scale ps correctly for the spin-restricted case. if spinDensitiesRESTRICTED: ps[""].Scale(2.0) # . If cross terms are not required condense the ps matrices. if (not QCROSS) and (not spinDensitiesRESTRICTED): tps = ps.pop(keys[0]) for key in keys[1:]: tps.AddScaledMatrix(1.0, ps[key]) ps = {"": tps} keys = [""] # . Get the bond-orders. bondOrders = {} for key1 in keys: for key2 in keys: bondOrders[(key1, key2)] = _MakeBondOrders(ps[key1], ps[key2], findices) # . Make the total bond-order if necessary. bokeys = bondOrders.keys() if len(bokeys) > 1: bokeys.sort() tbo = Clone(bondOrders[bokeys[0]]) for key in bokeys[1:]: tbo.AddScaledMatrix(1.0, bondOrders[key]) tkey = ("", "") bokeys.append(tkey) bondOrders[tkey] = tbo # . Compute the electronic contribution to the Mulliken charges. qmulliken = Real1DArray.WithExtent(len(atomicNumbersi)) qmulliken.Set(0.0) for key in keys: _MakeElectronicMullikenCharges(ps[key], findices, qmulliken) # . Determine atom valencies. free = Real1DArray.WithExtent(len(atomicNumbersi)) free.Set(0.0) valencies = Real1DArray.WithExtent(len(atomicNumbersi)) valencies.Set(0.0) tbo = bondOrders[("", "")] for i in range(len(atomicNumbersi)): valencies[i] = (-2.0 * qmulliken[i]) - tbo[i, i] totalbo = 0.0 for j in range(len(atomicNumbersi)): totalbo += tbo[i, j] free[i] = (-2.0 * qmulliken[i]) - totalbo # . Add in the core contributions to the Mulliken charges. for (i, q) in enumerate(atomicNumbersi): qmulliken[i] += float(q) if outfile.QECP: for (i, q) in enumerate(outfile.ecpelectrons): qmulliken[i] -= float(q) # . Output the results. if LogFileActive(log): # . Get the spin label. if spinDensitiesRESTRICTED: spinlabel = "Spin Restricted" else: spinlabel = "Spin Unrestricted" # . Create the atom names. atomnames = [] for (i, n) in enumerate(atomicNumbersi): atomnames.append(PeriodicTable.Symbol(n, index=i + 1)) # . Atom data. columns = [10, 20, 20, 20] if not spinDensitiesRESTRICTED: columns.append(20) table = log.GetTable(columns=columns) table.Start() table.Title("Atom Data (" + spinlabel + ")") table.Heading("Atom") table.Heading("Charge") table.Heading("Self Bond Order") table.Heading("Valence") if not spinDensitiesRESTRICTED: table.Heading("Free Valence") for (i, ni) in enumerate(atomnames): table.Entry(ni) table.Entry("{:.3f}".format(qmulliken[i])) table.Entry("{:.3f}".format(tbo[i, i])) table.Entry("{:.3f}".format(valencies[i])) if not spinDensitiesRESTRICTED: table.Entry("{:.3f}".format(free[i])) table.Stop() # . Bond orders. for key in bokeys: orders = bondOrders[key] table = log.GetTable(columns=[10, 10, 20, 20]) table.Start() if key == ("", ""): table.Title("Total Bond Orders") else: table.Title(key[0].title() + "/" + key[1].title() + " Bond Orders") table.Heading("Atom 1") table.Heading("Atom 2") table.Heading("Order") table.Heading("Distance") for (i, ni) in enumerate(atomnames): for (j, nj) in enumerate(atomnames[0:i]): b = orders[i, j] if math.fabs(b) > bondOrdertolerance: table.Entry(ni) table.Entry(nj) table.Entry("{:.3f}".format(b)) table.Entry("{:.3f}".format(coordinates3.Distance( i, j))) table.Stop() # . Checks on the calculation. # . Free valence. if spinDensitiesRESTRICTED: deviation = free.AbsoluteMaximum() if deviation > chargetolerance: log.Paragraph( "Warning: the largest deviation between the free valence values and zero is {:.3f}." .format(deviation)) # . Total charge. deviation = math.fabs(qmulliken.Sum() - float(infile.charge)) if deviation > chargetolerance: log.Paragraph( "Warning: the total charge deviates from the formal charge by {:.3f}." .format(deviation)) # . Check for a valid reference set of Mulliken charges. qreference = getattr(outfile, "qmulliken", None) if (qreference is not None) and (len(qreference) == len(atomicNumbersi)): qmulliken.AddScaledArray(-1.0, qreference) deviation = qmulliken.AbsoluteMaximum() if deviation > chargetolerance: log.Paragraph( "Warning: the largest deviation between calculated and read Mulliken charges is {:.3f}." .format(deviation)) # . Finish up. return bondOrders
def SolvateSystemBySuperposition ( solute, solvent, log = logFile, excludeHydrogens = True, reorientSolute = True, printPairs = False, scaleradii = 0.75 ): """Solvate a system by superposition.""" # . Check that the solvent has a full connectivity. if not solvent.connectivity.HasFullConnectivity ( ): raise ValueError ( "The solvent must have a full connectivity." ) # . Create a sequence. if solute.sequence is not None: entityLabel = DetermineUniqueEntityLabel ( solute, label = "W" ) CreateHomogeneousIsolateSequence ( solvent, entityLabel = entityLabel ) # . Merge the two systems. solution = MergeByAtom ( solute, solvent ) # . Get the atom masses and selections corresponding to the solute and solvent atoms. masses = solution.atoms.GetItemAttributes ( "mass" ) nsolute = len ( solute.atoms ) nsolvent = len ( solvent.atoms ) soluteatoms = Selection.FromIterable ( range ( nsolute ) ) solventatoms = Selection.FromIterable ( range ( nsolute, nsolute + nsolvent ) ) # . Reorient the solute coordinates if required. if reorientSolute: solution.coordinates3.ToPrincipalAxes ( selection = soluteatoms, weights = masses ) # . Check the extents of the solute and solvent - to ensure the box is big enough! ( origin1, extents1 ) = solution.coordinates3.EnclosingOrthorhombicBox ( selection = soluteatoms ) ( origin2, extents2 ) = solution.coordinates3.EnclosingOrthorhombicBox ( selection = solventatoms ) extents2.AddScaledVector3 ( -1.0, extents1 ) if min ( extents2 ) < 0.0: raise ValueError ( "It is likely that the solvent box is too small for the solute." ) # . Move the center of the solute atoms to that of the solvent atoms. translation = Clone ( origin2 ) translation.AddScaledVector3 ( 0.5, extents2 ) # translation.AddScaledVector3 ( -0.5, extents1 ) # . Not needed as done above. translation.AddScaledVector3 ( -1.0, origin1 ) solution.coordinates3.Translate ( translation, selection = soluteatoms ) # . Reduce the selections if hydrogens are to be excluded. if excludeHydrogens: indices = [] for i in soluteatoms: if solution.atoms[i].atomicNumber != 1: indices.append ( i ) soluteatoms = Selection.FromIterable ( indices ) indices = [] for i in solventatoms: if solution.atoms[i].atomicNumber != 1: indices.append ( i ) solventatoms = Selection.FromIterable ( indices ) # . Get the radii. radii = solution.atoms.GetItemAttributes ( "vdwRadius" ) radii.Scale ( scaleradii ) # . Get the pairlist. pairlist = CrossPairList_FromSingleCoordinates3 ( solution.coordinates3, selection1 = solventatoms, selection2 = soluteatoms, radii = radii ) npairs = len ( pairlist ) # . Find the solvent atoms in the pairlist (which are the first indices in the list). indices = set ( ) for ( i, j ) in pairlist: indices.add ( i ) # . Now find the indices of all the atoms in the corresponding solvent isolates. if len ( indices ) > 0: toremove = solution.connectivity.isolates.GetAllIndices ( Selection.FromIterable ( indices ) ) ntoremove = len ( toremove ) # . Remove unwanted atoms. reduced = PruneByAtom ( solution, toremove.Complement ( upperBound = nsolute + nsolvent ) ) if solution.sequence is not None: RenumberEntityComponents ( reduced, entityLabels = [ entityLabel ] ) else: ntoremove = 0 reduced = solution # . Set the symmetry of solution by copying that of solvent. if ( solvent.symmetry is not None ) and ( solvent.symmetryParameters is not None ): keywordArguments = solvent.symmetryParameters.__getstate__ ( ) keywordArguments["crystalClass"] = solvent.symmetry.crystalClass keywordArguments["transformations"] = solvent.symmetry.transformations reduced.DefineSymmetry ( **keywordArguments ) # . Do some printing. if LogFileActive ( log ): # . Distances. if printPairs: table = log.GetTable ( columns = [ 10, 10, 20, 20 ] ) table.Start ( ) table.Title ( "Solute/Solvent Pairs" ) table.Heading ( "Solvent" ) table.Heading ( "Solute" ) table.Heading ( "Distance" ) table.Heading ( "Cutoff" ) for ( i, j ) in pairlist: table.Entry ( PeriodicTable.Symbol ( solution.atoms[i].atomicNumber, index = i ) ) table.Entry ( PeriodicTable.Symbol ( solution.atoms[j].atomicNumber, index = j ) ) table.Entry ( "{:.3f}".format ( solution.coordinates3.Distance ( i, j ) ) ) table.Entry ( "{:.3f}".format ( radii[i] + radii[j] ) ) table.Stop ( ) # . Summary. summary = log.GetSummary ( ) summary.Start ( "Solvation By Superposition Summary" ) summary.Entry ( "Solute Atoms" , "{:d}".format ( nsolute ) ) summary.Entry ( "Solvent Atoms" , "{:d}".format ( nsolvent - ntoremove ) ) summary.Entry ( "Atoms Removed" , "{:d}".format ( ntoremove ) ) summary.Entry ( "Solute/Solvent Pairs" , "{:d}".format ( npairs ) ) summary.Stop ( ) # . Finish up. return reduced
} # . Chirality. _CHIRALITYFULL = {"AL": "AL", "OH": "OH", "SP": "SP", "TB": "TB", "TH": "TH"} _CHIRALITYREDUCED = { "@": ("??", 1), "@@": ("??", 2), "@@@": ("??", 3), "@@@@": ("??", 4) } # . Element data. # . All element tokens. _ELEMENTTOKENS = {} for atomicNumber in PeriodicTable.AtomicNumbers(): _ELEMENTTOKENS[PeriodicTable.Symbol(atomicNumber)] = (atomicNumber, False) for atomicNumber in ELEMENTSAROMATIC: _ELEMENTTOKENS[string.lower( PeriodicTable.Symbol(atomicNumber))] = (atomicNumber, True) # . Tokens for elements that can take a reduced representation (outside of square brackets). _REDUCEDELEMENTTOKENS = {} for atomicNumber in ELEMENTSORGANIC: _REDUCEDELEMENTTOKENS[PeriodicTable.Symbol(atomicNumber)] = (atomicNumber, False) if atomicNumber in ELEMENTSAROMATIC: _REDUCEDELEMENTTOKENS[string.lower( PeriodicTable.Symbol(atomicNumber))] = (atomicNumber, True) # . Hydrogen count. _HCOUNTTOKENS = {"H": 1}
def WriteSystem(self, system, data=None, occupancies=None, QCONECT=False, selection=None, suppressICodeField=False, temperatureFactors=None, title=None, useSegmentEntityLabels=False): """Write out a system. |system| is the system to be written. |data| is the data to be written. The coordinates of system are written if |data| is absent. |QCONECT| is the option for writing CONECT records. If |QCONECT| is True all connections are written (not just those to heteroatoms). |selection| gives the atoms to write. The default is to write all atoms. |title| is an optional title. """ # . Check |system|. if not isinstance(system, System): raise TypeError("Invalid |system| argument.") # . Get the sequence. sequence = getattr(system, "sequence", None) if sequence is None: sequence = Sequence.FromAtomContainer(system.atoms, componentLabel="UNK.1") # . Get the coordinate data. if isinstance(data, Coordinates3): xyz = data else: xyz = system.coordinates3 if xyz is None: raise TypeError( "Unable to obtain coordinate data from |system| or |data| arguments." ) # . Check that the PDB model and coordinates are consistent. if len(system.atoms) != xyz.rows: raise TypeError( "The PDB model and coordinate data are of different lengths.") # . Get the selection. if selection is None: towrite = range(len(system.atoms)) else: towrite = selection # . Get polymer terminating atoms as the last atom of the terminating component. polymerTerminatingAtoms = set() for polymer in sequence.linearPolymers: polymerTerminatingAtoms.add( polymer.rightTerminalComponent.children[-1]) # . Get the label. label = None if isinstance(title, basestring): label = title.upper() elif isinstance(system.label, basestring): label = system.label.upper() if (label is not None) and (len(label) > _MAXIMUMTITLELENGTH): label = label[0:_MAXIMUMTITLELENGTH] # . Check for extra data. hasOccupancies = (occupancies is not None) and (len(occupancies) == len(system.atoms)) hasTemperatureFactors = (temperatureFactors is not None) and (len(temperatureFactors) == len(system.atoms)) # . Start writing. self.Open() # . Header. self.file.write( _HEADERLINEFORMAT.format("HEADER", "UNKNOWN", time.strftime("%d-%b-%y").upper())) # . Title. if (label is not None): self.file.write(_TITLELINEFORMAT.format("TITLE", label)) # . Author. self.file.write( _AUTHORLINEFORMAT.format("AUTHOR", "GENERATED BY PDYNAMO", PDYNAMO_VERSION)) # . Compound. self.file.write(_COMPOUNDLINEFORMAT.format("COMPND", "UNKNOWN")) # . Initialization. natm = 0 ncon = 0 nter = 0 cIndices = {} # . Loop over atom indices. for iatom in towrite: # . Get data. atom = system.atoms[iatom] # . Atom data. element = PeriodicTable.Symbol(atom.atomicNumber).upper() if element == "*": element = "" # . Component data. (resName, resSeq, iCode) = sequence.ParseLabel(atom.parent.label, fields=3) # . Entity data. entityLabel = atom.parent.parent.label if useSegmentEntityLabels: chainID = "" segID = entityLabel[0:4] else: chainID = entityLabel[0:1] segID = "" # . Atom output. if resName in _STANDARDRESIDUES: recordname = "ATOM" else: recordname = "HETATM" # . Output label. label = atom.label if len(label) >= 4: outputlabel = label[0:4] elif label[0:1].isdigit(): outputlabel = label else: outputlabel = " " + label # . Extra data. if hasOccupancies: occupancy = occupancies[iatom] else: occupancy = 0.0 if hasTemperatureFactors: temperatureFactor = temperatureFactors[iatom] else: temperatureFactor = 0.0 # . Output the line. if suppressICodeField: self.file.write( _ATOMLINEFORMAT2.format(recordname, natm + nter + 1, outputlabel, " ", resName[0:3], chainID, resSeq[:5], xyz[iatom, 0], xyz[iatom, 1], xyz[iatom, 2], occupancy, temperatureFactor, segID, element, " ")) else: self.file.write( _ATOMLINEFORMAT1.format(recordname, natm + nter + 1, outputlabel, " ", resName[0:3], chainID, resSeq[:4], iCode, xyz[iatom, 0], xyz[iatom, 1], xyz[iatom, 2], occupancy, temperatureFactor, segID, element, " ")) # . Save the atom index. cIndices[iatom] = (natm + nter + 1) # . Polymer termination output. if atom in polymerTerminatingAtoms: nter += 1 if suppressICodeField: self.file.write( _TERLINEFORMAT2.format("TER", natm + nter + 1, resName, chainID, resSeq)) else: self.file.write( _TERLINEFORMAT1.format("TER", natm + nter + 1, resName, chainID, resSeq, iCode)) # . Increment natm. natm += 1 # . Connection data. if QCONECT and (system.connectivity.bonds is not None) and (len(system.connectivity.bonds) > 0): system.connectivity.bonds.MakeConnections() for iatom in towrite: # . Get the connections. iconnections = system.connectivity.bonds.GetConnectedAtoms( iatom) # . Normal indices. # . Transform to pdb indices. iindex = cIndices[iatom] jindices = [] for j in iconnections: jindex = cIndices.get(j, -1) if jindex > 0: jindices.append(jindex) # . Output. nindices = len(jindices) if nindices > 0: for i in range(0, nindices, _NCONECTS): self.file.write( _CONECTLINEFORMAT1.format("CONECT", iindex)) for j in range(i, min(i + _NCONECTS, nindices)): self.file.write( _CONECTLINEFORMAT2.format(jindices[j])) self.file.write("\n") ncon += 1 # . Master. self.file.write( _MASTERLINEFORMAT.format("MASTER", 0, 0, 0, 0, 0, 0, 0, 0, len(system.atoms), nter, ncon, 0)) # . End. self.file.write(_ENDLINEFORMAT.format("END")) self.Close()
def WriteSystem ( self, system, datalabel = _DEFAULTDATALABEL ): """Write out a system.""" # . Check |system|. if not isinstance ( system, System ): raise TypeError ( "Invalid |system| argument." ) # . Get the sequence and other data. sequence = getattr ( system, "sequence", None ) if sequence is None: sequence = Sequence.FromAtomContainer ( system.atoms, componentLabel = "UNK.1" ) xyz = system.coordinates3 # . Find which entities are linear polymers. polymerEntities = set ( ) for polymer in sequence.linearPolymers: polymerEntities.add ( polymer.leftTerminalComponent.parent ) polymerEntities.add ( polymer.rightTerminalComponent.parent ) # . Start writing. self.Open ( ) # . Initial comment. self.file.write ( "# . mmCIF file generated by pMolecule {:s} on {:s}.\n".format ( PDYNAMO_VERSION, time.strftime ( "%d-%b-%y" ) ) ) # . Data header. self.file.write ( "data_" + datalabel.strip ( ) + "\n" ) # . Atoms. self.file.write ( _ATOMSITEHEADER ) self.file.write ( "loop_\n" ) self.file.write ( "_atom_site.id\n" ) self.file.write ( "_atom_site.type_symbol\n" ) self.file.write ( "_atom_site.label_atom_id\n" ) self.file.write ( "_atom_site.label_comp_id\n" ) self.file.write ( "_atom_site.label_seq_id\n" ) self.file.write ( "_atom_site.auth_seq_id\n" ) self.file.write ( "_atom_site.pdbx_PDB_ins_code\n" ) self.file.write ( "_atom_site.cartn_x\n" ) self.file.write ( "_atom_site.cartn_y\n" ) self.file.write ( "_atom_site.cartn_z\n" ) self.file.write ( "_atom_site.label_asym_id\n" ) self.file.write ( "_atom_site.label_entity_id\n" ) sequenceAtoms = sequence.GatherAtoms ( ) for atom in sequenceAtoms: index = atom.index element = PeriodicTable.Symbol ( system.atoms[index].atomicNumber ) # . Entity data. entity = atom.parent.parent fields = sequence.ParseLabel ( entity.label, fields = 1 ) asymlabel = fields[0] if len ( asymlabel ) <= 0: asymlabel = _UNDEFINEDCHARACTER entitylabel = entity.label if len ( entitylabel ) <= 0: entitylabel = _UNDEFINEDCHARACTER # . Component data. fields = sequence.ParseLabel ( atom.parent.label, fields = 3 ) name = fields[0] esequence = fields[1] insertioncode = fields[2] if len ( insertioncode ) <= 0: insertioncode = _UNDEFINEDCHARACTER if entity in polymerEntities: psequence = esequence else: psequence = _UNDEFINEDCHARACTER self.file.write ( "{:6d} {:<3s} {:<5s} {:<4s} {:<6s} {:<6s} {:<2s} {:10.5f} {:10.5f} {:10.5f} {:<2s} {:<s}\n".format ( index, element, atom.label, name, psequence, esequence, insertioncode, xyz[index,0], xyz[index,1], xyz[index,2], asymlabel, entitylabel ) ) # . Entities. npolymer = 0 self.file.write ( _ENTITYHEADER ) self.file.write ( "loop_\n" ) self.file.write ( "_entity.id\n" ) self.file.write ( "_entity.type\n" ) for entity in sequence.children: label = entity.label if len ( label ) <= 0: label = _UNDEFINEDCHARACTER entityType = "Non-Polymer" if entity in polymerEntities: entityType = "Polymer" npolymer += 1 self.file.write ( "{:<10s} {:s}\n".format ( label, entityType ) ) # . Entities - polymer sequence. if npolymer > 0: self.file.write ( _ENTITYPOLYMERSEQUENCEHEADER ) self.file.write ( "loop_\n" ) self.file.write ( "_entity_poly_seq.entity_id\n" ) self.file.write ( "_entity_poly_seq.mon_id\n" ) self.file.write ( "_entity_poly_seq.num\n" ) for entity in sequence.children: if entity in polymerEntities: label = entity.label if len ( label ) <= 0: label = _UNDEFINEDCHARACTER oldname = None oldsequence = None for component in entity.children: fields = sequence.ParseLabel ( component.label, fields = 2 ) name = fields[0] newSequence = fields[1] if ( name != oldname ) and ( newSequence != oldsequence ): self.file.write ( "{:<10s} {:<4s} {:<6s}\n".format ( label, name, newSequence ) ) oldname = name oldsequence = newSequence # . Finish up. self.Close ( )