Coding Guidelines - N-BodyShop/changa GitHub Wiki

Links

Table of Contents

ChaNGa Coding Guidelines

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.

Style/Practices guides

Here are some style-guides you should checkout.

Basic guidelines:

  1. COMMENT YOUR CODE
  2. Keeping a codebase readable and understandable requires some basic uniformity of style.
  3. Wherever possible, follow coding style already used in ChaNGa
    • This includes variable naming conventions, capitalization conventions, tab indentation, etc...
  4. Otherwise, default to using standard coding practices. A good style guide is the Google C++ Style Guide
  5. 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
  6. 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.
  7. COMMENT YOUR CODE

General Coding Guidelines

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.

C++

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.

Basic formatting

Most good practices for basic formatting can be found at the Google C++ Style Guide. I'll highlight a couple general good practices.

Indenting

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
  • 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.

Line wrap 80-characters

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 (\).

Descriptive Variable Names

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.

Example of well formatted code (note the curly braces locations)

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;
}

Commenting

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

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 style

  • 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

Physics Modules

Using macros for compile-time physics modules is the standard in ChaNGa (how to add compile-time flags). Whenever possible, try to keep #ifdefs 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.

Change function behavior

If possible, you can try removing #ifdefs 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.

Inline functions

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
}

Git

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

  1. Follow github's guidelines for using git GitHub Git Style Guide

  2. 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;
  3. Avoid unnecessary changes. Git's history is a powerful tool, and any unnecessary changes can pollute the history.

  4. 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.

  5. Before committing, do a git diff. You should review your changes. This is a great way to catch bugs.

  6. Use git for any decent sized project of yours! Even things outside of ChaNGa.

Python

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

Resources

⚠️ **GitHub.com Fallback** ⚠️