-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Analysis submodule and diatomic molecule analysis functions.
- Added new DynamicsOutput: OutputDesorptionTrajectory #317 - Added new DynamicsOutput: OutputDesorptionAngle #317 - Created an `Analysis` submodule for common analysis functions
- Loading branch information
Showing
6 changed files
with
234 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
""" | ||
Analysis functions common enough to be included in the main package. | ||
""" | ||
module Analysis | ||
using Reexport: @reexport | ||
|
||
# Diatomic analysis functions @alexsp32 | ||
include("diatomic.jl") | ||
export Diatomic | ||
|
||
# Rebinding of quantise_diatomic under Analysis. | ||
using NQCDynamics: InitialConditions | ||
@reexport using InitialConditions: quantise_diatomic | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
""" | ||
Analysis functions for surface chemistry of diatomic molecules. | ||
""" | ||
module Diatomic | ||
using NQCDynamics: AbstractSimulation, Simulation, get_positions | ||
using NQCBase | ||
using Unitful, UnitfulAtomic | ||
using LinearAlgebra | ||
|
||
""" | ||
get_desorption_frame(trajectory::Vector, diatomic_indices::Vector{Int}, simulation::NQCDynamics.AbstractSimulation;surface_normal::Vector=[0,0,1], surface_distance_threshold=5.0*u"Å") | ||
Determines the index in a trajectory where surface desorption begins. | ||
This is evaluated using two conditions: | ||
1. In the trajectory, the diatomic must be `surface_distance_threshold` or further away from the highest other atom. (In `surface_normal` direction). | ||
2. Desorption begins at the turning point of the centre of mass velocity component along `surface_normal`, indicating overall movement away from the surface. | ||
""" | ||
function get_desorption_frame(trajectory::Vector, diatomic_indices::Vector{Int}, simulation::NQCDynamics.AbstractSimulation;surface_normal::Vector = [0,0,1], surface_distance_threshold = 5.0*u"Å") | ||
# Two criteria: Distance between two atoms (wrt PBC) must be below `distance_threshold` and CoM velocity must be positive wrt surface normal vector. | ||
function surface_distance_condition(x) | ||
highest_z = max(NQCDynamics.get_positions(x)[3, symdiff(1:end, diatomic_indices)]...) | ||
if abs(au_to_ang(NQCBase.Structure.pbc_center_of_mass(x, diatomic_indices..., simulation)[3] - highest_z)) * u"Å" ≥ surface_distance_threshold | ||
@debug "Surface distance condition evaluated true" | ||
return true | ||
else | ||
return false | ||
end | ||
end | ||
function com_velocity_condition(x) | ||
if dot(NQCBase.Structure.velocity_center_of_mass(x, diatomic_indices..., simulation), normalize(surface_normal)) > 0 | ||
@debug "Normal velocity condition evaluated true" | ||
return true | ||
else | ||
return false | ||
end | ||
end | ||
desorbed_frame = findfirst(surface_distance_condition, trajectory) | ||
if isa(desorbed_frame, Nothing) | ||
@debug "No desorption event found. " | ||
return nothing | ||
else | ||
@debug "Found desorption event in frame $(desorbed_frame)" | ||
leaving_surface_frame = findfirst(!com_velocity_condition, reverse(trajectory[1:desorbed_frame])) | ||
if isa(leaving_surface_frame, Nothing) | ||
@debug "Couldn't find a sub-zero CoM velocity component relative to surface normal." | ||
return nothing | ||
else | ||
return desorbed_frame - leaving_surface_frame | ||
end | ||
end | ||
end | ||
|
||
function get_desorption_angle(trajectory::Vector, indices::Vector{Int}, simulation::NQCDynamics.AbstractSimulation; surface_normal = [0,0,1], surface_distance_threshold = 5.0*u"Å") | ||
# First determine where the reaction occurred on the surface. | ||
desorption_frame = get_desorption_frame(trajectory, indices, simulation;surface_distance_threshold = surface_distance_threshold, surface_normal = surface_normal) | ||
if isa(desorption_frame, Nothing) | ||
@debug "No desorption event detected in trajectory" | ||
return nothing | ||
end | ||
@debug "Desorption frame: $(desorption_frame)" | ||
# Determine the average centre of mass velocity to decrease error due to vibration and rotation orthogonal to true translational component. | ||
com_velocities = map(x -> NQCBase.Structure.velocity_center_of_mass(x, indices[1], indices[2], simulation), trajectory[desorption_frame:end]) | ||
average_velocity = mean(cat(com_velocities...;dims=2);dims=2) | ||
# Now convert into an angle by arccos((a•b)/(|a|*|b|)) | ||
return NQCBase.Structure.angle_between(vec(average_velocity), surface_normal) | ||
end | ||
|
||
# 6✖6 Cartesian to internal coordinate transformation. | ||
function transform_to_internal_coordinates(to_transform::Matrix, config::Matrix, index1::Int, index2::Int, sim::Simulation) | ||
U = transform_U(config, index1, index2, sim) # Generate transformation matrix | ||
return transpose(U) * to_transform * U | ||
end | ||
|
||
function transform_from_internal_coordinates(to_transform::Matrix, config::Matrix, index1::Int, index2::Int, sim::Simulation) | ||
U_inv = inv(transform_U(config, index1, index2, sim)) | ||
return transpose(U_inv) * to_transform * U_inv | ||
end | ||
|
||
function transform_r(config, index1::Int, index2::Int) | ||
return sqrt(sum(mapslices(x->(x[2]-x[1])^2, config[:, [index1, index2]];dims=2))) | ||
end | ||
|
||
function transform_r1(config, index1::Int, index2::Int) | ||
return transform_r(config[1:2, :], index1, index2) | ||
end | ||
|
||
|
||
""" | ||
transform_U(config::Matrix, index1::Int, index2::Int, sim::Simulation) | ||
Builds diatomic Cartesian to internal coordinate transformation matrix as described in the SI of `10.1021/jacsau.0c00066` | ||
""" | ||
function transform_U(config::Matrix, index1::Int, index2::Int, sim::Simulation) | ||
masses = NQCBase.Structure.fractional_mass(sim, index1, index2) | ||
config[:,index2] .+= NQCBase.Structure.minimum_distance_translation(config, index1, index2, sim) # PBC wrap positions for correct transformation. | ||
r = transform_r(config, index1, index2) | ||
r1 = transform_r1(config, index1, index2) | ||
unity = LinearAlgebra.I(3) | ||
U_i1 = vcat( | ||
mapslices(x->(x[1]-x[2])/r, config[:, [index1, index2]]; dims=2), | ||
mapslices(x->(x[2]-x[1])/r, config[:, [index2, index1]]; dims=2), | ||
) | ||
U_i2 = vcat( | ||
mapslices(x->(x[2]-x[1])*(config[3, index2]-config[3, index1])/(r^2*r1), config[1:2, [index1, index2]]; dims=2), | ||
-r1/r^2, | ||
mapslices(x->(x[1]-x[2])*(config[3, index2]-config[3, index1])/(r^2*r1), config[1:2, [index2, index1]]; dims=2), | ||
r1/r^2, | ||
) | ||
U_i3 = [ | ||
-(config[2, index1]-config[2, index2]), | ||
(config[1, index2]-config[1, index1]), | ||
0.0, | ||
-(config[2, index2]-config[2, index1]), | ||
(config[1, index1]-config[1, index2]), | ||
0.0, | ||
] ./ (r1^2) | ||
return hcat(U_i1, U_i2, U_i3, vcat(unity.*masses[1], unity.*masses[2])) | ||
end | ||
|
||
export get_desorption_frame, get_desorption_angle, transform_from_internal_coordinates, transform_to_internal_coordinates, transform_U | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
""" | ||
classical_translational_energy(config::Any, ind1::Union{Int, CartesianIndex}, ind2::Union{Int, CartesianIndex}, sim::Simulation) | ||
Returns the classical translational energy in Hartree | ||
""" | ||
function classical_translational_energy(config::Any, ind1::Union{Int, CartesianIndex}, ind2::Union{Int, CartesianIndex}, sim::Simulation) | ||
return sum(sim.atoms.masses[[ind1, ind2]])/2*norm(velocity_center_of_mass(config, ind1, ind2, sim))^2 | ||
end | ||
|
||
""" | ||
classical_rotation_energy(J::Union{Int, CartesianIndex}, config::Any, ind1::Union{Int, CartesianIndex}, ind2::Union{Int, CartesianIndex}, sim::Simulation) | ||
Classical rotation energy of a rigid diatomic rotor in Hartree | ||
""" | ||
function classical_rotation_energy(J::Union{Int, CartesianIndex}, config::Any, ind1::Union{Int, CartesianIndex}, ind2::Union{Int, CartesianIndex}, sim::Simulation) | ||
I = reduced_mass(sim, ind1, ind2) * austrip(pbc_distance(config, ind1, ind2, sim))^2 # Classical moment of inertia in atomic units. | ||
return J * (J+1) / (2*I) | ||
end | ||
|
||
""" | ||
harmonic_vibration_energy(ν::Union{Int, CartesianIndex}, k::Float, ind1::Union{Int, CartesianIndex}=1, ind2::Union{Int, CartesianIndex}=2, sim::Simulation) | ||
Vibrational energy of a harmonic oscillator with the force constant k and vibrational level ν. | ||
""" | ||
function harmonic_vibration_energy(ν::Union{Int, CartesianIndex}, k::Float64, ind1::Union{Int, CartesianIndex}, ind2::Union{Int, CartesianIndex}, sim::Simulation) | ||
return (ν + 1/2)*sqrt(k/reduced_mass(sim, ind1, ind2)) | ||
end | ||
|
||
export classical_translational_energy, classical_rotation_energy, harmonic_vibration_energy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters