A real-time GPU thermal management utility that dynamically controls NVIDIA GPU power limits using a PID controller based on real-time temperature monitoring.
- GpuPowerControl v1.0 - Latest release
- Download
- Description
- Features
- How It Works
- Installation
- Usage
- Configuration
- Architecture
- Testing
- Contributing
- License
- Acknowledgments
GpuPowerControl solves the problem of automatic thermal management for consumer NVIDIA GPUs that lack native temperature limiting. Instead of static power limits or manual adjustment, it uses a PID (Proportional-Integral-Derivative) control loop to smoothly regulate GPU power in response to temperature changes.
When the GPU temperature exceeds a configurable trigger threshold, the PID controller engages and reduces power to cool the GPU. Once temperature returns to safe levels, full power is gradually restored.
- PID-Based Thermal Control: Configurable Kp/Ki/Kd gains for precise temperature regulation
- Predictive Triggers: Derivative-based temperature prediction activates control before hitting threshold
- Emergency Safety: Automatic minimum power enforcement at critical temperatures (default 90°C)
- Real-Time Dashboard: Spectre.Console TUI with live metrics, PID breakdown, and state visualization
- Toast Notifications: Windows 11 toast notifications for thermal events
- Data Export: CSV and JSON logging of thermal data
- Simulated GPU Mode: Test without hardware using a physics-based thermal model
- NVIDIA NVML Integration: Direct GPU hardware access via NVML P/Invoke
The thermal controller operates in distinct states:
[1] IDLE - Slow temperature monitoring
│
▼
[2] TRIGGER - Temperature or derivative exceeds threshold
│ (Safety: temp ≥ 80°C OR Predictive: rising fast)
▼
[3] CONTROLLING - PID controller adjusts power limit
│
▼
[4] EMERGENCY - Temperature ≥ 90°C
│ Forces minimum power (150W) for 5 seconds
▼
[5] EMERGENCY RECOVERY - Rate-limited PID recovery (5 W/s)
│
▼
[6] STABLE - Temperature ≤ Target - Hysteresis AND power at max
│ Returns to idle monitoring
| From | To | Condition |
|---|---|---|
| Idle | Controlling | Temp ≥ 80°C or predictive trigger fires |
| Controlling | Emergency | Temp ≥ 90°C |
| Emergency | Recovery | Emergency hold timer expires |
| Recovery | Controlling | Normal PID resumes |
| Controlling/Recovery | Stable | Temp ≤ TargetTemp - ExitHysteresis AND power at max |
- Consecutive Read Failures: After 5 failed temperature reads, forces emergency temperature to trigger safety path
- Power Increase Rate Limit: Maximum 15 W/s during normal PID, 5 W/s during emergency recovery
- Power Increase Gate: Blocks power increases while temperature is at or above trigger threshold
- Minimum Delta Gate: Prevents tiny power reductions from noise (3W near target, 10W far from target)
- Minimum Adjustment Interval: 2.5s cooldown between power changes (bypassed if temp rising ≥ 2°C/s)
The PID controller calculates power adjustments using:
- Proportional (Kp): Responds to current temperature error
- Integral (Ki): Accumulates past temperature errors
- Derivative (Kd): Responds to temperature rate of change
Power Limit = Base Power - Kp·error - Ki·∫error·dt - Kd·d(error)/dt
- .NET 10 SDK (https://dotnet.microsoft.com/download)
- NVIDIA GPU with up-to-date drivers (for NVML support)
- Windows 10/11 (for toast notifications)
# Clone the repository
git clone https://github.com/driderr/GpuPowerControl.git
cd GpuPowerControl
# Restore dependencies
dotnet restore
# Build
dotnet build
# Run with real GPU
dotnet run
# Run with simulated GPU (no hardware needed)
dotnet run --simulate
# Build standalone executable
build-exe.cmd# Use simulated GPU device
dotnet run --simulate
# Run with a specific scenario
dotnet run --simulate --scenario Default
dotnet run --simulate --scenario Idle
dotnet run --simulate --scenario Spike
dotnet run --simulate --scenario SustainedLoad
dotnet run --simulate --scenario Emergency
dotnet run --simulate --scenario GradualWarmup
# Custom simulation parameters
dotnet run --simulate --scenario Default --base-temp 40 --peak-temp 85 --seed 42# Force the startup power-control check to fail, to demonstrate the failure banner
# on any machine (no real power limit is ever written). Values: not-supported
# (firmware-owned budget), not-permitted (run as admin), exception (probe throws).
GpuPowerControl.exe --probe-failure
GpuPowerControl.exe --probe-failure not-supported
GpuPowerControl.exe --probe-failure=not-permitted
GpuPowerControl.exe --simulate --probe-failure exception
# Post a test error/warning to the dashboard Event Log
GpuPowerControl.exe --test-error
# Disable Windows toast notifications
GpuPowerControl.exe --no-notifications| Scenario | Description | Peak Temp |
|---|---|---|
Default |
Rich multi-layer pattern with slow/fast cycles and periodic spikes | 80°C |
Idle |
Low workload, stays cool | 48°C |
Spike |
15s ramp → 10s hold → 20s cooldown (45s cycle) | 85°C |
SustainedLoad |
Gradual warmup then sustained full workload | 82°C |
Emergency |
Rapid 8s climb to extreme temperatures | 120°C |
GradualWarmup |
2-minute linear warmup to full load | 80°C |
When the console dashboard is active:
- ESC: Exit application
- P: Adjust PID coefficients interactively
- T: Test error/warning console output
- L: Toggle log panel visibility
- J: Toggle JSON logging
- C: Toggle config panel visibility
GpuPowerControl uses ThermalControllerConfig for all settings. Key parameters:
| Parameter | Default | Description |
|---|---|---|
TriggerTemp |
80°C | Temperature to activate PID control |
TargetTemp |
75°C | Target temperature for PID controller |
EmergencyTemp |
90°C | Critical temperature for emergency power limit |
DefaultMinPower |
150W | Operating power floor for PID control (clamped to the GPU's hardware range) |
DefaultMaxPower |
600W | Operating maximum power limit: initial limit and the value restored when control disengages (clamped to the GPU's hardware range) |
Kp |
20.0 | Proportional gain |
Ki |
0.5 | Integral gain |
Kd |
2.5 | Derivative gain |
IdleSleepMs |
1500ms | Sleep interval when idle |
ControllingSleepMs |
250ms | Sleep interval when controlling |
The configured power limits are clamped to the range reported by the GPU (NVML) at startup, so desktop-style defaults on a small-GPU laptop can never push the PID controller past what the hardware supports:
- Operating maximum =
min(defaultMaxPower, hardware max)— the initial power limit and the value restored when control disengages. - Operating minimum =
defaultMinPowerclamped to[hardware min, operating maximum]— the floor for normal PID control. - Emergency — when temperature hits
EmergencyTemp, power is forced to the GPU's hardware minimum (regardless ofdefaultMinPower) so cooling always has maximum headroom.
The startup banner prints both the hardware range and the effective operating range, with a note whenever a configured value is clamped. If defaultMaxPower is below the GPU's hardware minimum, the app refuses to start with an actionable error. If defaultMinPower ends up at or above the effective maximum (a zero-width envelope), the app starts but prints a warning that PID control cannot lower the power limit until defaultMinPower is lowered.
If the app ever stops unexpectedly, a full stack trace is written to crash-log.txt next to the executable — attach that file when reporting a crash.
GpuPowerControl supports an external JSON configuration file that allows you to customize all thermal control parameters without recompilation. Place appsettings.json in the same directory as GpuPowerControl.exe.
- Copy the sample
appsettings.json(included in the repository root) next to the executable - Edit the values to match your GPU model and thermal preferences
- Restart the application to apply changes
The configuration loader strips both line comments (//) and block comments (/* */) before parsing the JSON. This allows you to annotate your configuration file with explanations for each setting:
{
// Thermal thresholds
"thermal": {
"triggerTemp": 80,
"targetTemp": 75
}
}The appsettings.json file contains the following sections:
| Section | Description |
|---|---|
thermal |
Temperature thresholds (trigger, target, emergency, hysteresis, predictive triggering) |
pid |
PID controller tuning (Kp, Ki, Kd, integral clamping) |
power |
Power limits and gating (min/max power, rate limits, adjustment intervals) |
timing |
Control loop timing (PID interval, sleep durations) |
faultTolerance |
Error handling (max consecutive read failures) |
Command-line arguments override the corresponding appsettings.json values. This allows you to quickly test different configurations without editing the file.
Program.cs (entry point)
├── ThermalController (orchestration)
│ ├── IGpuDevice (hardware abstraction)
│ │ ├── NvmlGpuDevice (NVML P/Invoke)
│ │ ├── SimulatedGpuDevice (physics-based thermal model)
│ │ └── MockGpuDevice (test mock)
│ ├── PidController (control algorithm)
│ ├── TriggerEvaluator (trigger logic)
│ └── Events: OnStateChange, OnStep
│
├── DashboardDataProvider (event consumer)
│ └── IDashboardDataProvider
│ ├── ConsoleDashboard (Spectre.Console UI)
│ ├── JsonPublisher (JSON file output)
│ ├── CsvExporter (CSV export)
│ └── ErrorConsole (error display)
│
└── ToastNotificationService (Windows notifications)
| Directory | Description |
|---|---|
Core/ |
Thermal control logic (ThermalController, PidController, TriggerEvaluator, config) |
Dashboard/ |
Console UI components (Spectre.Console rendering, CSV/JSON export, key handling) |
Devices/ |
GPU device abstractions (NVIDIA NVML, simulated GPU, test mocks) |
Notifications/ |
Windows toast notification service |
Nvml/ |
NVIDIA NVML P/Invoke bindings |
Profiling/ |
Performance profiling tools and scripts |
GpuPowerControl.Tests/ |
xUnit test suite (unit + integration tests) |
The project includes xUnit tests:
# Run all tests
dotnet test
# Run with minimal output
dotnet test --logger "console;verbosity=minimal"Test structure:
- Unit tests: PID controller, trigger evaluator, dashboard components
- Integration tests: End-to-end thermal control loop
- Mocks:
MockGpuDevicefor testing without hardware
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Run
dotnet testto ensure all tests pass - Commit and push your branch
- Submit a pull request
This project is licensed under the MIT License.
- Built with Spectre.Console for the TUI
- Uses CommunityToolkit.MSIX for toast notifications
- NVIDIA NVML for GPU hardware access