Modelize a spring Python - python

I need to model a spring in Python for one of my classes at the university and it doesn't really work. I've the second order differential equation and I translated it in 2 first order ones to have a system.
Can someone look my code and give me an idea about what could I do? For me, the equations are good, I just don't know how to draw it...
from math import pi
from time import sleep
from turtle import *
k=1
m=1
def main():
L = 5
total_time = 0
vo = 0 #Vitesse angulaire initial (rad/s)
time_step = 0.05
while total_time < 100:
total_time += time_step
vo += (-k/m) * L * time_step
L += vo * time_step
if draw(L): break
sleep(time_step)
def init():
setup()
mode('logo')
radians()
speed(0)
tracer(False)
hideturtle()
def draw(L):
if speed() != 0: return True
clear()
pendown()
setheading(L)
penup()
pensize(5)
pencolor('red')
dot(L * 10)
home
update()
if __name__ == '__main__':
init()
main()

Your equations are good, as far as I remember from my physic lessons, but the L variable is varying from -5 to +5, not from 0 to +5.
The dot(radius) function draws a circle. The radius needs to be positive.
Just replace :
dot(L * 10)
by :
dot(51 + L * 10)
(I put "51" instead of "50" to be sure to avoid errors due to crappy approximation of float numbers)
Some little tip : when you don't understand what happens, try to determine the current values of your variables.
You could have guessed that L varies from -5 to +5 by adding print("vo:", vo, "L:", L, "total_time:", total_time) in the loop of the main function.

Related

pygame.Rect objects keep getting stuck at top = 0 and don't move when .move or .move_ip is used

This is a small part of some more complicated code but basically. I've narrowed down the problem to this one bit of code.
I'm using the formula s = ut + 1/2 at^2 to get the distance traveled in one instance to time, in this case, 0.001 seconds which will then further be used to draw the rects on a window.
import pygame
run = True
clock = pygame.time.Clock()
ex_s = pygame.Rect((500, 625, 50, 50))
start_height = ex_s.top
print(start_height, "m")
g = -9.8
v = 400
t = 0.001
while run:
ex_s.top -= (v * t) + ((g / 2) * (t ** 2))
print(ex_s.top - start_height)
if (ex_s.top - start_height) * -1 <= -10:
run = False
clock.tick(30)
I want the rectangles to move upwards/downwards based on their velocity.
The rectangles move up and get stuck whenever the top reaches y = 0. This doesn't happen if the value of "t" is increased to o.o1 or above but I need it to be 0.001 for the precision. The problem is also solved when the value of v is increased by an order of magnitude, changing g does nothing.
I tried using both move and move_ip and the y coordinate did not change at all.
I've also tried manually pushing the objects above y = 0 when they hit it but it doesn't do anything. The fact that the problem changes with decimal places has led me to believe it could be something to do with the integer coordinate system of pygame but I don't see how this would cause things to move first and then get stuck only at 0.
See Pygame doesn't let me use float for rect.move, but I need it. The problem is that a pygame.Rect object can only store integer data. So when you add a value less than 1.0 to the coordinate of a rectangle, than the position stays the same. You need to store the position of the rectangle in separate variables and update the position of the rectangle using these variables.
ex_s = pygame.Rect((500, 625, 50, 50))
ex_s_y = ex_s.top
# [...]
while run:
# [...]
# change position
ex_s_y = .....
ex_s.top = round(ex_s_y)
You didn't change the time t variable, and your formula for the motion is wrong. Also your initial time, initial velocity and g are wrong.
If you want to use an exact solution for the position, do like this.
dt = 0.05
t = 0
g = 9.8 # It's positive by definition.
v = -100 # It's upward.
while run:
y = 625 + v*t + 0.5*g*t**2 # The acceleration is +g, downward.
t += dt
ex_s.top = y
...
If you want to solve the equation of motion approximately and iteratively, do like this inside the loop.
...
y = 625
while run:
v += g * dt
y += v * dt
t += dt
ex_s.top = y
...
The above method is called sympletic Euler method. There're other more accurate methods, such as Runge-Kutta method.

Turtle slowing down for larger drawings

I have created some code in order to simulate sierpinski's triangle. For this, in my code, I set it so that it can simulate any number of points on the triangle, but I've noticed that increasing this to a large number, it ends up with the turtle taking an amount of time that increases as it goes on.
I did a sample output with 100 000 dots and this is what I got
0% done
Time since last update: 0.0019948482513427734
5.0% done
Time since last update: 1.2903378009796143
10.0% done
Time since last update: 1.8589198589324951
15.000000000000002% done
Time since last update: 2.325822114944458
20.0% done
Time since last update: 2.9351391792297363
25.0% done
Time since last update: 3.4773638248443604
30.0% done
Time since last update: 4.152036190032959
35.0% done
Time since last update: 4.7314231395721436
40.0% done
Time since last update: 5.260996103286743
44.99999999999999% done
Time since last update: 5.988528490066528
49.99999999999999% done
Time since last update: 6.804485559463501
54.99999999999999% done
Time since last update: 7.768667221069336
60.0% done
Time since last update: 8.379971265792847
65.0% done
Time since last update: 8.995774745941162
70.0% done
Time since last update: 15.876121282577515
75.00000000000001% done
Time since last update: 17.292492151260376
80.00000000000001% done
Time since last update: 29.57323122024536
85.00000000000001% done
Time since last update: 65.96741080284119
90.00000000000003% done
Time since last update: 148.21749567985535
95.00000000000003% done
the code in question to run the main loop of the program is this
t = turtle.Turtle()
t.penup()
curr_time = time()
for x in range(iterations):
#The code will take a while should the triangle be large, so this will give its % completion
if x / iterations > percent_done:
print(percent_done * 100, r"% done", sep='')
percent_done += 0.05
window.update()
print(f"Time since last update:\t{time() - curr_time}")
curr_time = time()
c = determine_point(t, init_c)
make_point(t, c)
determine_point and make_point do not iterate at all, so I can't find a reason for turtle to slow down this considerably. Why does turtle slow down as more points are being created? (screen tracer has already been set to (0,0) and the code itself works as intended
make point:
def make_point(turt: turtle.Turtle, point):
'''
Basically does turt.goto(*point) where point is a list of length 2, then places a dot at that place
I did not use goto because I forgot what the function was, and instead of doing a 2 second google search to find out,
I did a 15 second google search to find out how to set the angle of the turtle, and use trigonometry and pythagorean
theorem in order to move the turt to the right position
'''
if point[0] != turt.xcor():
y = point[1] - turt.ycor()
x = point[0] - turt.xcor()
if x > 0:
turt.setheading(math.degrees(math.atan(y / x)))
else:
turt.setheading(math.degrees(math.pi + math.atan(y / x)))
else:
turt.setheading(0 if point[1] < turt.ycor() else 180)
turt.fd(pythag(turt.xcor(), turt.ycor(), point[0], point[1]))
turt.dot(3)
determine_point:
def determine_point(turt: turtle.Turtle, initial_cords):
'''
Returns the midpoint between the turt's current coordinates and a random one of the of the 3 starting points
'''
coord = initial_cords[random.randint(0, 2)]
result = []
result.append((coord[0] - turt.xcor()) / 2 + turt.xcor())
result.append((coord[1] - turt.ycor()) / 2 + turt.ycor())
return result
pythag:
def pythag(a, b, x, y):
'''
Does pythagorean theorem to find the length between 2 points
'''
return math.sqrt((x - a) ** 2 + (y - b) ** 2)
Although your code doesn't require more resources on each iteration, your program does because it is built atop libraries that do. This is true of both turtle and tkinter.
Although we think of turtle dot() as putting up dead ink, as opposed to stamp which can be selectively removed, to the underlying tkinter graphics, everything is live ink and adds to its (re)display lists.
I couldn't completely remove the increase in time for each iteration, but by optimizing the code, I believe I've reduced it to no longer being an issue. Some of my optimizations: put turtle into radians mode to avoid calls to math.degrees(); reduce calls into turtle, eg. by getting x and y in one step via position() rather than calling xcor() and ycor(); turn off turtle's undo buffer as it keeps a growing list of graphic commands:
from turtle import Screen, Turtle
from time import time
from random import choice
from math import sqrt, atan, pi
iterations = 100_000
init_c = [(300, -300), (300, 300), (-300, -300)]
def make_point(t, point):
'''
Basically do turtle.goto(*point) where point is a list of length 2,
then places a dot at that place
I did not use goto() because I forgot what the function was, and instead
of doing a 2 second google search to find out, I did a 15 second google
search to find out how to set the angle of the turtle, and use trigonometry
and pythagorean theorem in order to move the turtle to the right position
'''
x, y = t.position()
if point[0] != x:
dx = point[0] - x
dy = point[1] - y
if dx > 0:
t.setheading(atan(dy / dx))
else:
t.setheading(pi + atan(dy / dx))
else:
t.setheading(0 if point[1] < y else pi)
t.forward(pythag(x, y, point[0], point[1]))
t.dot(2)
def determine_point(t, initial_cords):
'''
Return the midpoint between the turtles's current coordinates
and a random one of the of the 3 starting points
'''
coord = choice(initial_cords)
x, y = t.position()
return [(coord[0] - x) / 2 + x, (coord[1] - y) / 2 + y]
def pythag(a, b, x, y):
'''
Do pythagorean theorem to find the length between 2 points
'''
return sqrt((x - a) ** 2 + (y - b) ** 2)
screen = Screen()
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
turtle.setundobuffer(None)
turtle.radians()
turtle.penup()
fraction_done = 0
curr_time = time()
for iteration in range(iterations):
# The code will take a while should the triangle be large, so this will give its % completion
if iteration / iterations > fraction_done:
screen.update()
print(fraction_done * 100, r"% done", sep='')
fraction_done += 0.05
print(f"Time since last update:\t{time() - curr_time}")
curr_time = time()
make_point(turtle, determine_point(turtle, init_c))
screen.update()
screen.exitonclick()
CONSOLE
% python3 test.py
0% done
Time since last update: 0.024140119552612305
5.0% done
Time since last update: 1.2522082328796387
10.0% done
Time since last update: 1.619455099105835
15.000000000000002% done
Time since last update: 1.9793877601623535
20.0% done
...
Time since last update: 9.460317373275757
85.00000000000001% done
Time since last update: 10.161489009857178
90.00000000000003% done
Time since last update: 10.585438966751099
95.00000000000003% done
Time since last update: 11.479820966720581

Running trials in python

I'm a beginner when it comes to using Python and wants to do simulations where I estimate pi using trials. I first important random numbers for both the x and y values (between 0 and 1), then I check to see if those numbers are inside the circle. If they are then I estimate pi using the formula:
(4 * (number of points inside circle/number of trials)).
My code is attached below. With the seed I'm using I should get a number around 3.02 but instead, am getting 4.00. Could anyone point in the direction of where I've gone wrong?
import random
random.seed(1000)
x_value = random.random()
y_value = random.random()
print(x_value)
print(y_value)
pointsInCircle= 0
numberOfTrials = 1000
for trials in range(trials):
if 1 > ((x_value ** 2) + (y_value ** 2)):
pointsInCircle = pointsInCircle + 1
else:
print("No estimation possbile")
pi = 4 * (pointsInCircle/numberOfTrials)
print(pi)
The following 2 lines should go inside the for loop. At each trail it needs to have 2 random numbers which are different from the previous.
x_value = random.random()
y_value = random.random()
Hence the code should look like below.
import random
random.seed(1000)
# print(x_value)
# print(y_value)
pointsInCircle= 0
numberOfTrials = 1000
for trials in range(numberOfTrials):
x_value = random.random()
y_value = random.random()
if 1 > ((x_value ** 2) + (y_value ** 2)):
pointsInCircle = pointsInCircle + 1
else:
print("No estimation possbile")
pi = 4 * (pointsInCircle/numberOfTrials)
print(pi)
Which yields the answer 3.02.
I'm not sure I can provide a full solution without more information, but it seems like pointsInCircle/numberOfTrials == 1, therefore pointsInCircle == numberOfTrials.
Maybe use some print debugging to find the values of those two variables and see if that's the problem. If you get any more information, I'd be happy to help further!

How can i draw a nice spiral in python with turtle?

I have a homework to draw a spiral(from inside to outside) in python with turtle, but I cant think of a way to do that, beside what I did its need to be like this:
I tried to do it like that, but its not working properly.
import turtle
turtle.shape ('turtle')
d = 20 #Distance
a = 1 #StartingAngle
x = 200 #Num of loops
for i in range (x):
turtle.left(a)
turtle.forward(d)
a = a + 5
You are increasing the wrong variable in your loop.
A spiral is like a "circunference whose radius increases over time".
You should increase the d variable over time, and not the angle a. The amount of the increase of each loop will be essential to determine the appearance of your spiral, but you can obtain a good value for the increase with some calculation or by trial and error.
import turtle
from math import sin,cos,pi
t=turtle.Turtle()
t.speed(0)
n=50 #number of spirals
d=10 #distance between 2 spirals
r=0 #radius
x,y = 0, 0
cur_r = r
for i in range(n):
for a in range(1,360, 4):
r = cur_r + d*a/360.0
a *= pi/180.0
y = r*sin(a)
x = r*cos(a)
turtle.goto(x,y)
cur_r += d
import turtle
i=0
while i<4:
turtle.speed(0)
turtle.pensize(1+i)
turtle.forward(1+i)
t.color("blue")
turtle.bgcolor("pink")
t.lt(3)
i+=0.003
If you are using program other than IDLE(such as pycharm), add
turtle.mainloop()
at the end of the code
import turtle as t
for i in range(360):
t.circle(i,20)
Source: Explained here

Python Beginner - How to equate a regression line from clicks and display graphically?

I am reading Python Programming by John Zelle and I am stuck on one the exercises shown in the picture below.
You can view my code below. I know the code is very ugly. (Any tips are appreciated)
Here's my code so far:
from graphics import *
def regression():
# creating the window for the regression line
win = GraphWin("Regression Line - Start Clicking!", 500, 500)
win.setCoords(0.0, 0.0, 10.0, 10.0)
rect = Rectangle(Point(0.5, 0.1), Point(2.5, 2.1))
rect.setFill("red")
rect.draw(win)
Text(rect.getCenter(), "Done").draw(win)
message = Text(Point(5, 0.5), "Click in this screen")
message.draw(win)
points = [] # list of points
n = 0 # count variable
sumX = 0
sumY = 0
while True:
p = win.getMouse()
p.draw(win)
# if user clicks in a red square it exits the loop and calculates the regression line
if (p.getX() >= 0.5 and p.getX() <= 2.5) and (p.getY() >= 0.1 and p.getY() <= 2.1):
break
n += 1 # count of the points
# get the sum of the X and Y points
sumX = sumX + p.getX()
sumY = sumY + p.getY()
# tuple of the X and Y points
dot = (p.getX(), p.getY())
points.append(dot)
avgX = sumX / n
avgY = sumY / n
top = 0
bottom = 0
# my ugly attempt at the regression equation shown in the book
for i in points:
gp = 0
numer = points[gp][0] * points[gp][1]
top = top + numer
denom = points[gp][0] ** 2
bottom = bottom + denom
gp += 1
m = (top - sumX * sumY) / (bottom - sumX ** 2)
y1 = avgY + m * (0.0 - avgX)
y2 = avgY + m * (10.0 - avgX)
regressionline = Line(Point(0, y1), Point(10.0, y2))
regressionline.draw(win)
raw_input("Press <Enter> to quit.")
win.close()
regression()
When I run the program the regression line never appears to be the real line of best fit. I believe I am interpreting the regression equation incorrectly in my code. What needs to be changed to get the correct regression line?
Issues:
from my_library import * should be avoided; better to specify exactly what you want from it. This helps keep your namespace uncluttered.
you've got one massive block of code; better to split it into separate functions. This makes it much easier to think about and debug, and may help you reuse code later. Sure, it's a toy problem, you're not going to reuse it - but the whole point of doing exercises is to develop good habits, and factoring your code this way is definitely a good habit! A general rule of thumb - if a function contains more than about a dozen lines of code, you should consider splitting it further.
the exercise asks you to keep track of x, y, xx, and xy running sums while getting input points. I think this is kind of a bad idea - or at least more C-ish than Python-ish - as it forces you to do two different tasks at once (get points and do math on them). My advice would be: if you are getting points, get points; if you are doing math, do math; don't try doing both at once.
similarly, I don't like the way you've got the regression calculation worrying about where the sides of the window are. Why should it know or care about windows? I hope you like my solution to this ;-)
Here's my refactored version of your code:
from graphics import GraphWin, Point, Line, Rectangle, Text
def draw_window()
# create canvas
win = GraphWin("Regression Line - Start Clicking!", 500, 500)
win.setCoords(0., 0., 10., 10.)
# exit button
rect = Rectangle(Point(0.5, 0.1), Point(2.5, 2.1))
rect.setFill("red")
rect.draw(win)
Text(rect.getCenter(), "Done").draw(win)
# instructions
Text(Point(5., 0.5), "Click in this screen").draw(win)
return win
def get_points(win):
points = []
while True:
p = win.getMouse()
p.draw(win)
# clicked the exit button?
px, py = p.getX(), p.getY()
if 0.5 <= px <= 2.5 and 0.1 <= py <= 2.1:
break
else:
points.append((px,py))
return points
def do_regression(points):
num = len(points)
x_sum, y_sum, xx_sum, xy_sum = 0., 0., 0., 0.
for x,y in points:
x_sum += x
y_sum += y
xx_sum += x*x
xy_sum += x*y
x_mean, y_mean = x_sum/num, y_sum/num
m = (xy_sum - num*x_mean*y_mean) / (xx_sum - num*x_mean*x_mean)
def lineFn(xval):
return y_mean + m*(xval - x_mean)
return lineFn
def main():
# set up
win = draw_window()
points = get_points(win)
# show regression line
lineFn = do_regression(points)
Line(
Point(0., lineFn(0. )),
Point(10., lineFn(10.))
).draw(win)
# wait to close
Text(Point(5., 5.), "Click to exit").draw(win)
win.getMouse()
win.close()
if __name__=="__main__":
main()
the for loop is all messed up! you have an i that changes in the loop, but then use gp which is always 0.
you want something more like:
for (X, Y) in points:
numer += X * Y
denom += X * X
...or move gp = 0 to before the for loop.
...or drop that part completely and add a sumXY and a sumXX to the sumX and sumY.
either way, once you fix that it should be ok (well, or maybe some other bug....).

Categories

Resources