Reputation: 1
I am solving so called Blasius problem. I use the following code (copied from youtube lecutre https://www.youtube.com/watch?v=0L4h-hqZY2Y , timecode: 8:35):
import numpy as np
from scipy.integrate import odeint
from scipy.optimize import fsolve
eta = np.linspace(0, 10, 101)
def blas(f, t):
return (f[1], f[2], -(1/2)*f[2]*f[0])
def blas0(x, ts):
f0 = (0., 0., x)
f = odeint(blas, f0, ts)
return 1.-f[-1, 1]
print(blas0(0.0, eta))
print(blas0(0.1, eta))
print(blas0(0.2, eta))
print(blas0(0.332, eta))
print(eta)
xpp0 = fsolve(blas0, x0=0.1, args=(eta), xtol=1e-6)
All the listed print-functions work well and print correct results. However, the function fsolve results in the following error:
xpp0 = fsolve(blas0, x0=0.1, args=(eta), xtol=1e-6)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
ValueError: setting an array element with a sequence. The requested
array has an inhomogeneous shape after 1 dimensions. The detected
shape was (3,) + inhomogeneous part.
Neither of these methods work.
The packages list:
Package Version
--------------- -----------
contourpy 1.3.0
CoolProp 6.6.0
cycler 0.12.1
fonttools 4.53.1
kiwisolver 1.4.7
matplotlib 3.9.2
numpy 2.1.1
packaging 24.1
pandas 2.2.2
pillow 10.4.0
pip 24.2
pyparsing 3.1.4
PyQt6 6.7.1
PyQt6-Qt6 6.7.2
PyQt6_sip 13.8.0
python-dateutil 2.9.0.post0
pytz 2024.2
scipy 1.14.1
six 1.16.0
tzdata 2024.1
Does anybody knows what the problem can be?
Upvotes: 0
Views: 82
Reputation: 36623
Your issue is that fsolve
expects an sequence for x0
and it is silently converting the a sequence when a number is passed. And your need to pass in args
as a list or tuple.
You can fix this by just pulling the first element from x
in blas0
def blas0(x, ts):
x = list(x)[0]
f0 = (0., 0., x)
f = odeint(blas, f0, ts)
return 1.-f[-1, 1]
Here is the full solution:
import numpy as np
from scipy.integrate import odeint
from scipy.optimize import fsolve
eta = np.linspace(0, 10, 101)
def blas(f, t):
return (f[1], f[2], -(1/2)*f[2]*f[0])
def blas0(x, ts):
x = list(x)[0]
f0 = (0., 0., x)
f = odeint(blas, f0, ts)
return 1.-f[-1, 1]
xpp0 = fsolve(blas0, x0=0.1, args=(eta,), xtol=1e-6)
print(xpp0)
# prints: [0.33205733]
Upvotes: 1
Reputation: 231395
Here's the FULL error message
In [124]: xpp0 = fsolve(blas0, x0=0.1, args=(eta), xtol=1e-6)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[124], line 1
----> 1 xpp0 = fsolve(blas0, x0=0.1, args=(eta), xtol=1e-6)
File ~\miniconda3\lib\site-packages\scipy\optimize\_minpack_py.py:163, in fsolve(func, x0, args, fprime, full_output, col_deriv, xtol, maxfev, band, epsfcn, factor, diag)
51 """
52 Find the roots of a function.
53
(...)
153
154 """
155 options = {'col_deriv': col_deriv,
156 'xtol': xtol,
157 'maxfev': maxfev,
(...)
160 'factor': factor,
161 'diag': diag}
--> 163 res = _root_hybr(func, x0, args, jac=fprime, **options)
164 if full_output:
165 x = res['x']
File ~\miniconda3\lib\site-packages\scipy\optimize\_minpack_py.py:229, in _root_hybr(func, x0, args, jac, col_deriv, xtol, maxfev, band, eps, factor, diag, **unknown_options)
227 if not isinstance(args, tuple):
228 args = (args,)
--> 229 shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,))
230 if epsfcn is None:
231 epsfcn = finfo(dtype).eps
File ~\miniconda3\lib\site-packages\scipy\optimize\_minpack_py.py:26, in _check_func(checker, argname, thefunc, x0, args, numinputs, output_shape)
24 def _check_func(checker, argname, thefunc, x0, args, numinputs,
25 output_shape=None):
---> 26 res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
27 if (output_shape is not None) and (shape(res) != output_shape):
28 if (output_shape[0] != 1):
Cell In[123], line 13, in blas0(x, ts)
11 def blas0(x, ts):
12 f0 = (0., 0., x)
---> 13 f = odeint(blas, f0, ts)
14 return 1.-f[-1, 1]
File ~\miniconda3\lib\site-packages\scipy\integrate\_odepack_py.py:242, in odeint(func, y0, t, args, Dfun, col_deriv, full_output, ml, mu, rtol, atol, tcrit, h0, hmax, hmin, ixpr, mxstep, mxhnil, mxordn, mxords, printmessg, tfirst)
240 t = copy(t)
241 y0 = copy(y0)
--> 242 output = _odepack.odeint(func, y0, t, args, Dfun, col_deriv, ml, mu,
243 full_output, rtol, atol, tcrit, h0, hmax, hmin,
244 ixpr, mxstep, mxhnil, mxordn, mxords,
245 int(bool(tfirst)))
246 if output[-1] < 0:
247 warning_msg = _msgs[output[-1]] + " Run with full_output = 1 to get quantitative information."
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (3,) + inhomogeneous part.
Note that actual error is in the odeint
call, not the fsolve
itself. All those clipped lines mislead you and your readers.
An error message like this means that some argument is a "ragged" array, something with a mix of shapes, and it can't convert it to a regular numeric array. Searching SO for that "inhomogeneous" phrase will turn up lots of examples. But here we need to determine which odeint
argument is bad, and why.
numpy
versions, and up/down grades have nothing to do with this kind of error. Something about your code, and arguments you pass is wrong.
Using args=(eta)
when it should be (eta,)
, a tuple looks suspicious, but doesn't fix this particular issue.
Testing blas0
calls is good, but we need to see exactly what fsolve
is doing.
Adding a print line to blas0
:
In [128]: def blas0(x, ts):
...: f0 = (0., 0., x)
...: print(x, f0, ts.shape)
...: f = odeint(blas, f0, ts)
...: return 1.-f[-1, 1]
...:
In [129]: eta.shape
Out[129]: (101,)
In [130]: blas0(0.1, eta)
0.1 (0.0, 0.0, 0.1) (101,)
Out[130]: 0.5507916014363659
Now look at the fsolve
:
In [131]: xpp0 = fsolve(blas0, x0=0.1, args=(eta,), xtol=1e-6)
[0.1] (0.0, 0.0, array([0.1])) (101,)
...
f0
is a tuple containing an array. Trying to make an array from that tuple raises your error
In [132]: np.array((0.0, 0.0, np.array([0.1])))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[132], line 1
----> 1 np.array((0.0, 0.0, np.array([0.1])))
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (3,) + inhomogeneous part.
changing blas0
so it treats x
as a scalar, and it works
In [133]: def blas0(x, ts):
...: f0 = (0., 0., x[0])
...: print(x, f0, ts.shape)
...: f = odeint(blas, f0, ts)
...: return 1.-f[-1, 1]
...:
In [134]: xpp0 = fsolve(blas0, x0=0.1, args=(eta,), xtol=1e-6)
[0.1] (0.0, 0.0, 0.1) (101,)
[0.1] (0.0, 0.0, 0.1) (101,)
[0.1] (0.0, 0.0, 0.1) (101,)
[0.1] (0.0, 0.0, 0.10000000149011612) (101,)
[0.28363035] (0.0, 0.0, 0.283630348083009) (101,)
[0.32424457] (0.0, 0.0, 0.3242445695754514) (101,)
[0.3318578] (0.0, 0.0, 0.33185780004663934) (101,)
[0.33205655] (0.0, 0.0, 0.3320565471407554) (101,)
[0.33205733] (0.0, 0.0, 0.3320573348245278) (101,)
[0.33205733] (0.0, 0.0, 0.3320573349034362) (101,)
In [135]: xpp0
Out[135]: array([0.33205733])
Upvotes: 1