-
Notifications
You must be signed in to change notification settings - Fork 28
Coding Guidelines
Links
Table of Contents
Since ChaNGa is the child of many different projects, developers, teams, and decades of coding, there is not a single, simple coding style which can be enforced, however some basic tips and guidelines should be considered before contributing to ChaNGa. Here are some coding/style suggestions relevant to ChaNGa.
This guide assumes you know C++ and git.
Here are some style-guides you should checkout.
- Good Enough Practices in Scientific Computing - take a quick look, this is a great guide!
- Google C++ Style Guide
- GitHub Git Style Guide
- Python PEP8 Style Guide
- COMMENT YOUR CODE
- Keeping a codebase readable and understandable requires some basic uniformity of style.
- Wherever possible, follow coding style already used in ChaNGa
- This includes variable naming conventions, capitalization conventions, tab indentation, etc...
- Otherwise, default to using standard coding practices. A good style guide is the Google C++ Style Guide
- Try to code in a way that interfaces well with git. For using git, GitHub has a nice, short style guide: GitHub Git Style Guide
- Break these rules if it makes sense IF YOU MUST! When breaking style rules, take a minute and ask yourself if you really need to.
- COMMENT YOUR CODE
I'll just mention a few basic, general considerations whenever coding. You'll hear these all the time, and for a good reason.
- Comment your code
- Avoid early optimization
- Especially in C++ it's very easy to write code that is faster than it needs to be and is unreadable. Try for readability first, then if it needs to be faster you can try to optimize it later.
- Keep functions short
- Functions should be shorter than 100 lines. The exception is procedural code, but long functions are not modular and cannot be easily read. Many functions should only be a couple lines long!
- Don't write the same code twice. If you are using a section of code multiple times, make it a function.
This is not a comprehensive style guide. For anything not mentioned here, try the Google C++ Style Guide. If you are encountering something not mentioned in either place, take a minute to google best practices for what you are trying to accomplish.
Most good practices for basic formatting can be found at the Google C++ Style Guide. I'll highlight a couple general good practices.
Make sure to indent your code blocks! Always, always, always! Don't ever skip this! Follow these rules:
- When adding code to an existing, indented block, follow whatever is being used there
- ChaNGa can be a patchwork, so you will find lots of mixed spaces/tabs. To keep git history clean, you'll want to use what is used there. Some sections will even be indented by
[tab][4 spaces][tab][2 spaces]
. Be aware of this
- ChaNGa can be a patchwork, so you will find lots of mixed spaces/tabs. To keep git history clean, you'll want to use what is used there. Some sections will even be indented by
- When making a new clean section, use 4 spaces to indent the code. Don't use tabs. 2 spaces is okay too, but just be consistent.
For readability, all lines should be less than the 80-character limit. This is standard and makes it easier to read code. You can use continue line characters (\
).
Give your variables names which let us understand what they are wherever possible! Simple short names like x
or y
are fine when referring to actual x
or y
positions. Otherwise, don't shy away from long names.
double calcz(double x, double y, double someVeryLongVariableName,
double anotherLongName) {
double z;
int i=3;
if (x > y) {
z = x*y;
if (z > i) {
z = (i * someVeryLongVariableName + anotherLongName)\
* (someVeryLongVariableName - anotherLongName);
}
z *= 2;
} else {
z = x + y;
}
return z;
}
Rules of thumb:
- Comment! A lot!
- Use doxygen comments (see below)
- Use full sentences when possible
- Usually, comments should go on their own line
- Avoid using variable names in comments (variable names change!)
- Include units in comments when not using code units
- Specify whether quantities are comoving or physical
Comment your code! It's always better to over-comment code than under-comment. Commenting is boring, but it really doesn't take long and can save future generations hours and hours and hours of time. Most scientific code is horribly under-commented, do not follow this trap! You should have short comments within the code to describe what is going on and you should comment your functions, methods, and classes.
ChaNGa uses doxygen to generated documentation from comments in the code. When commenting functions/methods you should have a description of what the function does and a description of the what the function parameters/returns are. Function comments should be placed directly above the function definition. Doxygen comments are marked either by three slashes: ///
or a double asterisk: /**
at the beginning of the comment. Here is an example (slightly modified from the code with a return, for illustration):
/**
* @brief updateParticle is used to update particle attributes during the
* SPH pressure terms calculations.
*
* The updating of particle p and the neighbor q during this loop is symmetric
* (up to a possible sign change). For example, to update p and its neighbor
* q is two lines:
* updateParticle(p, q, params, pParams, qParams, 1);
* updateParticle(q, p, params, qParams, pParams, -1);
* @param a particle to update
* @param b interacting neighbor particle
* @param params prefactor params
* @param aParams params specific to a
* @param bParams params specific to b
* @param sign 1 for a = p (the self particle) and -1 for a = q (the neighbor)
* @return dt is the updating time step
*/
double updateParticle(GravityParticle *a, GravityParticle *b,
PressSmoothUpdate *params, PressSmoothParticle *aParams,
PressSmoothParticle *bParams, int sign) {
Macros and preprocessor directives are used extensively in ChaNGa for 4 reasons:
- To allow modularity: i.e. selecting which physics modules to compile [GOOD!]
- As a holdover from old C standards [BAD!]
- Define constants [BAD! (usually)]
- Header guards [GOOD!]
Macros do have their place, but wherever possible, macros should be avoided. Generally, the only time you should be using macros in ChaNGa is for selecting which physics modules to use. You can use them for defining constants, but that can usually be accomplished better by putting a const
into a header, e.g.:
// okay
#define SQRTPI 1.77245385091
// much better
const double sqrtpi = 1.77245385091;
As a rule of thumb, avoid using #define
-
Macro names should be in ALL CAPS:
#ifdef MACRONAME
is good -
Preprocessor directives should not be tab indented (if they are not nested) see below:
-
It may be a good idea to close
#ifdef
blocks with comments (see below)#ifdef MACRONAME dostuff(); #else //don't do stuff #endif //MACRONAME
Using macros for compile-time physics modules is the standard in ChaNGa (how to add compile-time flags). Whenever possible, try to keep #ifdef
s out of the main code. Modularizing code is a bit of a chore, but very important for optimization. If you have a physics module which adds 20 attributes to SPH particles it is very expensive to keep those attributes around when they are not being used.
Here are a couple guidelines for using physics modules.
If possible, you can try removing #ifdef
s from the main code by putting them into a function. For instance:
// in the middle of some long code
...
#ifdef DIFFUSION
x += y * z;
x *= x * x;
...
y = x;
#endif
...
Can be replaced by a function definition (which you could inline as well):
void diffUpdateXY(double &x, double &y, double z){
#ifdef DIFFUSION
x += y * z;
x *= x * x;
...
y = x;
#endif
}
Another way to modularize code is to have the interface (what is used by the rest of the code) stay the same, but at compile time select what it does, for instance by changing which implementation (.c, cpp) files to compile. Right now, this is beyond the scope of this tutorial, but worth checking out.
Don't use macros to inline
functions! This is an old way to optimize code in C, but now can be considered deprecated. If you want to have optimized, compile-time inlined code, use inlined fuctions placed in a header file. If needed you can use templates for arbitrary types. Example:
/* header.h */
// BAD!
#ifdef CUBEX
#define CUBE (x) x*x*x
#else
#define CUBE (x) x
#endif
...
// GOOD (doubles only)
inline double cube(const double x){
#ifdef CUBEX
return x*x*x;
#else
return x;
#endif
}
...
// GOOD (any type, if required!)
template <typename T>
inline T cube(const T x){
#ifdef CUBEX
return x*x*x;
#else
return x;
#endif
}
It can be difficult to get used to version control, but it is very, very useful. I'll just make a few suggestions here about working with version control
-
Follow github's guidelines for using git GitHub Git Style Guide
-
When coding, prefer making changes on new lines. This makes it easier for git to keep track of changes. For example, let's say we want to add a variable and we start with one line:
double x;
If we want to add a variable
y
, there's a good and a bad way to do this:\\ BAD: double x, y; \\ GOOD double x; double y;
-
Avoid unnecessary changes. Git's history is a powerful tool, and any unnecessary changes can pollute the history.
-
Don't rebase/rewrite history unless you are CERTAIN that nobody else is working off your code! This is mentioned in the GitHub style guide, but don't do this! This can cause horrible, unfixable problems.
-
Before committing, do a
git diff
. You should review your changes. This is a great way to catch bugs. -
Use git for any decent sized project of yours! Even things outside of ChaNGa.
I'll only mention it here, because ChaNGa is not written in python, but many of us use python a lot. There is a very good, complete style guide you should use: Python PEP8 Style Guide