Skip to content
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

Several improvements to colvar component classes #643

Merged
merged 15 commits into from
Mar 12, 2024

Conversation

giacomofiorin
Copy link
Member

@giacomofiorin giacomofiorin commented Dec 19, 2023

This PR contains several improvements to colvar::cvc and its derived classes, including substantial simplification of the code and some fixes where the previous implementation was incorrect (distanceVec metrics) or less robust (period).

  • There is now only one type of CVC constructor, which takes no arguments: this removes the need to maintain two different types of constructor. init(conf) now initializes an object from the configuration string, by modifying existing defaults.
  • Default values for plain old data are set right where the member is declared in the header, at long last! :-)
  • (Fix) The distanceVec metric functions are now consistent with the documentation.
  • There is no longer a need to explicitly derive the metric functions: the base class assumes a scalar-valued function, and only special cases are overridden. A ton of duplicated functions were removed with this.
  • period can now be set only for those CVCs that allow it: that includes distanceZ as well all periodic angles (only where someone needs something else than 360°).
  • std::shared_ptr is now used to collect the CVC objects inside the colvar level. I have not yet touched the arrays of CVC pointers inside complex CVC objects (e.g. the protein CVCs or the combination ones) because those will be modified anyway in upcoming work.

Other possible improvements that were out of scope to do:

  • Because all CVCs have now a derived init() function, it would be theoretically possible to call that from colvar::update_cvc_config(), i.e. the backend for the modifycvcs scripting command. Currently, the base class method cvc::init() is still called, because not all the derived ones are idempotent yet.
  • Atom groups could now be tracked much better with smart pointers, allowing them to be shared to save data/computation (this is done in Allow reusing computation and data between CVC objects #644).
  • colvarcomp.h could be split up to reduce interdependencies.
  • Feature objects could definitely use some smart-pointer love, too.

@giacomofiorin giacomofiorin added stability maintenance No user-visible effects labels Dec 19, 2023
@giacomofiorin giacomofiorin removed the maintenance No user-visible effects label Jan 3, 2024
@giacomofiorin giacomofiorin force-pushed the cvc-default-constructors branch 2 times, most recently from 43da335 to b705999 Compare February 20, 2024 19:18
@giacomofiorin giacomofiorin marked this pull request as ready for review February 20, 2024 19:19
Copy link
Member

@HanatoK HanatoK left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the original constructor code with cvm::error return as early as possible, because continue to execute the code following cvm::error will result in a crash. When moving to an init function this behavior should keep the same. Otherwise VMD users may experience crashes after loading the wrong data into some of the CVs, for example, a pathFile with the wrong format for gpathCV.

@@ -317,13 +353,13 @@ colvar::gzpath::gzpath(std::string const &conf): CartesianBasedPath(conf) {
cvm::log(std::string("Geometric path z(σ) will use the square of distance from current frame to path compute z\n"));
}
if (total_reference_frames < 2) {
cvm::error("Error: you have specified " + cvm::to_str(total_reference_frames) + " reference frames, but gzpath requires at least 2 frames.\n");
return;
error_code |= cvm::error("Error: you have specified " + cvm::to_str(total_reference_frames) + " reference frames, but gzpath requires at least 2 frames.\n", COLVARS_INPUT_ERROR);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the return statement moved to the end of the function? I am a bit worried that if it doesn't return here, the following code would segfault or crash, which is OK for NAMD but not a good user experience for VMD.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok with me to return early, I just overlooked the possibility of a crash.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the instances that you pointed out into an early return with error.


int colvar::gspath::init(std::string const &conf)
{
int error_code = CartesianBasedPath::init(conf);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we need to check the error code and return early too? If you think it is too complicated to return error code in derived classes, we can throw exceptions and catch them in some of the upper level functions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dude, are you really advocating for supporting VMD and and propose adding exceptions in the same review round? ;-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I should be clearer. Exception is just one of the solutions, and if I were you I might use exceptions in any init functions and catch them in colvar::update_cvc_config. Early return is another one, which may need more lines of code for both base and derived classes. Using either of them should be enough. As for VMD, it is just an example of interactive applications. Colvars as a library should not terminate the main interactive program if there is an error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should be clearer, too :-) Exceptions would be convenient on their own, actually. We haven't used them in the past mostly to play it safe with VMD, which traditionally aims to support exotic and/or ancient compilers. Although compiler support has become more uniform with C++11, best practices like the Google Style Guide don't encourage vendors to support them well.

I think that adding early returns wherever it looks like the code may crash is probably the safest right now. I think that's still a minority of all error conditions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a common pattern in several places, I made similar "early return" changes in #650 and Haochuan added a bunch more in this PR.
I wouldn't mind having this in more places, that is, everywhere, and I wonder if there is a more concise / elegant / modern / systematic way of formulating this, besides exceptions. The C programmer in me would make it a macro... There, I said it, I'm old. C++11 best practices and Google Style Guide: get off my lawn.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be clear, I'm old too but I would be happy to embrace anything that the maintainers of a major package (whatever their age might be) will commit to approve.

In writing, please.

@giacomofiorin
Copy link
Member Author

@HanatoK Thanks for revising the PCV constructors!

src/colvarcomp.cpp Show resolved Hide resolved

int colvar::gspath::init(std::string const &conf)
{
int error_code = CartesianBasedPath::init(conf);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a common pattern in several places, I made similar "early return" changes in #650 and Haochuan added a bunch more in this PR.
I wouldn't mind having this in more places, that is, everywhere, and I wonder if there is a more concise / elegant / modern / systematic way of formulating this, besides exceptions. The C programmer in me would make it a macro... There, I said it, I'm old. C++11 best practices and Google Style Guide: get off my lawn.

@jhenin jhenin self-requested a review March 12, 2024 20:35
@giacomofiorin giacomofiorin merged commit ec69db1 into master Mar 12, 2024
15 checks passed
@giacomofiorin giacomofiorin deleted the cvc-default-constructors branch March 12, 2024 20:56
giacomofiorin referenced this pull request Mar 28, 2024
silence uninitialized access valgrind warning
lammps/lammps@c3cc497
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants