slope_field_r1.py

Created by john-c-2021

Created on January 17, 2021

6.19 KB

An updated (Rev 1) of slope_field.py to add a few user selected diffyQs. Note: selecting the max number of points (axes -4 to +4 = 81 points seems to run the Numworks calculator “out of memory” when the diffyQ is: dy/dx = sin(x)/y. No error is thrown but the display is blank. YMMV.


from matplotlib.pyplot import *
from math import *

# The user will input a minimum value for the x axis.
# The user input should be an integer between -4 and -2
# The x and y axes will be set equal to each other at +- this value
# Note: at -4 a 9 point x 9 point slope field is produced.
# Currently, any slope field wih > 81 (9x9) points = out of memory.
# The user will also choose from a selection of predetmined functions
# to use for the display of the slope field,
# By John Cantlin 1-16-2021 with help from Logan Burch.

# obtain and validate user input for the axes
def valid_axis(_a):
  if(-4 <= _a <= -2) and (isinstance(_a,int)):
    return "Good"
  else:
    print("x axis min: int -4 to -2")
    return False  

x_axis_good = False     
x_axis_min = int(input("Enter x axis min -4 to -2: "))
x_axis_good = valid_axis(x_axis_min)

while not x_axis_good:
  x_axis_min = int(input("Enter x axis min  -4 to -2: "))
  x_axis_good = valid_axis(x_axis_min)

# set the x-y axes min/max to equal (symmetric) +- values  
y_axis_min = x_axis_min
x_axis_max = abs(x_axis_min)
y_axis_max = abs(x_axis_min)
print("x-axis: ", x_axis_min, " to ", x_axis_max)
print("y-axis: ", y_axis_min, " to ", y_axis_max)

# create a list of x and corresponding y lattice point coordinates usingaxes max/min
x = list(range(x_axis_min, x_axis_max + 1, 1))  # range(start, stop. step)
y = list(range(x_axis_min, x_axis_max + 1, 1))  # range(start, stop. step)
print("x: ", x)
print("y: ", y)
wait = input("Press Enter to continue.")

# obtain and validate the user selection for the function to display
print("Enter function number:")
print("1: dy/dx=x+y")
print("2: dy/dx=x-y")
print("3: dy/dx=xy")
print("4: dy/dx=x/y")
print("5: dy/dx=sin(x)/y")

# use text labels to echo selection to user and for graph label
# put a dummy string in position 0 so list indices match item selected
dydxstr = ["dummystr", "dy/dx=x+y", "dy/dx=x-y",  "dy/dx=xy", "dy/dx=x/y", "dy/dx=sin(x)/y"]

def valid_selection(_s):
  if(1 <= _s <= 5) and (isinstance(_s,int)):
    return "Good"
  else:
    print("Enter int 1 to 4.")
  return False

fselect_good = False
fselect = int(input("Enter function number: "))
fselect_good = valid_selection(fselect)
    
while not fselect_good:
  fselect = int(input("Enter function number: "))
  fselect_good = valid_selection(fselect)
  
print("Plot ", fselect, ": ", dydxstr[fselect])

# define the selected derivative function of y in terms of x and y 
# that is used for the slope field, include the label used for the graph
if (fselect == 1):
  def dydx(_x,_y):    # define the derivative as a function of x and y
    m = _x + _y   # find slope m for given x and y, do not divide by zero
    return m
elif (fselect == 2):
  def dydx(_x,_y):    # define the derivative as a function of x and y
    m = _x - _y       # find slope m for given x and y, do not divide by zero
    return m
elif (fselect == 3):
  def dydx(_x,_y):    # define the derivative as a function of x and y
    m = _x * _y       # find slope m for given x and y, do not divide by zero
    return m
elif (fselect == 4):
  def dydx(_x,_y):       # define the derivative as a function of x and y
    try:
      m = _x / _y   # define the derivative as a function of x and y
      return m
    except ZeroDivisionError:
      if(_x == 0 and _y == 0):
        m = 0
        return m
      else:
        m = 1000    # cheating a little here since m is actually undefined
        return m
elif (fselect == 5):
  def dydx(_x,_y):       # define the derivative as a function of x and y
    try:
      m = sin(_x) / _y   # define the derivative as a function of x and y
      return m
    except ZeroDivisionError:
      if(_x == 0 and _y == 0):
        m = 1
        return m
      else:
        m = 0
        return m
else:
  pass

# create list of x, y pairs Note: x and y lists are equal length
x_y = [] # initialize empty list of x, y pairs
xm = []  # initialize empty list of x midpoint values
ym = []  # initialize empty list of y midpoint values

# create nested list of lists of all ordered pairs then extract xm and ym lists
for i in range(len(x)):
    for j in range(len(y)):
        x_y.append((x[i], y[j]))

for i in range(len(x_y)):
    xm.append(x_y[i][0]) # select all x midpoint values

for i in range(len(x_y)):
    ym.append(x_y[i][1]) # select all y midpoint values   

wait = input("Press Enter to continue.")

# create x and y axes, extend by 1 in each direction so the slope lines are visible
axis((x_axis_min-1,x_axis_max+1,y_axis_min-1,y_axis_max+1))   # axis(Xmin,Xmax,Ymin,Ymax)
axis("on")                  # mathplotlib: turn axes "on"
grid(False)                 # mathplotlib: turn grid "off"

# mathplotlib: draw arrows(x,y,dx,dy) where (x,y) start and (x + dx, y + dy) is end
# i.e. arrows(xs=xm-dx, ys=ym-dy, 2dx, 2dy)
# where length of arrow is set to 2d where d is set to 1/8 = 0.125
# and dx = 2d/root(1+m^2), dy = m*dx
d  = 0.250 # initial value of half the length of each line segment
xs = []    # initialize empty list of xm starting x coordinate for arrows
ys = []    # initialize empty list of xm starting y coordinate for arrows
m  = []    # initialize empty list of m at each value of xm
dx = []    # initialize empty list of dx at each value of xm
dy = []    # initialize empty list of dy at each value of xm

for i in range(len(xm)):
    m.append(dydx(xm[i],ym[i]))  # call dydx to calculate m for each (xm,ym) ordered pair
for i in range(len(xm)):         # calculate dx for each for each (xm,ym) ordered pair
    dx.append((d)/(sqrt(1 + pow(m[i],2)))) # calculate dx for each xs
for i in range(len(xm)):         #  calculate dy  for each (xm,ym) ordered pair
    dy.append(m[i] * dx[i])      # calculate dy for each xs
for i in range(len(xm)):   
    xs.append(xm[i] - dx[i])     # xs = starting x values of arrows
    ys.append(ym[i] - dy[i])     # ys = starting y values of arrows

print("length of lines: ", 2*d)
print("slope m at each (xm,ym): ", m)
wait = input("Press Enter to continue.")
   
# draw arrows starting at each (xs,ys) ordered pair and moving by 2dx and 2dy
for i in range(len(xs)):   
  arrow(xs[i],ys[i],2*dx[i],2*dy[i],color="red",head_width = 0.05)

text(x_axis_min,y_axis_max,dydxstr[fselect])   # label graph in upper-left with derivative function

show()  # mathplotlib show display