def Point ( self, ix, iy, iz ):
     """Return the coordinates of a point."""
     point   = Vector3.Null ( )
     point[0] = self.origin[0] + float ( ix ) * self.gridspacing
     point[1] = self.origin[1] + float ( iy ) * self.gridspacing
     point[2] = self.origin[2] + float ( iz ) * self.gridspacing
     return point
예제 #2
0
파일: Site.py 프로젝트: Lucioric2000/pcetk
 def _CalculateCenter(self, centralAtom=None):
     """Calculate center of geometry of a site."""
     ceModel = self.parent
     system = ceModel.owner
     if centralAtom:
         found = False
         for index in self.siteAtomIndices:
             atom = system.atoms[index]
             if atom.label == centralAtom:
                 found = True
                 break
         if found:
             center = system.coordinates3[index]
         else:
             raise ContinuumElectrostaticsError(
                 "Cannot find central atom %s in site %s %s %d"
                 % (centralAtom, self.segName, self.resName, self.resSerial)
             )
     else:
         center = Vector3()
         natoms = len(self.siteAtomIndices)
         for atomIndex in self.siteAtomIndices:
             center.AddScaledVector3(1.0, system.coordinates3[atomIndex])
         center.Scale(1.0 / natoms)
     self.center = center
예제 #3
0
 def ParseDipole(self):
     """Parse a dipole."""
     line = self.GetLine()
     if line.find("Dipole Moments") >= 0:
         tokens = self.GetTokens(
             converters=[None, float, None, float, None, float])
         self.dipole = Vector3.Null()
         self.dipole[0] = tokens[1]
         self.dipole[1] = tokens[3]
         self.dipole[2] = tokens[5]
def BuildSolventBox ( crystalClass, symmetryParameters, molecule, density, log = logFile, randomNumberGenerator = None ):
    """Build a solvent box.

    No attempt is made to avoid overlapping molecules as it is assumed this will be corrected in a subsequent optimization process.
    """

    # . Initialization.
    solvent = None

    # . Find the number of solvent molecules that fit in the box.
    nmolecules = SolventMoleculeNumber ( molecule, symmetryParameters, density )

    # . Check the number of molecules.
    if nmolecules > 0:

        # . Create the solvent system.
        molecule.coordinates3.ToPrincipalAxes ( )
        solvent = MergeRepeatByAtom ( molecule, nmolecules )
        solvent.DefineSymmetry ( crystalClass = crystalClass, a     = symmetryParameters.a,     \
                                                              b     = symmetryParameters.b,     \
                                                              c     = symmetryParameters.c,     \
                                                              alpha = symmetryParameters.alpha, \
                                                              beta  = symmetryParameters.beta,  \
                                                              gamma = symmetryParameters.gamma  )

        # . Check the random number generator.
        if randomNumberGenerator is None: randomNumberGenerator = RandomNumberGenerator.WithRandomSeed ( )

        # . Do random rotations and translations.
        natoms      = len ( molecule.atoms )
        rotation    = Matrix33.Null ( )
        selection   = Selection.FromIterable ( range ( natoms ) )
        translation = Vector3.Null ( )
        for i in range ( nmolecules ):
            rotation.RandomRotation ( randomNumberGenerator )
            solvent.coordinates3.Rotate  ( rotation,    selection = selection )
            for j in range ( 3 ): translation[j] = randomNumberGenerator.NextReal ( )
            symmetryParameters.M.ApplyTo ( translation )
            solvent.coordinates3.Translate ( translation, selection = selection )
            selection.Increment ( natoms )

        # . Do some printing.
        if LogFileActive ( log ):
            summary = log.GetSummary ( )
            summary.Start ( "Cubic Solvent Box Summary" )
            summary.Entry ( "Number of Molecules", "{:d}"  .format ( nmolecules                        ) )
            summary.Entry ( "Density (kg m^-3)",   "{:.3f}".format ( SystemDensity ( solvent )         ) )
            summary.Entry ( "Box Volume",          "{:.3f}".format ( solvent.symmetryParameters.volume ) )
            summary.Stop ( )

    # . Return the system.
    return solvent
예제 #5
0
 def DipoleMoment(self, center=None):
     """Calculate the dipole moment vector of the system."""
     dipole = None
     if self.energyModel is not None:
         em = self.energyModel
         if em.mmModel is not None:
             dipole = em.mmAtoms.DipoleMoment(self.configuration, center)
         else:
             dipole = Vector3.Null()
         if em.qcModel is not None:
             dipole.AddScaledVector3(
                 1.0, em.qcModel.DipoleMoment(self.configuration, center))
     return dipole
예제 #6
0
 def ExtractTransformation ( self, dataBlock, tag ):
     """Extract a transformation from a data block."""
     # . tag is "fract" for Cartesians -> fractionals and "Cartn" for Cartesians -> fractionals.
     isOK        = True
     rotation    = Matrix33 ( ) ; rotation.Set    ( 0.0 )
     translation = Vector3  ( ) ; translation.Set ( 0.0 )
     for i in range ( 3 ):
         for j in range ( 3 ):
             c = self.ExtractReal ( dataBlock, "atom_sites_{:s}_tran_matrix_{:d}{:d}".format ( tag, i, j ) )
             if c is None: isOK = False
             else:         rotation[i,j] = c
         c = self.ExtractReal ( dataBlock, "atom_sites_{:s}_tran_vector_{:d}".format ( tag, i ) )
         if c is None: isOK = False
         else:         translation[i] = c
     if isOK: return ( rotation, translation )
     else:    return ( None    , None        )
예제 #7
0
    def Setup ( selfClass, qcAtoms, electronicState, nbState, job, scratch, deleteJobFiles = False, useRandomJob = False, useRandomScratch = False ):
        """Set up a state given the relevant data."""
        self = selfClass ( )
        # . QC atoms.
        # . Basic attributes.
        self.charge          = electronicState.charge
        self.multiplicity    = electronicState.multiplicity
        self.numberOfQCAtoms = len ( qcAtoms )
        # . File paths.
        if useRandomJob: job = RandomString ( )
        if useRandomScratch:
            self.scratchPath = os.path.join ( scratch, RandomString ( ) )
            if not os.path.exists ( self.scratchPath ): os.mkdir ( self.scratchPath )
            self.jobPath     = os.path.join ( self.scratchPath, job )
        else:
            self.scratchPath = None
            self.jobPath     = os.path.join ( scratch, job )
        self.engradPath = self.jobPath + ".engrad"
        self.inputPath  = self.jobPath + ".inp"
        self.outputPath = self.jobPath + ".log"
	self.pcgradPath = self.jobPath + ".pcgrad"
	self.pcPath     = self.jobPath + ".pc"
        # . Options.
        self.deleteJobFiles = deleteJobFiles
        # . Symbols.
        atomicNumbers = qcAtoms.GetAtomicNumbers ( )
        self.symbols  = []
        for i in atomicNumbers:
            self.symbols.append ( PeriodicTable.Symbol ( i ) )
        # . Array attributes.
        self.qcCoordinates3  = Coordinates3.WithExtent ( self.numberOfQCAtoms ) ; self.qcCoordinates3.Set  ( 0.0 )
        self.dipole          = Vector3.Null ( )
        self.qcGradients3    = Coordinates3.WithExtent ( self.numberOfQCAtoms ) ; self.qcGradients3.Set    ( 0.0 )
        self.chelpgCharges   = Real1DArray.WithExtent  ( self.numberOfQCAtoms ) ; self.chelpgCharges.Set   ( 0.0 )
        self.lowdinCharges   = Real1DArray.WithExtent  ( self.numberOfQCAtoms ) ; self.lowdinCharges.Set   ( 0.0 )
        self.lowdinSpins     = Real1DArray.WithExtent  ( self.numberOfQCAtoms ) ; self.lowdinSpins.Set     ( 0.0 )
        self.mullikenCharges = Real1DArray.WithExtent  ( self.numberOfQCAtoms ) ; self.mullikenCharges.Set ( 0.0 )
        self.mullikenSpins   = Real1DArray.WithExtent  ( self.numberOfQCAtoms ) ; self.mullikenSpins.Set   ( 0.0 )
        # . Orbital data.
        self.H**O            = -1
        self.LUMO            = -1
        self.orbitalEnergies = []
        # . NB state.
        self.nbState = nbState
        return self
예제 #8
0
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))
예제 #9
0
    def _ParseZMatrix ( self ):
        """Parse a Z-matrix geometry specification."""
        converters = [ MopacInputFileReader.GetAtomicNumber, float, None, float, None, float, None ]
        ncards     = 0
        oldfatal   = self.nfatal
        self.atomicNumbers = []
        zmatrixcards       = []
        while True:
            tokens  = self.GetTokens ( converters = converters )
            ntokens = len ( tokens )
            if ntokens == 0:
                break
            else:
                # . Append int to the converters for the first three cards.
                if ( ncards < 3 ): converters.append ( int )
                # . Basic checks on cards and tokens.
                if ( ( ncards == 0 ) and ( ntokens >=  7 ) ) or ( ( ncards == 1 ) and ( ntokens >=  8 ) ) or ( ( ncards == 2 ) and ( ntokens >=  9 ) ) or ( ( ncards >  2 ) and ( ntokens >= 10 ) ):
                    card = [ tokens[1], tokens[3], tokens[5] ]
                    if ( ncards > 0 ): card.append ( tokens[7] - 1 )
                    if ( ncards > 1 ): card.append ( tokens[8] - 1 )
                    if ( ncards > 2 ): card.append ( tokens[9] - 1 )
                    # . Check that the atom number values are valid.
                    QOK = True
                    for i in card[3:]:
                        QOK = QOK and ( i >= 0 ) and ( i < ncards )
                    if QOK:
                        ncards += 1
                        self.atomicNumbers.append ( tokens[0] )
                        zmatrixcards.append       ( card      )
                    else: self.Warning ( "Invalid atom number specification on Z-matrix input line.", True )
                else: self.Warning ( "Insufficient number of tokens ({:d}) on Z-matrix input line.".format ( ntokens ), True )
        # . Create the coordinates if there have been no fatal errors during the reading process.
        if self.nfatal == oldfatal:
            self.coordinates3 = Coordinates3.WithExtent ( len ( self.atomicNumbers ) )
            self.coordinates3.Set ( 0.0 )
            direction = Vector3.Null ( )
            for ( i, card ) in enumerate ( zmatrixcards ):
                if   i == 1:
                    direction.Set ( 0.0 )
                    direction[0] = 1.0
#                    direction.BasisVector ( 0 ) # . x-direction.
                    QOK = self.coordinates3.BuildPointFromDistance              ( i, card[3],                   card[0],          direction )
                elif i == 2:
                    direction.Set ( 0.0 )
                    direction[1] = 1.0
#                    direction.BasisVector ( 1 ) # . y-direction.
                    QOK = self.coordinates3.BuildPointFromDistanceAngle         ( i, card[3], card[4],          card[0], card[1], direction )
                elif i > 2:
                    QOK = self.coordinates3.BuildPointFromDistanceAngleDihedral ( i, card[3], card[4], card[5], card[0], card[1], card[2]   )
#                elif i > 1:
#                    # . Check for co-linear atoms.
#                    if math.fabs ( card[1] - 180.0 ) <= _LINEARTOLERANCE:
#                        j = card[3]
#                        k = card[4]
#                        for c in range ( 3 ):
#                            direction[c] = self.coordinates3[j,c] - self.coordinates3[k,c]
#                        direction.Normalize ( )
#                        QOK = self.coordinates3.BuildPointFromDistance ( i, j, card[0], direction )
#                    else:
#                        if i == 2:
#                            direction.BasisVector ( 1 ) # . y-direction.
#                            QOK = self.coordinates3.BuildPointFromDistanceAngle         ( i, card[3], card[4],          card[0], card[1], direction )
#                        else:
#                    # . Check for linear atoms.
#                            QOK = self.coordinates3.BuildPointFromDistanceAngleDihedral ( i, card[3], card[4], card[5], card[0], card[1], card[2]   )
                if not QOK:
                    self.Warning ( "Unable to build Cartesian coordinates from Z-matrix card number {:d}.".format ( i+1 ), True )
                    break
예제 #10
0
def BuildCubicSolventBox ( molecule, nmolecules, log = logFile, moleculesize = None, excludeHydrogens = True, QRANDOMROTATION = True, randomNumberGenerator = None, scalesafety = 1.1 ):
    """Build a cubic solvent box."""

    # . Get the number of molecules in each direction.
    nlinear = int ( math.ceil ( math.pow ( float ( nmolecules ), 1.0 / 3.0 ) ) )

    # . Get the indices of the occupied sites.
    sites = sample ( range ( nlinear**3 ), nmolecules )
    sites.sort ( )

    # . Get the number of atoms in the molecule and an appropriate selection.
    natoms    = len ( molecule.atoms )
    selection = Selection.FromIterable ( range ( natoms ) )

    # . Get a copy of molecule's coordinates (reorientated).
    coordinates3 = Clone ( molecule.coordinates3 )
    coordinates3.ToPrincipalAxes ( )

    # . Get the molecule size depending upon the input options.
    # . A molecule size has been specified.
    if moleculesize is not None:
        size = moleculesize
    # . Determine the size of the molecule as the diagonal distance across its enclosing orthorhombic box.
    else:
        radii = molecule.atoms.GetItemAttributes ( "vdwRadius" )
        if excludeHydrogens:
            atomicNumbers = molecule.atoms.GetItemAttributes ( "atomicNumber" )
            for ( i, atomicNumber ) in enumerate ( molecule.atoms.GetItemAttributes ( "atomicNumber" ) ):
                if atomicNumber == 1: radii[i] = 0.0
        ( origin, extents ) = coordinates3.EnclosingOrthorhombicBox ( radii = radii )
        size                = extents.Norm2 ( ) * scalesafety

    # . Create the new system - temporarily resetting the coordinates.
    temporary3            = molecule.coordinates3
    molecule.coordinates3 = coordinates3
    solvent               = MergeByAtom ( nmolecules * [ molecule ] )
    molecule.coordinates3 = temporary3

    # . Set the system symmetry.
    solvent.DefineSymmetry ( crystalClass = CrystalClassCubic ( ), a = size * float ( nlinear ) )

    # . Set up for random rotations.
    if QRANDOMROTATION:
        if randomNumberGenerator is None: randomNumberGenerator = RandomNumberGenerator.WithRandomSeed ( )
        rotation = Matrix33.Null ( )

    # . Loop over the box sites.
    origin      = 0.5 * float ( 1 - nlinear ) * size
    n           = 0
    translation = Vector3.Null ( )
    for i in range ( nlinear ):
        translation[0] = origin + size * float ( i )
        for j in range ( nlinear ):
            translation[1] = origin + size * float ( j )
            for k in range ( nlinear ):
                if len ( sites ) == 0: break
                translation[2] = origin + size * float ( k )
                # . Check for an occupied site.
                if sites[0] == n:
                    sites.pop ( 0 )
                    # . Randomly rotate the coordinates.
                    if QRANDOMROTATION:
                        rotation.RandomRotation ( randomNumberGenerator )
                        solvent.coordinates3.Rotate ( rotation, selection = selection )
                    # . Translate the coordinates.
                    solvent.coordinates3.Translate ( translation, selection = selection )
                    # . Increment the selection for the next molecule.
                    selection.Increment ( natoms )
                n += 1

    # . Do some printing.
    if LogFileActive ( log ):
        summary = log.GetSummary ( )
        summary.Start ( "Cubic Solvent Box Summary" )
        summary.Entry ( "Number of Molecules", "{:d}"  .format ( nmolecules                   ) )
        summary.Entry ( "Density (kg m^-3)",   "{:.3f}".format ( SystemDensity ( solvent )    ) )
        summary.Entry ( "Box Side",            "{:.3f}".format ( solvent.symmetryParameters.a ) )
        summary.Entry ( "Molecule Size",       "{:.3f}".format ( size                         ) )
        summary.Stop ( )

    # . Return the cubic system.
    return solvent
예제 #11
0
    def runTest(self):
        """The test."""

        # . Initialization.
        dataPath = os.path.join(os.getenv("PDYNAMO_PMOLECULE"), "data", "mol")
        log = self.GetLog()
        translation = Vector3.Uninitialized()

        # . Get the individual systems.
        energies = []
        molecules = []
        for (i, label) in enumerate(_Molecules):
            molecule = MOLFile_ToSystem(os.path.join(dataPath, label + ".mol"))
            molecule.label = label
            molecule.DefineMMModel(_mmModel)
            molecule.DefineNBModel(_nbModel)
            molecule.Summary(log=log)
            molecule.coordinates3.TranslateToCenter()
            molecule.configuration.Clear()
            energies.append(molecule.Energy(log=log, doGradients=True))
            molecules.append(molecule)

        # . Data initialization.
        observed = {}
        referenceData = TestDataSet("Merge/Prune Energies")

        # . Loop over the tests.
        for (testIndex, (moleculeFrequencies,
                         moleculePruneIndices)) in enumerate(_Tests):

            # . Heading.
            if log is not None:
                log.Heading("Merge/Prune Test {:d}".format(testIndex),
                            QBLANKLINE=True)

            # . Initialization.
            mergedEnergy = 0.0
            prunedEnergy = 0.0
            translation.Set(0.0)

            # . Gather items.
            index = 0
            numberAtoms = 0
            pruneIndices = []
            toMerge = []
            for (i, frequency) in enumerate(moleculeFrequencies):
                molecule = molecules[i]
                mergedEnergy += energies[i] * frequency
                for f in range(frequency):
                    cloned = Clone(molecule)
                    translation[0] += _Displacement
                    cloned.coordinates3.Translate(translation)
                    toMerge.append(cloned)
                    if index in moleculePruneIndices:
                        pruneIndices.extend(
                            range(numberAtoms,
                                  numberAtoms + len(cloned.atoms)))
                        prunedEnergy += energies[i]
                    index += 1
                    numberAtoms += len(cloned.atoms)

            # . Merging.
            merged = toMerge[0].Merge(toMerge[1:])
            merged.Summary(log=log)
            eMerged = merged.Energy(log=log)

            # . Pruning.
            pruned = merged.Prune(Selection.FromIterable(pruneIndices))
            pruned.Summary(log=log)
            ePruned = pruned.Energy(log=log)

            # . Get the observed and reference data.
            for (tag, eObserved,
                 eReference) in (("Merged Energy {:d}".format(testIndex),
                                  eMerged, mergedEnergy),
                                 ("Pruned Energy {:d}".format(testIndex),
                                  ePruned, prunedEnergy)):
                observed[tag] = eObserved
                referenceData.AddDatum(
                    TestReal(
                        tag,
                        eReference,
                        referenceData,
                        absoluteErrorTolerance=_EnergyAbsoluteErrorTolerance,
                        toleranceFormat="{:.3f}",
                        valueFormat="{:.3f}"))

        # . Finish up.
        if log is not None: log.Separator()

        # . Check for success/failure.
        if len(observed) > 0:
            results = referenceData.VerifyAgainst(observed)
            results.Summary(log=log, fullSummary=self.fullVerificationSummary)
            isOK = results.WasSuccessful()
        else:
            isOK = True

        # . Success/failure.
        self.assertTrue(isOK)
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
예제 #13
0
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