Developer Guide

This guide is for developers who want to contribute to the OpenThermo project. It covers the codebase structure, development setup, coding standards, and contribution guidelines.

Project Overview

Architecture

OpenThermo is a C++17 application designed for high-performance molecular thermochemistry calculations. The codebase follows a modular architecture with clear separation of concerns:

Key Components

Main Entry Point (main.cpp)
  • Orchestrates the entire calculation workflow

  • Processes command-line arguments

  • Coordinates file loading, calculations, and output

File Loading (loadfile.cpp/h)
  • Parses quantum chemistry output files

  • Supports multiple formats (Gaussian, ORCA, GAMESS, NWChem, CP2K, VASP, Q-Chem)

  • Extracts geometry, frequencies, and electronic energies

Thermochemistry Calculations (calc.cpp/h)
  • Implements statistical mechanics methods

  • Handles partition function calculations

  • Applies low-frequency treatment methods

Symmetry Analysis (symmetry.cpp/h)
  • Detects molecular point groups

  • Calculates rotational symmetry numbers

  • Handles linear molecule detection

Atomic Mass Handling (atommass.cpp/h)
  • Manages atomic mass assignments

  • Supports isotopic substitution

  • Handles custom mass modifications

Utility Functions (util.cpp/h)
  • Mathematical utilities

  • String processing

  • File I/O helpers

OpenMP Configuration (omp_config.h)
  • Thread management

  • HPC scheduler detection

  • Parallelization strategy selection

Key Design Principles

Modularity
  • Each module has a single responsibility

  • Clear interfaces between components

  • Easy to test and maintain

Performance
  • Optimized C++17 implementation

  • Optional OpenMP parallelization

  • Efficient algorithms for large systems

Accuracy
  • Rigorous statistical mechanics implementation

  • Multiple low-frequency treatment methods

  • Comprehensive validation

Usability
  • Intuitive command-line interface

  • Extensive help system

  • Configuration file support

Development Setup

Prerequisites

Required Tools:

  • C++ Compiler: Clang 6.0+, GCC 7.0+, or Intel C++ Compiler 18.0+

  • Build System: GNU Make 3.8.1+ or CMake 3.26.5+

  • Standard Library: C++17 standard library

  • Git: Version control system

Optional Tools:

  • Doxygen: 1.8+ (API documentation generation)

  • Python: 3.6+ (for test automation scripts)

  • OpenMP: For parallel computation support

  • Valgrind: Memory debugging

  • Clang-Tidy: Code analysis

Getting the Source Code

# Clone the repository
git clone https://github.com/lenhanpham/OpenThermo.git
cd OpenThermo

# Create a development branch
git checkout -b feature/your-feature-name

Building for Development

Debug Build:

# Build with debug symbols and AddressSanitizer
make debug

# Or with CMake
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
make

Release Build:

# Optimized release build
make release

# Or with CMake
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make

Development Build with OpenMP:

# Build with OpenMP support
make OPENMP=1

# Or with CMake
mkdir build && cd build
cmake -DENABLE_OPENMP=ON ..
make

Testing

Running Tests:

# Build and run tests
make test

# Run with verbose output
python3 tests/run_tests.py --verbose

# Run specific test by ID
python3 tests/run_tests.py --test gaussian_h2co_freq

Test Coverage:

The suite includes 27 tests organized by category:

  • Format coverage (10 tests) — one per supported QC program at default settings

  • Option coverage (9 tests) — different temperatures, pressures, low-frequency methods, scaling, concentration, imaginary frequency handling, and condensed phase mode

  • Output file tests (3 tests) — verify .otm, .vibcon, and temperature scan (.UHG/.SCq) file generation

  • Error handling (2 tests) — non-existent and invalid input files

  • Batch/ensemble (1 test) — conformer ensemble with Boltzmann averaging

Regenerating Reference Values:

After modifying calculation logic:

make test-generate

This runs OpenThermo on all test inputs and writes the results to tests/reference_values.json.

Code Quality Tools

Static Analysis:

# Run clang-tidy
clang-tidy src/*.cpp -- -std=c++17 -Isrc

# Run cppcheck
cppcheck --enable=all --std=c++17 src/

Code Formatting:

# Format code with clang-format
find src/ -name "*.cpp" -o -name "*.h" | xargs clang-format -i

# Check formatting
find src/ -name "*.cpp" -o -name "*.h" | xargs clang-format --dry-run -Werror

Documentation

Building Documentation:

# Install Sphinx
pip install sphinx sphinx-rtd-theme furo

# Build HTML documentation
cd docs
make html

# View documentation
firefox _build/html/index.html

API Documentation:

OpenThermo uses Doxygen-style comments for API documentation. To generate API docs:

# Generate Doxygen documentation (if Doxyfile exists)
doxygen Doxyfile

# View API docs
firefox doxygen/html/index.html

Coding Standards

Code Style

Naming Conventions:

// Classes and structs
class ChemicalSystem;
struct CalculationParams;

// Functions and methods
void calculate_thermodynamics(const ChemicalSystem& system);
double compute_partition_function(double temperature);

// Variables
int atom_count;
std::string input_file;

// Constants
const double R = 8.3144648;           // Gas constant (J/mol/K)
const double kb = 1.3806503e-23;      // Boltzmann constant (J/K)

// Member variables (with m_ prefix)
class MyClass {
private:
    int m_atom_count;
    std::string m_input_file;
};

File Organization:

  • Headers (.h): Class declarations, function prototypes, constants

  • Implementations (.cpp): Function definitions, implementation details

  • One class per file when possible

  • Related functionality grouped in modules

Documentation Standards

Doxygen Comments:

OpenThermo uses Doxygen-style comments for all public APIs:

/**
 * @file calc.cpp
 * @brief Thermochemistry calculation functions
 * @author Le Nhan Pham
 * @date 2025
 *
 * This file contains functions for calculating thermodynamic properties
 * using statistical mechanics methods.
 */

/**
 * @brief Calculate Gibbs free energy
 *
 * Computes the Gibbs free energy using partition functions and
 * statistical mechanics relationships.
 *
 * @param temperature Temperature in Kelvin
 * @param enthalpy Enthalpy in atomic units
 * @param entropy Entropy in J/mol/K
 * @return Gibbs free energy in atomic units
 *
 * @note This function assumes ideal gas behavior
 * @see calculate_enthalpy()
 * @see calculate_entropy()
 */
double calculate_gibbs_free_energy(double temperature, double enthalpy, double entropy);

Inline Comments:

// Use comments for complex logic
if (frequency < threshold) {
    // Apply low-frequency treatment method
    apply_low_frequency_treatment(frequency, method);
}

// Use TODO comments for future improvements
// TODO: Optimize partition function calculation for large systems

Error Handling

Exception Safety:

try {
    // Operation that might fail
    load_molecular_data(filename);
} catch (const std::invalid_argument& e) {
    // Handle invalid arguments
    std::cerr << "Invalid argument: " << e.what() << std::endl;
    return 1;
} catch (const std::runtime_error& e) {
    // Handle runtime errors
    std::cerr << "Runtime error: " << e.what() << std::endl;
    return 2;
} catch (const std::exception& e) {
    // Handle all other exceptions
    std::cerr << "Unexpected error: " << e.what() << std::endl;
    return 3;
}

Return Codes:

/**
 * @return 0 on success
 * @return 1 on general error
 * @return 2 on invalid arguments
 * @return 3 on resource unavailable
 * @return 4 on operation interrupted
 */
int process_molecular_data(const std::string& input_file);

Memory Management

RAII Pattern:

class FileProcessor {
public:
    FileProcessor(const std::string& filename)
        : m_file(filename) {
        if (!m_file.is_open()) {
            throw std::runtime_error("Failed to open file");
        }
    }

    ~FileProcessor() {
        // Automatic cleanup
        if (m_file.is_open()) {
            m_file.close();
        }
    }

private:
    std::ifstream m_file;
};

Smart Pointers:

// Use unique_ptr for exclusive ownership
std::unique_ptr<ChemicalSystem> system = std::make_unique<ChemicalSystem>();

// Use shared_ptr for shared ownership
std::shared_ptr<CalculationParams> params = std::make_shared<CalculationParams>();

Thread Safety

OpenMP Parallelization:

OpenThermo uses OpenMP for parallel computation. When adding parallel code:

#ifdef _OPENMP
#pragma omp parallel for
for (size_t i = 0; i < frequencies.size(); ++i) {
    // Thread-safe operations
    results[i] = calculate_contribution(frequencies[i], temperature);
}
#endif

Threading Guidelines:

  • Document thread safety guarantees

  • Use appropriate synchronization primitives

  • Avoid global mutable state

  • Test concurrent access patterns

Contributing

Development Workflow

1. Choose an Issue:

Check available issues on GitHub.

2. Create a Branch:

# Create and switch to feature branch
git checkout -b feature/descriptive-name

# Or for bug fixes
git checkout -b bugfix/issue-number-description

3. Make Changes:

  • Follow coding standards

  • Add tests for new functionality

  • Update documentation as needed

  • Ensure all tests pass

4. Test Your Changes:

# Build and test
make debug
make test

# Run code quality checks
clang-tidy src/*.cpp -- -std=c++17 -Isrc

5. Commit Your Changes:

# Stage your changes
git add .

# Commit with descriptive message
git commit -m "feat: add new feature description

- What was changed
- Why it was changed
- How it was tested"

6. Push and Create Pull Request:

# Push your branch
git push origin feature/your-feature-name

# Create pull request on GitHub

Pull Request Guidelines

PR Title Format:

type(scope): description

Types: feat, fix, docs, style, refactor, test, chore

PR Description Template:

## Description
Brief description of the changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing performed

## Checklist
- [ ] Code follows style guidelines
- [ ] Documentation updated
- [ ] Tests pass
- [ ] No breaking changes

Code Review Process

Review Checklist:

  • [ ] Code follows established patterns

  • [ ] Appropriate error handling

  • [ ] Thread safety considerations (if applicable)

  • [ ] Performance implications

  • [ ] Documentation updated

  • [ ] Tests included

  • [ ] No security vulnerabilities

Review Comments:

  • Be constructive and specific

  • Suggest improvements, don’t just point out problems

  • Reference coding standards when applicable

  • Acknowledge good practices

Testing Guidelines

Unit Testing

Test Structure:

OpenThermo uses a regression test suite. When adding new features:

# tests/run_tests.py
def test_new_feature():
    """Test new feature functionality."""
    result = run_openthermo("test_input.log", ["-new-option", "value"])
    assert result["gibbs_free_energy"] == expected_value

Running Tests:

# Run all tests
make test

# Run specific test
python3 tests/run_tests.py --test test_name

Integration Testing

End-to-End Tests:

The regression test suite includes integration tests that verify:

  • Complete calculation workflows

  • Multiple input formats

  • Various calculation methods

  • Output file generation

Performance Testing

Benchmarking:

# Profile application
valgrind --tool=callgrind OpenThermo molecule.log

# Memory profiling
valgrind --tool=massif OpenThermo molecule.log

Continuous Integration

CI/CD Pipeline

Automated Testing:

OpenThermo uses GitHub Actions for CI/CD:

  • Build: Compile on multiple platforms (Linux, macOS, Windows)

  • Test: Run regression test suite

  • Sanitizers: AddressSanitizer and ThreadSanitizer builds

  • OpenMP: Test with and without OpenMP support

GitHub Actions Workflow:

Tests run automatically on every push via GitHub Actions:

  • Linux and macOS: Build + regression tests (serial and OpenMP at 1 and 4 threads)

  • Windows: MSYS2/MinGW build + regression tests (serial and OpenMP)

  • AddressSanitizer: Separate build with ASan on Linux to detect memory errors

  • ThreadSanitizer: OpenMP build with TSan on Linux to detect data races

Release Process

Version Numbering

Semantic Versioning:

MAJOR.MINOR.PATCH

- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes (backward compatible)

Release Checklist:

  • [ ] Update version in src/version.h

  • [ ] Update CHANGELOG.md (if present)

  • [ ] Update documentation

  • [ ] Create release branch

  • [ ] Run full test suite

  • [ ] Create GitHub release

  • [ ] Tag release

Release Commands:

# Create release branch
git checkout -b release/v0.001.5

# Update version in version.h
# Commit and tag
git add src/version.h
git commit -m "Release v0.001.5"
git tag -a v0.001.5 -m "Release v0.001.5"

# Push release
git push origin release/v0.001.5
git push origin v0.001.5

Support and Communication

Communication Channels:

  • GitHub Issues: Bug reports and feature requests

  • GitHub Discussions: General questions and discussions

  • Pull Request Comments: Code review discussions

Getting Help:

  • Check existing issues and documentation first

  • Use descriptive titles for issues

  • Provide minimal reproducible examples

  • Include system information and versions

Community Guidelines:

  • Be respectful and constructive

  • Help newcomers learn and contribute

  • Follow the code of conduct

  • Acknowledge contributions from others

This developer guide provides comprehensive information for contributing to the OpenThermo project. Following these guidelines ensures high-quality, maintainable code that benefits the entire community.