on-the-fly computation of MSAD for trimers#76
Conversation
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
| elseif alg == "MSADTracker" | ||
| # read theta_T from parameters | ||
| parameters = get(output, "parameters", Dict()) | ||
| theta_T = Float64(get(parameters, "theta_T", π/4)) | ||
|
|
||
| # build output schedule separately from compute schedule | ||
| output_scheduler_params = get(output, "output_scheduler_params", Dict()) | ||
| if "log_base" in keys(output_scheduler_params) | ||
| output_sched = build_schedule(steps, burn, 2.0) | ||
| elseif "linear_interval" in keys(output_scheduler_params) | ||
| output_sched = build_schedule(steps, burn, | ||
| output_scheduler_params["linear_interval"]) | ||
| else | ||
| output_sched = sched # default: same as compute | ||
| end | ||
|
|
||
| # convert to Vector{Int} — build_schedule returns this already | ||
| algorithm = ( | ||
| algorithm=MSADTracker, | ||
| scheduler=sched, # compute schedule | ||
| theta_T=theta_T, | ||
| output_schedule=output_sched, | ||
| path=output_path, | ||
| ) | ||
|
|
There was a problem hiding this comment.
The code never gets to this branch, alg is undefined anyway.
Need to remove, this is a copy/paste mistake from the output section below.
This is a bit misleading, as for the other outputs, scheduler_params codes for the times at which you output. |
|
|
||
| # open output file — write header | ||
| tracker.files[c] = open(tracker.paths[c], "w") | ||
| println(tracker.files[c], "# t msad_euler msad_integral msad_thresh") |
There was a problem hiding this comment.
remove double space between column names
| for m in 1:N_mol | ||
| dR = state.R_ref_thresh[m]' * R_all[m] | ||
| phi_current[m] = rotation_vector(dR) | ||
| phi_total[m] = state.phi_acc[m] + phi_current[m] |
There was a problem hiding this comment.
This variable is redundant, as it can be recomputed any time from the other two
There was a problem hiding this comment.
I agree, plus I don't think creating two empty vectors is necessary.
I guess one could write:
dR = state.R_ref_thresh[m]' * R_all[m]
phi_current = rotation_vector(dR)
if norm(phi_current) >= tracker.theta_T
state.phi_acc[m] = state.phi_acc[m] + phi_current
state.R_ref_thresh[m] = R_all[m]
endThere was a problem hiding this comment.
I agree. Then I also changed
phi_total[m]
by
state.phi_acc[m] + phi_current[m]
in the output for the threshold method
romainljsimon
left a comment
There was a problem hiding this comment.
Thanks a lot @cyfraysse for your work and this PR, it's great!
However, I bet that the MSAD is not the only observable we're going to want to look at in the future. For example, we'd like to study the angular Van Hove (angular distributions) in our molecules.
Thus, I think it should be better to store trajectories of angular displacements of all molecules (for both the integral and the threshold method), just as it's done for positions. This would allow us to compute the MSAD, the Van Hove (why not the Fs(q,t)?) in postprocessing.
What do you think @cyfraysse @V-Francois ?
I also added a few minor comments.
| function rotation_vector(R::SMatrix{3,3,T}) where {T} | ||
| cos_θ = clamp((tr(R) - 1) / 2, -one(T), one(T)) | ||
| # skew-symmetric part encodes sin(θ)*n | ||
| skew = (R - R') / 2 | ||
| sin_n = SVector(skew[3,2], skew[1,3], skew[2,1]) # julia starts index at 1 | ||
| sin_θ = norm(sin_n) | ||
| θ = atan(sin_θ, cos_θ) | ||
| return sin_θ > 1e-10 ? (θ / sin_θ) * sin_n : zero(SVector{3,T}) ## might need to add the theta = pi case (unprobable for MC steps) | ||
| end |
There was a problem hiding this comment.
This works, but it's not the best option is
There was a problem hiding this comment.
I agree with what is said in Sec IIC. However, taking n as the eigenvector associated with the eigenvalue 1 is misleading because n and -n are solutions. Both values are taken randomly when extracting eigenvectors and this lead to "false" rotation of ±π. Using the skew matrix we remove this ambiguity. Then should I add a case where theta is close to pi?
There was a problem hiding this comment.
I'm not sure why would this be a problem ?
Anyways for the thresh method, you take the absolute value of the angle to check the condition.
For the integral method, you sum
Am I missing something ?
There was a problem hiding this comment.
I agree that it is irrelevant for the condition check in th threshold method. However, when doing the accumulation sum(θn) we will accumulate random sign flip no? And then norm(ɸ) does not represent the true rotation.
When I was validating my operation in python post process I had to use skew symmetric part to define n instead of eigenvectors.
There was a problem hiding this comment.
When you do the calculation you indeed find two solutions
There was a problem hiding this comment.
OK yes you're right sorry... I am changing for the eigenvector rn.
There was a problem hiding this comment.
No worries! There should be an implementation here : https://github.com/romainljsimon/MoonWalk.jl
There was a problem hiding this comment.
Just one question : when using the eigenvector/value decomposition Julia might need to store things in memory. Isn't that going to slow down the simulation?
There was a problem hiding this comment.
Hey, yes it is a slower but safer implementation!
| # called once at t = 0, set the system, the mutable struct and the storing paths | ||
| ########## ########## | ||
|
|
||
| function Arianna.initialise(tracker::MSADTracker, simulation::Simulation) |
There was a problem hiding this comment.
(algorithm::MSADTracker, simulation::Simulation)
For consistency with Arianna.jl.
So the same remark for the other algorithm functions.
| for m in 1:N_mol | ||
| dR = state.R_ref_thresh[m]' * R_all[m] | ||
| phi_current[m] = rotation_vector(dR) | ||
| phi_total[m] = state.phi_acc[m] + phi_current[m] |
There was a problem hiding this comment.
I agree, plus I don't think creating two empty vectors is necessary.
I guess one could write:
dR = state.R_ref_thresh[m]' * R_all[m]
phi_current = rotation_vector(dR)
if norm(phi_current) >= tracker.theta_T
state.phi_acc[m] = state.phi_acc[m] + phi_current
state.R_ref_thresh[m] = R_all[m]
end|
Thank you for your review @romainljsimon! I have changed what you mentioned with @V-Francois. About the observables... we had this discussion with @V-Francois. If we store the whole trajectory of phi for the threshold and integral methods we will be back at the storage problem no? And if we log sample it then we will have very few points for the distribution. We thought about adding a third scheduler that would be triggered once in the long time regime that will store phi trajectories for few time step there... what do you think about that? |
:)
Yes I agree that we shouldn't store them at each time step or we'll be back to the storage problem. I think the log sampling is the way to go, I'm not sure why this would be a problem for the distribution, that's how we do it for translations. You can run multiple simulations in parallel with the same scheduler, and have a distribution at different times with a lot of points. Again, am I missing something ? |
|
Ok with the multiple simulations in parallel I totally agree, thank you! Then we only need to extract in log the phi for the three methods. Post process for MSAD and Van Hove. For Fs C2 and MSD we continue to extract the configurations in log also. Am I right? |
Yes I agree! |
| files_thresh::Vector{IOStream} # file to write rotation vector in threshold method | ||
| end | ||
|
|
||
| function MSADTracker(chains, theta_T::T, compute_schedule::Vector{Int}, output_schedule::Vector{Int}, path::String) where {T} |
There was a problem hiding this comment.
I'm also thinking that StorePhiTrajectories or something of the kind would be a better name for this as we're not calculating directly the MSAD anymore.
Maybe even two functions:
StorePhiIntegralTrajectory
StorePhiThreshTrajectory
There was a problem hiding this comment.
Good Idea! Just one point about doing two functions, we will loose the shared states. Like we will always have to compute 2 times R_all for example.
There was a problem hiding this comment.
Is it possible to do only one function and ask for one or both methods? Boolean parameter?
There was a problem hiding this comment.
I'm thinking about this, I'm not sure what would be the best solution.
There was a problem hiding this comment.
Did you want to commit this change ?
|
Yes it is. Only the marge time behaviour seems wrong. It seems like the phi vector is bounded in all methods |
The marge time ? |
|
The Large time sorry |
…w matrix symmetric part. This version is more stable but might be slower
…d method. I add to use again the phi_total variable... also I've dropped phi_euler as it can be computed directly from the configuration


Computes MSAD on-the-fly during simulation for three methods: Euler, Integral, and Threshold.
Two independent schedules:
New file:
Changed file:
-src/Particles.jl
TOML usage:
[[simulation.output]]
algorithm = "MSADTracker"
scheduler_params = {linear_interval = 1}
parameters = {theta_T = @Theta@}
output_scheduler_params = {log_base = 2.0}