diff --git a/Sou_Sapphire_Solver.py b/Sou_Sapphire_Solver.py new file mode 100644 index 0000000..74e02c8 --- /dev/null +++ b/Sou_Sapphire_Solver.py @@ -0,0 +1,230 @@ +import numpy as np +import matplotlib.pyplot as plt +from functools import lru_cache + +''' + +All the differential equations were solved by hand. +It took a while. + +-Souhail + +''' + +# af''+bf'+cf=0 +def DIFF_SOLVE(a,b,c,y0,derivative=False): + tauBC = b/c + tauBA = b/a + tau = tauBC*tauBA - 4 + omega2 = c/a + if tau > 0: + #print('TAU - 1') + r1 = omega2*(-b+np.sqrt(tau/c)) + r2 = omega2*(-b-np.sqrt(tau/c)) + if not derivative: + return lambda t: y0*((r2*(np.exp(r1*t)-np.exp(r2*t))/(r2-r1))+np.exp(r2*t)) + else: + return lambda t: y0*((r2*(r1*np.exp(r1*t)-r2*np.exp(r2*t))/(r2-r1))+r2*np.exp(r2*t)) + elif tau < 0: + #print('TAU - 2') + aph = -b*omega2 + bet = omega2*np.sqrt(-tau/c) + if not derivative: + return lambda t: y0*np.exp(aph*t)*(np.cos(bet*t)-(aph/bet)*np.sin(bet*t)) + else: + return lambda t: -aph*y0*np.exp(aph*t)*np.sin(bet*t)*((aph/bet)+(bet/aph)) + else: + #print('TAU - 0') + r0 = -b*omega2 + if not derivative: + return lambda t: y0*(1-t*r0)*np.exp(r0*t) + else: + return lambda t: -y0*r0*r0*np.exp(r0*t) + +def EXTREMAS(arr): + sign = np.sign(np.diff(arr)) + sign_d = np.where(np.diff(sign)!=0)[0] + 1 + + #boundary check + if sign[0] == -1: + sign_d = np.concatenate(([0],sign_d)) + if sign[-1] == 1: + sign_d = np.concatenate((sign_d,[len(arr)-1])) + + #the main deal + maxima = np.zeros_like(arr,dtype=bool) + minima = np.zeros_like(arr,dtype=bool) + for i in sign_d: + try: + if sign[i-1] == 1 and sign[i] == -1: + maxima[i] = True + elif sign[i-1] == -1 and sign[i] == 1: + minima[i] = True + except IndexError: + pass + + return np.where(maxima)[0], np.where(minima)[0] + +''' +ay''+by'+cy=g(t) +with g(t)=Asin(w*t+phi) +''' + +def LAPLACE_SOLVE(a,b,c,y0,A,w,phi,derivative=False): + delta = b*b-4*a*c + sgn = np.sign(delta) + + cphi = np.cos(phi) + sphi = np.sin(phi) + + if sgn == 1: + r1 = (-b+np.sqrt(delta))/(2*a) + r2 = (-b-np.sqrt(delta))/(2*a) + + B = A*r1*sphi+A*w*cphi + B /= a*(r1-r2)*(r1*r1+w*w) + C = A*r2*sphi+A*w*cphi + C /= a*(r2-r1)*(r2*r2+w*w) + D = A*np.exp(phi*1j) + E = D - 2*A*cphi + D /= 2*a*1j*(r1-w*1j)*(r2-w*1j) + E /= 2*a*1j*(r1+w*1j)*(r2+w*1j) + F = (a*r1*y0+b*y0)/(a*(r1-r2)) + G = (a*r2*y0+b*y0)/(a*(r2-r1)) + + #y(t)= + if not derivative: + return lambda t: (B+F)*np.exp(r1*t)+(C+G)*np.exp(r2*t)+(D*np.exp(w*t*1j)+E*np.exp(-w*t*1j))*np.sin(w*t) + #y'(t)= + else: + return lambda t: (B+F)*r1*np.exp(r1*t)+(C+G)*r2*np.exp(r2*t)+w*np.sin(w*t)*(D*(1+1j)*np.exp(w*t*1j)+E*(1-1j)*np.exp(-w*t*1j)) + + elif sgn == 0: + r = -b/(2*a) + + B = -r*r*sphi-2*r*w*cphi+w*w*sphi + B *= (A/(a*((r*r+w*w)**2))) + C = r*sphi+w*cphi + C *= (A/(a*(r*r+w*w))) + D = A*np.exp(phi*1j) + E = D - 2*A*cphi + D /= 2*a*1j*((r-w*1j)**2) + E /= 2*a*1j*((r+w*1j)**2) + F = y0 + G = (r+(b/a))*y0 + + #y(t)= + if not derivative: + return lambda t: (B+F+C*t+G*t)*np.exp(r*t)+np.sin(w*t)*(D*np.exp(w*t*1j)+E*np.exp(-w*t*1j)) + #y'(t)= + else: + return lambda t: (B+F+C+G)*np.exp(r*t)+(C+G)*t*r*np.exp(r*t)+w*np.sin(w*t)*(D*(1+1j)*np.exp(w*t*1j)+E*(1-1j)*np.exp(-w*t*1j)) + + elif sgn == -1: + aph = -b/(2*a) + bet = np.sqrt(-delta)/(2*a) + r1 = aph + bet*1j + r2 = aph - bet*1j + + B = A*(r1)*sphi+A*w*cphi + C = A*(r2)*sphi+A*w*cphi + B /= 2*a*1j*bet*(((r1)**2)+w*w) + C /= -2*a*1j*bet*(((r2)**2)+w*w) + D = A*np.exp(phi*1j) + E = D - 2*A*cphi + D /= 2*a*1j*(((aph-w*1j)**2)+bet*bet) + E /= 2*a*1j*(((aph+w*1j)**2)+bet*bet) + F = a*r1*y0+b*y0 + G = a*r2*y0+b*y0 + F /= 2*a*bet*1j + G /= 2*a*bet*1j + + #y(t)= + if not derivative: + return lambda t: (B+F)*np.exp(t*r1)+(C+G)*np.exp(t*r2)+D*np.exp(w*t*1j)+E*np.exp(-w*t*1j) + #y'(t)= + else: + return lambda t: r1*(B+F)*np.exp(t*r1)+r2*(C+G)*np.exp(t*r2)+1j*w*(D*np.exp(w*t*1j)-E*np.exp(-w*t*1j)) + +@lru_cache +def LAPLACE_SOLVE_MECH(m,h,k,x0,v0,derivative=False): + delta = h*h-4*m*k + sgn = np.sign(delta) + + if sgn == 1: + r1 = (-h+np.sqrt(delta))/(2*m) + r2 = (-h-np.sqrt(delta))/(2*m) + + A = (m*r1*x0+m*v0+h*x0)/(m*(r1-r2)) + B = (m*r2*x0+m*v0+h*x0)/(m*(r2-r1)) + + if not derivative: + return lambda t: A*np.exp(r1*t)+B*np.exp(r2*t) + else: + return lambda t: r1*A*np.exp(r1*t)+r2*B*np.exp(r2*t) + + elif sgn == -1: + aph = -h/(2*m) + bet = np.sqrt(-delta)/(2*m) + r1 = aph+bet*1j + r2 = aph-bet*1j + A = (m*r1*x0+m*v0+h*x0)/(m*(r1-r2)) + B = (m*r2*x0+m*v0+h*x0)/(m*(r2-r1)) + + if not derivative: + return lambda t: A*np.exp(r1*t)+B*np.exp(r2*t) + else: + return lambda t: r1*A*np.exp(r1*t)+r2*B*np.exp(r2*t) + + else: + r = -h/(2*m) + + B = (m*r*x0+m*v0+h*v0)/m + + if not derivative: + return lambda t: np.exp(r*t)*(x0+B*t) + else: + return lambda t: np.exp(r*t)*(x0+B+B*r*t) + +def tickUnit(data, unit): + abs_data = np.abs(data) + scale_factors = [(1e24, 'Y'), (1e21, 'Z'), (1e18, 'E'),(1e15,'P'), + (1e12, 'T'), (1e9, 'G'), (1e6, 'M'),(1e3, 'k'), + (1, ''), (1e-3, 'm'),(1e-6, 'μ'), (1e-9, 'n'), + (1e-12, 'p'),(1e-15,'f'),(1e-18,'a'), + (1e-21,'z'),(1e-24,'y')] + for factor, prefix in scale_factors: + if np.max(abs_data) >= factor: + return f'{{:.1e}} {prefix}{unit}{{}}{{}}' + return f'{{:.{int(-np.log10(abs_data.min()))+1}f}} {unit}{{}}{{}}' + +if __name__ == '__main__': + TIME = np.linspace(0,100,400) + a = 1 + b = 1 + c = 1 + y0 = 10 + A = 10 + w = 1 + phi = 0 + func = LAPLACE_SOLVE(a,b,c,y0,A,w,phi) + values = np.round(func(TIME),2) + + plt.plot(TIME,A*np.sin(w*TIME+phi),label='Générateur',color='green') + plt.plot(TIME,values,label='Partie Réelle (Observée)') + plt.plot(TIME,-1j*values, color='red',label='Partie Imaginaire') + plt.grid() + plt.legend() + plt.show() + '''TAU_2 = DIFF_SOLVE(1,1,1,1) + TAU_1 = DIFF_SOLVE(0.25,1,0.25,1) + TAU_0 = DIFF_SOLVE(0.25,1,1,1) + print('TAU - 2') + for t in TIME: + print('TIME:',np.round(t,2),'VALUE:',np.round(TAU_2(t),2)) + print('TAU - 1') + for t in TIME: + print('TIME:',np.round(t,2),'VALUE:',np.round(TAU_1(t),2)) + print('TAU - 0') + for t in TIME: + print('TIME:',np.round(t,2),'VALUE:',np.round(TAU_0(t),2))''' diff --git a/build main.bat b/build main.bat index 632bef5..2e1a47f 100644 --- a/build main.bat +++ b/build main.bat @@ -1 +1 @@ -pyinstaller main.py --add-data "C:/Users/x64/AppData/Local/Programs/Python/Python39/Lib/site-packages/customtkinter;customtkinter/" --add-data "./res/case.png;res/" --add-data "./res/condensateur.png;res/" --add-data "./res/electron.png;res/" --add-data "./res/generateur.png;res/" --add-data "./res/chemsList.txt;res/" --add-data "./res/lucyna.png;res/" --add-data "./res/neutron.png;res/" --add-data "./res/proton.png;res/" --add-data "./res/resistor.png;res/" --add-data "./res/soleil.png;res/" --add-data "./res/terre.png;res/" -n "Suite de Physique" -y \ No newline at end of file +pyinstaller main.py --upx-dir="C:/UPX" --add-data "C:/Users/x64/AppData/Local/Programs/Python/Python39/Lib/site-packages/customtkinter;customtkinter/" --add-data "./res/case.png;res/" --add-data "./res/condensateur.png;res/" --add-data "./res/electron.png;res/" --add-data "./res/generateur.png;res/" --add-data "./res/chemsList.txt;res/" --add-data "./res/lucyna.png;res/" --add-data "./res/neutron.png;res/" --add-data "./res/proton.png;res/" --add-data "./res/resistor.png;res/" --add-data "./res/soleil.png;res/" --add-data "./res/terre.png;res/" -n "Suite de Physique" -y \ No newline at end of file diff --git a/guiWorks.py b/guiWorks.py index 4b880da..baee800 100644 --- a/guiWorks.py +++ b/guiWorks.py @@ -135,7 +135,7 @@ def Zwork(feedback=None): elemVal.configure(text=str(currVal)+" électron(s)") elemsBox.set("1-Hydrogène") print("Hydrogène 1") - + def elemsBoxy(elemy): #Ztextbox.delete("0.0","end") #Ztextbox.insert("0.0",elemy.split("-")[0]) @@ -207,7 +207,7 @@ def circle_dist(cx,cy,N,n1,n2,nd1,nd2,rayon,randy=False): nd1 += 1 else: nd2 += 1 - + Zwork(1) def updateGraphics(): global t, dessN, dessZ, currZ, currN, NZ diff --git a/main.py b/main.py index 500ac28..2d06549 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ import rc import guiWorks import nbodyTk +import mech import guideManager as gum #import pyi_splash @@ -26,13 +27,17 @@ def GRAV_PROG(): def RC_PROG(): rc.MAIN_RC(app) +def MECH_PROG(): + mech.MAIN_MECH(app) #pyi_splash.close() ATOM = ctk.CTkButton(app, text="L'atome",command=ATOM_PROG,font=("Arial", 25),width=250,height=30) -ATOM.place(relx=0.5,rely=0.25,anchor=S) +ATOM.place(relx=0.5,rely=0.2,anchor=S) GRAV = ctk.CTkButton(app, text="Gravitation", command=GRAV_PROG,font=("Arial", 25),width=250,height=30) -GRAV.place(relx=0.5,rely=0.5,anchor=S) +GRAV.place(relx=0.5,rely=0.4,anchor=S) COND = ctk.CTkButton(app, text="Circuit RC",command=RC_PROG,font=("Arial", 25),width=250,height=30) -COND.place(relx=0.5,rely=0.75,anchor=S) +COND.place(relx=0.5,rely=0.6,anchor=S) +COND = ctk.CTkButton(app, text="Mécanique",command=MECH_PROG,font=("Arial", 25),width=250,height=30) +COND.place(relx=0.5,rely=0.8,anchor=S) app.mainloop() diff --git a/mech.py b/mech.py new file mode 100644 index 0000000..9945698 --- /dev/null +++ b/mech.py @@ -0,0 +1,305 @@ +from tkinter import * +import customtkinter as ctk +import matplotlib.pyplot as plt +from matplotlib.ticker import EngFormatter +from functools import lru_cache +from Sou_Sapphire_Solver import LAPLACE_SOLVE_MECH, EXTREMAS +from PIL import Image,ImageTk +import numpy as np +import guideManager as gum +import time + +def MAIN_MECH(mainy): + ctk.set_appearance_mode("dark") + ctk.set_default_color_theme("dark-blue") + + app = ctk.CTkToplevel(mainy) + app.geometry("750x450") + app.resizable(False,False) + app.title("Mécanique") + + gum.createGuideWindow(app, "Guide: Mécanique", "mech.txt") + + canvas = Canvas(app,bg="#1A1A1A",width=750,height=450) + canvas.pack() + + mEntry = ctk.CTkEntry(app, font=("Arial", 25), width = 100,height=5) + hEntry = ctk.CTkEntry(app, font=("Arial", 25), width = 100,height=5) + kEntry = ctk.CTkEntry(app, font=("Arial", 25), width = 100,height=5) + x0Entry = ctk.CTkEntry(app, font=("Arial", 25), width = 100,height=5) + v0Entry = ctk.CTkEntry(app, font=("Arial", 25), width = 100,height=5) + mEntry.place(relx=0.25,rely=0.5,anchor='center') + hEntry.place(relx=0.5,rely=0.5,anchor='center') + kEntry.place(relx=0.75,rely=0.5,anchor='center') + x0Entry.place(relx=1/3,rely=2/3,anchor='center') + v0Entry.place(relx=2/3,rely=2/3,anchor='center') + mEntry.insert(0,'1') + hEntry.insert(0,'0.1') + kEntry.insert(0,'1') + x0Entry.insert(0,'0') + v0Entry.insert(0,'0') + ctk.CTkLabel(app,text="Masse [Kg]", font=("Arial", 22)).place(relx=0.25,rely=0.45,anchor=S) + ctk.CTkLabel(app,text="Frottement [Kg.s-1]", font=("Arial", 22)).place(relx=0.5,rely=0.45,anchor=S) + ctk.CTkLabel(app,text="Raideur [N.m-1]", font=("Arial", 22)).place(relx=0.75,rely=0.45,anchor=S) + ctk.CTkLabel(app,text="Position Initiale [m]", font=("Arial", 22)).place(relx=1/3,rely=0.62,anchor=S) + ctk.CTkLabel(app,text="Vitesse Initiale [m.s-1]", font=("Arial", 22)).place(relx=2/3,rely=0.62,anchor=S) + + def imgManip(name,width,height): + return ImageTk.PhotoImage(Image.open("./res/"+name+".png").resize((int(width), int(height)), Image.Resampling.LANCZOS)) + + boxImg = imgManip('box',70,70) + boxOffset = 8 + xBox = 400 + yBox = 100 + boxOffset + xBorder = 50 + yBorder = 100 + yLine = 135 + borderImg = imgManip('border',70,70) + springIm = Image.open("./res/spring.png") + springScale = 350-70//2+boxOffset + springImg = ImageTk.PhotoImage(springIm.resize((int(abs(springScale)), 70), Image.Resampling.LANCZOS)) + boxShape = canvas.create_image(xBox,yBox,anchor='center',image=boxImg) + canvas.create_image(xBorder,yBorder,anchor='center',image=borderImg) + springShape = canvas.create_image(xBorder+springScale//2,yBox,anchor='center',image=springImg) + canvas.create_line(49, yLine, 730, yLine,width=2,fill='white') + xO, yO = 400, 100 + canvas.create_line(xO,yO+35,xO,yO+35+boxOffset//2,width=2,fill='white') + canvas.create_text(xO-0.75,yO+35+boxOffset*1.5,anchor='center',text='o',font=('Arial',20),fill='white') + globals().update({'springGarbage' : {}}) + + def updatePosValue(event): + springGarbage.clear() + try: + actualX = 10*float(x0Entry.get())+400 + except: + actualX = 0 + x0Entry.delete(0,END) + x0Entry.insert(0,'0') + canvas.coords(boxShape,actualX,yBox) + springScale = actualX-70-boxOffset + springImg = ImageTk.PhotoImage(springIm.resize((int(np.max([abs(springScale),1])), 70), Image.Resampling.LANCZOS)) + springGarbage.update({'t'+str(actualX):springImg}) + canvas.coords(springShape,xBorder+springScale//2,yBox) + canvas.itemconfig(springShape,image=springImg) + def updaterPos(event): + updatePosValue(event) + canvas.after(2,updatePosValue,event) + def updatePos(event): + springGarbage.clear() + canvas.coords(boxShape,event.x,yBox) + springScale = event.x-70-boxOffset + springImg = ImageTk.PhotoImage(springIm.resize((int(np.max([abs(springScale),1])), 70), Image.Resampling.LANCZOS)) + springGarbage.update({'t'+str(event.x):springImg}) + canvas.coords(springShape,xBorder+springScale//2,yBox) + canvas.itemconfig(springShape,image=springImg) + def set_newpos(event): + updatePos(event) + x0Entry.delete(0,END) + x0Entry.insert(0,str((event.x-400)/10)) + canvas.bind('', updatePos) + canvas.bind('', set_newpos) + x0Entry.bind('',updaterPos) + x0Entry.bind('',updaterPos) + + def unpack_values(): + m = float(mEntry.get()) + h = float(hEntry.get()) + k = float(kEntry.get()) + x0 = float(x0Entry.get()) + v0 = float(v0Entry.get()) + try: + assert (m > 0) and (h >= 0) and (k > 0) + except: + print("Donnez des valuers positives/valides.") + return [0,0,0,0,0] + return [m,h,k,x0,v0] + + def nulReturn(): + return 0 + sim = False + func = nulReturn() + funcPRIME = nulReturn() + T0 = time.time() + def simuler(): + global sim, func, funcPRIME, T0 + m, h, k,x0,v0 = unpack_values() + sim = True + if m*k == 0: + sim = False + if sim: + func = LAPLACE_SOLVE_MECH(m,h,k,x0,v0) + funcPRIME = LAPLACE_SOLVE_MECH(m,h,k,x0,v0,1) + T0 = time.time() + updateGraphics(h,k,0) + + def arreter(memes=False): + global sim + sim = False + canvas.delete('all') + springGarbage.clear() + boxOffset = 8 + xBox = 400 + yBox = 100 + boxOffset + xBorder = 50 + yBorder = 100 + yLine = 135 + springScale = 350-70//2+boxOffset + boxShape = canvas.create_image(xBox,yBox,anchor='center',image=boxImg) + canvas.create_image(xBorder,yBorder,anchor='center',image=borderImg) + springShape = canvas.create_image(xBorder+springScale//2,yBox,anchor='center',image=springImg) + canvas.create_line(49, yLine, 730, yLine,width=2,fill='white') + canvas.create_line(xO,yO+35,xO,yO+35+boxOffset//2,width=2,fill='white') + canvas.create_text(xO-0.75,yO+35+boxOffset*1.5,anchor='center',text='o',font=('Arial',20),fill='white') + def updatePos(event): + springGarbage.clear() + canvas.coords(boxShape,event.x,yBox) + springScale = event.x-70-boxOffset + springImg = ImageTk.PhotoImage(springIm.resize((int(np.max([abs(springScale),1])), 70), Image.Resampling.LANCZOS)) + springGarbage.update({'t'+str(event.x):springImg}) + canvas.coords(springShape,xBorder+springScale//2,yBox) + canvas.itemconfig(springShape,image=springImg) + def set_newpos(event): + updatePos(event) + x0Entry.delete(0,END) + x0Entry.insert(0,str((event.x-400)/10)) + def updatePosValue(event): + springGarbage.clear() + try: + actualX = 10*float(x0Entry.get())+400 + except: + actualX = 0 + x0Entry.delete(0,END) + x0Entry.insert(0,'0') + canvas.coords(boxShape,actualX,yBox) + springScale = actualX-70-boxOffset + springImg = ImageTk.PhotoImage(springIm.resize((int(np.max([abs(springScale),1])), 70), Image.Resampling.LANCZOS)) + springGarbage.update({'t'+str(actualX):springImg}) + canvas.coords(springShape,xBorder+springScale//2,yBox) + canvas.itemconfig(springShape,image=springImg) + def updaterPos(event): + updatePosValue(event) + canvas.after(2,updatePosValue,event) + if memes: + canvas.bind('', updatePos) + canvas.bind('', set_newpos) + x0Entry.bind('',updaterPos) + x0Entry.bind('',updaterPos) + if not memes: + app.after(50,arreter,True) + + def energyPlot(): + m, h, k,x0,v0 = unpack_values() + X, Y = [],[] + if not (m*k == 0): + TIME = np.linspace(0,20*np.sqrt(m/k),400) + X = np.real(LAPLACE_SOLVE_MECH(m,h,k,x0,v0)(TIME)) + V = np.real(LAPLACE_SOLVE_MECH(m,h,k,x0,v0,1)(TIME)) + Ep = 0.5*(X**2)*k + Ec = 0.5*(V**2)*m + Et = [Ep[i]+Ec[i] for i in range(len(TIME))] + maxE = max(Et) + plt.figure() + title = 'Énergie en Oscillations en Régime ' + sgn = -int(np.sign(h*h-4*m*k)) + if h==0: + title+='Harmonique' + else: + title += ['Apériodique','Critique','Pseudo-Périodique'][sgn+1] + title += r', $\zeta$ = '+str(np.round(h/(2*np.sqrt(k*m)),2)) + plt.title(title) + plt.plot(X, Ep,color='fuchsia',label='Énergie Potentielle',lw=2) + plt.plot(X, Ec,color='dodgerblue',label='Énergie Cinétique',lw=2) + plt.gca().xaxis.set_major_formatter(EngFormatter(unit='m')) + plt.gca().yaxis.set_major_formatter(EngFormatter(unit='J')) + try: + extX = EXTREMAS(X) + maxX = X[extX[0][0]] + minX = X[extX[1][0]] + if x0 < minX: + minX = x0 + elif x0 > maxX: + maxX = x0 + plt.vlines(maxX,0,maxE,linestyles='--',color='black') + plt.vlines(minX,0,maxE,linestyles='--',color='black') + if h==0: + plt.gca().text(maxX,0,r'$X_m$') + plt.gca().text(minX,0,r'$-X_m$') + except IndexError: + print('') + plt.plot(X,Et,color='mediumorchid',label='Énergie Totale') + if h==0: + plt.gca().text(0,maxE,'E') + plt.xlabel('Position') + plt.ylabel('Énergie') + plt.legend() + plt.grid() + plt.show() + + + ctk.CTkButton(app,text="Simuler", font=("Arial", 24),command=simuler).place(relx=1/3,rely=0.8,anchor=CENTER) + ctk.CTkButton(app,text="Arrêter", font=("Arial", 24),command=arreter).place(relx=2/3,rely=0.8,anchor=CENTER) + ctk.CTkButton(app,text="Tracer l'énergie", font=("Arial", 24),command=energyPlot).place(relx=1/2,rely=0.9,anchor=CENTER) + + + FPS = 24 + #@lru_cache + def updateGraphics(h,k,t): + global sim, func, funcPRIME,T0 + #canvas.place(rely=0.5,relx=0.5,anchor=CENTER) + canvas.delete('all') + springGarbage.clear() + + if sim: + #print(func(t)) + xBox = np.real(func(t))*10+400 + + canvas.create_image(xBox,yBox,anchor='center',image=boxImg) + canvas.create_image(xBorder,yBorder,anchor='center',image=borderImg) + + #springScale = abs((300-70//2+boxOffset)*np.cos(10*t)) + #springScale = abs((300-70//2+boxOffset)*2*np.cos(10*t)) + #springImg = imgManip('spring',springScale,70) + T = k*(xBox-400) + springScale = xBox-70-boxOffset + springImg = ImageTk.PhotoImage(springIm.resize((int(np.max([abs(springScale),1])), 70), Image.Resampling.LANCZOS)) + springGarbage.update({'t'+str(t):springImg}) + canvas.create_image(xBorder+springScale//2,yBox,anchor='center',image=springImg) + + canvas.create_line(49, yLine, 730, yLine,width=2,fill='white') + + prOffset = 1 + + #Vecteur Poids + canvas.create_text(xBox+16-prOffset,yBox+44,text=u'\u20D7',font=("Arial", 24),fill='lime') + canvas.create_text(xBox+15-prOffset,yBox+45,text='P',font=("Arial", 15),fill='lime') + canvas.create_line(xBox-prOffset, yBox, xBox-prOffset, yBox+70, arrow=LAST,fill='lime',width=2) + + #Vecteur Réaction + canvas.create_text(xBox+16+prOffset,yBox-1,text=u'\u20D7',font=("Arial", 24),fill='red') + canvas.create_text(xBox+15+prOffset,yBox,text='R',font=("Arial", 15),fill='red') + canvas.create_line(xBox+prOffset, yBox+35-boxOffset, xBox+prOffset, yBox-75+35, arrow=LAST,fill='red',width=2) + + #Vecteur Tension + canvas.create_text(xBox-35+boxOffset-T/8,yBox-boxOffset-8,text=u'\u20D7',font=("Arial", 24),fill='cyan') + canvas.create_text(xBox-35+boxOffset-T/8,yBox-boxOffset-7,text='T',font=("Arial", 15),fill='cyan') + canvas.create_line(xBox-35+boxOffset, yBox, xBox-35+boxOffset-T/4, yBox, arrow=LAST,fill='cyan',width=2) + + #Vecteur Tension + f = 50*np.real(funcPRIME(t))*h + canvas.create_text(xBox-f/4,yBox-boxOffset-8+35,text=u'\u20D7',font=("Arial", 24),fill='red') + canvas.create_text(xBox-f/4,yBox-boxOffset-7+35,text='f',font=("Arial", 15),fill='red') + canvas.create_line(xBox, yBox+35-boxOffset, xBox-f/2, yBox+35-boxOffset, arrow=LAST,fill='red',width=2) + + canvas.create_line(xO,yO+35,xO,yO+35+boxOffset//2,width=2,fill='white') + canvas.create_text(xO-0.75,yO+35+boxOffset*1.5,anchor='center',text='o',font=('Arial',20),fill='white') + + #print(time.time()) + t = time.time() - T0 + #t += 1 + canvas.after(10,updateGraphics, h,k,t) + + #app.after(10, updateGraphics, 0) + app.mainloop() + +if __name__ == '__main__': + MAIN_MECH(ctk.CTk()) diff --git a/rc.py b/rc.py index b62bb3b..b98153c 100644 --- a/rc.py +++ b/rc.py @@ -2,8 +2,7 @@ import customtkinter as ctk import matplotlib.pyplot as plt from matplotlib.ticker import EngFormatter -from scipy.integrate import solve_ivp -from scipy.signal import argrelextrema +from Sou_Sapphire_Solver import DIFF_SOLVE, EXTREMAS, LAPLACE_SOLVE from PIL import Image,ImageTk import numpy as np import guideManager as gum @@ -96,11 +95,17 @@ def RLCTools(choice): ctk.CTkLabel(app,text="Ω",font=(fontName,fontSize)).grid(row=4,column=2,pady=PADY_CONST,padx=PADX_CONST) def unpack_values(): - R = float(entryResistance.get()) - r = float(entryResInducta.get()) - C = float(entryCapacitance.get()) - L = float(entryInductance.get()) - E = float(entryFEM.get()) + try: + R = float(entryResistance.get()) + r = float(entryResInducta.get()) + C = float(entryCapacitance.get()) + L = float(entryInductance.get()) + E = float(entryFEM.get()) + assert ((R+r)>=0) and (C>0) and (L>0) and (E>0) + except: + print("Merci d'entrer des valuers valides et positives") + print("Utilisant les valeurs par défaut...") + return [0.1,1,1,12] return [R+r,C,L,E] def diff_q(t,y): @@ -116,19 +121,25 @@ def plot_q(): t_span = (0, 5*2*np.pi*np.sqrt(L*C)) RLC_diff = r'$L\frac{\mathrm{d^2} Q(t)}{\mathrm{d} t^2}+R \frac{\mathrm{d} Q(t)}{\mathrm{d} t}+\frac{1}{C}Q(t)=0$' - sol_q = solve_ivp(diff_q, t_span, y0, method='RK23') + #sol_q = solve_ivp(diff_q, t_span, y0, method='RK23') + + #q_t = sol_q.t + #sol_q0 = sol_q.y[0] + #q_prime = sol_q.y[1] - q_t = sol_q.t - sol_q0 = sol_q.y[0] - q_prime = sol_q.y[1] + q_t = np.linspace(t_span[0],t_span[1],400) + diff_qsol = DIFF_SOLVE(L,R,1/C,C*E) + sol_q0 = np.array([diff_qsol(t) for t in q_t]) plt.figure() plt.title("Charge au Condensateur") XY_Units('s','C','Temps','Charge') plt.plot(q_t,sol_q0,label=RLC_diff,color='red') plt.ylim(min(sol_q0),max(sol_q0)) - extremas = list(argrelextrema(sol_q0, np.greater))[0] - minimas = list(argrelextrema(sol_q0, np.less))[0] + #extremas = list(argrelextrema(sol_q0, np.greater))[0] + #minimas = list(argrelextrema(sol_q0, np.less))[0] + extremas = EXTREMAS(sol_q0)[0] + minimas = EXTREMAS(sol_q0)[1] '''engFormatted = EngFormatter()(tensionCircuit*(1-np.exp(-i))) engSplit = engFormatted.split('.') @@ -181,14 +192,17 @@ def formatterThing(num): def plot_i(): R, C, L, E = unpack_values() - y0 = [C*E, 0] t_span = (0, 5*2*np.pi*np.sqrt(L*C)) RLC_diff = r'$L\frac{\mathrm{d^2} i(t)}{\mathrm{d} t^2}+R \frac{\mathrm{d} i(t)}{\mathrm{d} t}+\frac{1}{C}i(t)=0$' - sol_q = solve_ivp(diff_q, t_span, y0, method='RK23') + #sol_q = solve_ivp(diff_q, t_span, y0, method='RK23') + + #q_t = sol_q.t + #sol_i0 = sol_q.y[1] #I = Q' - q_t = sol_q.t - sol_i0 = sol_q.y[1] #I = Q' + q_t = np.linspace(t_span[0],t_span[1],400) + diff_isol = DIFF_SOLVE(L,R,1/C,C*E,derivative=True) + sol_i0 = np.array([diff_isol(t) for t in q_t]) plt.figure() plt.title("Intensité au circuit") @@ -217,11 +231,21 @@ def plot_e(): #E_e = E*2*np.pi*np.abs(np.cos(C*E*t/antiF)**2)/C E_t = E_m + E_e else: - sol_q = solve_ivp(diff_q, t_span, y0, method='BDF') + '''sol_q = solve_ivp(diff_q, t_span, y0, method='BDF') t = sol_q.t E_m = 0.5*L*(sol_q.y[1]**2) E_e = 0.5*(sol_q.y[0]**2)/C E_t = E_m + E_e + ''' + q_t = np.linspace(t_span[0],t_span[1],400) + diff_qsol = DIFF_SOLVE(L,R,1/C,C*E,False) + sol_q = np.array([diff_qsol(t) for t in q_t]) + diff_isol = DIFF_SOLVE(L,R,1/C,C*E,True) + sol_i = np.array([diff_isol(t) for t in q_t]) + t = q_t + E_m = 0.5*L*((sol_i/2)**2) + E_e = 0.5*(sol_q**2)/C + E_t = E_m + E_e plt.figure() plt.plot(t,E_m,color='red', label='Energie Magnétique') plt.plot(t,E_e,color='black', label='Energie Electrostatique') @@ -250,7 +274,7 @@ def MAIN_RLC_FORCE(mainy): ctk.set_default_color_theme("dark-blue") app = ctk.CTkToplevel(mainy) - app.geometry("750x475") + app.geometry("800x475") app.resizable(False,False) app.title("Circuit RLC Forcé") gum.createGuideWindow(app, "Guide: Circuit RLC Forcé","rlcf.txt") @@ -371,13 +395,19 @@ def RLCTools(choice): def unpack_values(): - R = float(entryResistance.get()) - r = float(entryInductRes.get()) - C = float(entryCapacitance.get()) - L = float(entryInductance.get()) - f = float(entryN.get()) - Q0 = float(entryQ0.get()) - Um = float(entryUm.get()) + try: + R = float(entryResistance.get()) + r = float(entryInductRes.get()) + C = float(entryCapacitance.get()) + L = float(entryInductance.get()) + f = float(entryN.get()) + Q0 = float(entryQ0.get()) + Um = float(entryUm.get()) + assert ((R+r)>=0) and (C>0)and(L>0)and(Um>0)and(f>0) + except: + print("Merci d'entrer des valuers valides et positives") + print("Utilisant les valeurs par défaut...") + return [0.1,0,5e-2,1e-2,1e-3,5,10] return [C, Q0, R, L, r, Um, f] #C, Q0, R, L, r, Um, f = unpack_values() @@ -404,13 +434,12 @@ def plot_i(): t_span = (0,10/f) y0 = [Q0,0.0] - qSol = solve_ivp(diff_i, t_span, y0, method='BDF') - t = qSol.t - i = qSol.y[0] + TIME = np.linspace(t_span[0],t_span[1],400) + solI = np.real(LAPLACE_SOLVE(L,R,1/C,y0[0],Um,2*np.pi*f,0,1)(TIME)) plt.figure() plt.title("Intensité au Circuit") - plt.plot(t,i,color='red') + plt.plot(TIME,solI,color='red') XY_Units('s','A','Temps','Intensité') plt.grid() plt.show() @@ -419,18 +448,34 @@ def plot_q(): C, Q0, R, L, r, Um, f = unpack_values() t_span = (0,10/f) y0 = [Q0,0.0] - - qSol = solve_ivp(diff_q, t_span, y0, method='BDF') - t = qSol.t - i = qSol.y[0] + + TIME = np.linspace(t_span[0],t_span[1],400) + solQ = np.real(LAPLACE_SOLVE(L,R,1/C,y0[0],Um,2*np.pi*f,0)(TIME)) plt.figure() plt.title("Charge au Condensateur") - plt.plot(t,i,color='purple') + plt.plot(TIME,solQ,color='purple',label='Charge') XY_Units('s','C','Temps','Charge') plt.grid() plt.show() + def plot_uc(): + C, Q0, R, L, r, Um, f = unpack_values() + t_span = (0,10/f) + y0 = [Q0,0.0] + + TIME = np.linspace(t_span[0],t_span[1],400) + solQ = np.real(LAPLACE_SOLVE(L,R,1/C,y0[0],Um,2*np.pi*f,0)(TIME)) + + plt.figure() + plt.title("Tension au Condensateur") + plt.plot(TIME,solQ/C,color='purple',label='Tension') + plt.plot(TIME,Um*np.sin(2*np.pi*f*TIME),color='green',label='Tension du Générateur') + XY_Units('s','V','Temps','Tension') + plt.legend() + plt.grid() + plt.show() + def plot_phi(): C, Q0, R, L, r, Um, f = unpack_values() resN=1/np.sqrt(L*C) @@ -482,6 +527,8 @@ def plot_im(): btn_i.grid(row=10,column=5) btn_q = ctk.CTkButton(app,text="Tracer la charge", font=(fontName,fontSize),command=plot_q) btn_q.grid(row=10,column=7) + btn_uc = ctk.CTkButton(app,text="Tracer la tension", font=(fontName,fontSize),command=plot_uc) + btn_uc.grid(row=10,column=6) btn_im = ctk.CTkButton(app,text="Tracer les amplitudes", font=(fontName,fontSize),command=plot_im) btn_im.grid(row=11,column=5) btn_phi = ctk.CTkButton(app,text="Tracer les déphasages", font=(fontName,fontSize),command=plot_phi) diff --git a/res/border.png b/res/border.png new file mode 100644 index 0000000..01ea090 Binary files /dev/null and b/res/border.png differ diff --git a/res/box.png b/res/box.png new file mode 100644 index 0000000..fac32b4 Binary files /dev/null and b/res/box.png differ diff --git a/res/guidy/boxdiag.png b/res/guidy/boxdiag.png new file mode 100644 index 0000000..1d8887c Binary files /dev/null and b/res/guidy/boxdiag.png differ diff --git a/res/guidy/main.txt b/res/guidy/main.txt index 8be63f4..1f992f6 100644 --- a/res/guidy/main.txt +++ b/res/guidy/main.txt @@ -1,3 +1,4 @@ + @Bonjour au "Suite de Physique"!-£purple Réalisé par: @@ -12,9 +13,15 @@ Visualiser la strucutre des $./res/lucyna.png;$./res/soleil.png;$./res/terre.png Simuler l'interaction gravitationnelle entre les corps massives. Vous pouvez aussi ajouter des corps en cliquant. +@Mécanique_ +$./res/border.png;$./res/spring.png;$./res/box.png +Simuler les oscillations mécaniques selon le coefficient du frottement, la masse et le raideur. @Circuit RLC_ $./res/condensateur.png;$./res/generateur.png;$./res/resistor.png Contenant les circuits RC, RLC libre et RLC forcé. Circuit RC: Tracer la tension, l'intensité et la charge. Circuit RLC libre: Tracer l'intensité, la charge et l'énergie. -Circuit RLC forcé: Tracer l'intensité et la charge, ainsi que les amplitudes et les dephases d'après un rang de fréquences. \ No newline at end of file +Circuit RLC forcé: Tracer l'intensité et la charge, ainsi que les amplitudes et les dephases d'après un rang de fréquences. + + +. \ No newline at end of file diff --git a/res/guidy/mech.txt b/res/guidy/mech.txt new file mode 100644 index 0000000..aa5f634 --- /dev/null +++ b/res/guidy/mech.txt @@ -0,0 +1,16 @@ + +@Mécanique- + +En précisant le coefficient du frottement, la masse et le raideur, vous pouvez cliquer "Simuler" ou tracer l'énergie. +En suite, cliquez sur "Arrêter" afin de réinitialiser le système. +Vous pouvez cliquer et faire glisser pour définir la position initiale +$./res/guidy/boxdiag.png +Il y a 3 cas possibles: +Sous-amorti:- +Régime pseudo périodique (h²<4mk) +Critiquement amorti:- +Régime critique (h²=4mk) +Suramorti:- +Régime apériodique (h²>4mk) +Sans amortissement:- +Régime harmonique (h=0) diff --git a/res/spring.png b/res/spring.png new file mode 100644 index 0000000..bad2611 Binary files /dev/null and b/res/spring.png differ