solving colebrook (nonlinear) equation in python

爱⌒轻易说出口 提交于 2020-12-06 04:21:10

问题


I want to do in python what this guy did in MATLAB.

I have installed anaconda, so i have numpy and sympy libraries. So far I have tried with numpy nsolve, but that doesn't work. I should say I'm new with python, and also that I konw how to do it in MATLAB :P.

The equation:

-2*log(( 2.51/(331428*sqrt(x)) ) + ( 0.0002 /(3.71*0.26)) ) = 1/sqrt(x)

Normally, I would solve this iteratively, simply guessing x on the left and than solving for the x on the right. Put solution on the left, solve again. Repeat until left x is close to right. I have an idea what solution should be.

So I could do that, but that's not very cool. I want to do it numerically. My 15€ Casio calculator can solve it as is, so I think it shouldn't be to complicated?

Thank you for your help,

edit: so I have tried the following:

from scipy.optimize import brentq

w=10;
d=0.22;
rho=1.18;
ni=18.2e-6;

Re=(w*d*rho)/ni
k=0.2e-3;
d=0.26;

def f(x,Re,k,d):
    return (
        -2*log((2.51/(Re*sqrt(x)))+(k/(3.71*d)),10)*sqrt(x)+1
            );

print(
    scipy.optimize.brentq
        (
        f,0.0,1.0,xtol=4.44e-12,maxiter=100,args=(),full_output=True,disp=True
        )
    );

And i get this result:

    r = _zeros._brentq(f,a,b,xtol,maxiter,args,full_output,disp)
TypeError: f() takes exactly 4 arguments (1 given)

Is it because I'm solving also solving for constants?

edit2: so I think I have to assign constants via args=() keyword, so I changed:

f,0.0,1.0,xtol=4.44e-12,maxiter=100,args=(Re,k,d),full_output=True,disp=True

but now I get this:

-2*log((2.51/(Re*sqrt(x)))+(k/(3.71*d)),10)*sqrt(x)+1
TypeError: return arrays must be of ArrayType

Anyway, when I put in a different equation; lets say 2*x*Re+(k*d)/(x+5) it works, so I guess I have to transform the equation.

so it dies here: log(x,10)..

edit4: correct syntax is log10(x)... Now it works but I get zero as a result


回答1:


This works fine. I've done a few things here. First, I've used a simpler definition of the function using the global variables you've defined anyway. I find this a little nicer than passing the args= to the solvers, it also enables easier use of your own custom solvers if you ever need something like that. I've used the generic root function as an entry point rather than using a particular algorithm - this is nice because you can simply pass a different method later. I've also fixed up your spacing to be as recommended by PEP 8 and fixed your erronious rewriting of the equation. I find it more intuitive simply to write LHS - RHS rather than manipulate as you did. Also, notice that I've replaced all the integer literals with 1.0 or whatever to avoid problems with integer division. 0.02 is regarded as a pretty standard starting point for the friction factor.

import numpy
from scipy.optimize import root

w = 10.0
d = 0.22
rho = 1.18
ni = 18.2e-6

Re = w*d*rho/ni
k = 0.2e-3

def f(x):
    return (-2*numpy.log10((2.51/(Re*numpy.sqrt(x))) + (k/(3.71*d))) - 1.0/numpy.sqrt(x))

print root(f, 0.02)

I must also mention that fixed point iteration is actually faster than even Newton's method for this problem. You can use the built-in fixed point iteration routine by defining f2 as follows:

def f2(x):
    LHS = -2*numpy.log10((2.51/(Re*numpy.sqrt(x))) + (k/(3.71*d)))
    return 1/LHS**2

Timings (starting further from the root to show speed of convergence):

%timeit root(f, 0.2)
1000 loops, best of 3: 428 µs per loop

%timeit fixed_point(f2, 0.2)
10000 loops, best of 3: 148 µs per loop



回答2:


Your tags are a little off: you're tagging it as sympy which is a library for symbolic computations, but say that you want to solve it numerically. In case the latter is your actual intention, here are relevant scipy docs:

http://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html#root-finding



来源:https://stackoverflow.com/questions/18880134/solving-colebrook-nonlinear-equation-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!