JoshuaDavis icon

nvidia-fan-control

JoshuaDavis | PRO | 02/18/26 06:50:54 PM UTC | 0 ⭐ | 11743 👁️ | Never ⏰ | []
Python |

2.91 KB

|

None

|

0 👍

/

0 👎

#!/usr/bin/env python3
"""
NVIDIA Aggressive Fan Control Daemon
RTX Pro 6000 Blackwell - Gaming-style fan curve
Runs headless, no X11 required.
"""
 
import pynvml
import time
import logging
import sys
 
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s %(message)s',
    handlers=[logging.StreamHandler(sys.stdout)]
)
log = logging.getLogger(__name__)
 
# --- Fan curve: (temp_C, fan_pct) ---
# More aggressive than stock workstation defaults.
# By 70C fans are already at 80%, by 80C they're maxed.
FAN_CURVE = [
    (0,  30),
    (45, 40),
    (55, 55),
    (63, 70),
    (70, 82),
    (75, 90),
    (80, 100),
]
 
POLL_INTERVAL = 3   # seconds between checks
GPU_INDEX     = 0   # change if you have multiple GPUs
 
 
def interpolate_fan(temp):
    """Return target fan % for a given GPU temp using the curve above."""
    if temp <= FAN_CURVE[0][0]:
        return FAN_CURVE[0][1]
    if temp >= FAN_CURVE[-1][0]:
        return FAN_CURVE[-1][1]
    for i in range(1, len(FAN_CURVE)):
        t1, f1 = FAN_CURVE[i - 1]
        t2, f2 = FAN_CURVE[i]
        if t1 <= temp <= t2:
            ratio = (temp - t1) / (t2 - t1)
            return int(f1 + ratio * (f2 - f1))
    return 100
 
 
def set_fans(handle, num_fans, pct):
    pct = max(0, min(100, pct))
    for fan_idx in range(num_fans):
        pynvml.nvmlDeviceSetFanSpeed_v2(handle, fan_idx, pct)
 
 
def main():
    pynvml.nvmlInit()
    driver = pynvml.nvmlSystemGetDriverVersion()
    log.info(f"NVIDIA driver: {driver}")
 
    handle   = pynvml.nvmlDeviceGetHandleByIndex(GPU_INDEX)
    name     = pynvml.nvmlDeviceGetName(handle)
    num_fans = pynvml.nvmlDeviceGetNumFans(handle)
    log.info(f"Controlling: {name}  ({num_fans} fans)")
    log.info("Fan curve (temp->fan%): " +
             "  ".join(f"{t}C->{f}%" for t, f in FAN_CURVE))
 
    # Take manual control of the fan
    for fan_idx in range(num_fans):
        pynvml.nvmlDeviceSetFanControlPolicy(
            handle, fan_idx,
            pynvml.NVML_FAN_POLICY_MANUAL
        )
    log.info("Fan control set to MANUAL")
 
    last_pct = -1
    try:
        while True:
            temp    = pynvml.nvmlDeviceGetTemperature(
                handle, pynvml.NVML_TEMPERATURE_GPU)
            target  = interpolate_fan(temp)
 
            if target != last_pct:
                set_fans(handle, num_fans, target)
                log.info(f"GPU {temp}C -> fans {target}%")
                last_pct = target
 
            time.sleep(POLL_INTERVAL)
 
    except KeyboardInterrupt:
        log.info("Interrupted - restoring automatic fan control")
    finally:
        # Hand control back to the driver on exit
        for fan_idx in range(num_fans):
            pynvml.nvmlDeviceSetFanControlPolicy(
                handle, fan_idx,
                pynvml.NVML_FAN_POLICY_TEMPERATURE_CONTINOUS_SW
            )
        pynvml.nvmlShutdown()
        log.info("Fan control restored to automatic")
 
 
if __name__ == "__main__":
    main()
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎