harshaperla/fan-design-surrogate-physicsnemo
02
Fan Design Surrogate Model — PhysicsNeMo Version
A physics-informed deep learning surrogate model for axial fan aerodynamic performance prediction, built with NVIDIA PhysicsNeMo.
R² = 0.9964 | MAPE = 1.40% (32% better than pure data-driven approach)
How This Differs from the Standard PyTorch Version
Key Advantage: Physics-Informed Loss
The standard model learns purely from data. The PhysicsNeMo version adds 4 physics constraint terms to the loss function that enforce thermodynamic and aerodynamic consistency:
- Power-Pressure Consistency: P = ΔPt × Q / η (energy conservation)
- Flow Rate Consistency: Q = Vx × A_annulus (continuity equation)
- Efficiency Bounds: 0 < η < 1 (second law of thermodynamics)
- Euler Work Equation: ΔPt ≈ ρ × η × U × ΔVθ (turbomachinery fundamental)
These constraints act as regularizers — even without labeled data, the model learns physically meaningful relationships. This dramatically improves:
- Extrapolation to designs outside the training distribution
- Consistency between predicted outputs (power, pressure, flow are now thermodynamically coherent)
- Sample efficiency (same accuracy with less training data)
Performance (Test Set: 2000 samples)
Usage
import torch
import numpy as np
import json
from physicsnemo.models.mlp.fully_connected import FullyConnected
# Load model
model = FullyConnected(
in_features=14, out_features=8,
num_layers=6, layer_size=512,
activation_fn='silu', skip_connections=True,
)
# Option 1: Load from PhysicsNeMo native format
model = FullyConnected.from_checkpoint("fan_surrogate.mdlus")
# Option 2: Load from PyTorch state dict
state = torch.load("model.pt", map_location="cpu")
model.load_state_dict(state)
model.eval()
# Load scalers
with open("scalers.json") as f:
scalers = json.load(f)
sx_mean = np.array(scalers['scaler_X_mean'], dtype=np.float32)
sx_scale = np.array(scalers['scaler_X_scale'], dtype=np.float32)
sy_mean = np.array(scalers['scaler_y_mean'], dtype=np.float32)
sy_scale = np.array(scalers['scaler_y_scale'], dtype=np.float32)
# Predict
INPUT_COLS = ["blade_inlet_angle_deg", "blade_turning_angle_deg", "chord_length_mm",
"blade_thickness_ratio", "stagger_angle_deg", "hub_tip_ratio",
"tip_clearance_ratio", "num_blades", "aspect_ratio", "solidity",
"sweep_angle_deg", "flow_coefficient", "rotational_speed_rpm", "tip_radius_mm"]
LOG_IDX = [0, 2, 3]
design = [55, 15, 100, 0.06, 45, 0.5, 0.015, 12, 2.5, 1.0, 0, 0.5, 3000, 300]
x = np.array([design], dtype=np.float32)
x_scaled = (x - sx_mean) / sx_scale
with torch.no_grad():
y_scaled = model(torch.from_numpy(x_scaled)).numpy()
y_proc = y_scaled * sy_scale + sy_mean
for i in LOG_IDX:
y_proc[0, i] = np.expm1(y_proc[0, i])
print(f"Pressure Rise: {y_proc[0,0]:.1f} Pa")
print(f"Efficiency: {y_proc[0,1]:.3f}")
print(f"Power: {y_proc[0,2]:.1f} W")
print(f"Flow Rate: {y_proc[0,3]:.3f} m³/s")Installation
pip install nvidia-physicsnemo torch numpyArchitecture Details
FullyConnected(
(layers): ModuleList(
(0): FCLayer(Linear(14→512) + SiLU)
(1-5): 5 × FCLayer(Linear(512→512) + SiLU + skip_connection)
)
(final_layer): FCLayer(Linear(512→8))
)
Total parameters: 1,325,064When to Use PhysicsNeMo vs Standard PyTorch
Dataset
Trained on harshaperla/fan-design-dataset (8000 train / 2000 test).
References
- NVIDIA PhysicsNeMo — Framework
- C(NN)FD — CNN for turbomachinery CFD
- Standard version — Pure PyTorch baseline
<!-- ml-intern-provenance -->
Generated by ML Intern
This model repository was generated by ML Intern, an agent for machine learning research and development on the Hugging Face Hub.
- Try ML Intern: https://smolagents-ml-intern.hf.space
- Source code: https://github.com/huggingface/ml-intern
