NOX Development
Loading...
Searching...
No Matches
NOX Developer's Coding Guidelines

Adapted largely from Programming in C++, Rules and Recommendations, by Mats Henricson and Erik Nyquist.

Structure of the Files

Naming Conventions
  • C++ header files end in .H and source files end in .C
     
  • The name of the files should correspond to the name of the class they define, with double-colons replaced by underscores. For example, the definition of the class NOX::Abstract::Group is in the file NOX_Abstract_Group.H.
     
General File Structure
  • Each file should begin as follows:
    //@HEADER
    // ************************************************************************
    //
    //            NOX: An Object-Oriented Nonlinear Solver Package
    //                 Copyright (2002) Sandia Corporation
    //
    // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
    // license for use of this work by or on behalf of the U.S. Government.
    //
    // Redistribution and use in source and binary forms, with or without
    // modification, are permitted provided that the following conditions are
    // met:
    //
    // 1. Redistributions of source code must retain the above copyright
    // notice, this list of conditions and the following disclaimer.
    //
    // 2. Redistributions in binary form must reproduce the above copyright
    // notice, this list of conditions and the following disclaimer in the
    // documentation and/or other materials provided with the distribution.
    //
    // 3. Neither the name of the Corporation nor the names of the
    // contributors may be used to endorse or promote products derived from
    // this software without specific prior written permission.
    //
    // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
    // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
    // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    //
    // Questions? Contact Roger Pawlowski (rppawlo@sandia.gov) or
    // Eric Phipps (etphipp@sandia.gov), Sandia National Laboratories.
    // ************************************************************************
    //  CVS Information
    //  $Source$
    //  $Author$
    //  $Date$
    //  $Revision$
    // ************************************************************************
    //@HEADER
    
    Once the file is committed to the CVS repository, the CVS Information lines will look something like the following:
    //  $Source$
    //  $Author$
    //  $Date$
    //  $Revision$
    
    The header information is automatically filled in between the two //@HEADER keys when we run the nox/maintenance/update_nox_headers.sh command.
Include File Structure
  • No include file should define more than one class.
    • The exception is for utility classes that are only used by the main class defined in the header file.

     
  • Every include file must contain a mechanism that prevents multiple inclusions of the file. For example, the following should follow the header information for the NOX_Abstract_Vector.H header file.
    #ifndef NOX_ABSTRACT_VECTOR_H
    #define NOX_ABSTRACT_VECTOR_H
    
    ...body of include file goes here...
    
    #endif
    
  • Do not include system files (e.g., iostream) directly in your files. Instead, include NOX_Common.H. The goal is to better enable system portability since some machines have math.h and others have cmath and so on. Currently, we have the following system headers:
     
    • cstdlib
    • cstdio
    • cassert
    • iostream
    • iomanip
    • string
    • cmath
    • vector
    • map
    • deque
    • algorithm
    • sstream
    • fstream
       
  • Do not use relative or absolute paths when including header files. The autotools will set the paths correctly for you. This is especially important since installing the headers and libraries moves the headers into a flat directory structure.
    • DO NOT do this:
      #include "../test/utils/NOX_TestCompare.H"
    • Do this:
      #include "NOX_TestCompare.H"

     
  • Definitions of classes that are only accessed via pointers (*) or references (&) should be declared using forward declarations, and not by including the header files.
     
  • These are the cases when header files should be included in the header file:
     
    • classes that are used as base classes,
    • classes that are used as member variables,
    • classes that appear as return types or as argument types in function/member function prototypes.

Naming Conventions

  • Everything must be declared within the NOX namespace. No exceptions!
     
  • Furthermore, each class should be within the appropriate sub-namespace. The choices are:
     
    • Abstract
    • Parameter
    • Solver
    • StatusTest
    • LineSearch
    • Direction
    • Epetra
       
  • Class names should begin with an uppercase letter. Variable and function names should begin with a lowercase letter.
     
  • In names (function, class, variable, etc) which consist of more than one word, the words are written together and each word that follows the first is begun with an uppercase letter. (e.g., NOX::Linesearch::MoreThuente).
     
  • Do not use identifiers which begin with one or two underscores (`_' or `__').
     
  • Names should not include abbreviations that are not generally accepted.
     
  • Choose variable names that suggest the usage.

Style

Classes
  • The public, protected, and private sections of a class are to be declared in that order (the public section is declared before the protected section which is declared before the private section).
     
  • No inline functions, except the empty {} function. The reason for this is that if the function does not inline correctly, it can actually lead to slower code rather than faster code.
Functions
  • Always provide the return type of a function explicitly.
     
  • When declaring functions, the leading parenthesis and the first argument (if any) are to be written on the same line as the function name. If space permits, other arguments and the closing parenthesis may also be written on the same line as the function name. Otherwise, each additional argument is to be written on a separate line (with the closing parenthesis directly after the last argument).
     
  • Always write the left parenthesis directly after a function name.
       void foo ();    // No!!
       void foo();     // Better
    
Variable declarations
  • Only one variable per line.
       int i,j;   // No!!
    
       int i;     // Yes
       int j;
    
  • The characters `*' and `&' should be written together with the types of variables instead of with the names of variables in order to emphasize that they are part of the type definition. Instead of saying that *i is an int, say that i is an int*.
       int *i;   // No!!
       int* i;   // Yes
    
Loops and conditionals: if, for, while, etc.
  • User parens to make code readable.
      if (a == b && c < d || e == f)  // No!
      {
        /* Stuff */
      }
    
      if (((a == b) && (c < d)) || (e == f))  // Yes
      {
        /* Stuff */
      }
    
  • The block of any if statement should always follow on a separate line.
       if ( /*Something*/ ) i++; // No!!
    
       if ( /*Something*/ )      // Yes!
         i++;
    
  • Braces ("{}") which enclose a block should be aligned as follows:
       if ( /*Something*/ ) // Yes!
       {
         i++;
         j++;
       }
    
       if ( /*Something*/ ) { // Okay
         i++;
         j++;
       }
    
       if ( /*Something*/ ) // No!
         {
           i++;
           j++;
         }
    
    Adding the following line to your \c .emacs file will help:
    (c-set-offset 'substatement-open 0)
    
Miscellaneous
  • Always provide a space on both sides of = signs and all logical operators.
     
  • Each statement shall always be in a separate line, however small it may appear.
     
  • Do not use spaces around `.' or `->', nor between unary operators and operands.
     
  • Use the c++ mode in GNU Emacs to format code.

Coding Rules

  • A public member function must never return a non-const reference or pointer to member data.
     
  • Constants are to be defined using const or enum; never using #define.
     
  • A switch statement must always contain a default branch which handles unexpected cases.

Output

The NOX::Utils class has utility functions related to printing. To use it, include NOX_Utils.H.
  • For any non-error print statement, call the NOX::Utils::out or NOX::Utils::pout functions with the appropriate MsgType flag. The flags are:
     
    • NOX::Utils::Error
    • NOX::Utils::Warning
    • NOX::Utils::OuterIteration
    • NOX::Utils::InnerIteration
    • NOX::Utils::Parameters
    • NOX::Utils::Details
    • NOX::Utils::OuterIterationStatusTest
    • NOX::Utils::LinearSolverDetails
    • NOX::Utils::TestDetails
    • NOX::Utils::StepperIteration
    • NOX::Utils::StepperDetails
    • NOX::Utils::StepperParameters
    • NOX::Utils::Debug

Error Handling

  • Always check return values of functions for errors.
  • In general, try to recover from errors.
  • If you must throw an exception, always print an explanation with the function name to NOX::Utils::err or NOX::Utils::perr and then throw an exception with the std::string "NOX Error". For example if utils is a NOX::Utils object,
      if (/* Error Condition */)
      {
        utils.err() << "ERROR: NOX::Epetra::Group::getNewton() - invalid Newton vector" << std::endl;
        throw "NOX Error";
      }
    

Comments

We use Doxygen for the comments. To generate the documentation. Instructions can be found in the README file in the top-level nox directory.
  • Document each class, function, and enum in the header files.
     
    • The one exception is that functions in derived objects do not need to be documented if the documentation is inherited from the base class. This should be tested in Doxygen to be sure that it works correctly.
       
  • Here's an example of documented a class. Note the formatting of the comments. It's a C-style comment. The open comment marker (/*) is followed by an exclamation mark to indicate that it's a Doxygen comment. The open and close comment markers are on lines by themselves, and the text of the comment is indented two spaces. Always include a \brief description. The long description follows. Observe the use of the formatting tags \c and \e. The \note tag is used for any special notes. The \author tag is recommended.
    /*!
      \brief Arbitrary combination of status tests.
    
      In the \c AND (see NOX::Status::Combo::ComboType) combination, the
      result is \c Unconverged (see NOX::Status::StatusType) if \e any of
      the tests is \c Unconverged. Otherwise, the result is equal to the
      result of the \e first test in the list that is either \c Converged
      or \c Failed. It is not recommended to mix \c Converged and \c
      Failed tests in an \c AND combination.
    
      In the \c OR combination, the result is \c Unconverged if \e all of
      the tests are \c Unconverged. Otherwise, it is the result of the \e
      first test in the list that is either \c Converged or \c
      Failed. Therefore, it will generally make sense to put the \c Failed
      -type tests at the end of the \c OR list.
    
      \note We always runs through all tests, even if we don't need
      to. This is useful so that the user knows which tests have and have
      not be satisfied.
    
      \author Tammy Kolda (SNL 8950)
    */
    class Combo : public Test {
    ...
    }; // class Combo
    
  • Any parameters that are used within the class must be documented in the class description and in the file NOX_Description.H on the parameters "page". Note that the name, a brief description, and the default value for each parameter is listed.
    /*!
      \brief %Newton-like solver with a line search.
    
      The following parameters are valid for this solver:
    
      - "Line Search" - Sublist of the line search parameters, passed to
        the NOX::Linesearch::Factory. Defaults to an empty list.
    
      - "Linear Solver" - Sublist of the linear solver parameters, passed
        to Abstract::Group::computeNewton(). Furthermore, the "Tolerance"
        within this list may be modified by the
        resetForcingTerm(). Defaults to an empty list.
    
      - "Forcing Term Method" - Method to compute the forcing term, i.e.,
        the tolerance for the linear solver. Defaults to ""
        (nothing). Choices are "Type 1" and "Type 2".
    
      - "Forcing Term Minimum Tolerance" - Minimum acceptable linear
        solver tolerance. Defaults to 1.0e-6.
    
      - "Forcing Term Maximum Tolerance" = Maximum acceptable linear
        solver tolerance. Default to 0.01.
    
      - "Forcing Term Alpha" - Used for the "Type 2" forcing term
        calculation. Defaults to 1.5.
    
      - "Forcing Term Gamma" - Used for the "Type 2" forcing term
        calculation. Defaults to 0.9.
    
      \author Tammy Kolda (SNL 8950), Roger Pawlowski (SNL 9233)
    */
    
    Here's a more complicated example to produce a two-tiered list.
    /*!
       The parameters must specify the type of line search as well as all
       the corresponding parameters for that line search.
    
       <ul>
       <li> "Method" - Name of the line search. Valid choices are
         <ul>
         <li> "Full Step" (NOX::Linesearch::FullStep)
         <li> "Interval %Halving" (NOX::Linesearch::Halving)
         <li> "Polynomial" (NOX::Linesearch::Polynomial)
         <li> "More'-Thuente" (NOX::Linesearch::MoreThuente)
         </ul>
       </ul>
     */
    
  • Constants and enums can generally be described with simple \brief comments. Those can be formatted in either of two ways, as follows.
      /*!
        \brief The test can be either the AND of all the component
        tests, or the OR of all the component tests.
      */
      enum ComboType {AND, OR};
    
      //! Constructor
      Combo(ComboType t = OR);
    
  • Doxygen does automatically cross-linking, which is very convenient. However, sometimes it cross-links when you don't intend for it to. For example, the following line would automatically generate a link from the word Newton to the NOX::Solver::Newton class.
    //! Newton-like solver with a line search.
    
    To prevent that automatic link, insert a percent sign (%) immediately before the word that is causing the link. For example,
    //! %Newton-like solver with a line search.