private static void CalculateIonicForces(Molecule molecule, Dictionary <uint, Vector3D> forceLookup, AtomNeighborhoodMap neighborhoodMap) { var vertexIds = molecule.MoleculeStructure.Vertices.Select(v => v.Id); foreach (var vertexId in vertexIds) { var atom = molecule.GetAtom(vertexId); var charge1 = atom.EffectiveCharge; var atom1Position = atom.Position; var neighborhood = neighborhoodMap.GetNeighborhood(vertexId); foreach (var neighborVertexId in neighborhood) { if (neighborVertexId <= vertexId) { continue; } var neighborAtom = molecule.GetAtom(neighborVertexId); var charge2 = neighborAtom.EffectiveCharge; var r = atom1Position.VectorTo(neighborAtom.Position); var distance = r.Magnitude().In(SIPrefix.Pico, Unit.Meter); var chargeProduct = PhysicalConstants.CoulombsConstant.Value * charge1.Value * charge2.Value; var ionicForce = -5 * 1e-1 * (chargeProduct / (distance * distance)) * r.Normalize().ToVector3D(); forceLookup[vertexId] += ionicForce; forceLookup[neighborVertexId] += -ionicForce; } } }
public static ForceCalculatorResult CalculateForces(Molecule molecule, AtomNeighborhoodMap neighborhoodMap = null) { if (!molecule.IsPositioned) { molecule.PositionAtoms(); } if (neighborhoodMap == null) { neighborhoodMap = new AtomNeighborhoodMap(molecule); } var graph = molecule.MoleculeStructure; var forceLookup = CalculateBondForces(graph); //CalculateIonicForces(molecule, forceLookup, neighborhoodMap); CalculateAtomShellRepulsion(molecule, forceLookup, neighborhoodMap); var lonePairForceLookup = CalculateLonePairRepulsion(graph, forceLookup); if (forceLookup.Values.Any(v => v.X.IsNaN())) { throw new Exception("Force cannot be 'NaN'"); } if (lonePairForceLookup.Values.Any(v => v.X.IsNaN())) { throw new Exception("Lone pair repulsion cannot be 'NaN'"); } return(new ForceCalculatorResult(forceLookup, lonePairForceLookup, neighborhoodMap)); }
public ForceCalculatorResult(Dictionary <uint, Vector3D> forceLookup, Dictionary <Orbital, Vector3D> lonePairForceLookup, AtomNeighborhoodMap neighborhoodMap) { ForceLookup = forceLookup; LonePairForceLookup = lonePairForceLookup; NeighborhoodMap = neighborhoodMap; }
private void RunSimulation(CancellationToken cancellationToken) { if (!Molecule.IsPositioned) { Molecule.PositionAtoms(); } var currentAtomPositions = Molecule.MoleculeStructure.Vertices .ToDictionary(vertex => vertex.Id, vertex => Molecule.GetAtom(vertex.Id).Position); var lastNeighborhoodUpdate = 0.To(Unit.Second); var atomNeighborhoodMap = new AtomNeighborhoodMap(Molecule); for (var t = 0.To(Unit.Second); t < simulationSettings.SimulationTime; t += simulationSettings.TimeStep) { if (cancellationToken.IsCancellationRequested) { break; } if (t - lastNeighborhoodUpdate > 400.To(SIPrefix.Femto, Unit.Second)) { atomNeighborhoodMap.Update(); } var forces = ForceCalculator.CalculateForces(Molecule, atomNeighborhoodMap); AddCustomForces(Molecule, t, forces.ForceLookup, customAtomForces); ApplyAtomForces(Molecule, t, forces, simulationSettings); ApplyLonePairRepulsion(forces); // TODO: Redistribute electrons (either here or as a step after molecule is fully connected //WriteDebug(molecule); var newAtomPositions = Molecule.MoleculeStructure.Vertices .ToDictionary(vertex => vertex.Id, vertex => Molecule.GetAtom(vertex.Id).Position); if (simulationSettings.StopSimulationWhenAtomAtRest && t > simulationSettings.ForceRampUpPeriod) { var maximumPositionChange = currentAtomPositions.Keys .Select(atom => currentAtomPositions[atom].DistanceTo(newAtomPositions[atom]).In(SIPrefix.Pico, Unit.Meter)) .Max(); if (maximumPositionChange / simulationSettings.TimeStep.Value < simulationSettings.MovementDetectionThreshold.Value) { break; } } currentAtomPositions = newAtomPositions; OnTimestepCompleted(new SimulationTimestepCompleteEventArgs(t, Molecule)); } OnSimulationFinished(); }
private static void CalculateAtomShellRepulsion(Molecule molecule, Dictionary <uint, Vector3D> forceLookup, AtomNeighborhoodMap neighborhoodMap) { var vertices = molecule.MoleculeStructure.Vertices.Select(v => v.Id); foreach (var vertex in vertices) { var atom = molecule.GetAtom(vertex); var atom1Position = atom.Position; var atom1Radius = atom.Radius.Value; //var bondedNeighbors = new HashSet<Atom>(atom.OuterOrbitals // .Where(o => o.IsPartOfBond) // .Select(o => GetBondedAtom(o, atom))); var neighborhood = neighborhoodMap.GetNeighborhood(vertex); foreach (var neighborVertex in neighborhood) { if (neighborVertex <= vertex) { continue; } var neighborAtom = molecule.GetAtom(neighborVertex); //if (bondedNeighbors.Contains(neighborAtom)) // continue; var atom2Radius = atom.Radius.Value; var atomRadiusSum = atom1Radius + atom2Radius; var r = atom1Position.VectorTo(neighborAtom.Position); var distance = r.Magnitude().In(SIPrefix.Pico, Unit.Meter); var shellRepulsionForce = -(1e-5 * (1e-12 / distance) * Math.Exp(Math.Min(1e12 * (atomRadiusSum - distance), 0))) * r.Normalize().ToVector3D(); forceLookup[vertex] += shellRepulsionForce; forceLookup[neighborVertex] += -shellRepulsionForce; } } }