0

I've got a function and some arguments in a python script but I want to change the script so that when it is called from the command line, new arguments can be entered. I know I need to use sys module but I haven't worked much with it before and I'm having trouble. This is the original code that I want to recreate with arguments from the command line for r, a, z and e:

import scipy as sc 
import scipy.integrate as integrate

def dCR_dt(pops, t=0):

    R = pops[0]
    C = pops[1]
    dRdt = r * R - a * R * C
    dCdt = -z * C + e * a * R * C

    return sc.array([dRdt, dCdt])

r = 1
a = 0.1
z = 1.5
e = 0.75

t = sc.linspace(0, 15, 1000)
R0 = 10
C0 = 5
RC0 = sc.array([R0, C0])
pops, infodict = integrate.odeint(dCR_dt, RC0, t, full_output = True)

And this is what I've tried to do so far:

import scipy as sc
import scipy.integrate as integrate
import sys

def main(r, a, z, e):
    def dCR_dt(pops, t=0):
        R = pops[0]
        C = pops[1]
        dRdt = r * R - a * R * C
        dCdt = -z * C + e * a * R * C

        return sc.array([dRdt, dCdt])

t = sc.linspace(0, 15, 1000)
R0 = 10
C0 = 5
RC0 = sc.array([R0, C0])
pops, infodict = integrate.odeint(dCR_dt, RC0, t, full_output = True)

if (__name__ == "__main__"):
    status = main(sys.argv)
    sys.exit(status)

When I try and run it in command line with 4 new arguments I get the following error:

Traceback (most recent call last): File "practisesys.py", line 31, in pops, infodict = integrate.odeint(dCR_dt, RC0, t, full_output = True) NameError: name 'dCR_dt' is not defined

I'm new to coding and want to learn more about how to use the sys module to modify scipts. Any help is much appreciated! Thank you and I hope everyone's having a nice weekend :)

botinky
  • 9
  • 2
  • 1
    The undefined name problem is not related to the question. You need to put the code from `t = ...` up to `pops, infodict = ...` under `main()` too. I.e. indent this code block. Furthermore you need to call main like this: `status = main(*sys.argv)`. It would also be better if your `main()` would accept a variable number of arguments. Otherwise it would crash if not exactly four parameters are supplied. Say `def main(*args)`. See https://stackoverflow.com/q/36901/2311167 for more info. – Adrian W Nov 16 '19 at 18:58

1 Answers1

0

There are many unrelated problems in your code. Let's just focus on the part you are actually trying to ask about.

def main(r, a, z, e):
    return stuff

if __name__ == "__main__":  # the parentheses were unnecessary and unidiomatic
    status = main(sys.argv)
    sys.exit(status)

There are two preblems here. Firstly, sys.argv[0] contains the name of your script; so you want to use sys.argv[1:] to refer to just the command-line arguments. Secondly, you are passing a single parameter to a function which requires four parameters. You could of course say

    status = main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])

but Python has a convenient shorthand for "use this list as the parameters":

    status = main(*sys.argv[1:])

More fundamentally, you should modify your function so it doesn't use any global variables.

def dCR_dt(pops, t=0, r, a, z, e):
    R = pops[0]
    C = pops[1]
    dRdt = r * R - a * R * C
    dCdt = -z * C + e * a * R * C

    return sc.array([dRdt, dCdt])

and probably move the rest of the logic into main:

def main(r, a, z, e):
    t = sc.linspace(0, 15, 1000)
    R0 = 10
    C0 = 5
    RC0 = sc.array([R0, C0])
    pops, infodict = integrate.odeint(dCR_dt, RC0, t, full_output=True)

I don't know enough about your problem domain to understand what main is supposed to return. You pass the value to sys.exit so it should be an integer between 0 and 255, where zero signifies success and every other value indicates an error. Perhaps it would make more sense to print the result and simply return 0`?

(Also, your function doesn't seem to use t for anything.)

tripleee
  • 175,061
  • 34
  • 275
  • 318