Solving a cubic polynomial using numpy

Hi all, @c.poupin

I intend to design the foundation of an elevated water tank, consisting of a raft with the shape shown in the image below. To do so, I need to determine the raft diameter Dr by solving the following inequality:

𝑁𝑇∙𝐷𝑟 ≥ 8∙𝑀 (where NT and M are inputs in my code)

Based on my hand calculations, after taking into account all the necessary parameters to evaluate NT, the final equation to be solved is a cubic polynomial with the following expression:

5694·Dr³ - 0.942·Dr² +2540.505·Dr - 73612.480 = 0

The real solution obtained from my hand calculation is: Dr = 17,36 m

However, when I run the code below, the result is:

cubic polynomial equation: 6.676·Dr³ - 0.942·Dr² +2540.505·Dr - 73612.480 = 0

reel solution: Dr = 16.744 m

What could be causing this discrepancy, and how can it be fixed?

Raft shaoe:

Please check my code here:

import sys
import os
sys.path.append(r'C:\Users\nono\AppData\Local\Programs\Python\Python38\Lib\site-packages')
import numpy as np
import math

def vertical_load(H, a, b, c, di, de, gb, gs, P0, Dr):
    # calculating the entire vertical force at the base
    # H is embedment depth
    # de: shaft outer diameter
    # di: shaft inner diameter
    # gb is the concrete density = 2.5T/m3
    # gs is the soil density = 1.9T/m3
    # P0 vertical load from the superstructure
    # Dr raft diameter to find    
    h = H / 4

    d1 = de + 2*a
    d2 = d1 + 2*b
    d3 = Dr - 2*c

    # raft area calculation
    
    s1 = de**2 - di**2
    s2 = Dr**2
    s3 = (d3**2 - d2**2) / 2
    s4 = d2**2
    s5 = (d2**2 - d1**2) / 2
    s6 = d1**2
    
    # raft weight calculation
    Pr = gb * h * math.pi / 4 * (s1 + s2 + s3 + s4 + s5 + s6)

    # soil area calculation
    
    t1 = Dr**2 - d3**2
    t2 = (d3**2 - d2**2) / 2
    t3 = Dr**2 - d2**2
    t4 = (d2**2 - d1**2) / 2
    t5 = Dr**2 - de**2

    # soil weight
    Pt = gs * h * math.pi / 4 * (t1 + t2 + t3 + t4 + t5)

    # Total vertical load
    NT = P0 + Pr + Pt
    return NT

def Equi_inequality(Dr, H, a, b, c, di, de, gb, gs, P0, M):
    NT = vertical_load(H, a, b, c, di, de, gb, gs, P0, Dr)
    return NT * Dr - 8 * M

def polynom(H, a, b, c, di, de, gb, gs, P0, M):
    Dr_vals = np.array([0.0, 1.0, 2.0, 3.0])
    F_vals = np.array([
        Equi_inequality(Dr, H, a, b, c, di, de, gb, gs, P0, M)
        for Dr in Dr_vals
    ])

    
    A = np.array([
        [0, 0, 0, 1],
        [1, 1, 1, 1],
        [8, 4, 2, 1],
        [27, 9, 3, 1]
    ])

    coeffs = np.linalg.solve(A, F_vals)
    return coeffs  

def solve_Dr(H, a, b, c, di, de, gb, gs, P0, M):
    a3, a2, a1, a0 = polynom(
        H, a, b, c, di, de, gb, gs, P0, M
    )

    roots = np.roots([a3, a2, a1, a0])

    real_roots = [
        r.real for r in roots
        if abs(r.imag) < 1e-8 and r.real > 0
    ]

    if not real_roots:
        raise ValueError("no solution found")

    return min(real_roots), (a3, a2, a1, a0)

# Inputs
H  = 4.00
a  = 1.05
b  = 2.00
c  = 1.00
di = 6.80
de = 7.60
gb = 2.5
gs = 1.9
P0 = 2492.52
M  = 9201.56

Dr, coeffs = solve_Dr(H, a, b, c, di, de, gb, gs, P0, M)


print(f"{coeffs[0]:.3f}·Dr³ {coeffs[1]:+.3f}·Dr² "
      f"{coeffs[2]:+.3f}·Dr {coeffs[3]:+.3f}")

print(f"Dr = {Dr:.3f} m")

OUT = Dr, coeffs = solve_Dr(H, a, b, c, di, de, gb, gs, P0, M)

Thanks.

Hi,
are you sure about this result?

result I founded with scipy

import sys
from scipy.optimize import fsolve

def equation(Dr):
    return 5694 * Dr**3 - 0.942 * Dr**2 + 2540.505 * Dr - 73612.480

# initial guess for the solver
initial_guess = 0.0

root = fsolve(equation, initial_guess)
OUT = root[0]
2 Likes

@c.poupin
Sorry I was wrong in typing the equation, I wrote 5694·Dr³ instead of 5.694·Dr³
Please check the updated equation expression:
5.694·Dr³ - 0.942·Dr² +2540.505·Dr - 73612.480 = 0

the solution should be equal to 17.360 m as you can see below

Thanks

hi

if you try on CPython3

cordially

christian.stan

1 Like

Thanks @christian.stan for your reply

I forgot to mention that, in addition to solving the polynomial equation to find Dr, I would like to automatically determine the polynomial coefficients instead of computing them manually. This would allow me to reuse the code later to design another raft based on different inputs, without having to manually recalculate these coefficients.

When I use @c.poupin code, where the coefficients are written manually, I can easily obtain the solution I am looking for, as shown in the output below.

So my question is: How can I fix the code so that it can both automatically compute the coefficients from the input parameters and determine Dr at the same time?*

Thanks.

1 Like

I’d like to point out that this thread was based on incorrect data, and I apologize for that.
In fact, my hand calculation of Pr was wrong, which led to a discrepancy between the equilibrium inequality I derived and the one generated by the Equi_inequality function in the code (which was correct in this case).
Simply testing the Pr formula with an arbitrary diameter would have been sufficient to detect the mistake…something I unfortunately did not do!!

The issue is now resolved, and the results verified by spreadsheet calculations match exactly those obtained from the updated code below.

Remark: I had to modify the Pr and Pt formulas to account for the volume of the conical-shaped parts of the overall raft geometry shown in this image.

import sys
sys.path.append(r'C:\Users\nono\AppData\Local\Programs\Python\Python38\Lib\site-packages')

import numpy as np
import typing
from typing import Union

TNum = Union[float, np.ndarray]


class System(typing.NamedTuple):
    H: float   # embedment depth (m)
    a: float   # extension distance needed for calculation of radius r1  (m)
    b: float   # extension distance needed for calculation of  radius r2 (m)
    c: float   # extension distance needed for calculation of radius r3 (m)
    re: float  # shaft outer radius (m)
    ri: float  # shaft inner radius (m)
    gb: float  # concrete density (T/m³)
    gs: float  # soil density (T/m³)
    P0: float  # vertical load from the superstructure (T)
    M: float   # overturning bending moment (T.m) 

    # R is the raft radius to find

    def vertical_load(self, R: TNum) -> TNum:
        """Calculate the total vertical force at the base"""
        h = self.H / 4

        r1 = self.re + self.a
        r2 = r1 + self.b
        r3 = R - self.c

        # raft areas
        s1 = R**2
        s2 = (r3**2 + r2**2 + r3 * r2) / 3
        s3 = (r2**2 + r1**2 + r2 * r1) / 3
        s4 = self.re**2 - self.ri**2

        # raft weight
        mu = self.gb * h * np.pi
        Pr = mu * (s1 + s2 + s3 + s4)

        # soil areas
        t1 = R**2 - r3**2
        t2 = r3**2 - s2
        t3 = R**2 - r2**2
        t4 = r2**2 - s3
        t5 = R**2 - self.re**2

        # soil weight
        Pt = self.gs * h * np.pi * (t1 + t2 + t3 + t4 + t5)

        return self.P0 + Pr + Pt

    def equi_inequality(self, R: TNum) -> TNum:
        NT = self.vertical_load(R)
        return NT * R - 4 * self.M

    def polynom(self) -> np.ndarray:
        R_min = self.re + 2 * (self.a + self.b + self.c)
        R = np.linspace(R_min, 1.1 * R_min, 4)

        A = R[:, None] ** np.arange(3, -1, -1)
        F = self.equi_inequality(R)

        return np.linalg.solve(A, F)

    def solve_R(self) -> typing.Tuple[float, np.ndarray]:
        coeffs = self.polynom()
        roots = np.roots(coeffs)

        mask = (np.abs(roots.imag) < 1e-8) & (roots.real > 0)
        if not mask.any():
            raise ValueError("No physical solution found")

        return roots[mask].real.min(), coeffs


system = System(
    H=4.0, a=1.05, b=2.0, c=1.0,
    ri=3.4, re=3.8, gb=2.5, gs=1.9,
    P0=2492.52, M=9201.56,
)

R, coeffs = system.solve_R()
a, b, c, d = coeffs

OUT = {
    "Polynomial": f"{a:.3f}·R³ {b:+.3f}·R² {c:+.3f}·R {d:+.3f}",
    "R (m)": round(R, 3),
    "NT": float(system.vertical_load(R)),
    "Inequality": float(system.equi_inequality(R)),
    "Coefficients": coeffs.tolist(),
}

Here the output:

Thanks.