This document gives a short overview of the current GPUMD code structure and the main extension points. It focuses on the gpumd and nep executables.
GPUMD uses GNU Make.
For NVIDIA GPUs:
cd src
makeThe default build produces:
gpumd
nep
The CUDA architecture can be changed when needed, for example:
make CUDA_ARCH="-arch=sm_89"For AMD GPUs:
cd src
make -f makefile.hipThe makefiles are the reference for compiler flags, GPU architecture settings, and linked libraries.
The main source directories are:
| Directory | Purpose |
|---|---|
src/main_gpumd/ |
gpumd entry point, input dispatch, and top-level simulation flow |
src/model/ |
Atoms, simulation box, groups, and related data structures |
src/force/ |
Potential models and force evaluation |
src/integrate/ |
Ensembles, integration, constraints, and motion control |
src/measure/ |
Run-time actions, measurements, dumps, and related operations |
src/minimize/ |
Energy minimization |
src/phonon/ |
Phonon calculations |
src/mc/ |
Monte Carlo functionality |
src/utilities/ |
Shared utilities, GPU wrappers, input helpers, and containers |
src/main_nep/ |
nep executable |
Files added under an existing source directory are normally collected automatically by the makefiles.
The top-level execution is controlled by Run in src/main_gpumd/.
The main objects are:
Run
├── Atom
├── Velocity
├── Box
├── Group[]
├── Force
├── Integrate
└── Measure
Their roles are deliberately separated:
Runcontrols the overall simulation flow and dispatches top-level input commands.Forceowns the activePotentialobjects and evaluates forces, energies, and virials.Integrateowns the activeEnsembleand controls integration-related operations.Measureowns run-timeActionobjects.
run.in is first parsed by RunInput. Each keyword is then sent to the module that owns the corresponding functionality.
In simplified form:
run.in
|
v
Run
+--> Force
+--> Integrate
+--> Measure
+--> top-level operations handled by Run
A new keyword should normally be implemented in the module responsible for its behavior rather than directly in Run.
The main MD loop follows this structure:
Integrate::initialize()
Measure::pre_run()
Force::compute()
Measure::setup_force()
for each step:
Integrate::compute1()
Measure::post_integrate1()
Measure::pre_force()
Force::compute()
Measure::post_force()
Integrate::compute2()
Measure::end_of_step()
Measure::post_run()
Integrate::finalize()
This lifecycle determines where new run-time functionality should be placed.
Potential models derive from Potential and are managed by Force.
A new potential should keep model-specific data and reusable GPU workspaces inside the potential object and implement the required compute() interface. Expensive setup, allocation, or data conversion should not be repeated inside every force evaluation unless necessary.
Ensembles derive from Ensemble and are managed by Integrate.
The main integration interfaces are compute1() and compute2(). Run-level initialization and cleanup can be implemented through the corresponding ensemble lifecycle functions.
Algorithm-specific state should remain inside the derived ensemble class.
Many commands used during MD are implemented as classes derived from Action and managed by Measure.
Available hooks include:
pre_run
setup_force
post_integrate1
pre_force
post_force
end_of_step
post_run
Use the lifecycle hook that matches the intended operation instead of adding special cases to the main MD loop.
Functionality belongs directly in Run only when it changes the overall execution flow or performs a system-level operation, such as starting a run, minimization, or major reconfiguration of the simulated system.
GPU_Vector is the standard container for many persistent device arrays.
For performance-sensitive code:
- reuse persistent GPU memory when possible;
- avoid unnecessary host-device transfers and synchronization;
- avoid repeated allocation inside MD steps;
- use the GPU abstraction layer in
src/utilities/gpu_macro.cuhfor code shared by CUDA and HIP; - keep backend-specific code localized when a common implementation is not practical.
The main ownership pattern for polymorphic objects uses std::unique_ptr, for example for potentials, ensembles, and actions.
Input lines are tokenized by RunInput before being passed to individual modules.
When adding a keyword:
- validate the number and type of arguments before use;
- use the existing parsing and error utilities;
- keep parsing in the module that owns the keyword;
- keep the implementation synchronized with the user manual.
Common error utilities are defined in src/utilities/error.cuh.
The nep executable is implemented mainly under src/main_nep/ and shares utilities with GPUMD.
Its top-level structure is compact:
Parameters
|
v
Fitness
|
v
SNES
Parameters handles the input configuration, Fitness contains the model/data evaluation logic, and SNES performs optimization.
Changes to nep.in, model formats, or training data formats should remain synchronized with the NEP manual.
The basic internal units are:
- energy: eV
- length: Å
- mass: Dalton
- temperature: K
- charge: elementary charge
User-facing changes to keywords, file formats, units, or physical definitions should be reflected in the documentation under doc/.