-
Notifications
You must be signed in to change notification settings - Fork 19
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
Fix spline interpolation segfaults. #309
Conversation
This seems to fix the segfault when running tests with pytest.
@ProkopHapala The way I did it is maybe not exactly what you were suggesting, but I think this should work quite nicely. It's now more explicit when the spline interpolation is used. The downside is of course that there is more boilerplate passing of the parameters in several functions, but maybe it's acceptable. See if you like it or not. |
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
ppafm/core.py
Outdated
def __init__(self, xs, ydys): | ||
super().__init__() | ||
self.rff_n = len(xs) | ||
self.rff_xs = np.ctypeslib.as_ctypes(xs.astype(np.double)) |
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 yo store just the ctypes.pointer, not numpy array? Does this prevent GC to deallocate the memory? Anyway, I would perhaps store the numpy array for debugging purposes (so that we can easily plot the spline, for example)
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 originally tried to do it with the np.ctypeslib.ndpointer
so that you could just use the numpy array, but for some reason that does not work with structs, so I opted to use the ctypes
pointer instead which works.
I changed it so that the original numpy arrays are stored there as well in a separate variables starting with np_
.
ppafm/HighLevel.py
Outdated
FFboltz=None, | ||
FFkpfm_t0sV=None, | ||
FFkpfm_tVs0=None, | ||
spline_parameters=None, |
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 name tip_spline is better than spline_params, because it tell more what is the purpose of the spline. But that is just cosmetic, perhaps does not matter to much
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 changed the name to tip_spline
.
tests/human_eye/test_TipForce.py
Outdated
|
||
parameters = common.PpafmParameters() | ||
|
||
print("TipRSpline.ini overrides harmonic tip") |
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.
hmm, so the user should do this manually? I think it would be nice if the spline-class can initialized itself form spline_ini
file, just for convenience
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, actually I also made it so that you can load it directly from a file using core.SplineParameters.from_file
. This was already used in relaxed_scan.py
. I now changed it in this test script as well.
tests/human_eye/test_TipForce.py
Outdated
print("ydys: ", ydys) | ||
plt.plot(xs, ydys[:, 0], "o") | ||
plt.plot(xs, ydys[:, 0] + ydys[:, 1], ".") | ||
spline_parameters = core.SplineParameters(xs, ydys) |
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 mean it would be convenient to call core.SplineParameters(fname='TipRSpline.ini')
instead
I mean I would rather do this
tip_spline = core.SplineParameters( fname='TipRSpline.ini' )
plt.plot(tip_spline.xs, tip_spline.ydys[:,0], "o")
plt.plot(tip_spline.xs, tip_spline.ydys[:,0] + tip_spline.ydys[:,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.
Now it's like this.
tests/human_eye/test_vdwDamping.py
Outdated
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 just some formating? I don't see relation to spline (?)
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, it's just moving the code inside a function block, which makes it work better with the automated test system.
OK, if you want why not. just to give some background / explanation. This spline-thing was just quick hack which somebody (Adam Sweetman ?) asked me at one conference. It has very limited niche. Therefore I wanted to implement it with minimal effort and minimal changes to the rest of the code. Usually I try to avoid changes to function interfaces especially on the python/C++ interface, and keep the argument list short.
On the other hand I agree that it was introducing some hidden variable which was not obvisous/explicit. So perhaps it is better this way. |
…nged variable name.
Yes, this is exactly the issue, the hidden nature of it made it difficult to know what the state of the data is at any given moment. Especially since the pointers were set on the C++ side, then it's impossible know from the Python side what pointers exist and don't exist. Actually it's kind of this way with the force field / potential grids as well. Maybe these could be made into something that can be managed on the Python side as well.
If we have some common parameter lists that we pass to the functions, then maybe these could be packed into structs as well to keep the function signatures shorter. The structs can be easily extended as well without changing the function signature. |
@ProkopHapala, you already approved this, but since I made some changes, I'll wait a bit if you want to comment on something before I merge. |
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
self.rff_xs = np.ctypeslib.as_ctypes(self.np_rff_xs) | ||
self.rff_ydys = np.ctypeslib.as_ctypes(self.np_rff_ydys.reshape(-1)) | ||
|
||
@classmethod |
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 did not know this pattern @classmethod
before. Basically it is like overloaded constructor (?)
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.
Kind of, yes. It makes the method so that it can be called without creating an instance of the class, and the first argument, instead of being self
pointing to the instance, is cls
which points to the class constructor.
I think it is good, go ahead. (In general, I don't want to block you, inc case I forget to look on it.) |
Yes, I think if we use something else than
Talking about big structural changes, it would be nice to make everything into classes inside C/C++ so that we can dynamically create and destroy instances of ppafm simulator and keep multiple different instances in memory simultanously. I think for some applications it could be useful. I that case I would make python classes which mimic the C++ classes and keep the same data fields, and destroy/nullify them in sync.
perhaps yes. At the beggining I did not want to use structs because it looks a bit more complicated and add some boilerplate. But form some complexity it is probably worth it. |
Yes, if we want to do this, then |
Fixes #308
Fixes #279
This PR moves the spline parameters in the
TIP
namespace into a struct in the C++ code and introduces a corresponding Python class for managing the parameter arrays. A pointer to the parameters are now explicitly passed into the relevant functions instead of being set on a global level. This gets rid of the problem where the parameter array pointer could still be set even after the arrays were freed.Also fixes one uninitialized variable in the spline interpolation function that was sometimes segfaulting.
All tests are now enabled and seem to be passing without problems again.