-
Notifications
You must be signed in to change notification settings - Fork 2
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 Einstein-Helfand viscosity implementation #25
Conversation
transport_analysis/viscosity.py
Outdated
|
||
# args | ||
self.dim_type = dim_type | ||
self._parse_dim_type() |
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 this should be a static method.
self.dim_type = dim_type
self._parse_dim_type()
is much less clear than
self.dim_type, self.dim_fac = self._parse_dim_type(dim_type)
The latter makes it clearer what _parse_dim_type is doing. In the former, you don't even know that a self.dim_fac variable has been created.
transport_analysis/viscosity.py
Outdated
def _parse_dim_type(self): | ||
r"""Sets up the desired dimensionality of the calculation.""" | ||
keys = { | ||
"x": [0], | ||
"y": [1], | ||
"z": [2], | ||
"xy": [0, 1], | ||
"xz": [0, 2], | ||
"yz": [1, 2], | ||
"xyz": [0, 1, 2], | ||
} | ||
|
||
self.dim_type = self.dim_type.lower() | ||
|
||
try: | ||
self._dim = keys[self.dim_type] | ||
except KeyError: | ||
raise ValueError( | ||
"invalid dim_type: {} specified, please specify one of xyz, " | ||
"xy, xz, yz, x, y, z".format(self.dim_type) | ||
) | ||
|
||
self.dim_fac = len(self._dim) |
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.
Could we make this a static method that returns dim_type and dim_fac?
Generally, I am of the opinion that it's always best to use static methods when possible. It makes it much easier to inspect the function call and understand what's happening.
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.
So do you mean we would set the instance variables self._dim
and self.dim_fac
in __init__()
?
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.
Oh, I see what you mean now. Missed the comment above, my bad!
transport_analysis/viscosity.py
Outdated
masses = self._mass_reshape.astype(np.float64) | ||
velocities = self._velocity_array.astype(np.float64) | ||
positions = self._position_array.astype(np.float64) |
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 believe that the default float in python is already 64 bits, what will this do?
class ViscosityHelfand(AnalysisBase): | ||
""" |
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's our plan for benchmarking the viscosity analysis?
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.
@xhgchen you can benmark as part of your reproduction.
With the most recent commit, the calculation now actually has all the necessary elements! Still very early though and I definitely need to do some proper testing. |
diff = ( | ||
self._masses_rs | ||
* self._velocities[:-lag, :, :] | ||
* self._positions[:-lag, :, :] | ||
- self._masses_rs | ||
* self._velocities[lag:, :, :] | ||
* self._positions[lag:, :, :] | ||
) |
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.
Can we break down this math to make it more understandable? something like
something = self._masses_rs * self._velocities[:-lag, :, :] * self._positions[:-lag, :, :]
something_else = self._masses_rs * self._velocities[:-lag, :, :] * self._positions[:-lag, :, :]
diff = something - something_else
transport_analysis/viscosity.py
Outdated
self.results.visc_by_particle = self.results.visc_by_particle / ( | ||
2 | ||
* constants["Boltzman_constant"] | ||
* self._vol_avg | ||
* self.temp_avg | ||
) |
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.
same as above here, I'd advocate for adding intermediate variables over putting a single term on each line. Ultimately a matter of taste though. I sense this will be a reoccuring thing so curious what @hmacdope thinks.
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.
Wouldn't adding intermediate variables take more memory and make the calculation scale worse with larger datasets? If the difference doesn't really matter, I would be good with adding them. I'm also curious what @hmacdope thinks.
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'm actually not sure. I suspect the difference is negligable if it exists at all. This could be something to try out with %timeit in a jupyter notebook.
* Use class `VelocityAutocorr` as template * Add items to `__init__()` * Write `_prepare()` and `_single_frame()` * Early work on `_conclude()`
* Change `self.*_array` to simply `self.*` * Fix `self._masses_rs`
* Set up `self._volumes` in `_prepare()` and `_single_frame()`
* Complete `_conclude()` with 2, Boltzman constant, average volume, and average temp
* Check that AtomGroup is accepted * Test some exceptions * Functionality not yet tested here
* Move `visc_by_particle` division step outside of for loop
* Contains mass, velocities, positions, and volume
* Format `viscosity.py` with Black
b2f9366
to
5000af0
Compare
* Simplify `characteristic_poly_helfand()`
5000af0
to
b0e3033
Compare
* CI will not work otherwise
* use try except and update with mda 2.6.0 release
a9857be
to
760bf9d
Compare
* Improve accuracy and agreement with `ViscosityHelfand` class * Improve performance
|
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.
Quick review, looking good!
|
||
|
||
@pytest.fixture(scope="module") | ||
def NSTEP(): |
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.
No need to back this in as a fixtue, just use 5001 in place.
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.
Sure, just wondering why 5001 in place is better? I thought setting NSTEP as a fixture would be more maintainable in case we ever decide to test it with another value (and your MSD module uses the fixture too)?
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.
fair enough I agree.
|
||
@pytest.fixture(scope="module") | ||
def visc_helfand(ag): | ||
vh_t = VH(ag, fft=False) |
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.
Parametrize over fft=True and fft=False
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'll remove FFT for now then and raise an issue.
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.
👍
start=0, | ||
step=1, | ||
): | ||
# update when mda 2.6.0 releases with typo fix |
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.
Link to the issue number.
step=1, | ||
): | ||
# update when mda 2.6.0 releases with typo fix | ||
try: |
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.
Grab the actual value 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.
Was wondering also - why is grabbing the actual value better 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.
Just to reduce code volume, you still have to index the dict later with this approach.
|
||
|
||
class TestViscosityHelfand: | ||
# fixtures are helpful functions that set up a test |
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.
Can remove this comment now.
class ViscosityHelfand(AnalysisBase): | ||
""" |
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.
@xhgchen you can benmark as part of your reproduction.
transport_analysis/viscosity.py
Outdated
self.temp_avg = temp_avg | ||
self.dim_type = dim_type.lower() | ||
self._dim, self.dim_fac = self._parse_dim_type(self.dim_type) | ||
# self.fft = fft # consider whether fft is possible later |
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 its not possibe raise exception or remove.
2 * constants[self.boltzmann] * self._vol_avg * self.temp_avg | ||
) | ||
# average over # particles and update results array | ||
self.results.timeseries = self.results.visc_by_particle.mean(axis=1) |
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.
Add plotting?
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 wanted to do that in a separate PR to avoid making this PR too bloated. Would it be better to add it 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.
thats fine to do next PR
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 am going to approve so we can move forward here. If any issues come up when testing, lets address them in a new PR. I would however love an additional look over by @orionarcher :)
Fixes #17 and #9
Changes made in this Pull Request:
VelocityAutocorr
as template__init__()
_prepare()
and_single_frame()
_conclude()
Areas to work on:
_conclude()
For later:
tidynamics
source code to see how feasible this is (raised in Consider adding FFT support to Einstein-Helfand viscosity #27)