2021-12-15 19:50:18 +00:00
|
|
|
|
module Shape
|
|
|
|
|
|
2022-01-06 23:28:40 +00:00
|
|
|
|
export center_of_mass,
|
|
|
|
|
gyration_tensor_eigvals_ratio, gyration_tensor_eigvecs, elliptical_distance
|
2021-12-15 19:50:18 +00:00
|
|
|
|
|
2021-12-03 13:41:01 +00:00
|
|
|
|
using StaticArrays: SVector, SMatrix
|
2022-01-25 23:20:53 +00:00
|
|
|
|
using LinearAlgebra: LinearAlgebra as LA
|
2021-12-03 13:16:35 +00:00
|
|
|
|
|
2022-01-14 12:26:51 +00:00
|
|
|
|
using ..ReCo: ReCo, Particle
|
2021-12-15 19:50:18 +00:00
|
|
|
|
|
2022-01-18 01:17:52 +00:00
|
|
|
|
function project_to_unit_circle(x::Real, half_box_len::Real)
|
2022-01-29 00:04:51 +00:00
|
|
|
|
θ = (x + half_box_len) * π / half_box_len
|
|
|
|
|
si, co = sincos(θ)
|
2022-01-06 23:28:40 +00:00
|
|
|
|
|
2021-12-08 20:53:15 +00:00
|
|
|
|
return SVector(co, si)
|
2021-12-03 13:16:35 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-18 01:17:52 +00:00
|
|
|
|
function project_back_from_unit_circle(θ::Real, half_box_len::Real)
|
2021-12-10 02:16:18 +00:00
|
|
|
|
x = θ * half_box_len / π - half_box_len
|
2022-01-06 23:28:40 +00:00
|
|
|
|
|
2022-01-14 12:01:14 +00:00
|
|
|
|
return ReCo.restrict_coordinate(x, half_box_len)
|
2021-12-03 13:16:35 +00:00
|
|
|
|
end
|
|
|
|
|
|
2021-12-28 16:13:31 +00:00
|
|
|
|
function center_of_mass_from_proj_sums(
|
2022-01-30 13:45:22 +00:00
|
|
|
|
x_proj_sum::SVector{2,<:Real}, y_proj_sum::SVector{2,<:Real}, half_box_len::Real
|
|
|
|
|
)
|
2021-12-03 13:16:35 +00:00
|
|
|
|
# Prevent for example atan(1e-16, 1e-15) != 0 with rounding
|
|
|
|
|
digits = 5
|
|
|
|
|
|
2021-12-10 02:16:18 +00:00
|
|
|
|
# No need for 1/n_particles with atan
|
2022-02-08 20:20:18 +00:00
|
|
|
|
# If proj is (0, 0) then COM is 0 or L or -L. Here, 0 is chosen with θ = π
|
2021-12-04 15:19:14 +00:00
|
|
|
|
if round(x_proj_sum[1]; digits=digits) == round(x_proj_sum[2]; digits=digits) == 0
|
2021-12-03 13:16:35 +00:00
|
|
|
|
x_θ = π
|
|
|
|
|
else
|
|
|
|
|
x_θ = atan(x_proj_sum[2], x_proj_sum[1])
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-04 15:19:14 +00:00
|
|
|
|
if round(y_proj_sum[1]; digits=digits) == round(y_proj_sum[2]; digits=digits) == 0
|
2021-12-03 13:16:35 +00:00
|
|
|
|
y_θ = π
|
|
|
|
|
else
|
|
|
|
|
y_θ = atan(y_proj_sum[2], y_proj_sum[1])
|
|
|
|
|
end
|
|
|
|
|
|
2021-12-10 02:16:18 +00:00
|
|
|
|
COM_x = project_back_from_unit_circle(x_θ, half_box_len)
|
|
|
|
|
COM_y = project_back_from_unit_circle(y_θ, half_box_len)
|
2021-12-08 20:53:15 +00:00
|
|
|
|
|
|
|
|
|
return SVector(COM_x, COM_y)
|
2021-12-03 13:41:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-30 14:15:25 +00:00
|
|
|
|
function center_of_mass(centers::AbstractVector{<:SVector{2,<:Real}}, half_box_len::Real)
|
2021-12-28 16:13:31 +00:00
|
|
|
|
x_proj_sum = SVector(0.0, 0.0)
|
|
|
|
|
y_proj_sum = SVector(0.0, 0.0)
|
|
|
|
|
|
|
|
|
|
for c in centers
|
|
|
|
|
x_proj_sum += project_to_unit_circle(c[1], half_box_len)
|
|
|
|
|
y_proj_sum += project_to_unit_circle(c[2], half_box_len)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return center_of_mass_from_proj_sums(x_proj_sum, y_proj_sum, half_box_len)
|
|
|
|
|
end
|
|
|
|
|
|
2022-01-18 01:17:52 +00:00
|
|
|
|
function center_of_mass(particles::AbstractVector{Particle}, half_box_len::Real)
|
2021-12-28 16:13:31 +00:00
|
|
|
|
x_proj_sum = SVector(0.0, 0.0)
|
|
|
|
|
y_proj_sum = SVector(0.0, 0.0)
|
|
|
|
|
|
|
|
|
|
for p in particles
|
|
|
|
|
x_proj_sum += project_to_unit_circle(p.c[1], half_box_len)
|
|
|
|
|
y_proj_sum += project_to_unit_circle(p.c[2], half_box_len)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return center_of_mass_from_proj_sums(x_proj_sum, y_proj_sum, half_box_len)
|
|
|
|
|
end
|
|
|
|
|
|
2022-01-14 12:01:14 +00:00
|
|
|
|
function gyration_tensor(
|
2022-01-30 13:45:22 +00:00
|
|
|
|
particles::AbstractVector{Particle}, half_box_len::Real, COM::SVector{2,<:Real}
|
|
|
|
|
)
|
2021-12-03 13:41:01 +00:00
|
|
|
|
S11 = 0.0
|
|
|
|
|
S12 = 0.0
|
|
|
|
|
S22 = 0.0
|
|
|
|
|
|
2021-12-08 20:53:15 +00:00
|
|
|
|
for p in particles
|
2022-01-14 12:01:14 +00:00
|
|
|
|
shifted_c = ReCo.restrict_coordinates(p.c - COM, half_box_len)
|
2021-12-03 13:41:01 +00:00
|
|
|
|
|
2021-12-08 20:53:15 +00:00
|
|
|
|
S11 += shifted_c[1]^2
|
|
|
|
|
S12 += shifted_c[1] * shifted_c[2]
|
|
|
|
|
S22 += shifted_c[2]^2
|
2021-12-03 13:41:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-25 23:20:53 +00:00
|
|
|
|
return LA.Hermitian(SMatrix{2,2}(S11, S12, S12, S22))
|
2021-12-04 15:19:14 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-30 13:45:22 +00:00
|
|
|
|
function gyration_tensor(
|
2022-01-30 14:15:25 +00:00
|
|
|
|
centers::AbstractVector{<:SVector{2,<:Real}}, half_box_len::Real, COM::SVector{2,<:Real}
|
2022-01-30 13:45:22 +00:00
|
|
|
|
)
|
|
|
|
|
S11 = 0.0
|
|
|
|
|
S12 = 0.0
|
|
|
|
|
S22 = 0.0
|
|
|
|
|
|
|
|
|
|
for c in centers
|
|
|
|
|
shifted_c = ReCo.restrict_coordinates(c - COM, half_box_len)
|
|
|
|
|
|
|
|
|
|
S11 += shifted_c[1]^2
|
|
|
|
|
S12 += shifted_c[1] * shifted_c[2]
|
|
|
|
|
S22 += shifted_c[2]^2
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return LA.Hermitian(SMatrix{2,2}(S11, S12, S12, S22))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function gyration_tensor(
|
2022-01-30 14:15:25 +00:00
|
|
|
|
particles_or_centers::Union{
|
|
|
|
|
AbstractVector{Particle},AbstractVector{<:SVector{2,<:Real}}
|
|
|
|
|
},
|
2022-01-30 13:45:22 +00:00
|
|
|
|
half_box_len::Real,
|
|
|
|
|
)
|
|
|
|
|
COM = center_of_mass(particles_or_centers, half_box_len)
|
2022-01-14 12:01:14 +00:00
|
|
|
|
|
2022-01-31 01:04:16 +00:00
|
|
|
|
return gyration_tensor(particles_or_centers, half_box_len, COM)
|
2022-01-14 12:01:14 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-30 13:45:22 +00:00
|
|
|
|
function eigvals_ratio(matrix)
|
|
|
|
|
ev = LA.eigvals(matrix) # Eigenvalues are sorted
|
|
|
|
|
return abs(ev[1] / ev[2])
|
|
|
|
|
end
|
|
|
|
|
|
2022-01-18 01:17:52 +00:00
|
|
|
|
function gyration_tensor_eigvals_ratio(
|
2022-01-30 14:15:25 +00:00
|
|
|
|
particles_or_centers::Union{
|
|
|
|
|
AbstractVector{Particle},AbstractVector{<:SVector{2,<:Real}}
|
|
|
|
|
},
|
2022-01-30 13:45:22 +00:00
|
|
|
|
half_box_len::Real,
|
2022-01-18 01:17:52 +00:00
|
|
|
|
)
|
2022-01-30 13:45:22 +00:00
|
|
|
|
g_tensor = gyration_tensor(particles_or_centers, half_box_len)
|
|
|
|
|
return eigvals_ratio(g_tensor)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function gyration_tensor_eigvals_ratio(
|
2022-01-30 14:15:25 +00:00
|
|
|
|
particles_or_centers::Union{
|
|
|
|
|
AbstractVector{Particle},AbstractVector{<:SVector{2,<:Real}}
|
|
|
|
|
},
|
2022-01-30 13:45:22 +00:00
|
|
|
|
half_box_len::Real,
|
|
|
|
|
COM::SVector{2,<:Real},
|
|
|
|
|
)
|
|
|
|
|
g_tensor = gyration_tensor(particles_or_centers, half_box_len, COM)
|
|
|
|
|
return eigvals_ratio(g_tensor)
|
2021-12-15 19:50:18 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-01-14 12:01:14 +00:00
|
|
|
|
function gyration_tensor_eigvecs(
|
2022-01-30 13:45:22 +00:00
|
|
|
|
particles::AbstractVector{Particle}, half_box_len::Real, COM::SVector{2,<:Real}
|
|
|
|
|
)
|
2022-01-14 12:01:14 +00:00
|
|
|
|
g_tensor = gyration_tensor(particles, half_box_len, COM)
|
2022-01-25 23:20:53 +00:00
|
|
|
|
eig_vecs = LA.eigvecs(g_tensor)
|
2022-01-06 23:28:40 +00:00
|
|
|
|
|
|
|
|
|
v1 = eig_vecs[:, 1]
|
|
|
|
|
v2 = eig_vecs[:, 2]
|
|
|
|
|
|
|
|
|
|
return (v1, v2)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function elliptical_distance(
|
2022-01-30 13:45:22 +00:00
|
|
|
|
v::SVector{2,<:Real},
|
|
|
|
|
COM::SVector{2,<:Real},
|
|
|
|
|
gyration_tensor_eigvec_to_smaller_eigval::SVector{2,<:Real},
|
|
|
|
|
gyration_tensor_eigvec_to_bigger_eigval::SVector{2,<:Real},
|
2022-02-01 21:57:56 +00:00
|
|
|
|
elliptical_b_a_ratio::Real,
|
2022-01-30 13:45:22 +00:00
|
|
|
|
half_box_len::Real,
|
|
|
|
|
)
|
2022-01-27 17:48:25 +00:00
|
|
|
|
v′ = ReCo.restrict_coordinates(v - COM, half_box_len)
|
2022-01-14 12:01:14 +00:00
|
|
|
|
|
2022-01-25 23:20:53 +00:00
|
|
|
|
x = LA.dot(v′, gyration_tensor_eigvec_to_bigger_eigval)
|
|
|
|
|
y = LA.dot(v′, gyration_tensor_eigvec_to_smaller_eigval)
|
2022-01-06 23:28:40 +00:00
|
|
|
|
|
2022-02-01 21:57:56 +00:00
|
|
|
|
return sqrt(x^2 + (y / elliptical_b_a_ratio)^2)
|
2022-01-06 23:28:40 +00:00
|
|
|
|
end
|
|
|
|
|
|
2022-03-19 22:11:03 +00:00
|
|
|
|
end # module
|