-
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
Raplace the macro with a function #28
base: master
Are you sure you want to change the base?
Conversation
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.
In my opinion this is a much better than the macro we use. And I propose we deprecate the macro in favor of this.
I agree with the deprecation. I'm still a little unsure about this function, because it may be too magical and clever.
Of course such a function is great if it works, but it has a chance of hiding the details too much from the developer, such that in weird edge cases one still needs to implement everything manually (think of an odd calculator where the best dispatch path just happens to be different than the one you have implemented for a weird reason). I think too magical functions tend to increase the chance for bugs and technical debt, which may be hard to detect and fix in the long run.
My main gripe is that it is a lot of code to make an automated decision, which could instead be put in the responsibility of the developer calling this function. If they are forced to specify what kind of things they want implemented, then the mechanics of this function is more visible and explicit, which also means that a developer can choose to not do one thing for whatever reason, without missing out of all the convenience of the function.
push!(out, tmp) | ||
elseif ! status[:forces] && status[:calculate_forces] | ||
tmp = generate_only_forces_from_calculator(calc_type) | ||
push!(out, tmp) | ||
end | ||
elseif status[:energy_forces] | ||
tmp = generate_forces_from_energy_forces(calc_type) | ||
push!(out, tmp) | ||
tmp = generate_calculator_forces(calc_type) | ||
push!(out, tmp) | ||
elseif status[:energy_forces_virial] | ||
tmp = generate_forces_from_energy_forces_virial(calc_type) | ||
push!(out, tmp) | ||
tmp = generate_calculator_forces(calc_type) | ||
push!(out, tmp) | ||
elseif status[:forces!] | ||
tmp = generate_allocating_forces(calc_type) | ||
push!(out, tmp) | ||
tmp = generate_calculator_forces(calc_type) | ||
push!(out, tmp) | ||
elseif status[:energy_forces!] | ||
tmp = generate_allocating_forces_from_energy_forces!(calc_type) | ||
push!(out, tmp) | ||
tmp = generate_calculator_forces(calc_type) | ||
push!(out, tmp) | ||
elseif status[:energy_forces_virial!] | ||
tmp = generate_allocating_forces_from_energy_forces_virial!(calc_type) | ||
push!(out, tmp) | ||
tmp = generate_calculator_forces(calc_type) | ||
push!(out, tmp) | ||
end | ||
|
||
# Nonallocating force calls | ||
if ! status[:forces!] && (status[:energy_forces!] || status[:energy_forces_virial!]) | ||
tmp = generate_nonalloc_forces_from_energy_forces!(calc_type) | ||
push!(out, tmp) | ||
elseif ! status[:forces!] && | ||
(status[:forces] || status[:calculate_forces] || status[:energy_forces] || status[:energy_forces_virial]) | ||
tmp = generate_nonallocating_forces(calc_type) | ||
push!(out, tmp) | ||
end | ||
|
||
# Generate only virial calls | ||
if status[:virial] || status[:calculate_virial] | ||
if status[:virial] && ! status[:calculate_virial] | ||
tmp = generate_calculator_virial(calc_type) | ||
push!(out, tmp) | ||
elseif ! status[:virial] && status[:calculate_virial] | ||
tmp = generate_virial(calc_type) | ||
push!(out, tmp) | ||
end | ||
elseif status[:energy_forces_virial] | ||
tmp = generate_virial_from_energy_forces_virial(calc_type) | ||
push!(out, tmp) | ||
tmp = generate_calculator_virial(calc_type) | ||
push!(out, tmp) | ||
elseif status[:energy_forces_virial!] | ||
tmp = generate_virial_from_energy_forces_virial!(calc_type) | ||
push!(out, tmp) | ||
tmp = generate_calculator_virial(calc_type) | ||
push!(out, tmp) | ||
end | ||
|
||
# Combination calls | ||
if ! status[:energy_forces] && status[:energy_forces!] | ||
tmp = generate_energy_forces_from_energy_forces!(calc_type) | ||
push!(out, tmp) | ||
elseif ! status[:energy_forces] && status[:energy_forces_virial] | ||
tmp = generate_energy_forces_from_energy_forces_virial(calc_type) | ||
push!(out, tmp) | ||
elseif ! status[:energy_forces] && status[:energy_forces_virial!] | ||
tmp = generate_energy_forces_from_energy_forces_virial!(calc_type) | ||
push!(out, tmp) | ||
end | ||
|
||
if ! status[:energy_forces!] && status[:energy_forces_virial!] | ||
tmp = generate_nonalloc_energy_forces_from_energy_forces_virial!(calc_type) | ||
push!(out, tmp) | ||
end | ||
|
||
if ! status[:energy_forces_virial] && status[:energy_forces_virial!] | ||
tmp = generate_energy_forces_virial_from_energy_forces_virial!(calc_type) | ||
push!(out, tmp) | ||
|
||
end | ||
|
||
if ! status[:calculate_energy] && ! status[:calculate_forces] && | ||
( status[:energy_forces] || status[:energy_forces!] || | ||
status[:energy_forces_virial] || status[:energy_forces!] || status[:energy_forces_virial!] ) | ||
# if this is true then optimized energy_forces exists | ||
tmp = generate_calculate_energy_forces_from_energy_forces(calc_type) | ||
push!(out, tmp) | ||
end | ||
|
||
if ! status[:calculate_energy] && ! status[:calculate_forces] && ! status[:calculate_virial] && | ||
( status[:energy_forces_virial] || status[:energy_forces_virial!] ) | ||
# if this is true then optimized energy_forces_virial exists | ||
tmp = generate_calculate_energy_forces_virial_from_energy_forces_virial(calc_type) | ||
push!(out, tmp) | ||
end |
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 a lot of code. Can this not be simplified by taking some flags (symbols) which the developer has to specify ? This makes this function shorter, easier to understand what it does and its action more explicit. I am almost sure this will avoid bugs in the long run.
|
||
|
||
|
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.
Looks like unintended commit.
This PR will implement a new function called
AtomsCalculators.complete_interface
mean to replace the old macro.The function works by checking what has been implemented for the given calculator and then implements the missing parts.
Here is in an example a complete energy interface
You can also do this to implement all energy and force calls
In practice you can now just implement whatever and after that call
AtomsCalculators.complete_interface
, and you will have a complete interface.The function is now also optimized in a way that it detects optimized calls, like
energy_forces
forms other optimized calls, likecalculate( (Energy(), Forces() ), sys, calc)
based on it.The opposite also works, if there is an optimized
calculate( (Energy(), Forces() ), sys, calc)
call, thenenergy_forces
call will use that.implementation_status helper function
There is a new helper function
implementation_status
that is also useful for problem cases. It basically checks what parts of interface have been implemented and what notE.g.
returns
This function is used by
complete_interface
function to determine what needs to be implented.`Discussion and comments
This should now implement all what was discussed in #26 and #27 (@rkurchin and @mfherbst).
In my opinion this is a much better than the macro we use. And I propose we deprecate the macro in favor of this.
The new function should be very robust. It only needs that
AtomsCalculators
is in scope. After that is should be completely safe to call and it should not give out any errors (other than AtomsCalculators has not been defined). In fact you can call the function several times and it would not do anything extra. You can also call the function in a different module and it should still complete the interface.Documentation page is still missing. I will add it, if we agree that this function is the way to go.
While the new function is much more simpler to use and less error prone, it is actually a lot more complicated that the old macro. This is a possible error source. But, I believe I managed to iron out the errors. The issue is that there are so many possible implementation combinations that it is not possible to test all of them. How ever, in practice only couple of all possibilities are used in practice. So this should be fine.