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 ParseECPs(self): """Parse the ECP section.""" # . Initialization. ecpelectrons = [] # . Header. self.GetLine() # . Blank line. self.GetLine() # . Table header. # . Loop over atom lines. while True: tokens = self.GetTokens() # . Blank line. if len(tokens) == 0: break # . Atom line. elif len(tokens) == 2: atomicNumber = PeriodicTable.AtomicNumber(tokens[0]) if (atomicNumber > 0): ecpelectrons.append(int(tokens[1])) # . Finish up. self.ecpelectrons = ecpelectrons self.QECP = True
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 BuildHydrogenCoordinates3FromConnectivity(system, log=logFile, randomNumberGenerator=None): """Build hydrogen coordinates. The coordinates are built using connectivity information only (bonds and angles) which means that no account is taken of non-connectivity information (such as hydrogen-bonding). These interactions will have to be optimized separately using energy minimization or dynamics. Note that bonds to other hydrogens are ignored and unbound hydrogens or hydrogens linked to more than one heavy atom will not be built.""" # . Check for a system object. if isinstance(system, System) and (system.connectivity is not None) and ( system.connectivity.HasFullConnectivity()): # . Check whether there are undefined coordinates. coordinates3 = system.coordinates3 numberUndefined0 = coordinates3.numberUndefined if numberUndefined0 > 0: # . Initialization. bonds = system.connectivity.bonds direction = Vector3.Null() if randomNumberGenerator is None: randomNumberGenerator = RandomNumberGenerator.WithRandomSeed() # . Loop over heavy atoms with defined coordinates. for (c, atom) in enumerate(system.atoms): if (atom.atomicNumber != 1) and (c not in coordinates3.undefined): # . Initialization. built = [] builth = [] tobuild = [] unbuildable = [] # . Loop over the connected atoms. others = bonds.GetConnectedAtoms(c) for i in others: other = system.atoms[i] QBUILT = (i not in coordinates3.undefined) # . Hydrogens. if other.atomicNumber == 1: if QBUILT: builth.append(i) elif len(bonds.GetConnectedAtoms(i)) == 1: tobuild.append(i) else: unbuildable.append(i) # . Other atoms. else: if QBUILT: built.append(i) else: unbuildable.append(i) # . Skip this atom if the number of connections is greater than four, there are no hydrogens to build or there are unbuildable atoms. if (len(others) > 4) or (len(tobuild) == 0) or (len(unbuildable) > 0): continue # . Order the lists and put built hydrogens after built heavy atoms as it is reasoned that heavy atom coordinates will be more reliable. built.sort() builth.sort() tobuild.sort() built += builth # . Get coordination data for the center. nconnections = len(built) + len(tobuild) bondlength = PeriodicTable.Element( atom.atomicNumber).GetSingleBondDistance(1) if bondlength is None: bondlength = _DEFAULTBONDDISTANCE angle = PeriodicTable.Element( atom.atomicNumber).GetCoordinationAngle(nconnections) if angle is None: angle = _COORDINATIONANGLES.get(nconnections, 0.0) planeangle = _COORDINATIONPLANEANGLES.get( nconnections, 0.0) # . Build the hydrogens. while len(tobuild) > 0: # . Get the hydrogen index. h = tobuild.pop(0) # . Build according to the number of built connected atoms. nbuilt = len(built) # . Get a random normalized vector. if (nbuilt == 0) or (nbuilt == 1): for i in range(3): direction[i] = 2.0 * ( randomNumberGenerator.NextReal() - 0.5) direction.Normalize() # . Put the hydrogen in a random direction from the center. # . Works for all cases. if nbuilt == 0: coordinates3.BuildPointFromDistance( h, c, bondlength, direction) # . Put the hydrogen at the correct angle from the center and built atom but in a random plane. # . Works for all cases given correct choice of angle. elif nbuilt == 1: coordinates3.BuildPointFromDistanceAngle( h, c, built[0], bondlength, angle, direction) # . Put the hydrogen away from the other built points at an appropriate angle from their plane. # . The sign of the plane angle is arbitrary. # . Works for cases 3, 4, 5 (square pyramidal), 6 with correct choice of planeangle. elif nbuilt == 2: coordinates3.BuildPointFromDistancePlaneAngle( h, c, built[0], built[1], bondlength, planeangle) # . Put the hydrogen using a tetrahedral tripod. # . Only works for tetrahedral coordination. elif nbuilt == 3: coordinates3.BuildPointFromDistanceTetrahedralTripod( h, c, built[0], built[1], built[2], bondlength) # . Cannot handle valencies greater than 4 for the moment. else: break # . The hydrogen has been built. built.append(h) coordinates3.FlagCoordinateAsDefined(h) # . Output a summary. if LogFileActive(log): numberToBuild = coordinates3.numberUndefined numberBuilt = (numberUndefined0 - numberToBuild) if numberBuilt <= 0: log.Paragraph("Coordinates for no hydrogens were built.") elif numberBuilt == 1: log.Paragraph("Coordinates for one hydrogen were built.") else: log.Paragraph( "Coordinates for {:d} hydrogens were built.".format( numberBuilt))
def ToSystem(self, mmModel=None, parameters=None): """Create a system.""" system = None if self.QPARSED: # . Get the sequence. sequence = self.ToSequence() # . Basic system. system = System.FromSequence(sequence) system.label = " ".join(self.title) # . Assign atomic numbers from masses. for (datum, atom) in zip(self.atoms, system.atoms): atom.atomicNumber = PeriodicTable.AtomicNumberFromMass( datum[7]) # . The MM model. if mmModel is None: mmModel = MMModelCHARMM() # . Build the model but only when PSF files are in XPLOR format. if (mmModel is not None) and (parameters is not None) and (self.isXPLOR): system.DefineMMModel(mmModel, buildModel=False) # . The MM atoms. mm = self.ToMMAtomContainer() if mm is not None: system.energyModel.mmAtoms = mm # . Various MM terms. mmTerms = [] mm = self.ToHarmonicBondContainer( rawParameters=parameters.bonds) if mm is not None: mmTerms.append(mm) mm = self.ToHarmonicAngleContainer( rawParameters=parameters.angles) if mm is not None: mmTerms.append(mm) mm = self.ToHarmonicUreyBradleyContainer( rawParameters=parameters.ureybradleys) if mm is not None: mmTerms.append(mm) mm = self.ToFourierDihedralContainer( rawParameters=parameters.dihedrals, rawParametersWild=parameters.dihedralwilds) if mm is not None: mmTerms.append(mm) mm = self.ToHarmonicImproperContainer( rawParameters=parameters.impropers, rawParametersWild=parameters.improperwilds) if mm is not None: mmTerms.append(mm) mm = self.ToCMAPDihedralContainer( rawParameters=parameters.cmaps) if mm is not None: mmTerms.append(mm) if len(mmTerms) > 0: system.energyModel.mmTerms = MMTermContainer(mmTerms) # . Exclusions. (exclusions, interactions14) = self.ToExclusionPairLists() if exclusions is not None: system.energyModel.exclusions = exclusions if interactions14 is not None: system.energyModel.interactions14 = interactions14 # . LJ terms. ( ljParameters, ljParameters14 ) = self.ToLJParameterContainers ( parameters = parameters.nonbonds , parametersWild = parameters.nonbondwilds, \ parameters14 = parameters.nonbond14s, parameters14Wild = parameters.nonbond14wilds ) if ljParameters is not None: system.energyModel.ljParameters = ljParameters if ljParameters14 is not None: system.energyModel.ljParameters14 = ljParameters14 # . Fixed atoms - do after MM model so MM terms are appropriately affected. self.ToFixedAtoms(system) return system
def GetAtomicNumber ( token ): """Convert a token into an atomic number.""" symbol = token.upper ( ) if symbol == _DUMMYATOM: return -3 elif symbol in _NONDUMMYATOMSYMBOLS: return -2 else: return PeriodicTable.AtomicNumber ( token )
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 AddAtomSite(self, **kwargs): """Add an atom site.""" atom = None if self.QFINALIZED: raise ValueError("Cannot add an atom site to a finalized model.") else: # . Initialization. warningtag = "Atom Site {:d}".format(len(self.atoms)) # . Get the entity. auth_asym_id = mmCIFModel.FetchFromKeywordArguments( kwargs, "auth_asym_id") label_asym_id = mmCIFModel.FetchFromKeywordArguments( kwargs, "label_asym_id") label_entity_id = mmCIFModel.FetchFromKeywordArguments( kwargs, "label_entity_id") entitykey = mmCIFModelEntity.MakeKey(label_entity_id, label_asym_id) entity = self.entities.get(entitykey, None) if entity is None: entity = self.AddEntity(entitykey) # . Check the alternate name. if entity is not None: if entity.alternateasymlabel is None: entity.alternateasymlabel = auth_asym_id elif entity.alternateasymlabel != auth_asym_id: self.Warning( warningtag, "Mismatch in alternate names for entity asymmetric unit - " + entity.alternateasymlabel + " and " + auth_asym_id + ".", False) # . Get the component. auth_comp_id = mmCIFModel.FetchFromKeywordArguments( kwargs, "auth_comp_id") auth_seq_id = mmCIFModel.FetchFromKeywordArguments( kwargs, "auth_seq_id") insertioncode = mmCIFModel.FetchFromKeywordArguments( kwargs, "pdbx_pdb_ins_code") name = mmCIFModel.FetchFromKeywordArguments( kwargs, "label_comp_id") sequencenumber = mmCIFModel.FetchFromKeywordArguments( kwargs, "label_seq_id") if name == _UNDEFINEDCHARACTER: name = auth_comp_id auth_comp_id = None if sequencenumber == _UNDEFINEDCHARACTER: sequencenumber = auth_seq_id auth_seq_id = None componentkey = mmCIFModelComponent.MakeKey( name, sequencenumber, insertioncode) component = entity.components.get(componentkey, None) if component is None: component = entity.AddComponent(componentkey) self.components.append(component) # . Check alternate names. if component is not None: if auth_comp_id is not None: if component.alternatename is None: component.alternatename = auth_comp_id elif component.alternatename != auth_comp_id: self.Warning( warningtag, "Mismatch in alternate names for component - " + component.alternatename + " and " + auth_comp_id + ".", False) if auth_seq_id is not None: if component.alternatesequence is None: component.alternatesequence = auth_seq_id elif component.alternatesequence != auth_seq_id: self.Warning( warningtag, "Mismatch in alternate sequences for component - " + component.alternatesequence + " and " + auth_seq_id + ".", False) # . Get the atom data. atomicNumber = PeriodicTable.AtomicNumber( mmCIFModel.FetchFromKeywordArguments( kwargs, "type_symbol")) auth_atom_id = mmCIFModel.FetchFromKeywordArguments( kwargs, "auth_atom_id") name = mmCIFModel.FetchFromKeywordArguments( kwargs, "label_atom_id") if name == _UNDEFINEDCHARACTER: name = auth_atom_id auth_atom_id = None atom = component.atoms.get(name, None) atomKey = name if atom is None: atom = component.AddAtom(name, atomicNumber=atomicNumber, entity=entity) self.atoms.append(atom) # . Check alternate labels. if atom is not None: if auth_comp_id is not None: if atom.alternatelabel is None: atom.alternatelabel = auth_atom_id elif atom.alternatelabel != auth_atom_id: self.Warning( warningtag, "Mismatch in alternate labels for atom - " + atom.alternatelabel + " and " + auth_atom_id + ".", False) # . Get the atom data. label_alt_id = mmCIFModel.FetchFromKeywordArguments( kwargs, "label_alt_id") pdbx_pdb_model_num = mmCIFModel.FetchFromKeywordArguments( kwargs, "pdbx_pdb_model_num", default=_DEFAULTMODELNUMBER) self.altLocs.add(label_alt_id) self.modelNumbers.add(pdbx_pdb_model_num) datakey = mmCIFModelAtomData.MakeKey( label_alt_id, pdbx_pdb_model_num) data = atom.data.get(datakey, None) if data is None: try: x = mmCIFModel.ToFloat( mmCIFModel.FetchFromKeywordArguments( kwargs, "cartn_x")) y = mmCIFModel.ToFloat( mmCIFModel.FetchFromKeywordArguments( kwargs, "cartn_y")) z = mmCIFModel.ToFloat( mmCIFModel.FetchFromKeywordArguments( kwargs, "cartn_z")) occupancy = mmCIFModel.ToFloat( mmCIFModel.FetchFromKeywordArguments( kwargs, "occupancy"), default=_DEFAULTOCCUPANCY) except: self.Warning( warningtag, "Unable to convert atom site floating point data.", False) atom.data[datakey] = mmCIFModelAtomData( altLoc=label_alt_id, atom=atom, model=pdbx_pdb_model_num, x=x, y=y, z=z, occupancy=occupancy) else: self.Warning( warningtag, "Adding duplicate atomsite (" + _MAJORSEPARATOR.join([ datakey, atomKey, componentkey, entitylabel ]) + ") to model.", False) return atom
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
"----": -4 } # . 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.
def ParseAtomBlock(self, line, QLOOP): """Parse the block with atom information.""" # . Initialization. atoms = [] QFIRST = True if QLOOP: # . Initialization. index = {} converters = [] QATOMS = False # . Atom lines. while True: if not QFIRST: line = self.GetLine() if line.startswith("#"): break # . Column headers. elif not QATOMS: if line.startswith("_chem_comp_atom.atom_id"): index["atom_id"] = len(converters) converters.append(None) elif line.startswith("_chem_comp_atom.charge"): index["charge"] = len(converters) converters.append(int) elif line.startswith("_chem_comp_atom.comp_id"): index["comp_id"] = len(converters) converters.append(None) elif line.startswith("_chem_comp_atom.pdbx_align"): index["pdbx_align"] = len(converters) converters.append(int) elif line.startswith("_chem_comp_atom.type_symbol"): index["type_symbol"] = len(converters) converters.append(PeriodicTable.AtomicNumber) elif line.startswith("_chem_comp_atom."): converters.append(None) else: QATOMS = True # . Atoms. if QATOMS: tokens = self.ParseTokens(line, converters=converters) atoms.append ( PDBComponentAtom ( atomicNumber = tokens[index["type_symbol"]], \ formalCharge = tokens[index["charge" ]], \ label = tokens[index["atom_id" ]], \ pdbAlign = tokens[index["pdbx_align" ]] ) ) QFIRST = False # . Single atom. else: while True: if not QFIRST: line = self.GetLine() tokens = self.ParseTokens(line) if tokens[0] == "#": break elif len(tokens) > 2: self.Warning("Too many tokens.", True) elif tokens[0] == "_chem_comp_atom.atom_id": label = tokens[1] elif tokens[0] == "_chem_comp_atom.charge": formalCharge = int(tokens[1]) elif tokens[0] == "_chem_comp_atom.pdbx_align": pdbAlign = int(tokens[1]) elif tokens[0] == "_chem_comp_atom.type_symbol": atomicNumber = PeriodicTable.AtomicNumber(tokens[1]) QFIRST = False atoms.append ( PDBComponentAtom ( atomicNumber = atomicNumber, \ formalCharge = formalCharge, \ label = label, \ pdbAlign = pdbAlign ) ) # . Finish up. self.active.atoms = atoms
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 ( )
def HardSphereIonMobilities(molecule, nreflections=30, ntrajectories=600000, randomNumberGenerator=None, temperature=298.0, log=logFile): """Calculate ion mobilities with a hard-sphere model.""" # . Get the atom data. hsradii = _GetHardSphereRadii(molecule.atoms) masses = molecule.atoms.GetItemAttributes("mass") totalmass = masses.Sum() # . Get initial coordinates, move to center of mass and convert to metres. xyz0 = Clone(molecule.coordinates3) xyz0.TranslateToCenter(weights=masses) xyz0.Scale(1.0e-10) # . Get the mass constant. massHe = PeriodicTable.Element(2).mass massconstant = _MASSCONSTANT * math.sqrt((1.0 / massHe) + (1.0 / totalmass)) # . Get the random number generator. if randomNumberGenerator is None: randomNumberGenerator = RandomNumberGenerator.WithRandomSeed() rotation = Matrix33.Null() # . Initialize some calculation variables. cof = Real1DArray.WithExtent(nreflections) cof.Set(0.0) crof = Real1DArray.WithExtent(nreflections) crof.Set(0.0) crb = 0.0 mreflections = 0 # . Loop over the trajectories. for it in range(ntrajectories): # . Randomly rotate the coordinate set. rotation.RandomRotation(randomNumberGenerator) xyz = Clone(xyz0) xyz.Rotate(rotation) # . Loop over the collisions. QCOLLISION = False for ir in range(nreflections): # . Initial collision - at a random point in the yz plane along the x-axis. if ir == 0: (origin, extents) = xyz.EnclosingOrthorhombicBox(radii=hsradii) yzarea = extents[1] * extents[2] yc = origin[1] + extents[1] * randomNumberGenerator.NextReal() zc = origin[2] + extents[2] * randomNumberGenerator.NextReal() xaxis = Vector3.WithValues(1.0, 0.0, 0.0) # . Subsequent collisions - always along the x-axis. else: yc = 0.0 zc = 0.0 # . Initialization. ic = -1 # . The index of the colliding particle. xc = origin[0] + extents[0] # . The largest x-coordinate. # . Loop over particles. for (i, h) in enumerate(hsradii): # . After the first collision only x-values > 0 are allowed. if (ir == 0) or (xyz[i, 0] > 1.0e-16): # . yd and zd are the coordinates of the impact points for the ith atom # . with respect to its own coordinates (if such a point exists). # . dev is the impact parameter. h2 = h * h y = yc - xyz[i, 1] z = zc - xyz[i, 2] yz2 = y * y + z * z # . If there is a collision with the ith atom, check to see if it occurs before previous collisions. if yz2 < h2: x = xyz[i, 0] - math.sqrt(h2 - yz2) if x < xc: xc = x ic = i # . Check mreflections. if ir >= mreflections: mreflections = ir + 1 # . There was a collision. if ic >= 0: QCOLLISION = True # . Translate the coordinates so that the collision point is at the origin. xyz.Translate(Vector3.WithValues(-xc, -yc, -zc)) # . Rotate the coordinates so that the outgoing vector is along the x-axis. h = xyz.GetRow( ic ) # . Normalized vector from the collision point to the ic-th atom. h.Normalize(tolerance=1.0e-20) axis = Vector3.WithValues( 0.0, h[2], -h[1]) # . Normalized axis of rotation. axis.Normalize(tolerance=1.0e-20) alpha = math.pi - 2.0 * math.acos(h[0]) # . Angle of rotation. rotation.RotationAboutAxis(alpha, axis) xyz.Rotate(rotation) rotation.ApplyTo(xaxis) # . Calculate the cosine of the angle between the incoming vector and the normal to a plane, # . the reflection from which would be equivalent to the accumulated reflection. # . This is equal to h[0] when ir = 0. cof[ir] = math.cos(0.5 * (math.pi - math.acos(xaxis[0]))) # . Check outgoing. # . Get the outgoing vector (the ingoing vector is always [1,0,0]). out = Vector3.WithValues(1.0 - 2.0 * h[0] * h[0], -2.0 * h[0] * h[1], -2.0 * h[0] * h[2]) rotation.ApplyTo(out) out[0] -= 1.0 if out.Norm2() > 1.0e-6: print( "Invalid Rotation: {:10.3f} {:10.3f} {:10.3f}.".format( out[0], out[1], out[2])) # . There was no collision. else: # . Top up the remaining elements of cof with the last valid value of cof. if ir == 0: t = 0.0 else: t = cof[ir - 1] for i in range(ir, nreflections): cof[i] = t # . Exit. break # . End of collisions. # . Projection approximation. if QCOLLISION: crb += yzarea # . Hard-sphere approximation. for ir in range(nreflections): crof[ir] += yzarea * cof[ir] * cof[ir] # . End of trajectories. crof.Scale(2.0 / float(ntrajectories)) pacs = crb / float(ntrajectories) pamob = massconstant / (pacs * math.sqrt(temperature)) hscs = crof[mreflections - 1] hsmob = massconstant / (hscs * math.sqrt(temperature)) # . Output results. if LogFileActive(log): summary = log.GetSummary() summary.Start("Hard-Sphere Ion Mobilities") summary.Entry("MC Trajectories", "{:d}".format(ntrajectories)) summary.Entry("Reflection Limit", "{:d}".format(nreflections)) summary.Entry("PA Mobility", "{:.4g}".format(pamob)) summary.Entry("PA Cross-Section", "{:.4g}".format(pacs * 1.0e+20)) summary.Entry("HS Mobility", "{:.4g}".format(hsmob)) summary.Entry("HS Cross-Section", "{:.4g}".format(hscs * 1.0e+20)) summary.Entry("Max. Reflections", "{:d}".format(mreflections)) summary.Stop() # . Finish up. results = { "MC Trajectories": ntrajectories, "Reflection Limit": nreflections, "PA Mobility": pamob, "PA Cross-Section": pacs * 1.0e+20, "HS Mobility": hsmob, "HS Cross-Section": hscs * 1.0e+20, "Maximum Reflections": mreflections } return results
def GenerateVanDerWaalsSurface(system, gridangularmomentum=21, log=logFile, qcAtomsOnly=False, scalingfactors=[1.0]): """Generate a superposition of van der Waals surfaces represented by grid points.""" # . QC atoms only (including link atoms). if qcAtomsOnly: qcAtoms = system.energyModel.qcAtoms atomicNumbers = qcAtoms.GetAtomicNumbers() coordinates3 = qcAtoms.GetCoordinates3(system.coordinates3) # . All atoms. else: atomicNumbers = system.atoms.GetItemAttributes("atomicNumber") coordinates3 = system.coordinates3 # . Find the number of atoms. natoms = len(atomicNumbers) # . Set radii. radii = Real1DArray.WithExtent(natoms) for (i, n) in enumerate(atomicNumbers): radii[i] = PeriodicTable.Element(n).vdwRadius # . Get the grid points for a single center. (basicgrid, weights) = LebedevLaikovGrid_GetGridPoints(gridangularmomentum) # . Allocate space for the possible number of grid points. npossible = natoms * basicgrid.rows * len(scalingfactors) gridPoints = Coordinates3.WithExtent(npossible) gridPoints.Set(0.0) # . Initialization. nfound = 0 atomgrid = Coordinates3.WithExtent(basicgrid.rows) translation = Vector3.Null() atomgrid.Set(0.0) # . Loop over scaling factors. for factor in scalingfactors: # . Loop over points. for i in range(natoms): # . Get the radius. iradius = factor * radii[i] # . Get the translation. translation[0] = coordinates3[i, 0] translation[1] = coordinates3[i, 1] translation[2] = coordinates3[i, 2] # . Get the scaled grid centered at the point. basicgrid.CopyTo(atomgrid) atomgrid.Scale(iradius) atomgrid.Translate(translation) # . Remove points that are within the current scaled radii of other points. for p in range(atomgrid.rows): QOK = True x = atomgrid[p, 0] y = atomgrid[p, 1] z = atomgrid[p, 2] for j in range(natoms): if j != i: dx = coordinates3[j, 0] - x dy = coordinates3[j, 1] - y dz = coordinates3[j, 2] - z jradius2 = (factor * radii[j])**2 if (dx**2 + dy**2 + dz**2) <= jradius2: QOK = False break if QOK: gridPoints[nfound, 0] = x gridPoints[nfound, 1] = y gridPoints[nfound, 2] = z nfound += 1 # . Reduce the array size if necessary. if nfound < npossible: newpoints = Coordinates3.WithExtent(nfound) for p in range(nfound): newpoints[p, 0] = gridPoints[p, 0] newpoints[p, 1] = gridPoints[p, 1] newpoints[p, 2] = gridPoints[p, 2] gridPoints = newpoints # . Create a system. # from pBabel import XYZFile_FromSystem # from pMolecule import System # ngrid = gridPoints.rows # junk = System ( ngrid * [ 1 ] ) # junk.coordinates3 = gridPoints # XYZFile_FromSystem ( "junk.xyz", junk ) # . Do some printing. if LogFileActive(log): summary = log.GetSummary() summary.Start("van der Waals Surface Generation Summary") summary.Entry("Atoms", "{:d}".format(natoms)) summary.Entry("Surfaces", "{:d}".format(len(scalingfactors))) summary.Entry("Found Points", "{:d}".format(nfound)) summary.Entry("Possible Points", "{:d}".format(npossible)) summary.Stop() # . Finish up. return gridPoints