-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add resize MC move. #58
Conversation
This should be possible, I've never done it. Maybe something using rebase, and then amend the commit to add an author. I don't even know if you can have more than one author for a commit. Anyway, you can leave it as it is, I can live without being cited as author on this commit 😉. I'll give this PR an eye later today. |
So do I. If we find a way to add/change author, I'd be happy.
Great! I will write a test case. |
I checked that, and git can not associate two authors with the a given commit. You can add me in the commit message (`Implemented with ...); or I can do this before merging. |
range: Range::new(-delta, delta), | ||
old_system: System::new(), | ||
pressure: pressure, | ||
rc_max: 100.0, // arbitrarily large value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use std::f64::INFINITY
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recognized that there has to be a zero. std::f64::INFINITY
would lead to the error, since any side length is smaller than infinity.
// cut off distance. | ||
// abort simulation when box gets too small | ||
if new_cell.lengths_as_vec().iter().any(|&d| 0.5 * d < self.rc_max) { | ||
fatal_error!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd argue this should not be a fatal error. It should send an error message saying that the energy will be wrong for now on, but not abort the process, as fatal_error!
does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, wrong energies lead to wrong configurations. In the end, the ensemble averages computed are incorrect. We could adjust the cutoff so that it fits into the new box. That is done in some codes. Personally, I think this solution is also not a very good idea, since it essentially changes the force field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the idea of changing the cutoff too.
I prefer to leave control to the user, instead of doing things behind him. We can leave a fatal_error, and reconsider if this is being very annoying.
cache.unused(); | ||
// get system energy before change: this is stored in `self.old_system` | ||
let old_energy = self.old_system.potential_energy(); | ||
// get |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfinished comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the catch.
@@ -174,6 +174,23 @@ impl UnitCell { | |||
return (x, y, z); | |||
} | |||
|
|||
/// Get a vector of the distances between faces of the unit cell | |||
pub fn lengths_as_vec(&self) -> Vec<f64> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not need another function here, the UnitCell::lengths
can do this too! You seems to need iteration on the lengths, so why not change the signature to be pub fn lengths(&self) -> [f64; 3]
? This way you get iteration, and do not pay for a dynamic allocation.
The current function is only used once, for the exact same reason as here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I will change that. Actually, I tried to get the maximal value out of the tuple but it was kind of messy. I am not used to the fact that there is no max
function for arrays and tuples.
Please, be so kind and do this when merging 😄 |
This is missing some integration tests before merging. I would at least have a check that for a Lennard-Jones fluid, the pressure does converge to the requested value, adding a new case here.
Yes, will do so! |
I will add test case(s) tonight. |
Are you reading the file like this ?
Because your error looks like a problem with the current directory of the test process. |
That's exactly what I am doing. I copied this part from the existing test Edit: Oh, i see. I have to be in the |
Weird. I'll clone this branch and have a look. Can you commit your changes and push them ? |
Changes are commited. Both, example and test compile. Example just works when I call it from inside I did:
|
Ok, I got it ! You need to run cargo from the
At the same time, the examples do not use the |
Energies and volumes look good. I need to perform some long runs over night to actually check the statistics. Internal pressure seems (no good statistics) to work fine for the case without tail corrections. With tail corrections, there is an issue. I'll check that. |
Can you rebase this on master now? All the fixes should be here ! |
I did a lot more work on this. I will rebase and push the changes later today. |
This is still WIPI changed a lot in the latest iteration. Ran some short simulations, while
Edit: I realized I left some stuff in the PR that I included for testing, i.e. the whole |
// system. | ||
// TODO: include elecetrostatic interactions | ||
self.rc_max = system.interactions() | ||
.all_pairs() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would look better if aligned with the .interactions()
=)
Nice !
We can add a |
Another take would be to make this more like the translation move, i.e. not to clone the whole system, but to get only the components that actually change. For example /// Monte-Carlo move that changes the size of the simulation cell
pub struct Resize {
/// Delta for translation of the box length
delta: f64,
/// Sampling range for volume scaling
range: Range<f64>,
/// Positions after applying changes to the simulation cell
new_pos: Vec<Vector3D>,
/// New simulation cell
new_cell: UnitCell,
/// target pressure
pressure: f64,
/// largest cut off diameter of `PairPotentials`
rc_max: f64,
} We could then create a caching function like pub fn resize_cell_cost(&mut self, system: &System, new_pos: &[Vector3D], new_cell: UnitCell) -> f64 that only recomputes intermolecular changes. This would work nicely with the I will debug the current approach, as it is the most comfortable to work with for now. |
I added a function I also changed the sign of the tail correction to the virial. I did two quick runs for Lennard Jones particles and compared to NIST. Energies and densities are in very good (as far as I can tell from the short runs) agreement, internal pressure fits external pressure. Edit: Added a check in Todo:I will add tests/examples for TraPPE Ethane and Water to check if molecules/electrostatics also work as intended. |
|
||
let mut new_system = system.clone(); | ||
// translate the center of mass | ||
for pi in &system.molecules()[0] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be system.molecule(0)
until we get better API for molecules.
@@ -201,7 +200,7 @@ impl Compute for Virial { | |||
let nj = composition[&j] as f64; | |||
let potentials = system.interactions().pairs(i, j); | |||
for potential in potentials { | |||
virial -= 2.0 * PI * ni * nj * potential.tail_virial() / volume; | |||
virial += 2.0 * PI * ni * nj * potential.tail_virial() / volume; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in a separated commit (commits should do only one thing), and maybe with an explanation message (the commit message) and associated tests.
It is fine for this one, can you try to do it for your next commits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. I'll try to keep separate issues in separate commits in the future.
Yay ! \o/
This can be a separated PR. |
I added functionalities to wrap molecules into the simulation cell. I use these to shift molecules as a whole, as soon as the center of mass of the molecule leaves the cell (some particles may still be outside the cell, but this way the com is always inside the cell). I used this mainly to debug some weird stuff happening to the bond lengths. In the end, the molecule wrapping is not necessary but it might come in handy in the future, for example when writing the configuration to a file. I added a property I added an example case for ethane. I found very good agreement for energy and density w.r.t other codes, and internal pressure agrees very well with external pressure. Going to add a case for SPC/E water. |
system.set_cell(UnitCell::cubic(100.0)); | ||
|
||
// Add intermolecular interactions | ||
// TraPPE parameters for CH3 - note that masses are not correct. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can iterate over the particles and set the right mass like this:
for particle in &mut system {
assert_eq!(particle.name(), "C");
particle.mass = ...;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see. Here, it would not make a difference, since we don't output the density.
small changes added largest cutoff computation to setup routine added TODO to skip com computation
…ng for com functions.
…make functions more transparent.
|
||
let temperature = units::from(102.231042155, "K").unwrap(); | ||
let pressure = units::from(20.1586274874, "bar").unwrap(); | ||
//let mut mc = MonteCarlo::from_rng(temperature, rng); // This is not working, why? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleanup? And maybe open an issue with the error if this is not working =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I performed a last review on the whole code, can you do a last bit of cleanup? Then we'll get that merged!
match p.name() { | ||
"H" => p.charge = h, | ||
"O" => p.charge = o, | ||
_ => panic!("Particle {}", p.name()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use a better message here ("Unknown particle in charge setting")? And replace p
by particle
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comment, I added a better description.
/// | ||
/// This function does not check for the particles' positions' | ||
/// nearest images. To use this function properly, make sure | ||
/// that all particles of the molecule are adjacent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this warning. Is it in the case where we have some particles of the molecule on a side of the cell and some other particles on the other side? Why not check particles nearest image here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it in the case where we have some particles of the molecule on a side of the cell and some other particles on the other side?
Exactly. To avoid that, we can use wrap_molecule
. It get's messy when an input file is used and parts of a molecule are already wrapped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to address this in a separate PR, if that's ok for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's OK.
// cells, a wrap will return just the same vector and | ||
// hence `delta` will be zero; but it is faster. | ||
match self.cell().shape() { | ||
CellShape::Infinite => (), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed, as the wrap_vector
function already manage the Infinite cell case. The same code ca be used everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true. Removed the match statement.
type Output = [Particle]; | ||
#[inline] | ||
fn index(&self, index: Range<usize>) -> &[Particle] { | ||
// Index::index(&self.particles, index) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we keep indexing function via Range
? We currently don't use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do not use it, let's remove it for now. It is easy to add back if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I removed it.
|
||
let temperature = units::from(120.0, "K").unwrap(); | ||
let pressure = units::from(12.6, "bar").unwrap(); | ||
//let mut mc = MonteCarlo::from_rng(temperature, rng); // This is not working, why? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup?
|
||
// rng: set this here in case we want to change the seed | ||
//let mut rng = Box::new(rand::XorShiftRng::new_unseeded()); | ||
//rng.reseed([2015u32, 42u32, 3u32, 12u32]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup? This is not adding a lot of information to this test.
// run simulation for at least 500 cycles (so that a volume update is performed) | ||
simulation.run(&mut system, 500000); | ||
|
||
// [1] Lotfi et al.: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we not checking that in assert_approx_eq!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? Checking versus energy and density from the paper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, or move this from the test folder to the examples folder. It does not seems to test anything yet =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have a NPT example for argon. I remove this one. The simulation has to run very long to get comparable results.
I rebased the whole branch on top of master, and have added our names to the first commit. |
Thanks for the review. Can I make my changes locally and then simply push as usual or do I have to fetch your changes beforehand? |
You have to get the changes first. Use |
Changed according to review. @Luthaf Again, thanks for your patience and your time investment. The examples for SPCE water and ethane are still in the example folder although they currently do not produce correct samples. For ethane, pressure is wrong due to using the number of atoms for the ideal gas pressure which we discussed above and which will be resolved in a separate PR. The same is true for SPCE which is also affected by the bugs that were found and worked on in #75. I left the current implementation of the center of mass computation - without checking if a molecule is partly wrapped. This would result in a wrong center of mass. I'd address changes of the com in a separate PR. |
Thank you @g-bauer, that was an epic PR! More than 6 month between the initial implementation and the merge, that is the best I have done. |
This adds a MC move for changing the size of the simulation cell.
Todo issue #59 :
I added a check to compare the smallest inscribed spheres' radius vs the cut off radius
rc_max
. When the cell gets too small (so that the cut off is too large) it should cancel the simulation and report an error. I'd like to get the (maximum) cut off out of thePairInteractions
when invokingnew()
but it is not clear how to get it at the moment.@Luthaf we wrote the main part of this together. I created a new branch since the history was messed up in the old branch. Now, you are not reported as author. Can we change this somehow?