Search code examples
pythonfor-loopsympybessel-functions

How to Define a Piecewise Function in Python with Multiple Variables


I am trying to develop a plot for my helioseismology class and the question had provided a piecewise function describing the dynamics of the "fluids" in a star as if it is one thing its this and if its another its that. I am receiving over and over again this 'Mul' object cannot be interpreted as an integer but I am working with numbers in the reals not just the integer set. I do not know how to get around this and need guidance. The code is as follows.

import sympy as sy
from sympy import *
from sympy.physics.units import Unit
import numpy as np
import sys
import math
import scipy as sp
from scipy import special

phi = Symbol('phi', Variable = True)
x = Symbol('x', Variable = True, Real = True)
t = Symbol('t', Variable = True, Real = True)
xi = Symbol('xi', Function = True)
Solar_Radius = Symbol('R', Constant = True, unit = "meters")
Sound_Speed = Symbol('c', Constant = True, unit = "meters per second", Real = True)
gamma = Symbol('gamma', Constant = True)
gravity = Symbol('g', Constant = True, unit = "meters per second per second")

Solar_Radius = 6.963 * 10 ** 6
gamma = 5/3
g = 274.8265625336
gas_constant = 8201.25
c = 8.1 * 10 ** 3

for t in range(0,x/c):
    xi[x,t] = 0
for t in range(x/c,00):
    xi[x,t] = (1/2)*sy.exp(gamma*g*x/(2*c**2))*mpmath.besselj(0, (gamma*g/(2*c)*sy.sqrt(t**2 - ((x/c)**2))),derivative = 0)

Full Traceback:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-50-3506376f1686> in <module>()
----> 1 for t in range(0,x/c):
      2     xi[x,t] = 0
      3 for t in range(x/c,00):
      4     xi[x,t] = (1/2)*sy.exp(gamma*g*x/(2*c**2))*mpmath.besselj(0, (gamma*g/(2*c)*sy.sqrt(t**2 - ((x/c)**2))),derivative = 0)

TypeError: 'Mul' object cannot be interpreted as an integer

Solution

  • There are quite a few issues here:

    • None of the keyword arguments (Constant, Variable, unit, Real) that you are passing to Symbol are things that are recognized by SymPy. The only one that is close is real, which should be lowercase (like Symbol('x', real=True)). The rest do nothing. If you want units, you should use the SymPy units module in sympy.physics.units. There is no need to specify if a symbol is constant or variable.

    • You have redefined Solar_Radius and gamma as numbers. That means that the Symbol definitions for those variables are pointless.

    • If you are using Python 2, make sure to include from __future__ import division at the top of the file, or else things like 1/2 and 5/3 will be truncated with integer division (this isn't an issue in Python 3).

    • range(0, x/c) doesn't make sense. range creates a list of numbers, like range(0, 3) -> [0, 1, 2]. But x/c is not a number, it's a symbolic expression.

    • Additionally, xi[x, t] = ... doesn't make sense. xi is a Symbol, which doesn't allow indexing and certainly doesn't allow assignment.

    • Don't mix numeric (math, mpmath, numpy, scipy) functions with SymPy functions. They won't work with symbolic expressions. You should use only SymPy functions. If you create a symbolic expression and want to convert it to a numeric one (e.g., for plotting), use lambdify.

    What you want here is Piecewise. The syntax is Piecewise((expr, cond), (expr, cond), ..., (expr, True)), where expr is an expression that is used when cond is true ((expr, True) is the "otherwise" condition).

    For your example, I believe you want

    expr = Piecewise((0, t < x/c), (sy.exp(gamma*g*x/(2*c**2))*sy.besselj(0, (gamma*g/(2*c)*sy.sqrt(t**2 - (x/c)**2)))/2, t >= x/c))
    

    If you want to turn this into a numeric function in x and t, use

    xi = lambdify((x, t), expr)