Skip to content

Bug: friction_model="nikuradse" adds laminar + turbulent lambda instead of selecting by Reynolds number #803

Description

@Haplo98

Bug description

The default friction model "nikuradse" unconditionally adds lambda_laminar + lambda_nikuradse instead of selecting the appropriate value based on Reynolds number. This produces incorrect friction factors for all flow regimes, with the largest relative error in the laminar/transition zone.

Location

pandapipes/pf/derivative_calculation.py, function calc_lambda(...), the else branch at lines 194-197 (pandapipes 0.13.0):

else:
    # lambda_tot = np.where(re > 2300, lambda_laminar + lambda_nikuradse, lambda_laminar)
    lambda_tot = lambda_laminar + lambda_nikuradse
    return lambda_tot, re

This branch is taken whenever friction_model is neither "colebrook" nor "swamee-jain" — i.e. it is the path used for "nikuradse" (the default).

Expected behaviour

Standard textbook treatment: pick laminar or turbulent, do not sum them.

lambda_tot = np.where(re > 2300, lambda_nikuradse, lambda_laminar)
  • Laminar (Re < 2300): lambda_laminar = 64/Re (Hagen-Poiseuille)
  • Turbulent (Re >= 2300): lambda_nikuradse (Nikuradse fully-rough approximation)

The commented-out line directly above the bug also has an issue: it would still add both values in the turbulent branch (lambda_laminar + lambda_nikuradse) rather than using only lambda_nikuradse.

Actual behaviour / impact

Re lambda_laminar lambda_nikuradse Current (sum) Correct Error
500 0.1280 0.0196 0.1476 0.1280 +15%
2000 0.0320 0.0196 0.0516 0.0320 +61%
80979 0.0008 0.0196 0.0204 0.0196 +4%

The error is most severe near the transition zone (Re ~ 2000-3000) where both terms are similar in magnitude.

Minimal reproducer

import pandapipes as pp

net = pp.create_empty_network(fluid="water")
j0 = pp.create_junction(net, pn_bar=6, tfluid_k=363.15)
j1 = pp.create_junction(net, pn_bar=6, tfluid_k=363.15)
pp.create_pipe_from_parameters(net, j0, j1, length_km=0.5, diameter_m=0.1, k_mm=0.1)
pp.create_ext_grid(net, j0, p_bar=6.0, t_k=363.15)
pp.create_sink(net, j1, mdot_kg_per_s=2.0)

pp.pipeflow(net, mode="hydraulics", friction_model="nikuradse")
dp_nik = net.res_pipe.p_from_bar.iloc[0] - net.res_pipe.p_to_bar.iloc[0]

pp.pipeflow(net, mode="hydraulics", friction_model="colebrook")
dp_cb = net.res_pipe.p_from_bar.iloc[0] - net.res_pipe.p_to_bar.iloc[0]

print(f"dp nikuradse: {dp_nik:.4f} bar")
print(f"dp colebrook: {dp_cb:.4f} bar")
print(f"ratio:        {dp_nik/dp_cb:.3f}")
# Observed ratio ~0.9 at high Re (should be ~1.0); discrepancy comes from the
# spurious +lambda_laminar term that is still added in the fully turbulent regime.

Workaround

Pass friction_model="colebrook" (or "swamee-jain") explicitly — both code paths return early before the buggy else branch.

pp.pipeflow(net, friction_model="colebrook")

Suggested fix

Replace line 196 with:

lambda_tot = np.where(re > 2300, lambda_nikuradse, lambda_laminar)
return lambda_tot, re

I am happy to open a PR if the maintainers agree with the fix.

Environment

  • pandapipes 0.13.0 (installed at site-packages/pandapipes/)
  • Python 3.14.3
  • Windows 10

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions