1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
# encoding:utf8
''' Simulateur de relativité restreinte
==================================================
Ce script permet de visualiser l'exemple ultra classique illustrant les distorsions
du temps et de l'espace en relativité restreinte :
Le wagon en déplacement par rapport à un quai.
Il suffit de lancer ce script et de jouer avec les flèches droite et gauche.
On peut voir, sous le wagon, des horloge qui donnent l'heure "locale" à différentes positions.
Quelques explications à peu près scientifiques :
On commence par définir le référentiel ("immobile") de lobservateur : le quai.
Ensuite, dans le le référentiel du wagon qui se déplace à une vitesse V par rapport au quai,
on a un point p qui se déplace à la vitesse v, et est à la position x0 quand t = 0.
On a donc : position de p dans le référentiel du wagon = x = x0 + vt
A un instant T sur le quai, on veut connaître X, la position de p vu depuis le quai.
On utilise la transformation de Lorentz
t = γ( T - βX/c )
x = γ( X - βcT )
en remplaçant x par x0 + vt :
x0 + vt = γ( X - βcT )
On connaît x0, v et T, et on veut X et t.
x0 + vγ( T - βX/c ) = γ( X - βcT )
x0 + vγT - vγβX/c = γX - γβcT
γX + vγβX/c = x0 + γβcT + vγT
X( γ + vβγ/c ) = x0 + γβcT + vγT
X = ( x0 + γβcT + vγT ) / ( vβγ/c + γ )
X = ( x0/γ + βcT + vT ) / ( vβ/c + 1 )
X = ( x0/γ + T( v + βc )) / ( vβ/c + 1 ) <-- voici X
t = γ( T - βX/c ) <-- et t
Dans les formules qui précèdent et le code qui suit, les capitales
désignent les coordonnées dans le référentiel du quai,
et les minuscules, celles du référentiel en mouvement (le wagon).
Les variables "q" désignent des "quasi-quadri-vecteurs", c'est-à-dire,
dans ce contexte, une paire (z,t) où z donne les coordonnées
spatiales 2D sous forme d'un nombre complexe, et t le temps.
Par ailleurs, les variables "c", "b", et "g" contiennent respectivement
la vitesses de la lumière dans les unités de mesures choisies,
β = le rapport v/c, et γ = le facteur de Lorentz.
Hadrien Flammang - nov 2015
'''
def lorentz ( x0,y0,vx,vy,T,c,b,g ):
''' Variante de la transformation de Lorentz qui calcule
X,Y,t en fonction de x0,y0,vx,vy,T '''
X = ( x0/g + T*( vx + b*c )) / ( vx*b/c + 1 )
t = g*( T - b*X/c )
Y = y0 + vy*t
return X,Y,t
#-------------------------------------------------------------------
#-- quelques outils génériques -------------------------------------
iterable = lambda obj : hasattr( obj,'__iter__')
flatize = lambda obj : sum( map( flatize,obj ),[] ) if iterable( obj ) else [obj]
invcomplex = lambda z : complex( z.imag,z.real )
def to_complex ( *z ) :
try : return complex( *z )
except : pass
try : return complex( *z[0] )
except : return 0
#-------------------------------------------------------------------
#-- Les classes des différents objets en mouvement (dans un repère qui sera donné)
nope,box,dot,clock,text = range( 5 )
class Object:
def __init__(self,type=nope,**param):
self.type = type
self.pos = to_complex( param.get('pos',0 ))
self.size = to_complex( param.get('size',0 ))
self.speed = to_complex( param.get('speed',0 ))
self.clock = param.get('clock',False )
self.text = param.get('text',None )
self.start = param.get('start',0 )
self.color = param.get('color',0 )
def draw (self,t,trans,drawer): # trans = fonction de transformation : ( x,v,T )-> (X,t) avec x,v,X complexes
if self.type == nope : return
q0 = trans( self.pos,self.speed,t )
q1 = trans( self.pos+self.size,self.speed,t )
if self.type == box : drawer.box ( q0,q1,self.color )
elif self.type == dot : drawer.dot ( q0,self.color )
elif self.type == text : drawer.text ( q0,self.text,self.color )
elif self.type == clock : drawer.clock( q0,self.color )
if self.clock : drawer.clock( (q0[0]+1j,q0[1]),self.color )
def Nop ( **param ) : return Object() # objet invisible et qui ne fait rien...
def Box ( **param ) : return Object( box ,**param ) # rectangle (pour symboliser un wagon)
def Dot ( **param ) : return Object( dot ,**param ) # point (pour symboliser un projectile ou un photon)
def Text ( **param ) : return Object( text ,**param ) # explication bienvenue qui accompagne un objet
def Clock ( **param ) : return Object( clock,**param ) # horloge qui donne le temps propre dans un repère
#-------------------------------------------------------------------
#-- repère défini par une position et une vitesse ------------------
class Frame:
def __init__( self,position,speed,c=1 ):
speed = to_complex( speed )
self.onx = speed.imag == 0
self.V = speed
self.O = to_complex( position )
self.v = speed.real or speed.imag
self.c = c
self.b = self.v/c
self.g = (1-self.b**2)**-0.5
assert speed.real == 0 or speed.imag == 0 # la vitesse d'un repère ne peut être que horizontale ou verticale
def __call__ (self,x0,v,T): # fonction de transformation (ici : celle de Lorentz)
O,V,b,c,g = self.O,self.V,self.b,self.c,self.g
(x0x,x0y),(vx,vy) = (x0.real,x0.imag),(v.real,v.imag)
x0,y0,v,vy = (x0x,x0y,vx,vy) if self.onx else (x0y,x0x,vy,vx)
X,Y,t = lorentz( x0,y0,vx,vy,T,self.c,self.b,self.g )
Z = complex( X,Y ) if self.onx else complex( Y,X )
return self.O+Z,t
#-------------------------------------------------------------------
#-- classe d'affichage des objets ----------------------------------
class Drawer :
def __init__(self,canvas,origin=0,scale=1): # canvas doit se comporter comme un Tkinter.canvas
self.colors = ('#000','#00b','#080','#c00')
self.canvas = canvas
self.origin = to_complex(origin)
self.scale = to_complex(scale)
self.font = ('Arial narrow',int(scale*0.8))
def _int (self,*qpoints ):
qpoints = ( q[0]*self.scale+self.origin for q in qpoints )
qpoints = flatize( (q.real,q.imag) for q in qpoints )
return map( int,qpoints )
def box (self,q0,q1,color ):
v = self._int( q0,q1 )
self.canvas.create_rectangle( *v,width=2,outline=self.colors[color] )
def dot (self,q,color ):
r,(x,y) = 2,self._int( q )
self.canvas.create_oval( x-r,y-r,x+r,y+r,outline=self.colors[color],fill=self.colors[color] )
def text (self,q,txt,color ):
if not txt : return
(x,y) = self._int( q )
self.canvas.create_text( x,y,text=txt,font=self.font,fill=self.colors[color] )
def clock (self,q,color ):
t,(x,y) = q[1],self._int( q )
self.canvas.create_text( x,y,text='%.1f'%t,font=self.font,fill=self.colors[color] )
#-------------------------------------------------------------------
def relativity ( size,origin,scale,dT,*frames ):
def draw ( step = 0 ):
root.T += dT*step
can.delete('all')
can.create_text( size[0]/2,size[1]-10,text='FLÈCHES du temps , ECHAP pour quitter' )
for frame in frames :
for obj in frame[1:]:
obj.draw( root.T,frame[0],drawer )
import Tkinter
root = Tkinter.Tk()
root.T = 0
root.geometry('+20+20')
root.bind('<Left>' ,lambda e : draw(-1))
root.bind('<Right>' ,lambda e : draw(+1))
root.bind('<space>' ,lambda e : draw(+5))
root.bind('<Escape>',lambda e : root.quit())
can = Tkinter.Canvas( root,width = size[0],height = size[1] )
can.pack()
drawer = Drawer( can,origin,scale )
draw()
root.mainloop()
#-------------------------------------------------------------------
#--- démo avec unité de temps = seconde
# unité de longueur = seconde-lumière (~300000km) => c = 1
def build_wagon ( pos,speed,nbclock,color,text,vertical_photon=False ):
''' Construit un repère contenant un wagon long de 20sl + 2 photons qui partent de son centre. '''
wsize = 20
frame = Frame( pos*1j,speed,1 )
wagon = Box( pos=-wsize/2+1j,size=wsize+4j,color=color )
spot1 = Dot( pos=3j,speed= 1,clock=True,color=color )
spot2 = Dot( pos=3j,speed=-1,clock=True,color=color )
spotV = (Dot if vertical_photon else Nop)( pos=3j,speed=1j,clock=True,color=color )
name = Text( pos=2j,text=text,color=color )
clocks = tuple(Clock( pos=i+6j,color=color ) for i in xrange( -wsize/2,wsize/2+1,wsize/(nbclock-1)))
return (frame,wagon,spot1,spot2,spotV,name)+clocks
def demo1 ( scale = 10 ):
deck = build_wagon( 0,0 ,3,0,'wagon sur le quai' )+(Box( pos=-100,size=200+8j ),)
wagon1 = build_wagon( 9,0.1,5,1,'wagon 10%' )
wagon2 = build_wagon( 16,0.8,5,2,'wagon 80%' )
wagon3 = build_wagon( 23,0.9,5,3,'wagon 90%' )
relativity( (scale*80,scale*35),(scale*20,0),scale,0.1,deck,wagon1,wagon2,wagon3 )
demo1( 20 ) |
Partager