unit Physikunit; {$mode objfpc}{$H+} { $DEFINE Zeitschrittueberwachung} { $DEFINE Dichteueberwachung} interface uses Classes, SysUtils, Math; type TZeitverfahren = (zfEulerVorwaerts,zfRungeKuttaVier); TVerteilungsfunktion = function(x: extended): extended; TFeldInhalt = ( fiA,fiAX,fiAY,fiAZ, fidAXDT,fidAYDT,fidAZDT, fiN,fidPhiDX,fidPsiDX, fiP,fiPX,fiPY,fiPZ, fiGamma,fiiGamma); TAusgabeDatei = record Ableitung: Boolean; Inhalt: TFeldInhalt; Datei: File; end; TProtokolldatei = record datei: textfile; einrueckung: longint; end; { TProtokollant } TProtokollant = class(TObject) private sDat: ^TProtokolldatei; Bes: String; Kinder: array of TProtokollant; Elter: TProtokollant; public constructor create(Dateiname: string); overload; constructor create(Elter_: TProtokollant; Besitzer: string); overload; destructor destroy; override; procedure schreibe(s: string); procedure schreibe(s: string; tee: boolean); procedure destroyall; end; { TFeld } pTFeld = ^TFeld; TFeld = class(TObject) public Groessen: array[Boolean,TFeldInhalt] of extended; // A, p[xyz]? und i?Gamma haben keine sinnvolle Ableitung hier! dX,iDX,x,pDNMax: extended; lN,rN: pTFeld; constructor create(deltaX,pDNMa: extended); procedure berechneGammaUndP; procedure berechneNAbleitung; procedure berechneAbleitungen(dT: extended); end; { TGitter } TGitter = class(TObject) private groesse: longint; Prot: TProtokollant; procedure berechneAbleitungen(Felder: longint; dt: extended); overload; procedure berechneAbleitungen(Felder: longint; dt: extended; out dTMax: extended); overload; procedure setzeRaender(Felder: longint); public aktuelleFelder: longint; Felders: array of array of TFeld; dTMaximum,dX,iDX,xl,xr,t,pDNMax: extended; pDNMaxDynamisch: boolean; Zeitverfahren: TZeitverfahren; Al,Ar: TVerteilungsfunktion; constructor create(size: longint; deltaT,deltaX,pDNMa: extended; n0: TVerteilungsfunktion; ZV: TZeitverfahren; Protokollant: TProtokollant); destructor destroy; override; procedure iteriereSchritt(var dT: extended); procedure macheAusgabe(AD: TAusgabedatei; sDX: extended); function gibErhaltungsgroessen: string; end; function Nullfunktion(x: extended): extended; function timetostr(t: extended): string; const OptionNamen: array[TFeldInhalt] of string = ( 'A','AX','AY','AZ','dAXDT','dAYDT','dAZDT','N','dPhiDX','dPsiDX','P','PX','PY','PZ','Gamma','iGamma'); AusgabeNamen: array[TFeldInhalt] of string = ( 'fiA','fiAX','fiAY','fiAZ','fidAXDT','fidAYDT','fidAZDT','fiN','fidPhiDX','fidPsiDX','fiP','fiPX','fiPY','fiPZ','fiGamma','fiiGamma'); AusgabeHilfe = '--d?A[XYZ]? --d?dA[XYZ]dt --d?N --d?dPhiDX --d?dPsiDX --P[XYZ]? --Gamma --iGamma'; implementation { TProtokollant } constructor TProtokollant.create(Dateiname: string); begin inherited create; getmem(sDat,sizeof(TProtokolldatei)); sDat^.einrueckung:=10; assignfile(sDat^.datei,Dateiname); rewrite(sDat^.datei); Bes:=''; setlength(Kinder,0); Elter:=nil; end; constructor TProtokollant.create(Elter_: TProtokollant; Besitzer: string); begin inherited create; sDat:=Elter_.sDat; setlength(Elter_.Kinder,length(Elter_.Kinder)+1); Elter_.Kinder[length(Elter_.Kinder)-1]:=self; Elter:=Elter_; setlength(Kinder,0); Bes:=Elter.Bes+'.'+Besitzer; if pos('.',Bes)=1 then delete(Bes,1,1); if length(Bes)+4 > sDat^.einrueckung then sDat^.einrueckung:=length(Bes)+4; end; destructor TProtokollant.destroy; var i: longint; begin while length(Kinder)>0 do Kinder[length(Kinder)-1].free; if assigned(Elter) then begin flush(sDat^.datei); i:=0; while (iself) do inc(i); if i>=length(Elter.Kinder) then schreibe('destroy fehlgeschlagen, ich kann mich nicht sehen.'); inc(i); while i * deltax Groessen[true,fidPhiDX]:= lN^.Groessen[true,fidPhiDX] + (lN^.Groessen[true,fiN] + Groessen[true,fiN])*dX/2; // d2A/dt2 = - n/gamma p + Laplace(A) ... for b:=0 to 2 do Groessen[true,TFeldinhalt(Byte(fidAXDT)+b)]:= - (Groessen[false,fiN]*Groessen[false,fiiGamma]*Groessen[false,TFeldinhalt(Byte(fiPX)+b)]) + (rN^.Groessen[false,TFeldinhalt(Byte(fiAX)+b)] - 2*Groessen[false,TFeldinhalt(Byte(fiAX)+b)] + lN^.Groessen[false,TFeldinhalt(Byte(fiAX)+b)])*sqr(iDX); Groessen[true,fidAXDT]:=Groessen[true,fidAXDT] - Groessen[false,fidPhiDX]; // ... - Nabla(phi) // dA/dt = dA/dt for b:=0 to 2 do Groessen[true,TFeldInhalt(Byte(fiAX)+b)]:= Groessen[false,TFeldInhalt(Byte(fidAXDT)+b)] + Groessen[true,TFeldInhalt(Byte(fidAXDT)+b)]*dT; end; { TGitter } procedure TGitter.berechneAbleitungen(Felder: longint; dT: extended; out dTMax: extended); var i: longint; begin berechneAbleitungen(Felder,dT); dTMax:=dTMaximum; for i:=0 to groesse+3 do if Felders[Felder,i].Groessen[true,fiN]<0 then begin dTMax:=min(dTMax,Abs(Felders[Felder,i].Groessen[false,fiN]/Felders[Felder,i].Groessen[true,fiN])); end; end; procedure TGitter.berechneAbleitungen(Felder: longint; dT: extended); var i: longint; begin for i:=0 to groesse+3 do begin (* Felders[Felder,i].Groessen[false,fiN]:= max(Felders[Felder,i].Groessen[false,fiN],0); // n >= 0 *) Felders[Felder,i].berechneGammaUndP; end; Felders[Felder,1].Groessen[false,fiPX]:= // Teilchen werden reflektiert Abs(Felders[Felder,1].Groessen[false,fiPX]); Felders[Felder,groesse+2].Groessen[false,fiPX]:= -Abs(Felders[Felder,groesse+2].Groessen[false,fiPX]); setzeRaender(Felder); for i:=1 to groesse+2 do Felders[Felder,i].berechneNAbleitung; for i:=1 to groesse+2 do Felders[Felder,i].berechneAbleitungen(dT); end; procedure TGitter.setzeRaender(Felder: longint); var FI: TFeldInhalt; begin for FI:=fiAX to fiAZ do begin // Vakuumrandbedingungen für das A-Feld Felders[Felder,0].Groessen[true,FI]:= (Felders[Felder,1].Groessen[false,FI] - Felders[Felder,0].Groessen[false,FI])*iDX; Felders[Felder,groesse+3].Groessen[true,FI]:= (Felders[Felder,groesse+2].Groessen[false,FI] - Felders[Felder,groesse+3].Groessen[false,FI])*iDX; end; // (ein bisschen wird noch reflektiert, vmtl. durch numerische Fehler) Felders[Felder,0].Groessen[true,fiAy]:= Felders[Felder,0].Groessen[true,fiAy] + (Al(t+dTMaximum)-Al(t))/dTMaximum; Felders[Felder,groesse+3].Groessen[true,fiAy]:= Felders[Felder,groesse+3].Groessen[true,fiAy] + (Ar(t+dTMaximum)-Ar(t))/dTMaximum; end; constructor TGitter.create(size: longint; deltaT,deltaX,pDNMa: extended; n0: TVerteilungsfunktion; ZV: TZeitverfahren; Protokollant: TProtokollant); var i,j: longint; begin inherited create; Ar:=@Nullfunktion; Al:=@Nullfunktion; Zeitverfahren:=ZV; groesse:=size; Prot:=TProtokollant.create(Protokollant,'TGitter'); dTMaximum:=deltaT; dX:=deltaX; iDX:=1/dX; if pDNMa < 0 then begin pDNMax:=0; pDNMaxDynamisch:=true; end else pDNMax:=pDNMa; case Zeitverfahren of zfEulerVorwaerts: Setlength(Felders,2); zfRungeKuttaVier: Setlength(Felders,5); end{of Case}; xl:=dX/2; for i:=0 to length(Felders)-1 do begin setlength(Felders[i],size+4); // auf jeder Seite je zwei Felder Rand extra for j:=0 to length(felders[i])-1 do begin felders[i,j]:=TFeld.create(dX,pDNMax); felders[i,j].x:=xl+j*dX; felders[i,j].Groessen[false,fiN]:=n0(felders[i,j].x); felders[i,j].Groessen[false,fidPsiDX]:=0.0001*Cos(j/(length(felders[i])-1)*pi*15); end; felders[i,0].lN:=@felders[i,0]; for j:=1 to length(felders[i])-1 do begin felders[i,j].lN:=@felders[i,j-1]; felders[i,j-1].rN:=@felders[i,j]; end; felders[i,length(felders[i])-1].rN:=@felders[i,length(felders[i])-1]; end; xr:=xl+dX*(length(Felders[0])-1); aktuelleFelder:=0; t:=0; end; destructor TGitter.destroy; var i,j: longint; begin for i:=0 to 1 do for j:=0 to length(Felders[i])-1 do Felders[i,j].free; inherited destroy; end; procedure TGitter.iteriereSchritt(var dT: extended); var i: longint; FI: TFeldInhalt; dTMax: extended; {$IFDEF Zeitschrittueberwachung} Pro: TProtokollant; {$ENDIF} const zeitObergrenze = 1/4; zeitSollwert = 1/8; zeitUntergrenze = 1/16; begin berechneAbleitungen(aktuelleFelder,dT,dTMax); // y' = y'(t,y(t)) {$IFDEF Zeitschrittueberwachung} Pro:=TProtokollant.create(Prot,'iteriereSchritt'); {$ENDIF} while dT>=dTMax*zeitObergrenze do begin dT:=dTMax*zeitSollwert; berechneAbleitungen(aktuelleFelder,dT,dTMax); // y' = y'(t,y(t)) {$IFDEF Zeitschrittueberwachung} Pro.schreibe('+ neues dT: '+floattostr(dT)+' (t='+floattostr(t)+')'); {$ENDIF} end; {$IFDEF Zeitschrittueberwachung} if dT<1e-30 then begin for i:=1 to groesse+2 do if (Felders[aktuelleFelder,i].Groessen[true,fiN]<0) and (Abs(Felders[aktuelleFelder,i].Groessen[false,fiN]/ Felders[aktuelleFelder,i].Groessen[true,fiN])<1e-15) then Prot.schreibe( floattostr(t)+' '+ inttostr(i)+' '+ floattostr(Felders[aktuelleFelder,i-1].Groessen[false,fidPsiDX]* Felders[aktuelleFelder,i-1].Groessen[false,fiiGamma])+' '+ floattostr(Felders[aktuelleFelder,i-1].Groessen[false,fiN])+' '+ floattostr(Felders[aktuelleFelder,i-1].Groessen[true,fiN])+' '+ floattostr(Felders[aktuelleFelder,i].Groessen[false,fidPsiDX]* Felders[aktuelleFelder,i].Groessen[false,fiiGamma])+' '+ floattostr(Felders[aktuelleFelder,i].Groessen[false,fiN])+' '+ floattostr(Felders[aktuelleFelder,i].Groessen[true,fiN])+' '+ floattostr(Felders[aktuelleFelder,i+1].Groessen[false,fidPsiDX]* Felders[aktuelleFelder,i+1].Groessen[false,fiiGamma])+' '+ floattostr(Felders[aktuelleFelder,i+1].Groessen[false,fiN])+' '+ floattostr(Felders[aktuelleFelder,i+1].Groessen[true,fiN]), true); Pro.destroyall; halt(1); end; {$ENDIF} case Zeitverfahren of zfEulerVorwaerts: begin // y(t+dt) = y(t) + y' dt for i:=0 to groesse+3 do for FI:=low(TFeldinhalt) to fidPsiDX do Felders[1-aktuelleFelder,i].Groessen[false,FI]:= Felders[aktuelleFelder,i].Groessen[false,FI] + Felders[aktuelleFelder,i].Groessen[true,FI]*dT; end; zfRungeKuttaVier: begin for i:=0 to groesse+3 do // ya = y(t) + y' dt/2 for FI:=low(TFeldinhalt) to fidPsiDX do Felders[2,i].Groessen[false,FI]:= Felders[aktuelleFelder,i].Groessen[false,FI] + Felders[aktuelleFelder,i].Groessen[true,FI]*dT/2; berechneAbleitungen(2,dT/2,dTMax); // ya' = y'(t+dt/2,ya) if dT/2>dTMax*zeitObergrenze then begin {$IFDEF Zeitschrittueberwachung} Pro.schreibe('1. '+floattostr(dT/2)+'>'+floattostr(dTMax*zeitObergrenze)+' (t='+floattostr(t)+')',true); Pro.free; {$ENDIF} dT:=dTMax*zeitSollwert; exit; end; for i:=0 to groesse+3 do // yb = y(t) + ya' dt/2 for FI:=low(TFeldinhalt) to fidPsiDX do Felders[3,i].Groessen[false,FI]:= Felders[aktuelleFelder,i].Groessen[false,FI] + Felders[2,i].Groessen[true,FI]*dT/2; berechneAbleitungen(3,dT/2,dTMax); // yb' = y'(t+dt/2,yb) if dT/2>dTMax*zeitObergrenze then begin {$IFDEF Zeitschrittueberwachung} Pro.schreibe('2. '+floattostr(dT/2)+'>'+floattostr(dTMax*zeitObergrenze)+' (t='+floattostr(t)+')',true); Pro.free; {$ENDIF} dT:=dTMax*zeitSollwert; exit; end; for i:=0 to groesse+3 do // yc = y(t) + yb' dt for FI:=low(TFeldinhalt) to fidPsiDX do Felders[4,i].Groessen[false,FI]:= Felders[aktuelleFelder,i].Groessen[false,FI] + Felders[3,i].Groessen[true,FI]*dT; berechneAbleitungen(4,dT/2,dTMax); // yc' = y'(t+dt,yc) if dT/2>dTMax*zeitObergrenze then begin {$IFDEF Zeitschrittueberwachung} Pro.schreibe('3. '+floattostr(dT/2)+'>'+floattostr(dTMax*zeitObergrenze)+' (t='+floattostr(t)+')',true); Pro.free; {$ENDIF} dT:=dTMax*zeitSollwert; exit; end; for i:=0 to groesse+3 do // y(t+dt) = y(t) + (y' + 2(ya' + yb') + yc') dt/6 for FI:=low(TFeldinhalt) to fidPsiDX do Felders[1-aktuelleFelder,i].Groessen[false,FI]:= Felders[aktuelleFelder,i].Groessen[false,FI] + (Felders[aktuelleFelder,i].Groessen[true,FI] + 2*(Felders[2,i].Groessen[true,FI] + Felders[3,i].Groessen[true,FI]) + Felders[4,i].Groessen[true,FI])*dT/6; end; end{of case}; t:=t+dT; aktuelleFelder:=1-aktuelleFelder; if dT=sX do begin BlockWrite(AD.Datei,Felders[aktuelleFelder,i].Groessen[AD.Ableitung,AD.Inhalt],sizeof(extended)); sX:=sX+sDX; end; end; function TGitter.gibErhaltungsgroessen: string; var i,j,k: integer; n: extended; Pro: TProtokollant; begin Pro:=TProtokollant.create(Prot,'gibErhaltungsgroessen'); n:=0; for i:=0 to groesse+3 do n:=n+Felders[aktuelleFelder,i].Groessen[false,fiN]; for i:=1 to groesse+2 do if abs(Felders[aktuelleFelder,i].Groessen[false,fiPx]* (Felders[aktuelleFelder,i+1].Groessen[false,fiN]-Felders[aktuelleFelder,i-1].Groessen[false,fiN])/ Max(Felders[aktuelleFelder,i+1].Groessen[false,fiN]+Felders[aktuelleFelder,i-1].Groessen[false,fiN],1e-10)) > pDNMax then begin if pDNMaxDynamisch then begin pDNMax:= 2* abs(Felders[aktuelleFelder,i].Groessen[false,fiPx]* (Felders[aktuelleFelder,i+1].Groessen[false,fiN]-Felders[aktuelleFelder,i-1].Groessen[false,fiN])/ Max(Felders[aktuelleFelder,i+1].Groessen[false,fiN]+Felders[aktuelleFelder,i-1].Groessen[false,fiN],1e-10)); for j:=0 to length(felders)-1 do for k:=0 to length(felders[j])-1 do felders[j,k].pDNMax:=pDNMax; Pro.schreibe('Neues maximales px * dn/dx / n: '+floattostr(pDNMax)+' (t='+floattostr(t)+')'); end else if pDNMax>0 then begin Pro.schreibe('Warnung: maximaler Impuls * dn/dx / n von '+floattostr(pDNMax)+' wurde überschritten (t='+floattostr(t)+ 'T), die numerische Stabilität ist nicht mehr gewährleistet!',true); Pro.schreibe(' Lösung: größeren Diffusionsterm wählen (-D)',true); Pro.schreibe(' außerdem empfohlen: Ortsauflösung in gleichem Maße verbessern (-x)',true); pDNMax:=-1; end; end; result:='n='+floattostr(n); {$IFDEF Dichteueberwachung} if n>1000 then begin errorCode:=2; Pro.schreibe(' n > 1000, es scheinen sehr viele neue Teilchen entstanden zu sein. Die Simulation wird abgebrochen. (t='+floattostr(t)+')'); end; {$ENDIF} Pro.free; end; function Nullfunktion(x: extended): extended; begin result:=0*x; end; function timetostr(t: extended): string; var b: boolean; begin result:=''; b:=t>=1; if b then begin result:=result+inttostr(floor(t))+' Tage '; t:=t-floor(t); end; t:=t*24; b:=b or (t>=1); if b then begin result:=result+inttostr(floor(t))+'h '; t:=t-floor(t); end; t:=t*60; b:=b or (t>=1); if b then begin result:=result+inttostr(floor(t))+'min '; t:=t-floor(t); end; t:=t*60; result:=result+inttostr(round(t))+'s'; end; end.