A dimensionally safe C++ header-only mechanics and electromagnetics simulation library.
- Description
- Features
- Positioning
- Accuracy Verification
- Requirements
- Installation
- Quick Start
- Project Struct
- FAQ
- Known Limitations
- Contributing
- Acknowledgments
Phyth is a C++ simulation library designed for numerical method researchers. While maintaining lightweight and safety, it breaks down the simulation process into replaceable independent components, allowing you to change integrators or constraint solvers like building blocks, without being troubled by the complex architecture of large engines.
-
Type-safe physical quantities: Literals like
1_kg,1_mmake code concise and readable, while the type system catches dimension mismatches at compile time—assigning meters to seconds, or using force as mass, fails before runtime, eliminating unit-confusion bugs. -
Semantic unit conversion: Units of the same dimension (e.g., joules and newton-meters) retain distinct semantic labels. Conversions preserve meaning, and output formatting automatically distinguishes
JfromN*m—no redundant re-declaration, no ambiguity. -
Straightforward simulation workflow: Create particles → apply constraints → define forces → loop over integration and correction. The simulation loop follows the same sequence as a physics textbook, making the learning curve remarkably shallow.
-
Interchangeable components: Constraint solvers (e.g.,
DistanceConstraint) and force models (e.g.,GravityFormula) are designed as pluggable, independent components—swap integrators or implement custom algorithms without restructuring the entire framework. -
Zero dependencies, lightweight core: The core library has no third-party dependencies and compiles to a small binary, suitable for embedded environments, teaching demonstrations, and research scenarios that demand deep customization.
-
Built-in validation hooks: Energy monitoring interfaces (e.g.,
GetTotalEnergy()) are available out of the box, making it easy to verify physical conservation laws during simulation and simplifying debugging and experiment validation.
Existing physics engines sit at two extremes:
| Industrial Engines | Scientific Engines | |
|---|---|---|
| Speed | ✅ Fast | |
| Robustness | ✅ Battle-tested | |
| Transparency | ❌ Black box (stages fused) | ✅ Exposed internals |
| Modifiability | ❌ Fixed solver, hard to swap | |
| Dependencies | ✅ Minimal | ❌ Heavy dependency graph |
Phyth sits right in the middle:
Transparent like a scientific engine, lightweight like an industrial one.
It gives researchers the freedom to develop new numerical methods, without the friction of production-level complexity or academic-level baggage.
- More transparent than industrial engines — simulation stages are exposed as replaceable components, so you can swap integrators or constraint solvers without fighting the framework.
- More lightweight than scientific engines — zero dependencies, minimal codebase, easy to build, modify, and experiment with.
In short, Phyth gives researchers the freedom to develop new numerical methods, without the friction of production-level complexity or academic-level baggage.
The first image is the analysis (based on the Quick Start code with detailed output):
The latest image below shows the data analysis of the double pendulum simulation (dt=2e-4 s and sample 1000 points), and the calculated relative fluctuation is about 1.37e-3%(1.37e-5):
- C++17 or higher
- Compiler: GCC 9+, Clang 10+, MSVC 19.20+ (Visual Studio 2019+)
Phyth is a header-only library with no third-party dependencies and no platform-specific system calls. It relies exclusively on the C++ standard library, making it portable across platforms.
| Platform | Compiler | Status |
|---|---|---|
| Linux | GCC 9+, Clang 10+ | ✅ CI-tested |
| Windows | MSVC 19.20+, MinGW | ✅ Verified |
| macOS | Apple Clang 13+ |
Note: If you use Phyth on an untested platform and encounter issues, please file an issue — we'll help investigate.
git clone https://github.com/Bg-cr/Phyth.gitAdd the include/ directory to your compiler's header search path:
#include <Phyth/Phyth.hpp>Done. No compilation required.
The following code demonstrates single pendulum motion under gravity. The output results consist of the particle and total mechanical energy for each time step, which are used to verify numerical stability.
#include "Phyth/Phyth.hpp"
#include <iostream>
using namespace Phyth;
using namespace Phyth::Mechanics;
constexpr Quantity<Second> SIMULATION_TIME = 2.73_s;
int main() {
Config::dt = 1_s / 1000;
const auto anchor = std::make_shared<Particle>(1_kg, Vector3<Quantity<Meter>>{0_m, 0_m, 0_m});
anchor->SetFixed(true);
const auto pendulum = std::make_shared<Particle>(1_kg, Vector3<Quantity<Meter>>{1_m, 0_m, 0_m});
const DistanceConstrainer constraint{anchor, pendulum};
pendulum->SetComputeForcesFunction([](Particle *p) {
p->ApplyForce({0_N, -1_kg * Consts::g, 0_N});
});
for (Quantity<Second> t = 0_s; t < SIMULATION_TIME; t += Config::dt) {
pendulum->Integrate();
constraint.Correct();
std::cout << t << " " << *pendulum << " " << pendulum->GetTotalEnergy(-1_m) << std::endl;
}
return 0;
}Expected output example
0 s Particle{pos=(1 m, -4.90332e-06 m, 0 m), mass=1 kg} 9.80665 J
0.001 s Particle{pos=(1 m, -1.96133e-05 m, 0 m), mass=1 kg} 9.80665 J
0.002 s Particle{pos=(1 m, -4.41299e-05 m, 0 m), mass=1 kg} 9.80665 J
0.003 s Particle{pos=(1 m, -7.84532e-05 m, 0 m), mass=1 kg} 9.80665 J
0.004 s Particle{pos=(1 m, -0.000122583 m, 0 m), mass=1 kg} 9.80665 J
0.005 s Particle{pos=(1 m, -0.00017652 m, 0 m), mass=1 kg} 9.80665 J
0.006 s Particle{pos=(1 m, -0.000240263 m, 0 m), mass=1 kg} 9.80665 J
0.007 s Particle{pos=(1 m, -0.000313813 m, 0 m), mass=1 kg} 9.80665 J
0.008 s Particle{pos=(1 m, -0.000397169 m, 0 m), mass=1 kg} 9.80665 J
0.009 s Particle{pos=(1 m, -0.000490332 m, 0 m), mass=1 kg} 9.80665 J
0.01 s Particle{pos=(1 m, -0.000593302 m, 0 m), mass=1 kg} 9.80665 J
0.011 s Particle{pos=(1 m, -0.000706079 m, 0 m), mass=1 kg} 9.80665 J
0.012 s Particle{pos=(1 m, -0.000828662 m, 0 m), mass=1 kg} 9.80665 J
0.013 s Particle{pos=(1 m, -0.000961052 m, 0 m), mass=1 kg} 9.80665 J
0.014 s Particle{pos=(0.999999 m, -0.00110325 m, 0 m), mass=1 kg} 9.80665 J
0.015 s Particle{pos=(0.999999 m, -0.00125525 m, 0 m), mass=1 kg} 9.80665 J
...
Note: The above output is formatted for readability. Actual raw output may show slight variations in the last 3–4 significant digits due to floating-point rounding.
Verification method
If the above code is run, the total energy at each moment should be approximately 9.80665 J.
In fact, after running and analysis the data, it can be concluded that the relative fluctuation within 2.73s is approximately 1.32e-5,
which confirms that the integrator and constraint solver are working properly.
For specific analysis of the above program, see Accuracy Verification.
- Set
Config::dt(time step) - Add elements (e.g., Particle)
- Loop:
Integrate()updates every elementCorrect()applies Velocity Correct to particles.- process data
Below is the project tree diagram and introduction:
F:.
├─examples
├─include
│ └─Phyth
│ ├─Core
│ ├─Elements
│ │ ├─Electromagnetics
│ │ │ └─ChargeSources
│ │ └─Mechanics
│ ├─Physical
│ ├─System
│ └─Tools
└─Phyth
example/— Examples, likeSinglePendulum.cppPhyth/include/Core/— Compile-time dimensional analysis systemElements/— Physics building blocks (mechanics + electromagnetics)Physical/— Constants likeg,G,epsilon_0Tools/— Tools such asVector3and helper functions
Q: Is dimensionality checking performed during compilation or at runtime? Is it zero-cost?
A: Compile-time. In Release mode, Quantity<T, Dim> has a memory layout identical to that of a native double, with zero runtime overhead.
Q: Can units in a parallel relationship be transformed into each other?
A: Yes. For example, for N*m and J, the dimensional system will automatically deduce the units,
while the unit labels will retain the original semantics.
The display format is managed by UnitsRegistry to ensure that the physical meaning is not lost during output.
Q: Is electromagnetic field an analytical solution or a numerical solution?
Numerical integration. The electric field and potential are approximately calculated by dividing a rectangular prism into volume elements and superimposing Coulomb fields on each volume element. The accuracy is comparable to the analytical formula, but the speed is not very fast. This is the implementation decision of the current version - prioritizing code clarity over computational efficiency.
Q: What physics can be considered at present?
- Mechanics: Particle, Spring, Distance Constraint (Single/Double Pendulum Verified)
- Electromagnetics: Calculation of point/line/surface/bulk charges, electric dipoles, and electrostatic fields, etc.
- Performance: Electromagnetic field calculations use direct summation over volume elements . For large-scale 3D simulations, this may be slow.
- No GPU acceleration: All computations are CPU-bound in the current version.
- No collision detection: Phyth focuses on dynamics and field calculations, not contact mechanics.
- No automatic differentiation: Gradients must be computed manually if needed for optimization.
We welcome contributions from researchers, students, and developers!
- Report bugs or request features → Use our Issue Templates
- Submit code changes → Fork the repo, create a branch, and open a Pull Request
- Suggest new physics modules → Start a Discussion
- All new formulas must include validation tests (compare against analytical solutions)
- Follow Google C++ Style Guide
- Write descriptive commit messages
See CONTRIBUTING.md for full details.
Phyth's dimensional analysis system draws inspiration from:
- Boost.Units — compile-time dimensional analysis in C++
- The foundational work on dimensional analysis by Buckingham and Bridgman
The validation framework was inspired by best practices in computational physics education.

