-
Notifications
You must be signed in to change notification settings - Fork 189
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
Use global counter in thermostats #3884
base: python
Are you sure you want to change the base?
Conversation
In the previous implementation, thermostat noise for different seeds was correlated, the seed just shifted the sequence. For example with seed offset X, the cross-correlation used to have a peak at lag X. Co-authored-by: Jean-Noël Grad <[email protected]>
Co-authored-by: Jean-Noël Grad <[email protected]>
The SD external library uses Philox internally for both the CPU and GPU implementations. SD now uses the global counter instead of the simulation time divided by the time step, which are both mutable and can lead to correlated sequences when changed by the user.
Remove overhead from the std::function wrapper and heap allocation.
Reduce sample sizes and increase tolerances.
wouldn't it be a good idea to have a global that monotonically increases with time. It may be useful also outside of the thermostat context at some point? |
Yes, this is how the global counter is implemented. But we cannot rewind it by resetting the system time. It could be made a property of the integrator, or a property of the system to be able to re-use it elsewhere. |
This counter is incremented by the integrator and is completely dissociated from the thermostat infrastructure.
Pass the current value of the integrator_counter to the force kernels as a parameter instead of relying on external linkage. There are two exceptions: thermalized bonds and constraints, which were not designed to be extensible in this way, and still rely on external linkage.
Header file thermostat.hpp no longer includes integrate.hpp.
Fixes clang-analyzer-deadcode.DeadStores warning.
Using the same global counter in all thermostats solves the correlation issue but creates new problems. Some thermostats have specific needs: DPD needs extra incrementation when a DPD particle interacts with a constraint, and LB is not incremented when the integrator is steepest descent. Using a global counter incremented by the integrator means |
New attempt without the global counter in #3888. |
IIRC, the idea of the counter based RNG is that by knowing the value of the counter and two particle ids we determine one random number (in fact we could get 4). If we need more than one random number per particle pair and integration, the whole logic brakes down, right? |
what do you mean by that? |
why? |
I think the counter state should be passed as an argument to all methods that need to know about it |
force1 += | ||
dpd_pair_force(p, part_rep, ia_params, dist_vec, dist, dist * dist); | ||
// Additional use of DPD here requires counter increase | ||
dpd_rng_counter_increment(); |
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 counter has never been incremented twice... the control flow is in this if branch or in the one below
@@ -144,3 +151,9 @@ int integrate_set_npt_isotropic(double ext_pressure, double piston, | |||
bool zdir_rescale, bool cubic_box); | |||
|
|||
#endif | |||
|
|||
extern Utils::Counter<uint64_t> integrator_counter; |
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.
those declarations should come before the #endif
of the header guard
True for consumers of the thermostats. However, the thermostats functions can generate more than 1 random number per tuple On a side-note, adding a new salt means shifting all the following salts in the
The function that increments the LB counters (CPU and GPU) is not called when using steepest descent: espresso/src/core/integrate.cpp Lines 267 to 268 in 2fbea6e
With a global counter, the LB RNG sequence will be incremented anyway, just like the other thermostats. Looking again at the code, this should not pose an issue.
Looking at the code, I would say it prevents correlation in the noise when you have more than one shape-based constraint. The shape-based constraint has a particle representation This makes DPD quite different, because its counter gets out of sync with the number of integration steps, and gets incremented by espresso/testsuite/python/test_checkpoint.py Lines 187 to 191 in 2fbea6e
I tried in 486612d but this means adding an extra argument to all classes in the constraint infrastructure just for DPD. Or adding a getter function to get the value of the global counter variable (to avoid exposing it via |
here the id of the particle representation of the constraint has to be used |
Then this needs to change. |
This would also not be the right approach. Actually, the interface for the random numbers should be such that it cannot be used the wrong way... Then we would see that we neither have the correct RNG interface for the NPT case nor have we solved the special case of constraint interaction via DPD (which should be handled just like any other particle particle interaction). The real issue here is that the particle can be default initialized which leads to the issue that you don't know which information of the particle representation in the constraint can actually be used and which not. |
isn't the correct behavior to not propagate the system's state at all in case of steepest descent since there is no notion of time here? not sure about that |
What's wrong which the additional argument? Following your reasoning we should have more global variables. |
Not an issue if you add the salt at the end? |
offline discussion: the issue with the DPD interaction on ShapeBasedConstraint has it's origin in the fact that the particle representation in the Constraint is not a real particle and thus has not a meaningful particle id (which is in fact a part of the counter based RNG we are using). One possible solution could be to actually create the particle and only store the pointer to the particle in the constraint. The interaction of this solution with other parts of the code, however, is not clear. |
* | ||
* @return Vector of uniform random numbers. | ||
*/ | ||
template <RNGSalt salt, size_t N = 3, | ||
std::enable_if_t<(N > 1) and (N <= 4), int> = 0> | ||
auto noise_uniform(uint64_t counter, int key1, int key2 = 0) { | ||
auto noise_uniform(uint64_t counter, uint32_t seed, int key1, int key2 = 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.
i think what is missing is an abstraction for retrieving the random numbers specifically for the case of particle ids
Description of changes:
System
sim_time
ortime_step
during simulations with SD (fixes Noise behavior of stokesian dynamics and other thermostats differ #3840)integrate.hpp
are passed to thermostats as function parameters