diff --git a/.fortls b/.fortls new file mode 100644 index 0000000..7fca026 --- /dev/null +++ b/.fortls @@ -0,0 +1,4 @@ +{ +"ext_source_dirs": ["/misc/libs/bsplines/src", "/misc/libs/futils/src", "/usr/local/mumps_par-5.0.2/include", "/usr/local/intel/17.0/impi/2017.3.196/include64","/home/lebars/SISL/forSISL/src","/usr/local/intel/19.1/impi/2019.1.144/intel64/include"], +"source_dirs":["src"] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1ae7e37..09dbbfd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,49 @@ *.h5 *.h5_old *_genmod.f90 *.mod *.o +*.optrpt +*.d + *.out *.m~ dataanalysis/**/* *.optrpt wk/**/*.pdf wk/**/*.m wk/**/*.fig wk/**/*.log wk/**/*.err wk/**/*.out wk/advi/ wk/hotspots*/ wk/threading*/ wk/ahotspots*/ -espic2d \ No newline at end of file +espic2d +*.fig +*.trace +*.log +dataanalysis/**/* +src/release +src/debug +wk/advi +f2matlab + +matlab/C2xyz_v2 +matlab/export_fig + + +# Files for intel profiler +wk/espic2d +wk/hotspots* +wk/ahotspots* +wk/threading* +*.th +*.th.aux +*.options +*.options1.1 +*.cfg +*.cfg.1 + +wk/** diff --git a/.vscode/launch.json b/.vscode/launch.json index ace68c0..c946b7e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,59 +1,140 @@ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.0.1", "configurations": [ + + { "name": "Python: Current File", "type": "python", "request": "launch", "program": "${file}", "args": [ "/misc/espic2dsimus/openmp/trajectories.h5" ], "console": "integratedTerminal" }, { "name": "Fortran Launch (GDB)", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/src/espic2d", - "args": ["${workspaceFolder}/wk/espic2d.in"], + "args": ["${workspaceFolder}/wk/no_ela tst/job.in"], "stopAtEntry": false, "cwd": "${workspaceFolder}/wk", //"externalConsole": true, "preLaunchTask": "make debug", "miDebuggerPath": "gdb", }, { "name": "Fortran test.in (GDB)", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/src/espic2d", - "args": ["${workspaceFolder}/wk/job.in"], + "args": ["${workspaceFolder}/wk/test.in"], "stopAtEntry": false, "cwd": "${workspaceFolder}/wk", //"externalConsole": true, "preLaunchTask": "", "miDebuggerPath": "gdb", + "environment": [{"name":"OMP_NUM_THREADS","Value":"1"}] + }, + { + "name": "Fortran gt170.in (GDB)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/espic2d", + "args": ["${workspaceFolder}/wk/gt170.in"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/wk", + //"externalConsole": true, + "preLaunchTask": "make debug", + "miDebuggerPath": "gdb", + "environment": [{"name":"OMP_NUM_THREADS","Value":"6"}] + }, + { + "name": "Fortran gt170_coll.in (GDB)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/espic2d", + "args": ["${workspaceFolder}/wk/gt170_coll.in"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/wk", + //"externalConsole": true, + "preLaunchTask": "make debug", + "miDebuggerPath": "gdb", + "environment": [{"name":"OMP_NUM_THREADS","Value":"6"}] }, { "name": "Fortran Launch stable.in", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/src/espic2d", "args": ["${workspaceFolder}/wk/stable.in"], "stopAtEntry": false, "cwd": "${workspaceFolder}/wk", //"externalConsole": true, "preLaunchTask": "make debug", "miDebuggerPath": "gdb", + "environment": [{"name":"OMP_NUM_THREADS","Value":"1"}] + }, + { + "name": "Fortran Launch splinetest.in", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/espic2d", + "args": ["${workspaceFolder}/wk/splinetest.in"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/wk", + //"externalConsole": true, + "preLaunchTask": "make debug", + "miDebuggerPath": "gdb", + "environment": [{"name":"OMP_NUM_THREADS","Value":"6"}] + }, + { + "name": "Fortran Launch Deep_well", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/espic2d", + "args": ["${workspaceFolder}/wk/Deep_well/job.in"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/wk/Deep_well", + //"externalConsole": true, + "preLaunchTask": "make debug", + "miDebuggerPath": "gdb", + "environment": [{"name":"OMP_NUM_THREADS","Value":"6"}] + }, + { + "name": "Fortran Launch limup", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/espic2d", + "args": ["${workspaceFolder}/wk/limup/job.in"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/wk/limup", + //"externalConsole": true, + "preLaunchTask": "make debug", + "miDebuggerPath": "gdb", + "environment": [{"name":"OMP_NUM_THREADS","Value":"6"}] + }, + { + "name": "Fortran Launch ellipellip.in", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/espic2d", + "args": ["${workspaceFolder}/wk/ellipellip.in"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/wk", + //"externalConsole": true, + "preLaunchTask": "make debug", + "miDebuggerPath": "gdb", }, { "name": "Intel Debug Attach", "type": "cppdbg", "request": "attach", "processId": "${command:pickProcess}", - "program": "${workspaceFolder}/espic2d" + "program": "${workspaceFolder}/src/espic2d" } ] } diff --git a/espic2d.code-workspace b/espic2d.code-workspace index 66f6e41..bd1ddec 100644 --- a/espic2d.code-workspace +++ b/espic2d.code-workspace @@ -1,67 +1,68 @@ { "folders": [ { "path": "." } ], "settings": { "python.pythonPath": "dataanalysis/bin/python3", "workbench.colorCustomizations": { "activityBar.background": "#88d7ea", "activityBar.activeBorder": "#de41bf", "activityBar.foreground": "#15202b", "activityBar.inactiveForeground": "#15202b99", "activityBarBadge.background": "#de41bf", "activityBarBadge.foreground": "#e7e7e7", "titleBar.activeBackground": "#5dc9e2", "titleBar.inactiveBackground": "#5dc9e299", "titleBar.activeForeground": "#15202b", "titleBar.inactiveForeground": "#15202b99", "statusBar.background": "#5dc9e2", "statusBarItem.hoverBackground": "#32bbda", "statusBar.foreground": "#15202b", - "panel.border": "#88d7ea", - "sideBar.border": "#88d7ea", - "editorGroup.border": "#88d7ea", "tab.activeBorder": "#88d7ea", - "activityBar.activeBackground": "#88d7ea" + "activityBar.activeBackground": "#88d7ea", + "sash.hoverBorder": "#88d7ea", + "statusBarItem.remoteBackground": "#5dc9e2", + "statusBarItem.remoteForeground": "#15202b", + "commandCenter.border": "#15202b99" }, "peacock.color": "#5dc9e2", "cSpell.enabledLanguageIds": [ "asciidoc", "c", "cpp", "csharp", "css", "fortran", "fortran-modern", "git-commit", "go", "handlebars", "haskell", "html", "jade", "java", "javascript", "javascriptreact", "json", "jsonc", "latex", "less", "markdown", "php", "plaintext", "pug", "python", "restructuredtext", "rust", "scala", "scss", "text", "typescript", "typescriptreact", "yaml", "yml" ] } } \ No newline at end of file diff --git a/matlab/@espic2dhdf5/dispespicEkin.m b/matlab/@espic2dhdf5/dispespicEkin.m new file mode 100644 index 0000000..807a6b4 --- /dev/null +++ b/matlab/@espic2dhdf5/dispespicEkin.m @@ -0,0 +1,182 @@ +function f=dispespicEkin(M) +fieldstep=1; + + +f=uifigure('Name','Fluid Energy data'); + +mf=uipanel(f,'Position',[5 50 f.Position(3)-10 f.Position(4)-55]); +mf.AutoResizeChildren='off'; +m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); + +sgtitle(mf,sprintf('t=%0.5e s',M.t2d(fieldstep))) + +sld = uislider(m,'Position',[10 30 0.6*m.Position(3) 3]); +sld.Value=fieldstep; +sld.Limits=[1 length(M.t2d)]; + +edt = uieditfield(m,'numeric','Limits',[1 length(M.t2d)],'Value',1); +edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; +edt.RoundFractionalValues='on'; + + +Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); +%Playbt=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play/Pause'); + + +sld.ValueChangingFcn={@updatefigdata,edt,mf}; +edt.ValueChangedFcn={@updatefigdata,sld,mf}; +Printbt.ButtonPushedFcn={@plotGridButtonPushed}; +[R,Z]=meshgrid(M.rgrid,M.zgrid); +Rinv=1./R; +Rinv(:,1)=0; + +PlotEspic2ddata(mf,M,fieldstep); + + function PlotEspic2ddata(fig,M,fieldstep) + %PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep + sgtitle(fig,sprintf('t=%0.5e s',M.t2d(fieldstep))) + + + ax1=subplot(2,2,1,'Parent',fig); + + + surface(ax1,M.zgrid,M.rgrid,M.N(:,:,fieldstep),'edgecolor','none'); + hold(ax1,'on') + + border=contourc(M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0]); + zpos=interp2(M.zgrid, M.rgrid, M.N(:,:,fieldstep), border(1,2:end), border(2,2:end)); + plot3(ax1,border(1,2:end),border(2,2:end),zpos,'r-','linewidth',1.5,'Displayname','Boundaries') + + bfields=contourc(M.zgrid,M.rgrid,M.rAthet,15); + zpos=interp2(M.zgrid, M.rgrid, M.N(:,:,fieldstep), bfields(1,2:end), bfields(2,2:end)); + plot3(ax1,bfields(1,2:end),bfields(2,2:end),zpos,'m-.','linewidth',1.5,'Displayname','Boundaries') + + + xlim(ax1,[M.zgrid(1) M.zgrid(end)]) + ylim(ax1,[M.rgrid(1) M.rgrid(end)]) + xlabel(ax1,'z [m]') + ylabel(ax1,'r [m]') + title(ax1,'Position') + c = colorbar(ax1); + c.Label.String= 'n[m^{-3}]'; + %c.Limits=[0 max(M.N(:))]; + %caxis(ax1,[0 max(M.N(:))]); + view(ax1,2) + + %set(ax1,'colorscale','log') + + + UR=M.fluidUR(:,:,fieldstep); + UZ=M.fluidUZ(:,:,fieldstep); + + ax2=subplot(2,2,2,'Parent',fig); + + surface(ax2,M.zgrid,M.rgrid,squeeze(M.fluidEkin(1,:,:,fieldstep))/M.qe,'edgecolor','none'); + %plot(ax2,M.zgrid,data.pot(:,5)) + xlabel(ax2,'z [m]') + ylabel(ax2,'r [m]') + colormap(ax2,'jet') + c = colorbar(ax2); + c.Label.String= 'E_r [eV]'; + %c.Limits=[min(M.fluidUR(:,:,:)) max(M.fluidUR(:,:,:))]; + + grid(ax2, 'on') + %caxis(ax2,[-4e5 1e6]) + + view(ax2,2) + + ax3=subplot(2,2,3,'Parent',fig); + + surface(ax3,M.zgrid,M.rgrid,squeeze(M.fluidEkin(2,:,:,fieldstep))/M.qe,'edgecolor','none') + + xlabel(ax3,'z [m]') + ylabel(ax3,'r [m]') + colormap(ax3,'jet') + c = colorbar(ax3); + c.Label.String= 'E_\thet [eV]'; + + grid(ax3, 'on') + view(ax3,2) + + ax4=subplot(2,2,4,'Parent',fig); + + surface(ax4,M.zgrid,M.rgrid,squeeze(M.fluidEkin(3,:,:,fieldstep))/M.qe,'edgecolor','none') + xlabel(ax4,'z [m]') + ylabel(ax4,'r [m]') + colormap(ax4,'jet') + c = colorbar(ax4); + c.Label.String= 'E_z [eV]'; + titl=''; + labl=''; + + grid(ax4, 'on') + view(ax4,2) + + linkaxes([ax1 ax2 ax3 ax4],'xy') + + end + + function plotGridButtonPushed(btn,ax) + %UNTITLED2 Summary of this function goes here + % Detailed explanation goes here + f=figure(); + PlotEspic2dgriddata(f,M,sld.Value); + f.PaperOrientation='landscape'; + [~, name, ~] = fileparts(M.file); + print(f,sprintf('%sfluid%d',name,sld.Value),'-dpdf','-fillpage') + end + + function updatefigdata(control, event, Othercontrol, fig) + + + if strcmp(event.EventName,'ValueChanged') + fieldstep=floor(control.Value); + control.Value=fieldstep; + else + fieldstep=floor(event.Value); + end + Othercontrol.Value=fieldstep; + + sgtitle(fig,sprintf('t=%0.5e s',double((fieldstep-1)*M.it1)*M.dt)) + + %% update Position histogram + ax1=fig.Children(end); + + + dens=M.N(:,:,fieldstep); + dens(M.geomweight(:,:,1)<0)=0; + + zpos=interp2(M.zgrid, M.rgrid, dens, ax1.Children(end-1).XData, ax1.Children(end-1).YData); + ax1.Children(end-1).ZData=zpos; + zpos=interp2(M.zgrid, M.rgrid, dens, ax1.Children(end-2).XData, ax1.Children(end-2).YData); + ax1.Children(end-2).ZData=zpos; + + ax1.Children(end).ZData=dens; + ax1.Children(end).CData=dens; + + + ER=squeeze(M.fluidEkin(1,:,:,fieldstep))/M.qe; + EZ=squeeze(M.fluidEkin(3,:,:,fieldstep))/M.qe; + view(ax1,2) + %% update Radial velocity + + fig.Children(end-2).Children(1).CData=ER; + fig.Children(end-2).Children(1).ZData=ER; + %% update Azimuthal velocity + Ethet=squeeze(M.fluidEkin(2,:,:,fieldstep))/M.qe; + + fig.Children(end-4).Children(1).CData=Ethet; + fig.Children(end-4).Children(1).ZData=Ethet; + %% update Axial velocity + + fig.Children(end-6).Children(1).CData=EZ; + fig.Children(end-6).Children(1).ZData=EZ; + %drawnow limitrate + + + + end + +end + + diff --git a/matlab/@espic2dhdf5/dispespicFields.m b/matlab/@espic2dhdf5/dispespicFields.m new file mode 100644 index 0000000..13d3925 --- /dev/null +++ b/matlab/@espic2dhdf5/dispespicFields.m @@ -0,0 +1,350 @@ +function dispespicFields(obj,logdensity,showgrid,fixed,parper) +%dispespicFields Allows to display the time evolution of the density, electric potential and electric fields +% M is of class espic2dhdf5 and contains the simulation results +fieldstep=1; +if nargin <2 + logdensity=false; + showgrid=false; + fixed=false; +end +if nargin <3 + showgrid=false; + fixed=false; +end +if nargin <4 + fixed=false; +end +if nargin <5 + parper=false; +end +fixed=fi(fixed); + +f=uifigure('Name',sprintf('Grid data %s',obj.name)); + +mf=uipanel(f,'Position',[5 50 f.Position(3)-10 f.Position(4)-55]); +mf.AutoResizeChildren='off'; +m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); + +sgtitle(mf,sprintf('step=%d t=%0.5e s',fieldstep*obj.it1,obj.t2d(fieldstep))) + +sld = uislider(m,'Position',[10 30 0.6*m.Position(3) 3]); +sld.Value=fieldstep; +sld.Limits=[1 size(obj.t2d,1)]; + +edt = uieditfield(m,'numeric','Limits',[1 size(obj.t2d,1)],'Value',1); +edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; +edt.RoundFractionalValues='on'; + +MaxN=0; +Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); +Play=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play'); +Pause=uibutton(m,'Position',[Play.Position(1)+Play.Position(3)+10 5 40 20],'Text', 'Pause'); +%Playbt=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play/Pause'); + +stop=false; + +sld.ValueChangingFcn={@updatefigdata,edt,mf}; +edt.ValueChangedFcn={@updatefigdata,sld,mf}; +Printbt.ButtonPushedFcn={@plotGridButtonPushed}; +Play.ButtonPushedFcn={@plotPlayButtonPushed}; + +Pause.ButtonPushedFcn={@PauseButtonPushed}; + +set(f,'KeyPressFcn',{ @onKeyDown,sld,edt,mf}) + +PlotEspic2dgriddata(mf,obj,fieldstep); + + function plotPlayButtonPushed(btn,ax) + stop=false; + i=sld.Value; + while ~stop + edt.Value=i; + sld.Value=i; + updatesubplotsdata(i,mf); + pause(0.01) + i=sld.Value; + i=i+10; + if(i>sld.Limits(2)) + stop=true; + end + end + end + function PauseButtonPushed(btn,ax) + stop = true; + end + + function onKeyDown(src,event,slider,editfield, fig) + direction=0; + if strcmp(event.Key,'leftarrow') + direction=-1; + elseif strcmp(event.Key,'rightarrow') + direction=+1; + elseif strcmp(event.Key,'uparrow') + direction=+10; + elseif strcmp(event.Key,'downarrow') + direction=-10; + end + + if(direction~=0) + currval=slider.Value; + slider.Value=max(slider.Limits(1),min(currval+direction,slider.Limits(2))); + updatefigdata(slider, event, editfield ,fig) + end + end + + function PlotEspic2dgriddata(fig,M,fieldstep) + %PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep + sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,M.t2d(fieldstep))) + + ax1=subplot(2,2,1,'Parent',fig); + %% + dens=obj.N(:,:,fieldstep); + dens(obj.geomweight(:,:,1)<0)=NaN; + %[~,sf]=contourf(ax1,M.zgrid,M.rgrid,dens,40,'edgecolor','none'); + if(showgrid) + [sf]=surface(ax1,M.zgrid,M.rgrid,dens); + else + [~,sf]=contourf(ax1,M.zgrid,M.rgrid,dens,40,'edgecolor','none'); + end + xlim(ax1,[M.zgrid(1) M.zgrid(end)]) + ylim(ax1,[M.rgrid(1) M.rgrid(end)]) + xlabel(ax1,'z [m]') + ylabel(ax1,'r [m]') + title(ax1,'Density') + c = colorbar(ax1); + c.Label.String= 'n[m^{-3}]'; + %c.Limits=[0 max(M.N(:))]; + if(isboolean(fixed) && fixed) + climits=caxis(ax1); + MaxN=climits(2); + elseif(~isboolean(fixed)) + MaxN=fixed.data; + caxis(ax1,[-Inf MaxN]); + end + view(ax1,2) + hotmap=flipud(hot); + + colormap(ax1,hotmap); + hold(ax1, 'on') + %border=contourc(M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0]); + %zpos=interp2(M.zgrid, M.rgrid, M.N(:,:,fieldstep), border(1,2:end), border(2,2:end)); + %plot3(ax1,border(1,2:end),border(2,2:end),zpos+1e-6,'r-','linewidth',1.5,'Displayname','Boundaries') + contour(ax1,M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r-','linewidth',1.5); + + Blines=M.rAthet; + levels=linspace(min(Blines(M.geomweight(:,:,1)>0)),max(Blines(M.geomweight(:,:,1)>0)),20); + Blines(M.geomweight(:,:,1)<0)=NaN; +% bfields=contourc(M.zgrid,M.rgrid,Blines,real(levels)); +% zpos=interp2(M.zgrid, M.rgrid, M.N(:,:,fieldstep), bfields(1,2:end), bfields(2,2:end)); +% plot3(ax1,bfields(1,2:end),bfields(2,2:end),zpos+1e-6,'m-.','linewidth',1.5,'Displayname','Boundaries') + + contour(ax1,M.zgrid,M.rgrid,Blines,real(levels),'m-.','linewidth',1.5); + + if logdensity + %set(ax1,'zscale','log') + set(ax1,'colorscale','log') + end + + ax2=subplot(2,2,2,'Parent',fig); + pot=M.pot(:,:,fieldstep); + pot(M.geomweight(:,:,1)<0)=NaN; + contourf(ax2,M.zgrid,M.rgrid,pot,20); + %plot(ax2,M.zgrid,data.pot(:,5)) + %% + xlabel(ax2,'z [m]') + ylabel(ax2,'r [m]') + colormap(ax2,'jet') + c = colorbar(ax2); + c.Label.String= '\Phi [V]'; + title(ax2,'Es potential') + grid(ax2, 'on') + hold(ax2, 'on') + contour(ax2,M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r-','linewidth',2.5,'Displayname','Boundaries'); + Blines=M.rAthet; + levels=linspace(min(Blines(M.geomweight(:,:,1)>0)),max(Blines(M.geomweight(:,:,1)>0)),20); + Blines(M.geomweight(:,:,1)<0)=NaN; + contour(ax2,M.zgrid,M.rgrid,Blines,real(levels),'m-.','linewidth',1.5,'Displayname','Magnetic field lines'); + + %% + ax3=subplot(2,2,3,'Parent',fig); + %% + if parper + Ez=M.Epar(fieldstep); + else + Ez=M.Ez(:,:,fieldstep); + end + Ez(M.geomweight(:,:,1)<0)=NaN; + contourf(ax3,M.zgrid,M.rgrid,Ez,60) + hold(ax3, 'on') + contour(ax3,M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r-','linewidth',1.5,'Displayname','Boundaries'); + xlabel(ax3,'z [m]') + ylabel(ax3,'r [m]') + c = colorbar(ax3); + c.Label.String= 'E_{z} [V/m]'; + if parper + title(ax3,'Parallel Electric field') + else + title(ax3,'Axial Electric field') + end + grid(ax3, 'on') + + ax4=subplot(2,2,4,'Parent',fig); + %% + if parper + Er=M.Eperp(fieldstep); + else + Er=M.Er(:,:,fieldstep); + end + Er(M.geomweight(:,:,1)<0)=NaN; + contourf(ax4,M.zgrid,M.rgrid,Er,60) + hold(ax4, 'on') + contour(ax4,M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r-','linewidth',1.5,'Displayname','Boundaries'); + xlabel(ax4,'z [m]') + ylabel(ax4,'r [m]') + c = colorbar(ax4); + c.Label.String= 'E_{r} [V/m]'; + if parper + title(ax4,'Perpendicular Electric field') + else + title(ax4,'Radial Electric field') + end + grid(ax4, 'on') + + linkaxes([ax1,ax2,ax3,ax4]); + end + + function plotGridButtonPushed(btn,ax) + f=figure(); + PlotEspic2dgriddata(f,obj,floor(sld.Value)); + axold=mf.Children(end-2); + xlimits=xlim(axold); + ylimits=ylim(axold); + + ax1=subplot(2,2,1,'Parent',f); + xlim(ax1,xlimits); + ylim(ax1,ylimits); + f.PaperOrientation='landscape'; + f.PaperUnits='centimeters'; + f.PaperSize=[18,14]; + [~, name, ~] = fileparts(obj.file); + savefig(f,sprintf('%sGridFields%d',name,floor(sld.Value))) + print(f,sprintf('%sGridFields%d',name,floor(sld.Value)),'-dpdf','-fillpage') + set(f, 'Color', 'w'); + export_fig(f,sprintf('%sGridFields%d',name,floor(sld.Value)),'-eps') + end + + function updatefigdata(control, event, Othercontrol, fig) + + + if strcmp(event.EventName,'ValueChanged') + fieldstep=floor(control.Value); + control.Value=fieldstep; + elseif strcmp(event.EventName,'KeyPress') + fieldstep=floor(control.Value); + control.Value=fieldstep; + else + fieldstep=floor(event.Value); + end + Othercontrol.Value=fieldstep; + updatesubplotsdata(fieldstep, fig); + end + + function updatesubplotsdata(fieldstep, fig) + + sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*obj.it1,obj.t2d(floor(fieldstep)))) + + %% update Position histogram + ax1=fig.Children(end); + dens=obj.N(:,:,fieldstep); + dens(obj.geomweight(:,:,1)<0)=NaN; + if logdensity + dens(dens<=0)=NaN; + end + lvls=linspace(0,max(dens(:)),40); + xlimits=xlim(ax1); + ylimits=ylim(ax1); + + %zpos=interp2(obj.zgrid, obj.rgrid, dens, ax1.Children(end-1).XData, ax1.Children(end-1).YData); + %ax1.Children(end-1).ZData=zpos+1e-6; + %zpos=interp2(obj.zgrid, obj.rgrid, dens, ax1.Children(end-2).XData, ax1.Children(end-2).YData); + %ax1.Children(end-2).ZData=zpos+1e-6; + + ax1.Children(end).ZData=dens; + ax1.Children(end).LevelList=lvls; + if(showgrid) + ax1.Children(end).CData=dens; + end + if(isboolean(fixed) && fixed) + climits=caxis(ax1); + MaxN=climits(2); + nmax=max(dens(:)); + if(nmax>MaxN) + MaxN=nmax; + end + caxis(ax1,[0 MaxN]); + elseif(~isboolean(fixed)) + MaxN=fixed.data; + caxis(ax1,[-Inf MaxN]); + end + caxis(ax1,'auto') + + % view(ax1,2) + %% update ES Potential + + ax2=subplot(2,2,2,'Parent',fig); + %hold(ax2, 'off') + pot=obj.pot(:,:,fieldstep); + pot(obj.geomweight(:,:,1)<0)=NaN; + contourf(ax2,obj.zgrid,obj.rgrid,pot,20); + %ax2.Children(end).ZData=pot; + caxis(ax2,[-inf inf]) + +% xlabel(ax2,'z [m]') +% ylabel(ax2,'r [m]') +% colormap(ax2,'jet') +% c = colorbar(ax2); +% c.Label.String= '\Phi [V]'; +% title(ax2,'Es potential') +% %% +% hold(ax2, 'on') +% contour(ax2,obj.zgrid,obj.rgrid,obj.geomweight(:,:,1),[0 0],'r-','linewidth',2.5,'Displayname','Boundaries'); +% Blines=obj.rAthet; +% levels=linspace(min(Blines(obj.geomweight(:,:,1)>0)),max(Blines(obj.geomweight(:,:,1)>0)),20); +% Blines(obj.geomweight(:,:,1)<0)=NaN; +% contour(ax2,obj.zgrid,obj.rgrid,Blines,real(levels),'m-.','linewidth',1.5,'Displayname','Magnetic field lines'); + +% pot=obj.pot(:,:,fieldstep); +% pot(obj.geomweight(:,:,1)<0)=0; +% contourf(fig.Children(end-2),obj.zgrid,obj.rgrid,pot,20); + + + %% update Radial electric field + if parper + Ez=obj.Epar(fieldstep); + else + Ez=obj.Ez(:,:,fieldstep); + end + Ez(obj.geomweight(:,:,1)<0)=NaN; + ax3=subplot(2,2,3,'Parent',fig); + ax3.Children(end).ZData=Ez; + caxis(ax3,[-inf inf]) + %% update Axial electric field + if parper + Er=obj.Eperp(fieldstep); + else + Er=obj.Er(:,:,fieldstep); + end + Er(obj.geomweight(:,:,1)<0)=NaN; + ax4=subplot(2,2,4,'Parent',fig); + ax4.Children(end).ZData=Er; + caxis(ax4,[-inf inf]) + + xlim(ax4,xlimits); + ylim(ax4,ylimits); + %drawnow limitrate + + end + +end + + diff --git a/matlab/@espic2dhdf5/dispespicFluid.m b/matlab/@espic2dhdf5/dispespicFluid.m new file mode 100644 index 0000000..c945798 --- /dev/null +++ b/matlab/@espic2dhdf5/dispespicFluid.m @@ -0,0 +1,301 @@ +function f=dispespicFluid(M,flux,log,parper) +fieldstep=length(M.t2d); +if nargin<2 + flux=false; +end +if nargin<3 + log=false; +end +if nargin<4 + parper=false; +end + +f=uifigure('Name','Fluid data'); + +mf=uipanel(f,'Position',[5 50 f.Position(3)-10 f.Position(4)-55]); +mf.AutoResizeChildren='off'; +m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); + +sgtitle(mf,sprintf('t=%0.5e s',M.t2d(fieldstep))) + +sld = uislider(m,'Position',[10 30 0.6*m.Position(3) 3]); +sld.Limits=[1 length(M.t2d)]; +sld.Value=fieldstep; + + +edt = uieditfield(m,'numeric','Limits',[1 length(M.t2d)],'Value',1); +edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; +edt.RoundFractionalValues='on'; + + +Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); +%Playbt=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play/Pause'); + + +sld.ValueChangingFcn={@updatefigdata,edt,mf}; +edt.ValueChangedFcn={@updatefigdata,sld,mf}; +Printbt.ButtonPushedFcn={@plotGridButtonPushed}; + +set(f,'KeyPressFcn',{ @onKeyDown,sld,edt,mf}) + +[R,Z]=meshgrid(M.rgrid,M.zgrid); +Rinv=1./R; +Rinv(:,1)=0; + +PlotEspic2dfluiddata(mf,M,fieldstep); + +function onKeyDown(src,event,slider,editfield, fig) + direction=0; + if strcmp(event.Key,'leftarrow') + direction=-1; + elseif strcmp(event.Key,'rightarrow') + direction=+1; + elseif strcmp(event.Key,'uparrow') + direction=+10; + elseif strcmp(event.Key,'downarrow') + direction=-10; + end + + if(direction~=0) + currval=slider.Value; + slider.Value=max(slider.Limits(1),min(currval+direction,slider.Limits(2))); + updatefigdata(slider, event, editfield ,fig) + end +end + +function PlotEspic2dfluiddata(fig,M,fieldstep) +%PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep +sgtitle(fig,sprintf('t=%0.5e s',M.t2d(fieldstep))) + + +ax1=subplot(2,2,1,'Parent',fig); +N=M.N(:,:,fieldstep); +N(M.geomweight(:,:,1)<0)=NaN; +contourf(ax1,M.zgrid,M.rgrid,N,50,'linecolor','none') +xlim(ax1,[M.zgrid(1) M.zgrid(end)]) +ylim(ax1,[M.rgrid(1) M.rgrid(end)]) +xlabel(ax1,'z [m]') +ylabel(ax1,'r [m]') +title(ax1,'Density') +c = colorbar(ax1); +c.Label.String= 'n[m^{-3}]'; +%c.Limits=[0 max(M.N(:))]; +%caxis(ax1,[0 max(M.N(:))]); +view(ax1,2) + +%set(ax1,'colorscale','log') + +UR0=M.fluidUR(:,:,fieldstep); +UZ0=M.fluidUZ(:,:,fieldstep); + +if parper + UR=UR0.*M.sinthet-UZ0.*M.costhet; + UZ=UR0.*M.costhet+UZ0.*M.sinthet; +else + UR=UR0; + UZ=UZ0; +end + +UR(M.geomweight(:,:,1)<0)=NaN; +ax2=subplot(2,2,2,'Parent',fig); +if flux + UR=UR.*M.N(:,:,fieldstep); +end +if log + UR=abs(UR); +end +contourf(ax2,M.zgrid,M.rgrid,UR,50,'linecolor','none'); +%plot(ax2,M.zgrid,data.pot(:,5)) +xlabel(ax2,'z [m]') +ylabel(ax2,'r [m]') +colormap(ax2,'jet') +c = colorbar(ax2); +%c.Limits=[min(M.fluidUR(:,:,:)) max(M.fluidUR(:,:,:))]; +titl=''; +labl=''; +if parper + labl='U_{per}'; + titl= 'Perpendicular'; +else + labl='U_{r}'; + titl= 'Radial'; +end +if flux + labl= [labl ' [1/sm^2]']; + titl=[titl ' Flux']; + caxis(ax2,5e21*[-1 1]) +else + labl= [labl ' [m/s]']; + titl=[titl ' velocity']; + %caxis(ax2,[-4e5 2e4]) +end +if log + labl= ['|' labl '|']; + set(ax2,'ColorScale','log') +end +title(ax2,titl) +c.Label.String=labl; +grid(ax2, 'on') +%caxis(ax2,[-4e5 1e6]) + +view(ax2,2) + +ax3=subplot(2,2,3,'Parent',fig); +omegathet=M.fluidUTHET(:,:,fieldstep); +if flux + omegathet=omegathet.*M.N(:,:,fieldstep); +else + %omegathet=abs(omegathet.*Rinv'); +end +omegathet(M.geomweight(:,:,1)<0)=NaN; +contourf(ax3,M.zgrid,M.rgrid,omegathet,50,'linecolor','none') + +%mean(M.fluidUTHET(:,:,end).*Rinv') +xlabel(ax3,'z [m]') +ylabel(ax3,'r [m]') +colormap(ax3,'jet') +c = colorbar(ax3); +if flux + c.Label.String= 'U_\theta [1/sm^2]'; + title(ax3,'Azimuthal Flux') +else + c.Label.String= '\omega_\theta [m/s]'; + title(ax3,'Azimuthal rotation velocity') + +end + +if log + set(ax3,'colorscale','log') + end + +grid(ax3, 'on') +view(ax3,2) + +ax4=subplot(2,2,4,'Parent',fig); +UZ(M.geomweight(:,:,1)<0)=NaN; +if flux + UZ=UZ.*M.N(:,:,fieldstep); +end +if log + UZ=abs(UZ); +end +contourf(ax4,M.zgrid,M.rgrid,UZ,30,'linecolor','none') +xlabel(ax4,'z [m]') +ylabel(ax4,'r [m]') +colormap(ax4,'jet') +c = colorbar(ax4); +%c.Limits=[min(M.fluidUZ(:)) max(M.fluidUZ(:))]; +titl=''; +labl=''; +if parper + labl='U_{par}'; + titl= 'Parallel'; +else + labl='U_{z}'; + titl= 'Axial'; +end +if flux + labl= [labl ' [1/sm^2]']; + titl=[titl ' Flux']; +else + labl= [labl ' [m/s]']; + titl=[titl ' velocity']; +end +if log + labl= ['|' labl '|']; + set(ax2,'ColorScale','log') +end +title(ax4,titl) +c.Label.String=labl; +grid(ax4, 'on') +%caxis(ax4,[min(M.fluidUZ(:)) max(M.fluidUZ(:))]) +view(ax4,2) + +linkaxes([ax1 ax2 ax3 ax4],'xy') + +end + +function plotGridButtonPushed(btn,ax) +%UNTITLED2 Summary of this function goes here +% Detailed explanation goes here +f=figure(); +PlotEspic2dgriddata(f,M,sld.Value); +f.PaperOrientation='landscape'; +[~, name, ~] = fileparts(M.file); +print(f,sprintf('%sfluid%d',name,sld.Value),'-dpdf','-fillpage') +end + +function updatefigdata(control, event, Othercontrol, fig) + + + if strcmp(event.EventName,'ValueChanged') + fieldstep=floor(control.Value); + control.Value=fieldstep; + elseif strcmp(event.EventName,'KeyPress') + fieldstep=floor(control.Value); + control.Value=fieldstep; + else + fieldstep=floor(event.Value); + end + Othercontrol.Value=fieldstep; + + sgtitle(fig,sprintf('t=%0.5e s',double((fieldstep-1)*M.it1)*M.dt)) + + %% update Position histogram + ax1=fig.Children(end); + N=M.N(:,:,fieldstep); + N(M.geomweight(:,:,1)<0)=NaN; + %ax1.Children(1).CData=N; + ax1.Children(1).ZData=N; + + + UR0=M.fluidUR(:,:,fieldstep); + UZ0=M.fluidUZ(:,:,fieldstep); + if parper + UR=UR0.*M.sinthet-UZ0.*M.costhet; + UZ=UR0.*M.costhet+UZ0.*M.sinthet; +else + UR=UR0; + UZ=UZ0; + end + UR(M.geomweight(:,:,1)<0)=NaN; + UZ(M.geomweight(:,:,1)<0)=NaN; + view(ax1,2) + %% update Radial velocity + +if flux + UR=UR.*N; +end +if log + UR=abs(UR); +end + %fig.Children(end-2).Children(1).CData=UR; + fig.Children(end-2).Children(1).ZData=UR; + %% update Azimuthal velocity +omegathet=M.fluidUTHET(:,:,fieldstep); +omegathet(M.geomweight(:,:,1)<0)=NaN; +if flux + omegathet=omegathet.*M.N(:,:,fieldstep); +else + %omegathet=abs(omegathet.*Rinv'); +end + %fig.Children(end-4).Children(1).CData=omegathet; + fig.Children(end-4).Children(1).ZData=omegathet; + %% update Axial velocity +if flux + UZ=UZ.*N; +end +if log + UZ=abs(UZ); +end + %fig.Children(end-6).Children(1).CData=UZ; + fig.Children(end-6).Children(1).ZData=UZ; + %drawnow limitrate + + + +end + +end + + diff --git a/matlab/dispespicPressure.m b/matlab/@espic2dhdf5/dispespicPressure.m similarity index 92% rename from matlab/dispespicPressure.m rename to matlab/@espic2dhdf5/dispespicPressure.m index 7f8ab27..316d4ab 100644 --- a/matlab/dispespicPressure.m +++ b/matlab/@espic2dhdf5/dispespicPressure.m @@ -1,185 +1,191 @@ function dispespicPressure(M,logdensity,showgrid,fixed,temperature) %dispespicPressure Allows to display the time evolution of the pressure tensor % M is of class espic2dhdf5 and contains the simulation results fieldstep=1; if nargin <2 logdensity=false; end if nargin <3 showgrid=false; end if nargin <4 fixed=false; end if nargin <5 temperature=false; end fixed=fi(fixed); f=uifigure('Name',sprintf('Pressure data %s',M.name)); mf=uipanel(f,'Position',[5 50 f.Position(3)-10 f.Position(4)-55]); mf.AutoResizeChildren='off'; m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); sgtitle(mf,sprintf('step=%d t=%0.5e s',fieldstep*M.it1,M.t2d(fieldstep))) sld = uislider(m,'Position',[10 30 0.6*m.Position(3) 3]); sld.Value=fieldstep; sld.Limits=[1 size(M.t2d,1)]; edt = uieditfield(m,'numeric','Limits',[1 size(M.t2d,1)],'Value',1); edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; edt.RoundFractionalValues='on'; MaxP=0; MinP=0; Paxes=gobjects(6); Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); Play=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play'); Pause=uibutton(m,'Position',[Play.Position(1)+Play.Position(3)+10 5 40 20],'Text', 'Pause'); %Playbt=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play/Pause'); stop=false; sld.ValueChangingFcn={@updatefigdata,edt,mf}; edt.ValueChangedFcn={@updatefigdata,sld,mf}; Printbt.ButtonPushedFcn={@plotGridButtonPushed}; Play.ButtonPushedFcn={@plotPlayButtonPushed}; Pause.ButtonPushedFcn={@PauseButtonPushed}; PlotEspic2dgriddata(mf,M,fieldstep); function plotPlayButtonPushed(btn,ax) stop=false; for i=edt.Value:sld.Limits(2) edt.Value=i; sld.Value=i; updatesubplotsdata(i,mf); pause(0.01) if stop stop=false; break; end end end function PauseButtonPushed(btn,ax) stop = true; end function PlotEspic2dgriddata(fig,M,fieldstep) %PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,M.t2d(fieldstep))) if temperature Clabels={'T_{rr} [eV]', 'T_{r\theta} [eV]', 'T_{rz} [eV]', 'T_{\theta\theta} [eV]', 'T_{\theta z} [eV]', 'T_{zz} [eV]'}; else Clabels={'P_{rr} [Pa]', 'P_{r\theta} [Pa]', 'P_{rz} [Pa]', 'P_{\theta\theta} [Pa]', 'P_{\theta z} [Pa]', 'P_{zz} [Pa]'}; end for i=1:6 Paxes(i)=subplot(2,3,i,'Parent',fig); if temperature N=M.N(:,:,fieldstep); invN=1./N; invN(isinf(invN))=0; p=squeeze(M.Presstens(i,:,:,fieldstep)).*invN/M.qe; else p=squeeze(M.Presstens(i,:,:,fieldstep)); end - sf=surface(Paxes(i),M.zgrid,M.rgrid,p,'edgecolor','none'); + if logdensity + p=abs(p); + end + contourf(Paxes(i),M.zgrid,M.rgrid,p,'edgecolor','none'); + hold(Paxes(i),'on') if(showgrid) set(sf,'edgecolor','black') end + contour(Paxes(i),M.zgrid,M.rgrid,M.geomweight(:,:,1),[1e-8 1e-8],'r-','linewidth',2.5,'Displayname','Boundaries'); xlim(Paxes(i),[M.zgrid(1) M.zgrid(end)]) ylim(Paxes(i),[M.rgrid(1) M.rgrid(end)]) xlabel(Paxes(i),'z [m]') ylabel(Paxes(i),'r [m]') title(Paxes(i),Clabels{i}) c = colorbar(Paxes(i)); c.Label.String= Clabels{i}; if(isboolean(fixed) && fixed) climits=caxis(Paxes(i)); MaxP=max(MaxP,climits(2)); MinP=min(MinP,climits(1)); elseif(~isboolean(fixed)) MaxP=fixed.data; MinP=-Inf; caxis(ax1,[MinP MaxP]); end - view(Paxes(i),2) if logdensity - set(Paxes(i),'zscale','log') + %set(Paxes(i),'zscale','log') set(Paxes(i),'colorscale','log') end + colormap(Paxes(i),cool) end if(isboolean(fixed) && fixed) for i=1:6 caxis(Paxes(i),[MinP MaxP]); end end linkaxes(Paxes); + end function plotGridButtonPushed(btn,ax) %UNTITLED2 Summary of this function goes here % Detailed explanation goes here f=figure(); PlotEspic2dgriddata(f,M,sld.Value); f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); print(f,sprintf('%sGrid%d',name,sld.Value),'-dpdf','-fillpage') end function updatefigdata(control, event, Othercontrol, fig) if strcmp(event.EventName,'ValueChanged') fieldstep=floor(control.Value); control.Value=fieldstep; else fieldstep=floor(event.Value); end Othercontrol.Value=fieldstep; updatesubplotsdata(fieldstep, fig); end function updatesubplotsdata(fieldstep, fig) sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,double((fieldstep-1)*M.it1)*M.dt)) %% update Pressure subplots for i=1:6 if temperature N=M.N(:,:,fieldstep); invN=1./N; invN(isinf(invN))=0; P=squeeze(M.Presstens(i,:,:,fieldstep)).*invN/M.qe; else P=squeeze(M.Presstens(i,:,:,fieldstep)); end - Paxes(i).Children.ZData=P; - Paxes(i).Children.CData=P; + Paxes(i).Children(end).ZData=P; + %Paxes(i).Children.CData=P; if(isboolean(fixed) && fixed) climits=caxis(Paxes(i)); MaxP=max([P(:);climits(2)]); MinP=min([P(:);climits(1)]); caxis(Paxes(i),[0 MaxP]); elseif(~isboolean(fixed)) MaxP=fixed.data; caxis(Paxes(i),[-Inf MaxP]); end end if(isboolean(fixed) && fixed) for i=1:6 caxis(Paxes(i),[MinP MaxP]); end end drawnow limitrate end end diff --git a/matlab/@espic2dhdf5/dispespicRProf.m b/matlab/@espic2dhdf5/dispespicRProf.m new file mode 100644 index 0000000..072afdf --- /dev/null +++ b/matlab/@espic2dhdf5/dispespicRProf.m @@ -0,0 +1,330 @@ +function dispespicRProf(M,logdensity,showgrid,fixed,parper) +%dispespicFields Allows to display the time evolution of the density, electric potential and electric fields +% M is of class espic2dhdf5 and contains the simulation results +fieldstep=1; +zpos=1; +if nargin <2 + logdensity=false; + showgrid=false; + fixed=false; +end +if nargin <3 + showgrid=false; + fixed=false; +end +if nargin <4 + fixed=false; +end +if nargin <5 + parper=false; +end +fixed=fi(fixed); + +f=uifigure('Name',sprintf('Grid data %s',M.name)); + +mf=uipanel(f,'Position',[5 50 f.Position(3)-30 f.Position(4)-55]); +mf.AutoResizeChildren='off'; +m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); +rpanel=uipanel(f,'Position',[f.Position(3)-28 50 25 f.Position(4)-55]); + +sgtitle(mf,sprintf('step=%d t=%0.5e s',fieldstep*M.it1,M.t2d(fieldstep))) + +sld = uislider(m,'Position',[10 30 0.6*m.Position(3) 3]); +sld.Value=fieldstep; +sld.Limits=[1 size(M.t2d,1)]; +sld.Tag='timeslider'; + +sldr = uislider(rpanel,'Orientation','vertical','Position',[5 35 40 rpanel.Position(4)-40]); +sldr.Value=zpos; +sldr.Limits=[1 length(M.zgrid)]; +sldr.Tag='axialslider'; + +edr = uieditfield(rpanel,'numeric','Limits',[1 length(M.zgrid)],'Value',1); +edr.Position=[sldr.Position(1) sldr.Position(2)-30 40 20]; +edr.RoundFractionalValues='on'; +edr.Tag='axialfield'; + +edt = uieditfield(m,'numeric','Limits',[1 size(M.t2d,1)],'Value',1); +edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; +edt.RoundFractionalValues='on'; +edt.Tag='timefield'; + +MaxN=0; +Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); +Play=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play'); +Pause=uibutton(m,'Position',[Play.Position(1)+Play.Position(3)+10 5 40 20],'Text', 'Pause'); +%Playbt=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play/Pause'); + +stop=false; + +sld.ValueChangingFcn={@updatefigdata,edt,mf}; +edt.ValueChangedFcn={@updatefigdata,sld,mf}; + +sldr.ValueChangingFcn={@updatefigdata,edr,mf}; +edr.ValueChangedFcn={@updatefigdata,sldr,mf}; + +Printbt.ButtonPushedFcn={@plotGridButtonPushed}; +Play.ButtonPushedFcn={@plotPlayButtonPushed}; + +Pause.ButtonPushedFcn={@PauseButtonPushed}; + +set(f,'KeyPressFcn',{ @onKeyDown,sld,edt,mf}) + +PlotEspic2dgriddata(mf,M,fieldstep,zpos); + + function plotPlayButtonPushed(btn,ax) + stop=false; + i=sld.Value; + while ~stop + edt.Value=i; + sld.Value=i; + updatesubplotsdata(i,mf); + pause(0.01) + i=sld.Value; + i=i+10; + if(i>sld.Limits(2)) + stop=true; + end + end + end + function PauseButtonPushed(btn,ax) + stop = true; + end + + function onKeyDown(src,event,slider,editfield, fig) + direction=0; + if strcmp(event.Key,'leftarrow') + direction=-1; + elseif strcmp(event.Key,'rightarrow') + direction=+1; + elseif strcmp(event.Key,'uparrow') + direction=+10; + elseif strcmp(event.Key,'downarrow') + direction=-10; + end + + if(direction~=0) + currval=slider.Value; + slider.Value=max(slider.Limits(1),min(currval+direction,slider.Limits(2))); + updatefigdata(slider, event, editfield ,fig) + end + end + + function PlotEspic2dgriddata(fig,M,fieldstep,zpos) + %PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep + sgtitle(fig,sprintf('step=%d t=%0.5e s z=%0.3e mm',(fieldstep-1)*M.it1,M.t2d(fieldstep),M.zgrid(zpos)*1e3)) + + geomw=M.geomweight(:,zpos,1)>=0; + ax1=subplot(2,2,1,'Parent',fig); + p=plot(ax1,M.rgrid*1e3,M.N(:,zpos,fieldstep)); + xlim(ax1,[M.rgrid(1) M.rgrid(end)]*1e3) + xlabel(ax1,'r [mm]') + title(ax1,'Density') + ylabel(ax1,'n[m^{-3}]'); + %c.Limits=[0 max(M.N(:))]; + + hold(ax1, 'on') + [~,id1]=min(abs(M.geomweight(1:10,zpos,1))); + [~,id2]=min(abs(M.geomweight(11:end,zpos,1))); + id2=id2+10; + ylimits=ylim; + plot(ax1,M.rgrid(id1)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + plot(ax1,M.rgrid(id2)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + + yyaxis(ax1,'right') + hold(ax1, 'on') + Er=M.Er(:,zpos,fieldstep).*geomw; + Ez=M.Ez(:,zpos,fieldstep).*geomw; + p1=plot(ax1,M.rgrid*1e3,Er); + p2=plot(ax1,M.rgrid*1e3,Ez); + ylabel(ax1,'E [V/m]') + if max([Er Ez])>0 + ylim(ax1,[ -max([Er Ez]) max([Er Ez])]) + end + legend(ax1,[p p1 p2],{'n','Er','Ez'},'location','northwest') + + + + ax2=subplot(2,2,2,'Parent',fig); + ur=M.fluidUR(:,zpos,fieldstep); + plot(ax2,M.rgrid*1e3,ur); + xlim(ax2,[M.rgrid(1) M.rgrid(end)]*1e3) + xlabel(ax2,'r [mm]') + title(ax2,'radial velocity') + ylabel(ax2,'v_r [m/s]'); + %c.Limits=[0 max(M.N(:))]; + + hold(ax2, 'on') + if max(ur)>0 + ylim(ax2,[ -max(ur) max(ur)]) + end + %ylim(ax2,[ -max(ur) max(ur)]) + ylimits=ylim; + plot(ax2,M.rgrid(id1)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + plot(ax2,M.rgrid(id2)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + + ax3=subplot(2,2,3,'Parent',fig); + uthet=M.fluidUTHET(:,zpos,fieldstep); + plot(ax3,M.rgrid*1e3,uthet); + xlim(ax3,[M.rgrid(1) M.rgrid(end)]*1e3) + xlabel(ax3,'r [mm]') + title(ax3,'Azimuthal velocity') + ylabel(ax3,'v_\theta [m/s]'); + %c.Limits=[0 max(M.N(:))]; + + hold(ax3, 'on') + if max(uthet)>0 + ylim(ax3,[ -max(uthet) max(uthet)]) + end + ylimits=ylim; + plot(ax3,M.rgrid(id1)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + plot(ax3,M.rgrid(id2)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + + uExb=-M.Er(:,zpos,fieldstep)./M.Bz(zpos,:)'.*(uthet~=0); + plot(ax3,M.rgrid*1e3,uExb); + + ax4=subplot(2,2,4,'Parent',fig); + uz=M.fluidUZ(:,zpos,fieldstep); + plot(ax4,M.rgrid*1e3,uz); + xlim(ax4,[M.rgrid(1) M.rgrid(end)]*1e3) + xlabel(ax4,'r [mm]') + title(ax4,'Axial velocity') + ylabel(ax4,'v_z [m/s]'); + %c.Limits=[0 max(M.N(:))]; + + hold(ax4, 'on') + if max(uz)>0 + ylim(ax4,[ -max(uz) max(uz)]) + end + ylimits=ylim; + plot(ax4,M.rgrid(id1)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + plot(ax4,M.rgrid(id2)*[1 1]*1e3,ylimits,'k--','linewidth',1.5,'Displayname','Boundaries'); + + linkaxes([ax1,ax2,ax3,ax4],'x'); + end + + function plotGridButtonPushed(btn,ax) + %UNTITLED2 Summary of this function goes here + % Detailed explanation goes here + f=figure(); + PlotEspic2dgriddata(f,M,sld.Value,edr.Value); + f.PaperOrientation='landscape'; + [~, name, ~] = fileparts(M.file); + print(f,sprintf('%sGrid%d%d',name,sld.Value,edr.Value),'-dpdf','-fillpage') + end + + function updatefigdata(control, event, Othercontrol, fig) + + if contains(event.Source.Tag,'time') + if strcmp(event.EventName,'ValueChanged') + fieldstep=floor(control.Value); + control.Value=fieldstep; + elseif strcmp(event.EventName,'KeyPress') + fieldstep=floor(control.Value); + control.Value=fieldstep; + else + fieldstep=floor(event.Value); + end + Othercontrol.Value=fieldstep; + elseif contains(event.Source.Tag,'axial') + if strcmp(event.EventName,'ValueChanged') + zpos=floor(control.Value); + control.Value=zpos; + elseif strcmp(event.EventName,'KeyPress') + zpos=floor(control.Value); + control.Value=zpos; + else + zpos=floor(event.Value); + end + Othercontrol.Value=zpos; + end + + updatesubplotsdata(fieldstep, zpos, fig); + end + + function updatesubplotsdata(fieldstep, zpos, fig) + sgtitle(fig,sprintf('step=%d t=%0.5e s z=%0.3e mm',(fieldstep-1)*M.it1,M.t2d(fieldstep),M.zgrid(zpos)*1e3)) + + [~,id1]=min(abs(M.geomweight(1:10,zpos,1))); + [~,id2]=min(abs(M.geomweight(11:end,zpos,1))); + id2=id2+10; + rlim1=M.rgrid(id1)*[1 1]*1e3; + rlim2=M.rgrid(id2)*[1 1]*1e3; + + %% update density + ax1=fig.Children(end); + geomw=M.geomweight(:,zpos,1)>=0; + dens=M.N(:,zpos,fieldstep).*geomw; + Er=M.Er(:,zpos,fieldstep).*geomw; + Ez=M.Ez(:,zpos,fieldstep).*geomw; + yyaxis(ax1,'left') + ax1.Children(end).YData=dens; + ylimits=ylim(ax1); + ax1.Children(end-1).XData=rlim1; + ax1.Children(end-1).YData=ylimits; + ax1.Children(end-2).XData=rlim2; + ax1.Children(end-2).YData=ylimits; + + yyaxis(ax1,'right') + ax1.Children(end).YData=Er; + ax1.Children(end-1).YData=Ez; + if max(abs([Er; Ez]))>0 + ylim(ax1,max(abs([Er; Ez]))*[ -1 1]) + end + + % view(ax1,2) + %% update Radial velocity + ax2=fig.Children(end-2); + ur=M.fluidUR(:,zpos,fieldstep).*geomw; + + ax2.Children(end).YData=ur; + if max(abs(ur))>0 + ylim(ax2,max(abs(ur))*[ -1 1]) + end + ax2.Children(end-1).XData=rlim1; + ax2.Children(end-2).XData=rlim2; + ylimits=ylim(ax2); + ax2.Children(end-1).YData=ylimits; + ax2.Children(end-2).YData=ylimits; + + + %% update Azimuthal velocity + ax3=fig.Children(end-3); + uthet=M.fluidUTHET(:,zpos,fieldstep).*geomw; + uExb=-M.Er(:,zpos,fieldstep)./M.Bz(zpos,:)'.*(uthet~=0); + + ax3.Children(end-3).YData=uExb'; + ax3.Children(end).YData=uthet; + + if max(uthet)>0 + ylim(ax3,[ -max(uthet) max(uthet)]) + end + + ax3.Children(end-1).XData=rlim1; + ax3.Children(end-2).XData=rlim2; + ylimits=ylim(ax3); + ax3.Children(end-1).YData=ylimits; + ax3.Children(end-2).YData=ylimits; + + %% update Axial velocity + ax4=fig.Children(end-4); + uz=M.fluidUZ(:,zpos,fieldstep).*geomw; + + + ax4.Children(end).YData=uz; + + if max(abs(uz))>0 + ylim(ax4,max(abs(uz))*[ -1 1]) + end + ax4.Children(end-1).XData=rlim1; + ax4.Children(end-2).XData=rlim2; + ylimits=ylim(ax4); + ax4.Children(end-1).YData=ylimits; + ax4.Children(end-2).YData=ylimits; + drawnow limitrate + + end + +end + + diff --git a/matlab/dispespicWell.m b/matlab/@espic2dhdf5/dispespicWell.m similarity index 66% rename from matlab/dispespicWell.m rename to matlab/@espic2dhdf5/dispespicWell.m index 436012d..2a92d58 100644 --- a/matlab/dispespicWell.m +++ b/matlab/@espic2dhdf5/dispespicWell.m @@ -1,250 +1,282 @@ -function dispespicWell(M,logdensity,showgrid,fixed) +function dispespicWell(M,fracn,logdensity,showgrid,fixed) %dispespicFields Allows to display the time evolution of the density, and Penning potential well % M is of class espic2dhdf5 and contains the simulation results fieldstep=1; -if nargin <2 +if nargin <3 logdensity=false; end -if nargin <3 +if nargin <4 showgrid=false; end -if nargin <4 +if nargin <5 fixed=false; end +if nargin<2 +fracn=0.1; +end fixed=fi(fixed); f=uifigure('Name',sprintf('Well data %s',M.name)); mf=uipanel(f,'Position',[5 50 f.Position(3)-10 f.Position(4)-55]); mf.AutoResizeChildren='off'; m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); sgtitle(mf,sprintf('step=%d t=%0.5e s',fieldstep*M.it1,M.t2d(fieldstep))) sld = uislider(m,'Position',[10 30 0.4*m.Position(3) 3]); sld.Value=fieldstep; sld.Limits=[1 size(M.t2d,1)]; edt = uieditfield(m,'numeric','Limits',[1 size(M.t2d,1)],'Value',1); edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; edt.RoundFractionalValues='on'; MaxN=0; Minwell=0; Maxwell=0; plotaxes=gobjects(2); Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); Play=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play'); Pause=uibutton(m,'Position',[Play.Position(1)+Play.Position(3)+10 5 60 20],'Text', 'Pause'); rcoordbt=uicheckbox(m,'Position',[Pause.Position(1)+Pause.Position(3)+10 5 65 20],'Text', 'r/rAtheta'); stop=false; sld.ValueChangingFcn={@updatefigdata,edt,mf}; edt.ValueChangedFcn={@updatefigdata,sld,mf}; Printbt.ButtonPushedFcn={@plotGridButtonPushed}; Play.ButtonPushedFcn={@plotPlayButtonPushed}; rcoordbt.ValueChangedFcn={@change_radial_coord}; rcoordbt.Value=true; Pause.ButtonPushedFcn={@PauseButtonPushed}; PlotEspic2dgriddata(mf,M,fieldstep); function plotPlayButtonPushed(btn,ax) stop=false; for i=edt.Value:5:sld.Limits(2) edt.Value=i; sld.Value=i; updatesubplotsdata(i,mf); pause(0.01) if stop stop=false; break; end end end function PauseButtonPushed(btn,ax) stop = true; end function change_radial_coord(btn,ax) if rcoordbt.Value ylabel(plotaxes(2),'r [m]') ylim(plotaxes(2),[M.rgrid(1) M.rgrid(end)]) ylabel(plotaxes(1),'r [m]') ylim(plotaxes(1),[M.rgrid(1) M.rgrid(end)]) linkaxes(plotaxes) else ylabel(plotaxes(2),'rA_\theta [Tm^2]') ylim(plotaxes(2),[M.rAthet(1,1) M.rAthet(end,1)]) ylabel(plotaxes(1),'rA_\theta [Tm^2]') ylim(plotaxes(1),[M.rAthet(1,1) M.rAthet(end,1)]) %linkaxes(plotaxes,'off'); %ylim(plotaxes(1),[M.rgrid(1) M.rgrid(end)]) end updatesubplotsdata(sld.Value, mf); end function PlotEspic2dgriddata(fig,M,fieldstep) %PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,M.t2d(fieldstep))) - - plotaxes(1)=subplot(1,2,1,'Parent',fig); - sf=surface(plotaxes(1),M.zgrid,M.rgrid,M.N(:,:,fieldstep),'edgecolor','none'); + N=M.N(:,:,fieldstep); + plotaxes(1)=subplot(2,1,1,'Parent',fig); + sf=surface(plotaxes(1),M.zgrid,M.rgrid,N,'edgecolor','none'); if(showgrid) set(sf,'edgecolor','black') end + hold(plotaxes(1), 'on') + contour(plotaxes(1),M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r--') xlim(plotaxes(1),[M.zgrid(1) M.zgrid(end)]) ylim(plotaxes(1),[M.rgrid(1) M.rgrid(end)]) xlabel(plotaxes(1),'z [m]') ylabel(plotaxes(1),'r [m]') title(plotaxes(1),'Density') c = colorbar(plotaxes(1)); c.Label.String= 'n[m^{-3}]'; %c.Limits=[0 max(M.N(:))]; if(isboolean(fixed) && fixed) climits=caxis(plotaxes(1)); MaxN=climits(2); elseif(~isboolean(fixed)) MaxN=fixed.data; caxis(plotaxes(1),[-Inf MaxN]); end view(plotaxes(1),2) if logdensity set(plotaxes(1),'zscale','log') set(plotaxes(1),'colorscale','log') end - plotaxes(2)=subplot(1,2,2,'Parent',fig); - model=M.potentialwellmodel(fieldstep); - z=model.z; - r=model.r; - pot=model.pot; - rathet=model.rathet; - if rcoordbt.Value - [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rgrid); - pot=griddata(z,r,pot,Zmesh,Rmesh); - sf=surface(plotaxes(2),M.zgrid(1:end),M.rgrid(1:end),pot(1:end,1:end),'edgecolor','none'); - else - [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); - pot=griddata(z,rathet,pot,Zmesh,Rmesh); - sf=surface(plotaxes(2),M.zgrid(1:end),M.rAthet(:,1),pot(1:end,1:end),'edgecolor','none'); - end + plotaxes(2)=subplot(2,1,2,'Parent',fig); + model=M.potentialwellmodel(fieldstep); + z=model.z; + r=model.r; + pot=model.pot; + rathet=model.rathet; + dispz=M.zgrid; + if rcoordbt.Value + [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rgrid); + pot=griddata(z,r,pot,Zmesh,Rmesh); + dispr=M.rgrid; + else + [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); + pot=griddata(z,rathet,pot,Zmesh,Rmesh); + N=griddata(Zmesh,M.rAthet,N,Zmesh,Rmesh); + dispr=M.rAthet(:,1); + end + + sf=contourf(plotaxes(2),dispz,dispr,pot(1:end,1:end),'edgecolor','none'); + hold(plotaxes(2), 'on') + contour(plotaxes(2),dispz,dispr,N-fracn*max(N(:)),[0 0],'k--') + contour(plotaxes(2),dispz,dispr,M.geomweight(:,:,1),[0 0],'r--') if(showgrid) set(sf,'edgecolor','black') end xlim(plotaxes(2),[M.zgrid(1) M.zgrid(end)]) xlabel(plotaxes(2),'z [m]') if rcoordbt.Value ylabel(plotaxes(2),'r [m]') ylim(plotaxes(2),[M.rgrid(1) M.rgrid(end)]) else ylabel(plotaxes(2),'rA_\theta [Tm^2]') ylim(plotaxes(2),[M.rAthet(1,1) M.rAthet(end,1)]) - end + end title(plotaxes(2),'Well') c = colorbar(plotaxes(2)); c.Label.String= 'depth [eV]'; %c.Limits=[0 max(M.N(:))]; if(isboolean(fixed) && fixed) climits=caxis(plotaxes(2)); Minwell=climits(1); Maxwell=climits(2); elseif(~isboolean(fixed)) climits=caxis(plotaxes(2)); Minwell=climits(1); Maxwell=climits(2); end view(plotaxes(2),2) colormap(plotaxes(2),'jet'); + Blines=M.rAthet; + levels=linspace(min(Blines(M.geomweight(:,:,1)>0)),max(Blines(M.geomweight(:,:,1)>0)),20); + Blines(M.geomweight(:,:,1)<0)=NaN; + contour(plotaxes(2),M.zgrid,M.rgrid,Blines,real(levels),'m-.','linewidth',1.5,'Displayname','Magnetic field lines'); linkaxes(plotaxes); + axis(plotaxes(1), 'equal') + axis(plotaxes(2), 'equal') end function plotGridButtonPushed(btn,ax) f=figure(); - PlotEspic2dgriddata(f,M,sld.Value); + PlotEspic2dgriddata(f,M,edt.Value); f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); - print(f,sprintf('%sGrid%d',name,sld.Value),'-dpdf','-fillpage') + f.PaperSize=[18,12]; + savefig(f,sprintf('%sGridFields%d',name,floor(edt.Value))) + print(f,sprintf('%sGridWell%d',name,edt.Value),'-dpdf','-fillpage') end function updatefigdata(control, event, Othercontrol, fig) if strcmp(event.EventName,'ValueChanged') fieldstep=floor(control.Value); control.Value=fieldstep; else fieldstep=floor(event.Value); end Othercontrol.Value=fieldstep; updatesubplotsdata(fieldstep, fig); end function updatesubplotsdata(fieldstep, fig) sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,double((fieldstep-1)*M.it1)*M.dt)) %% update Position histogram dens=M.N(:,:,fieldstep); model=M.potentialwellmodel(fieldstep); - z=model.z; - r=model.r; - pot=model.pot; - rathet=model.rathet; - - if rcoordbt.Value - plotaxes(1).Children.YData=M.rgrid(1:end); - else - [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); - dens=griddata(Zmesh(:),M.rAthet(:),dens(:),Zmesh,Rmesh); - plotaxes(1).Children.YData=M.rAthet(:,1); - end - + z=model.z; + r=model.r; + pot=model.pot; + rathet=model.rathet; + geomweight=M.geomweight(:,:,1); + if rcoordbt.Value + plotaxes(1).Children(end).YData=M.rgrid(1:end); + gweight=geomweight; + else + [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); + dens=griddata(Zmesh(:),M.rAthet(:),dens(:),Zmesh,Rmesh); + gweight=griddata(Zmesh(:),M.rAthet(:),geomweight(:),Zmesh,Rmesh); + plotaxes(1).Children(end).YData=M.rAthet(:,1); + end - plotaxes(1).Children.ZData=dens; - plotaxes(1).Children.CData=dens; + dens(gweight<0)=NaN; + plotaxes(1).Children(end).ZData=dens; + plotaxes(1).Children(end).CData=dens; if(isboolean(fixed) && fixed) climits=caxis(plotaxes(1)); MaxN=climits(2); nmax=max(dens(:)); if(nmax>MaxN) MaxN=nmax; end caxis(plotaxes(1),[0 MaxN]); elseif(~isboolean(fixed)) MaxN=fixed.data; caxis(plotaxes(1),[-Inf MaxN]); end if rcoordbt.Value - [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rgrid); - well=griddata(z,r,pot,Zmesh,Rmesh); - plotaxes(2).Children.YData=M.rgrid(1:end); - else - [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); - well=griddata(z,rathet,pot,Zmesh,Rmesh); - plotaxes(2).Children.YData=M.rAthet(:,1); - end - plotaxes(2).Children.ZData=well; - plotaxes(2).Children.CData=well; + [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rgrid); + well=griddata(z,r,pot,Zmesh,Rmesh); + + plotaxes(2).Children(end).YData=M.rgrid(1:end); + plotaxes(2).Children(end-1).YData=M.rgrid(1:end); + else + [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); + well=griddata(z,rathet,pot,Zmesh,Rmesh); + + dens=griddata(Zmesh,M.rAthet,dens,Zmesh,Rmesh); + + plotaxes(2).Children(end).YData=M.rAthet(:,1); + plotaxes(2).Children(end-1).YData=M.rAthet(:,1); + end + well(gweight<0)=NaN; + + plotaxes(2).Children(end-1).ZData=dens-fracn*max(dens(:)); + + plotaxes(2).Children(end).ZData=well; + %plotaxes(2).Children(end).CData=well; if(isboolean(fixed) && fixed || ~isboolean(fixed)) climits=caxis(plotaxes(2)); Maxwell=max([well(:);climits(2)]); Minwell=min([well(:);climits(1)]); caxis(plotaxes(2),[Minwell Maxwell]); end drawnow limitrate - + end - + end diff --git a/matlab/@espic2dhdf5/espic2dhdf5.m b/matlab/@espic2dhdf5/espic2dhdf5.m new file mode 100644 index 0000000..fff8f9a --- /dev/null +++ b/matlab/@espic2dhdf5/espic2dhdf5.m @@ -0,0 +1,3542 @@ +classdef espic2dhdf5 + %espic2dhdf5 General class used to treat hdf5 result files of espic2d code + % A result file is loaded with a call to M=espic2dhdf5(filename) where filename is the relative or absolute file path + % after loading, several quantities and composite diagnostics such as moments of the distribution function or individual particles + % quantities can be accessed. + properties + filename + name + folder + fullpath + timestamp + info + t0d + t1d + t2d + tpart + it0 + it1 + it2 + + %% Physical constants + vlight=299792458; + qe=1.60217662E-19; + me=9.109383E-31; + eps_0=8.85418781762E-12; + kb=1.38064852E-23; + + %% Run parameters + dt % simulation time step + nrun % number of time steps simulated + nlres + nlsave + nlclassical % Was the equation of motion solved in the classical framework + nlPhis % Was the self-consistent electric field computed + nz % number of intervals in the z direction for the grid + nnr % number of intervals in the r direction for the grid for each of the 3 mesh regions + lz % physical axial dimension of the simulation space + nplasma % Number of initial macro particles + potinn % Normalized electric potential at the coaxial insert + potout % Normalized electric potential at the cylinder surface + B0 % Normalization for the magnetic field + Rcurv % Magnetic mirror ratio + width % Magnetic mirror length + n0 % Initial particle density in case of old particle loading + temp % Initial particle temperature in case of old particle loading + femorder % finite element method order in z and r direction + ngauss % Order of the Gauss integration method for the FEM + plasmadim % initial dimensions of the plasma for the old particle loading system + radii % Radial limits of the three mesh regions coarse,fine,coarse + H0 % Initial particle Energy for Davidsons distribution function + P0 % Initial particle Angular momentum for Davidsons distribution function + normalized % Are the parts quantities normalized in the h5 file + nbspecies % Number of species simulated + + + %% Frequencies + omepe % Reference plasma frequency used for normalization + omece % Reference cyclotronic frequency for normalization + + %% Normalizations + tnorm % Time normalization + rnorm % Dimension normalization + bnorm % Magnetic field normalization + enorm % Electric field normalization + phinorm % Electric potential normalization + vnorm % Velocity normalization + + %% Grid data + rgrid % Radial grid position points + zgrid % Axial grid position points + dz % Axial grid step + dr % Radial grid step for the three mesh regions + CellVol % Volume of the cell used for density calculation + celltype % type of cell -1 outside 1 inside 0 border + linked_s % location of linked spline + bsplinetype + + %% Magnetic field + Br % Radial magnetic field + Bz % Axial magnetic field + Athet % Azimuthal component of the Magnetic potential vector + rAthet % r*Athet used for the representation of magnetic field lines + B % Magnetic field amplitude + sinthet % ratio to project quantities along the magnetic field lines + costhet % ratio to project quantities along the magnetic field lines + + %% Energies + epot % Time evolution of the particles potential energy + ekin % Time evolution of the particles kinetic energy + etot % Time evolution of the particles total energy + etot0 % Time evolution of the reference particle total energy + eerr % Time evolution of the error on the energy conservation + npart % Time evolution of the number of simulated + + %% 2D time data evaluated on grid points + N % main specie Density + fluidUR % main specie radial fluid velocity + fluidUZ % main specie axial fluid velocity + fluidUTHET % main specie azimuthal fluid velocity + pot % Electric potential evaluated at grid points + potxt % External Electric potential evaluated at grid points + phi % Electric potential in spline form + Er % Radial electric field + Ez % Axial electric field + Erxt % External Radial electric field + Ezxt % External Axial electric field + Presstens % Pressure tensor + fluidEkin % average kinetic energy in each direction + + %% Splines + knotsr % Spline radial knots + knotsz % Spline axial knots + + %% Particle parameters + weight % Macro particle numerical weight of the main specie + qsim % Macro particle charge + msim % Macro particle mass + nbparts % Time evolution of the number of simulated particles + partepot % Electric potential at the particles positions + R % Particles radial position + Z % Particles axial position + Rindex % Particles radial grid index + Zindex % Particles axial grid index + partindex % Particles unique id for tracing trajectories + VR % Particles radial velocity + VZ % Particles axial velocity + VTHET % Particles azimuthal velocity + THET % Particles azimuthal position + species % Array containing the other simulated species + + %% Celldiag + celldiag % Array containing the cell diagnostic data + nbcelldiag % Total number of cell diagnostics + + %% Curvilinear geometry + conformgeom % stores if we use the conforming or nonconforming boundary conditions + r_a + r_b + z_r + z_0 + r_0 + r_r + L_r + L_z + Interior + above1 + above2 + interior + walltype + geomweight + dirichletweight + gtilde + spl_bound + + + + %% Maxwell source parameters + maxwellsrce + + %% Collision with neutral parameters + neutcol + nudcol % effective momentum collision frequency + + %% Non ideal power supply + psupply + + end + + methods + function file=file(obj) + % returns the h5 file name + file=obj.filename; + end + + function obj = espic2dhdf5(filename,readparts,old) + % Reads the new result file filename and read the parts data if readparts==true + + % adds the helper_classes folder to the path + matlabfuncpath = dir([mfilename('fullpath'),'.m']); + addpath(sprintf('%s/../helper_classes',matlabfuncpath.folder)); + addpath(sprintf('%s/../export_fig',matlabfuncpath.folder)); + % Try catch are there for compatibility with older simulation files + filedata=dir(filename); + if (isempty(filedata)) + error("File: ""%s"" doesn't exist",filename) + end + obj.folder=filedata.folder; + obj.filename=filename; + [~, obj.name, ext] = fileparts(obj.filename); + obj.filename=[obj.name,ext]; + obj.fullpath=[obj.folder,'/',obj.filename]; + obj.timestamp=filedata.date; + if nargin==1 + readparts=true; + end + if nargin<3 + old=false; + end + %obj.info=h5info(filename); + + + %% Read the run parameters + obj.dt = h5readatt(obj.fullpath,'/data/input.00/','dt'); + obj.nrun = h5readatt(obj.fullpath,'/data/input.00/','nrun'); + obj.nlres = strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlres'),'y'); + obj.nlsave = strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlsave'),'y'); + obj.nlclassical =strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlclassical'),'y'); + obj.nlPhis =strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlPhis'),'y'); + obj.nz = h5readatt(obj.fullpath,'/data/input.00/','nz'); + obj.nnr = h5read(obj.fullpath,'/data/input.00/nnr'); + obj.lz = h5read(obj.fullpath,'/data/input.00/lz'); + obj.qsim = h5readatt(obj.fullpath,'/data/input.00/','qsim'); + obj.msim = h5readatt(obj.fullpath,'/data/input.00/','msim'); + + try + obj.r_a=h5readatt(obj.fullpath,'/data/input.00/geometry','r_a'); + obj.r_b=h5readatt(obj.fullpath,'/data/input.00/geometry','r_b'); + obj.z_r=h5readatt(obj.fullpath,'/data/input.00/geometry','z_r'); + obj.r_r=h5readatt(obj.fullpath,'/data/input.00/geometry','r_r'); + obj.r_0=h5readatt(obj.fullpath,'/data/input.00/geometry','r_0'); + obj.z_0=h5readatt(obj.fullpath,'/data/input.00/geometry','z_0'); + obj.above1=h5readatt(obj.fullpath,'/data/input.00/geometry','above1'); + obj.above2=h5readatt(obj.fullpath,'/data/input.00/geometry','above2'); + obj.interior=h5readatt(obj.fullpath,'/data/input.00/geometry','interior'); + obj.walltype=h5readatt(obj.fullpath,'/data/input.00/geometry','walltype'); + try + obj.L_r=h5readatt(obj.fullpath,'/data/input.00/geometry','L_r'); + obj.L_z=h5readatt(obj.fullpath,'/data/input.00/geometry','L_z'); + catch + end + obj.conformgeom=false; + catch + obj.conformgeom=true; + obj.walltype=0; + obj.r_a=obj.rgrid(1); + obj.r_b=obj.rgrid(end); + obj.above1=1; + obj.above2=-1; + obj.L_r=0; + obj.L_z=0; + end + + try + obj.weight=h5readatt(obj.fullpath,'/data/part/','weight'); + catch + obj.weight=obj.msim/obj.me; + end + obj.nplasma = h5readatt(obj.fullpath,'/data/input.00/','nplasma'); + obj.potinn = h5readatt(obj.fullpath,'/data/input.00/','potinn'); + obj.potout = h5readatt(obj.fullpath,'/data/input.00/','potout'); + obj.B0 = h5readatt(obj.fullpath,'/data/input.00/','B0'); + obj.Rcurv = h5readatt(obj.fullpath,'/data/input.00/','Rcurv'); + obj.width = h5readatt(obj.fullpath,'/data/input.00/','width'); + obj.n0 = h5readatt(obj.fullpath,'/data/input.00/','n0'); + obj.temp = h5readatt(obj.fullpath,'/data/input.00/','temp'); + try + obj.it0 = h5readatt(obj.fullpath,'/data/input.00/','it0d'); + obj.it1 = h5readatt(obj.fullpath,'/data/input.00/','it2d'); + obj.it2 = h5readatt(obj.fullpath,'/data/input.00/','itparts'); + catch + obj.it0 = h5readatt(obj.fullpath,'/data/input.00/','it0'); + obj.it1 = h5readatt(obj.fullpath,'/data/input.00/','it1'); + obj.it1 = h5readatt(obj.fullpath,'/data/input.00/','it2'); + end + try + try + obj.nbspecies=h5readatt(obj.fullpath,'/data/part/','nbspecies'); + catch + obj.nbspecies=h5readatt(obj.fullpath,'/data/input.00/','nbspecies'); + end + obj.normalized=strcmp(h5readatt(obj.fullpath,'/data/input.00/','rawparts'),'y'); + catch + obj.nbspecies=1; + obj.normalized=false; + end + try + obj.nbcelldiag=h5readatt(obj.fullpath,'/data/celldiag/','nbcelldiag'); + catch + obj.nbcelldiag=0; + end + + obj.omepe=sqrt(abs(obj.n0)*obj.qe^2/(obj.me*obj.eps_0)); + obj.omece=obj.qe*obj.B0/obj.me; + + obj.npart= h5read(obj.fullpath, '/data/var0d/nbparts'); + try + obj.nudcol= h5read(obj.fullpath, '/data/var0d/nudcol'); + catch + end + try + obj.H0 = h5read(obj.fullpath,'/data/input.00/H0'); + obj.P0 = h5read(obj.fullpath,'/data/input.00/P0'); + catch + obj.H0=3.2e-14; + obj.P0=8.66e-25; + end + + % Normalizations + if old + obj.tnorm=abs(1/obj.omepe); + else + obj.tnorm=min(abs(1/obj.omepe),abs(1/obj.omece)); + end + obj.rnorm=obj.vlight*obj.tnorm; + obj.bnorm=obj.B0; + obj.enorm=obj.vlight*obj.bnorm; + obj.phinorm=obj.enorm*obj.rnorm; + obj.vnorm=obj.vlight; + + % Grid data + obj.rgrid= h5read(obj.fullpath, '/data/var1d/rgrid')*obj.rnorm; + obj.zgrid= h5read(obj.fullpath, '/data/var1d/zgrid')*obj.rnorm; + obj.dz=(obj.zgrid(end)-obj.zgrid(1))/double(obj.nz); + rid=1; + for i=1:length(obj.nnr) + obj.dr(i)=(obj.rgrid(sum(obj.nnr(1:i))+1)-obj.rgrid(rid))/double(obj.nnr(i)); + rid=rid+obj.nnr(i); + end + + Br = h5read(obj.fullpath,'/data/fields/Br')*obj.bnorm; + obj.Br= reshape(Br,length(obj.zgrid),length(obj.rgrid)); + Bz = h5read(obj.fullpath,'/data/fields/Bz')*obj.bnorm; + obj.Bz= reshape(Bz,length(obj.zgrid),length(obj.rgrid)); + try + Atheta = h5read(obj.fullpath,'/data/fields/Athet')*obj.bnorm; + obj.Athet= reshape(Atheta,length(obj.zgrid),length(obj.rgrid)); + [rmeshgrid,~]=meshgrid(obj.rgrid,obj.zgrid); + obj.rAthet=(rmeshgrid.*obj.Athet)'; + catch + end + obj.B=sqrt(obj.Bz.^2+obj.Br.^2); + obj.costhet=(obj.Br./obj.B)'; + obj.sinthet=(obj.Bz./obj.B)'; + clear Br Bz + try + obj.t0d=h5read(obj.fullpath,'/data/var0d/time'); + catch + obj.t0d=obj.dt.*double(0:length(obj.epot)-1); + end + try + for i=0:5 + grp=sprintf('/data/input.%02i/',i); + obj.Erxt(:,:,i+1)=reshape(h5read(obj.fullpath,[grp,'Erxt']),length(obj.zgrid),length(obj.rgrid))'*obj.enorm; + obj.Ezxt(:,:,i+1)=reshape(h5read(obj.fullpath,[grp,'Ezxt']),length(obj.zgrid),length(obj.rgrid))'*obj.enorm; + obj.potxt(:,:,i+1)=reshape(h5read(obj.fullpath,[grp,'potxt']),length(obj.zgrid),length(obj.rgrid))'*obj.phinorm; + end + catch + end + + + obj.femorder = h5read(obj.fullpath,'/data/input.00/femorder'); + obj.ngauss = h5read(obj.fullpath,'/data/input.00/ngauss'); + obj.plasmadim = h5read(obj.fullpath,'/data/input.00/plasmadim'); + obj.radii = h5read(obj.fullpath,'/data/input.00/radii'); + + obj.epot = h5read(obj.fullpath,'/data/var0d/epot'); + obj.ekin = h5read(obj.fullpath,'/data/var0d/ekin'); + obj.etot = h5read(obj.fullpath,'/data/var0d/etot'); + try + obj.etot0 = h5read(obj.fullpath,'/data/var0d/etot0'); + obj.eerr = obj.etot-obj.etot0; + catch + obj.eerr = obj.etot-obj.etot(2); + end + + if(obj.normalized) + obj.pot=gridquantity(obj.fullpath,'/data/fields/pot',sum(obj.nnr)+1, obj.nz+1,1); + obj.Er=gridquantity(obj.fullpath,'/data/fields/Er',sum(obj.nnr)+1, obj.nz+1,1); + obj.Ez=gridquantity(obj.fullpath,'/data/fields/Ez',sum(obj.nnr)+1, obj.nz+1,1); + else + obj.pot=gridquantity(obj.fullpath,'/data/fields/pot',sum(obj.nnr)+1, obj.nz+1,obj.phinorm); + obj.Er=gridquantity(obj.fullpath,'/data/fields/Er',sum(obj.nnr)+1, obj.nz+1,obj.enorm); + obj.Ez=gridquantity(obj.fullpath,'/data/fields/Ez',sum(obj.nnr)+1, obj.nz+1,obj.enorm); + end + + try + obj.t2d = h5read(obj.fullpath,'/data/fields/time'); + catch + info=h5info(obj.fullpath,'/data/fields/partdensity'); + obj.t2d=obj.dt*(0:info.objspace.Size(2)-1)*double(obj.it1); + end + + try + info=h5info(obj.fullpath,'/data/fields/moments'); + obj.femorder = h5read(obj.fullpath,'/data/input.00/femorder'); + kr=obj.femorder(2)+1; + obj.knotsr=augknt(obj.rgrid,kr); + + kz=obj.femorder(1)+1; + obj.knotsz=augknt(obj.zgrid,kz); + try + obj.CellVol= reshape(h5read(obj.fullpath,'/data/fields/volume'),length(obj.knotsz)-kz,length(obj.knotsr)-kr); + obj.CellVol=permute(obj.CellVol,[2,1,3])*obj.rnorm^3; + catch + zvol=fnder(spmak(obj.knotsz,ones(1,length(obj.knotsz)-kz)), -1 ); + rvol=fnder(spmak(obj.knotsr,2*pi*[obj.rgrid' 2*obj.rgrid(end)-obj.rgrid(end-1)]), -1 ); + ZVol=diff(fnval(zvol,obj.knotsz)); + RVol=diff(fnval(rvol,obj.knotsr)); + obj.CellVol=RVol(3:end-1)*ZVol(3:end-1)'; + obj.CellVol=padarray(obj.CellVol,[1,1],'replicate','post'); + end + + try + obj.geomweight = h5read(obj.fullpath,'/data/input.00/geometry/geomweight'); + obj.geomweight= reshape(obj.geomweight,length(obj.zgrid),length(obj.rgrid),[]); + obj.geomweight = permute(obj.geomweight,[2,1,3]); + catch + obj.geomweight=ones(length(obj.rgrid),length(obj.zgrid),3); + end + try + obj.dirichletweight = h5read(obj.fullpath,'/data/input.00/geometry/dirichletweight'); + obj.dirichletweight= reshape(obj.dirichletweight,length(obj.zgrid),length(obj.rgrid),[]); + obj.dirichletweight = permute(obj.dirichletweight,[2,1,3]); + catch + obj.dirichletweight=obj.geomweight; + end + try + obj.gtilde = h5read(obj.fullpath,'/data/input.00/geometry/gtilde'); + obj.gtilde= reshape(obj.gtilde,length(obj.zgrid),length(obj.rgrid),[]); + obj.gtilde = permute(obj.gtilde,[2,1,3]); + catch + obj.gtilde=zeros(length(obj.rgrid),length(obj.zgrid),3); + end + geomweight=ones(length(obj.rgrid),length(obj.zgrid)); + if(obj.normalized) + obj.N=splinedensity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, 1, geomweight, 1); + obj.phi=splinequantity(obj.fullpath,'/data/fields/phi', obj.knotsr, obj.knotsz, obj.femorder, 1, obj.geomweight(:,:,1), -1); + else + obj.N=splinedensity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, abs(obj.qsim/obj.qe), geomweight, 1); + end + obj.fluidUR=splinevelocity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.vnorm, geomweight, 2); + obj.fluidUTHET=splinevelocity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.vnorm, geomweight, 3); + obj.fluidUZ=splinevelocity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.vnorm, geomweight, 4); + if(obj.normalized) + obj.Presstens=splinepressure(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, obj.vnorm^2*obj.me, geomweight); + obj.fluidEkin=splineenergy(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, obj.vnorm^2*obj.me*0.5, geomweight); + else + obj.Presstens=splinepressure(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, obj.vnorm^2*obj.msim, geomweight); + obj.fluidEkin=splineenergy(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, obj.vnorm^2*obj.msim*0.5, geomweight); + end + try + obj.celltype=h5read(obj.fullpath,'/data/input.00/geometry/ctype')'; + obj.linked_s=h5read(obj.fullpath,'/data/input.00/geometry/linked_s'); + obj.bsplinetype=h5read(obj.fullpath,'/data/input.00/geometry/bsplinetype'); + obj.bsplinetype=reshape(obj.bsplinetype,length(obj.knotsz)-kz,length(obj.knotsr)-kr); + catch + obj.celltype=[]; + obj.linked_s=[]; + end + catch + obj.CellVol=(obj.zgrid(2:end)-obj.zgrid(1:end-1))*((obj.rgrid(2:end).^2-obj.rgrid(1:end-1).^2)*pi)'; + obj.CellVol=obj.CellVol'; + obj.N=griddensity(obj.fullpath, '/data/fields/partdensity', sum(obj.nnr)+1, obj.nz+1, obj.CellVol, abs(obj.qsim/obj.qe), true); + obj.fluidUR=gridquantity(obj.fullpath, '/data/fields/fluidur', sum(obj.nnr)+1, obj.nz+1, obj.vnorm, true); + obj.fluidUTHET=gridquantity(obj.fullpath, '/data/fields/fluiduthet', sum(obj.nnr)+1, obj.nz+1, obj.vnorm, true); + obj.fluidUZ=gridquantity(obj.fullpath, '/data/fields/fluiduz', sum(obj.nnr)+1, obj.nz+1, obj.vnorm, true); + end + + % If we have a maxwellian source, read its parameters + try + obj.maxwellsrce.rlim=h5read(obj.fullpath, '/data/input.00/maxwellsource/rlimits'); + obj.maxwellsrce.zlim=h5read(obj.fullpath, '/data/input.00/maxwellsource/zlimits'); + obj.maxwellsrce.frequency=h5readatt(obj.fullpath, '/data/input.00/maxwellsource','frequency'); + obj.maxwellsrce.radialtype=h5readatt(obj.fullpath, '/data/input.00/maxwellsource','radialtype'); + obj.maxwellsrce.temperature=h5readatt(obj.fullpath, '/data/input.00/maxwellsource','temperature'); + obj.maxwellsrce.time_end=h5readatt(obj.fullpath, '/data/input.00/maxwellsource','time_end'); + obj.maxwellsrce.time_start=h5readatt(obj.fullpath, '/data/input.00/maxwellsource','time_start'); + obj.maxwellsrce.vth=h5readatt(obj.fullpath, '/data/input.00/maxwellsource','vth'); + obj.maxwellsrce.rate=obj.maxwellsrce.frequency*obj.weight/(pi*(diff(obj.maxwellsrce.rlim.^2))*diff(obj.maxwellsrce.zlim)); + obj.maxwellsrce.present=true; + catch + obj.maxwellsrce.present=false; + end + %% load neutcol parameters + try + obj.neutcol.neutdens=double(h5readatt(obj.fullpath, '/data/input.00/neutcol','neutdens')); + obj.neutcol.neutpressure=double(h5readatt(obj.fullpath, '/data/input.00/neutcol','neutpressure')); + obj.neutcol.scatter_fac=double(h5readatt(obj.fullpath, '/data/input.00/neutcol','scatter_fac')); + obj.neutcol.Eion=double(h5readatt(obj.fullpath, '/data/input.00/neutcol','Eion')); + obj.neutcol.E0=double(h5readatt(obj.fullpath, '/data/input.00/neutcol','E0')); + obj.neutcol.Escale=double(h5readatt(obj.fullpath, '/data/input.00/neutcol','Escale')); + try + obj.neutcol.io_cross_sec=double(h5read(obj.fullpath, '/data/input.00/neutcol/io_cross_sec')); + obj.neutcol.io_cross_sec(:,2)=obj.neutcol.io_cross_sec(:,2)*obj.rnorm^2; + obj.neutcol.io_cross_sec(:,3)=[log(obj.neutcol.io_cross_sec(2:end,2)./obj.neutcol.io_cross_sec(1:end-1,2))... + ./log(obj.neutcol.io_cross_sec(2:end,1)./obj.neutcol.io_cross_sec(1:end-1,1)); 0]; + obj.neutcol.iom_cross_sec=zeros(500,3); + obj.neutcol.iom_cross_sec(:,1)=logspace(log10(obj.neutcol.Eion+0.001),log10(5e4),size(obj.neutcol.iom_cross_sec,1)); + obj.neutcol.iom_cross_sec(:,2)=obj.sigmiopre(obj.neutcol.iom_cross_sec(:,1),true); + obj.neutcol.iom_cross_sec(:,3)=abs([log(obj.neutcol.iom_cross_sec(2:end,2)./obj.neutcol.iom_cross_sec(1:end-1,2))... + ./log(obj.neutcol.iom_cross_sec(2:end,1)./obj.neutcol.iom_cross_sec(1:end-1,1)); 0]); + + catch + obj.neutcol.io_cross_sec=[]; + obj.neutcol.iom_cross_sec=[]; + end + try + obj.neutcol.ela_cross_sec=double(h5read(obj.fullpath, '/data/input.00/neutcol/ela_cross_sec')); + obj.neutcol.ela_cross_sec(:,2)=obj.neutcol.ela_cross_sec(:,2)*obj.rnorm^2; + obj.neutcol.ela_cross_sec(:,3)=[log(obj.neutcol.ela_cross_sec(2:end,2)./obj.neutcol.ela_cross_sec(1:end-1,2))... + ./log(obj.neutcol.ela_cross_sec(2:end,1)./obj.neutcol.ela_cross_sec(1:end-1,1)); 0]; + catch + obj.neutcol.ela_cross_sec=[]; + end + obj.neutcol.present=true; + catch + obj.neutcol.present=false; + end + + %% load spline boundaries + try + obj.spl_bound.nbsplines=h5readatt(obj.fullpath, '/data/input.00/geometry_spl','nbsplines'); + for i=1:obj.spl_bound.nbsplines + splgroup=sprintf('/data/input.00/geometry_spl/%02d',i); + obj.spl_bound.boundary(i).knots=h5read(obj.fullpath,sprintf('%s/knots',splgroup)); + obj.spl_bound.boundary(i).coefs=reshape(h5read(obj.fullpath,sprintf('%s/pos',splgroup)),2,[])'; + obj.spl_bound.boundary(i).order=h5readatt(obj.fullpath,splgroup,'order'); + obj.spl_bound.boundary(i).kind=h5readatt(obj.fullpath,splgroup,'kind'); + obj.spl_bound.boundary(i).fun=spmak(obj.spl_bound.boundary(i).knots,obj.spl_bound.boundary(i).coefs'); + end + catch + obj.spl_bound.nbsplines=0; + end + + %% load non ideal power supply parameters + try + obj.psupply.targetbias=h5readatt(obj.fullpath, '/data/input.00/psupply','targetbias'); + obj.psupply.expdens=h5readatt(obj.fullpath, '/data/input.00/psupply','expdens'); + obj.psupply.PSresistor=h5readatt(obj.fullpath, '/data/input.00/psupply','PSresistor'); + obj.psupply.geomcapacitor=h5readatt(obj.fullpath, '/data/input.00/psupply','geomcapacitor'); + obj.psupply.nbhdt=h5readatt(obj.fullpath, '/data/input.00/psupply','nbhdt'); + obj.psupply.biases=h5read(obj.fullpath, '/data/var0d/biases'); + obj.psupply.current=h5read(obj.fullpath, '/data/var0d/current'); + obj.psupply.tau=obj.psupply.PSresistor*obj.psupply.geomcapacitor*obj.psupply.expdens/obj.neutcol.neutdens; + obj.psupply.active=true; + obj.psupply.bdpos=h5read(obj.fullpath, '/data/input.00/psupply/bdpos'); + catch + obj.psupply.active=false; + end + + % Read the main particles parameters + if(readparts) + + if(obj.normalized) + obj.R = h5partsquantity(obj.fullpath,'/data/part','R'); + obj.Z = h5partsquantity(obj.fullpath,'/data/part','Z'); + else + obj.R = h5partsquantity(obj.fullpath,'/data/part','R',obj.rnorm); + obj.Z = h5partsquantity(obj.fullpath,'/data/part','Z',obj.rnorm); + end + try + obj.THET = h5partsquantity(obj.fullpath,'/data/part','THET'); + catch + clear obj.THET + end + try + obj.Rindex=h5partsquantity(obj.fullpath,'/data/part','Rindex'); + obj.Zindex=h5partsquantity(obj.fullpath,'/data/part','Zindex'); + catch + clear obj.Rindex obj.Zindex + end + vscale=obj.vnorm; + + obj.VR = h5partsquantity(obj.fullpath,'/data/part','UR',vscale); + obj.VZ = h5partsquantity(obj.fullpath,'/data/part','UZ',vscale); + obj.VTHET= h5partsquantity(obj.fullpath,'/data/part','UTHET',vscale); + + if(obj.normalized) + obj.partepot = h5partsquantity(obj.fullpath,'/data/part','pot',sign(obj.qsim)*obj.qe); + else + obj.partepot = h5partsquantity(obj.fullpath,'/data/part','pot',sign(obj.qsim)*obj.qe*obj.phinorm); + end + + try + obj.partindex = h5partsquantity(obj.fullpath,'/data/part/','partindex'); + catch + end + + if(obj.nbspecies >1) + obj.species=h5parts.empty(obj.nbspecies,0); + for i=2:obj.nbspecies + obj.species(i-1)=h5parts(obj.fullpath,sprintf('/data/part/%2d',i),obj); + end + end + end + + try + obj.tpart = h5read(obj.fullpath,'/data/part/time'); + obj.nbparts = h5read(obj.fullpath,'/data/part/Nparts'); + catch + obj.nbparts=obj.npart; + obj.tpart=obj.dt*(0:size(obj.R,2)-1)*double(obj.it2); + end + + + if(obj.nbcelldiag > 0) + obj.celldiag=h5parts.empty; + j=0; + for i=1:obj.nbcelldiag + nbparts=h5read(obj.fullpath,sprintf('%s/Nparts',sprintf('/data/celldiag/%02d',i))); + if (sum(nbparts)>0) + j=j+1; + obj.celldiag(j)=h5parts(obj.fullpath,sprintf('/data/celldiag/%02d',i),obj); + obj.celldiag(j).rindex=double(h5readatt(obj.fullpath, sprintf('/data/celldiag/%02d',i),'rindex'))+(1:2); + obj.celldiag(j).zindex=double(h5readatt(obj.fullpath, sprintf('/data/celldiag/%02d',i),'zindex'))+(1:2); + end + + end + end + end + %------------------------------------------ + % Functions for accesing secondary simulation quantities + function Atheta=Atheta(obj,R,Z) + %% returns the magnetic vector potential at position R,Z interpolated from stored Athet in h5 file + % halflz=(obj.zgrid(end)+obj.zgrid(1))/2; + % Atheta=0.5*obj.B0*(R-obj.width/pi*(obj.Rcurv-1)/(obj.Rcurv+1)... + % .*besseli(1,2*pi*R/obj.width).*cos(2*pi*(Z-halflz)/obj.width)); + Atheta=interp2(obj.rgrid,obj.zgrid,obj.Athet,R,Z); + end + + function quantity=H(obj,indices) + %% computes the total energy for the main specie + % for the particle with index indices{1} at timepart step indices{2} + % which is time obj.timepart(indices{2}) + if strcmp(indices{1},':') + p=1:obj.VR.nparts;% if nothing is defined we load all particles + else + p=indices{1}; + end + if strcmp(indices{2},':') + t=1:length(obj.tpart); %if nothing is defined all time steps are considered + else + t=indices{2}; + end + % if track is true we look at specific particles with their + % index and follow them in time + % if it is false we just care about the distribution function + % and specific particles can have different positions in the + % resulting array for each timestep + if size(indices,1)>2 + track=indices{3}; + else + track=false; + end + quantity=0.5*obj.me*(obj.VR(p,t,track).^2+obj.VTHET(p,t,track).^2+obj.VZ(p,t,track).^2)+obj.partepot(p,t,track); + end + + function quantity=P(obj,indices) + %P computes the canonical angular momentum for the main specie + % for the particle with index indices{1} at timepart step indices{2} + % which is time obj.timepart(indices{2}) + if strcmp(indices{1},':') + p=1:obj.R.nparts; + else + p=indices{1}; + end + if strcmp(indices{2},':') + t=1:length(obj.tpart); + else + t=indices{2}; + end + % if track is true we look at specific particles with their + % index and follow them in time + % if it is false we just care about the distribution function + % and specific particles can have different positions in the + % resulting array for each timestep + if size(indices,1)>2 + track=indices{3}; + else + track=false; + end + quantity=obj.R(p,t,track).*(obj.VTHET(p,t,track)*obj.me+sign(obj.qsim)*obj.qe*obj.Atheta(obj.R(p,t,track),obj.Z(p,t,track))); + end + + function quantity=Vpar(obj,varargin) + %Vpar Computes the parallel velocity for the main specie + % for the particle with index indices{1} at timepart step indices{2} + % which is time obj.timepart(indices{2}) + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + if strcmp(indices{1},':') + p=1:obj.R.nparts; + else + p=indices{1}; + end + if strcmp(indices{2},':') + t=1:length(obj.tpart); + else + t=indices{2}; + end + % if track is true we look at specific particles with their + % index and follow them in time + % if it is false we just care about the distribution function + % and specific particles can have different positions in the + % resulting array for each timestep + if size(indices,1)>2 + track=indices{3}; + else + track=false; + end + Zp=obj.Z(p,t,track);% get the particle axial positon + Rp=obj.R(p,t,track);% get the particle radial position + + % interpolate the magnetic field at the particle position + Bzp=interp2(obj.zgrid,obj.rgrid,obj.Bz',Zp,Rp,'makima'); + Brp=interp2(obj.zgrid,obj.rgrid,obj.Br',Zp,Rp,'makima'); + Bp=sqrt(Bzp.^2+Brp.^2); + % calculate the projection angle of the radial and axial + % directions on the magnetic field line + Costhet=Bzp./Bp; + Sinthet=Brp./Bp; + % calculate the actuale parallel velocity + quantity=obj.VR(p,t,track).*Sinthet+obj.VZ(p,t,track).*Costhet; + end + + function quantity=Vperp(obj,varargin) + %Vperp Computes the perpendicular velocity in the guidind center reference frame, + % for the main specie particle indices{1} at time indices{2} + + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + + if strcmp(indices{1},':') + p=1:obj.R.nparts; + else + p=indices{1}; + end + if strcmp(indices{2},':') + t=1:length(obj.tpart); + else + t=indices{2}; + end + % if track is true we look at specific particles with their + % index and follow them in time + % if it is false we just care about the distribution function + % and specific particles can have different positions in the + % resulting array for each timestep + if size(indices,2)>2 + track=indices{3}; + else + track=false; + end + + % if gcs is true, gives the perpendicular velocity in the + % guiding center system by substracting the EXB azimuthal + % velocity + % else gives the total perpendicular velocity + if size(indices,2)>3 + gcs=indices{4}; + else + gcs=false; + end + % get the particle position + Zp=obj.Z(p,t,track); + Rp=obj.R(p,t,track); + % interpolate the magnetic field at the particle position + Bzp=interp2(obj.zgrid,obj.rgrid,obj.Bz',Zp,Rp,'makima'); + Brp=interp2(obj.zgrid,obj.rgrid,obj.Br',Zp,Rp,'makima'); + Bp=sqrt(Bzp.^2+Brp.^2); + % calculate the projecting angles + Costhet=Bzp./Bp; + Sinthet=Brp./Bp; + Vdrift=zeros(size(Zp)); + + if gcs + % for each particle and each timestep + % calculate the azimuthal ExB drift velocity + for j=1:length(t) + [~, tfield]=min(abs(obj.t2d-obj.tpart(t(j)))); + timeEr=obj.Er(:,:,tfield); + timeEz=obj.Ez(:,:,tfield); + %posindE=sub2ind(size(timeEr),Rind(:,j),Zind(:,j)); + timeErp=interp2(obj.zgrid,obj.rgrid,timeEr,Zp(:,j),Rp(:,j)); + timeEzp=interp2(obj.zgrid,obj.rgrid,timeEz,Zp(:,j),Rp(:,j)); + Vdrift(:,j)=(timeEzp.*Brp(:,j)-timeErp.*Bzp(:,j))./Bp(:,j).^2; + end + end + % calculate the perpendicular velocity + quantity=sqrt((obj.VTHET(p,t,track)-Vdrift).^2+(obj.VR(p,t,track).*Costhet-obj.VZ(p,t,track).*Sinthet).^2); + end + + function quantity=cyclphase(obj,varargin) + %cyclphase Computes the cyclotronic phase for the main specie + % for particles with indices{1} at time indices{2} + + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + + if strcmp(indices{1},':') + p=1:obj.R.nparts; + else + p=indices{1}; + end + if strcmp(indices{2},':') + t=1:length(obj.tpart); + else + t=indices{2}; + end + % if track is true we look at specific particles with their + % index and follow them in time + % if it is false we just care about the distribution function + % and specific particles can have different positions in the + % resulting array for each timestep + if size(indices,2)>2 + track=indices{3}; + else + track=false; + end + Zp=obj.Z(p,t,track); + Rp=obj.R(p,t,track); + % [~, zind(1)]=min(abs(obj.zgrid-0.005262)); + % [~, zind(2)]=min(abs(obj.zgrid-0.006637)); + % [~, rind(1)]=min(abs(obj.rgrid-0.0784)); + % [~, rind(2)]=min(abs(obj.rgrid-0.07861)); + % indices=Zp=obj.zgrid(zind(1)) &... + % Rp=obj.rgrid(rind(1)); + %Zp=Zp(indices); + %Rp=Rp(indices); + %p=indices; + + Bzp=interp2(obj.zgrid,obj.rgrid,obj.Bz',Zp,Rp,'makima'); + Brp=interp2(obj.zgrid,obj.rgrid,obj.Br',Zp,Rp,'makima'); + Bp=sqrt(Bzp.^2+Brp.^2); + Costhet=Bzp./Bp; + Sinthet=Brp./Bp; + + % compute the projection of the perpendicular velocity in the + % radial direction + vr=(obj.VR(p,t,track).*Costhet-obj.VZ(p,t,track).*Sinthet); + % Get the perpendicular velocity + vperp=obj.Vperp(p,t,track,true); + vr=vr(indices); + vperp=vperp(indices); + cospsi=vr./vperp; + quantity=acos(cospsi); + end + + function p=borderpoints(obj,subdiv) + %borderpoints Return a cell array containing the curves + %defining the boundary of the domain + % for each boundary p(1,:) and p(2,:) give axial and radial position + % for each boundary p(3,:) and p(4,:) give axial and radial normals + + %gw= contourc(obj.zgrid,obj.rgrid,obj.geomweight(:,:,1),[0 0]) + p=cell(0,0); + if nargin<2 + subdiv=1; + end + + ndiv=sum(subdiv); + + %outer cylinder + if any(obj.geomweight(end,:,1)>=0) + idp=ceil(length(obj.zgrid)/ndiv); + imin=1; + for j=1:length(subdiv) + imax=min(imin+subdiv(j)*idp-1,length(obj.zgrid)); + p{end+1}=[obj.zgrid(imin:imax)';obj.rgrid(end)*ones(imax-imin+1,1)'; + zeros(imax-imin+1,1)';ones(imax-imin+1,1)']; + imin=imax; + end + + end + + %inner cylinder + if any(obj.geomweight(1,:,1)>=0) + idp=ceil(length(obj.zgrid)/ndiv); + imin=1; + for j=1:length(subdiv) + imax=min(imin+subdiv(j)*idp-1,length(obj.zgrid)); + p{end+1}=[obj.zgrid(imin:imax)';obj.rgrid(1)*ones(imax-imin+1,1)'; + zeros(imax-imin+1,1)';-ones(imax-imin+1,1)']; + imin=imax; + end + end + + if obj.walltype==2 + % We have an elliptic insert that we want to isolate + gw=obj.ellipseborder; + zpos=obj.zgrid(obj.zgrid<(min(gw(1,:))) | obj.zgrid>(max(gw(1,:)))); + p{2}=[zpos,obj.rgrid(end)*ones(size(zpos))]'; + p{1}=[obj.zgrid';obj.rgrid(1)*ones(size(obj.zgrid))']; + gw=obj.ellipseborder; + p{3}=gw; + elseif obj.walltype~=0 + % extract all the walls + gw=contourc(obj.zgrid,obj.rgrid,obj.geomweight(:,:,1),[0 0]); + [x,y,~]=C2xyz(gw); + for i=1:length(x) + %subdiv=[4,1,2]; + ndiv=sum(subdiv); + idp=ceil(length(x{i})/ndiv); + imin=1; + for j=1:length(subdiv) + imax=min(imin+subdiv(j)*idp-1,length(x{i})); + p{end+1}=[x{i}(imin:imax);y{i}(imin:imax)]; + imin=imax; + end + end + end + % figure + % for i=1:length(p) + % plot(p{i}(1,:),p{i}(2,:)) + % hold on + % end + end + + function p=ellipseborder(obj) + %ellipseborder returns the boundary points defining the + %elliptic insert + z=linspace(-0.998,0.998,1000)*obj.z_r; + p=zeros(4,length(z)); + for i=1:length(z) + p(1,i)=z(i)+obj.z_0; + p(2,i)=obj.r_0-obj.r_r*sqrt(1-(z(i)/obj.z_r)^2); + p(3,i)=2/(obj.z_r^2)*(z(i)); + p(4,i)=2/(obj.r_r^2)*(p(2,i)-obj.r_0); + end + norm=sqrt(p(3,:).^2+p(4,:).^2); + p(3,:)=double(obj.interior)*p(3,:)./norm; + p(4,:)=double(obj.interior)*p(4,:)./norm; + end + + function charge=totcharge(obj,fieldstep) + % Integrates the density profile over the full volume to obtain + % the total number of electrons in the volume + n=splinedensity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder,ones(size(obj.CellVol)), 1, 1); + charge=sum(sum(n(:,:,fieldstep))); + end + + function Gamma=Axialflux(obj,timestep,zpos) + % Computes the axial particle flux n*Uz at timestep timestep and axial position zpos + Gamma=obj.fluidUZ(:,zpos,timestep).*obj.N(:,zpos,timestep); + end + + function Gamma=Metallicflux(obj,timestep,subdiv) + % Computes the particle flux at time obj.t2d(timestep) on the + % metallic boundaries + + if nargin<3 + subdiv=1; + end + % We find the borderpoints + p=obj.borderpoints(subdiv); + gamma=cell(size(p)); + Nr=cell(size(p)); + Nz=cell(size(p)); + for i=1:length(p) + bp=p{i}; + if size(bp,1)==2 + % We get the normals at these positions and normalise them + Nr{i}=-interp2(obj.zgrid,obj.rgrid,obj.geomweight(:,:,3),bp(1,:),bp(2,:)); + Nz{i}=-interp2(obj.zgrid,obj.rgrid,obj.geomweight(:,:,2),bp(1,:),bp(2,:)); + norm=sqrt(Nr{i}.^2+Nz{i}.^2); + Nr{i}=Nr{i}./norm; + Nz{i}=Nz{i}./norm; + else + Nr{i}=bp(4,:); + Nz{i}=bp(3,:); + end + + gamma{i}=zeros(size(bp,2),length(timestep)); + end + [z,r]=ndgrid(obj.zgrid,obj.rgrid); + N=obj.N(:,:,timestep(1)); + n=griddedInterpolant(z,r,N'); + uz=griddedInterpolant(z,r,obj.fluidUZ(:,:,timestep(1))'); + ur=griddedInterpolant(z,r,obj.fluidUR(:,:,timestep(1))'); + % we get the density and fluid velocities at the desired time + % steps and interpolate them at the boundary position + for j=1:length(timestep) + n.Values=obj.N(:,:,timestep(j))'; + uz.Values=obj.fluidUZ(:,:,timestep(j))'; + ur.Values=obj.fluidUR(:,:,timestep(j))'; + for i=1:length(p) + bp=p{i}; + gamma{i}(:,j)=n(bp(1:2,:)').*(ur(bp(1:2,:)').*Nr{i}'+uz(bp(1:2,:)').*Nz{i}'); + end + end + % return the boundary position p and the corresponding flux + % gamma + Gamma.p=p; + Gamma.gamma=gamma; + end + + function [I, pos]=OutCurrents(obj,timestep, subdiv) + % Computes the Outgoing currens at the simulation axial boundaries at timestep timestep + % This is simply the surface integral of the axial flux + if nargin<3 + subdiv=1; + end + flux=obj.Axialflux(timestep,[1 obj.nz+1]); + Iz=squeeze(trapz(obj.rgrid,flux.*obj.rgrid)*2*pi*obj.qsim/obj.weight); + Iz(1,:)=-Iz(1,:); + gamm=obj.Metallicflux(timestep, subdiv); + Im=zeros(length(gamm.p),length(timestep)); + pos=cell(size(gamm.p)); + for i=1:length(gamm.p) + p=gamm.p{i}; + pos{i}=p; + flux=gamm.gamma{i}; + for j=1:length(timestep) + Im(i,j)=pi/2*sum((p(2,1:end-1)+p(2,2:end)).*(flux(2:end,j)+flux(1:end-1,j))'... + .*sqrt((p(1,2:end)-p(1,1:end-1)).^2+(p(2,2:end)-p(2,1:end-1)).^2)); + end + end + I=-cat(1,Iz,Im*obj.qsim/obj.weight); + end + + function model=potentialwellmodel(obj,timestep) + % Computes the potential well at the given timestep and return the model to be able to + % interpolate either using grid coordinates or magnetic field line coordinates + % the potential well is calculated along each magnetic field + % line by taking the difference between a local potential well maximum + % and the maximum local minimum along the line on + % each side of the maximum + + % model.z and model.r are the axial and radial positions where + % the potential depth has been calculated and is used with a + % scatteredinterpolant to calculate the well depth at the + % desired position + % model.pot is the potential well depth + + % model.rathet is the magnetic field vector value at the + % corresponding position multiplied by the radial position + if iscell(timestep) + timestep=cell2mat(timestep); + end + % if one of the timesteps is 0 we take the external potential + id=find(timestep==0); + if(~isempty(id)) + timestep(id)=[]; + end + Phi=-obj.pot(:,:,timestep); + if(~isempty(id)) + phiext=-obj.potxt(:,:,1); + if isempty(obj.potxt) + phiext=-obj.pot(:,:,1); + end + Phi=cat(3,Phi(:,:,1:id-1),phiext,Phi(:,:,id:end)); + end + + % We get the magnetic field lines rA_theta values + %lvls=sort(unique([obj.rAthet(:,1)',obj.rAthet(:,end)', obj.rAthet(1,:),obj.rAthet(end,:)])); + Blines=obj.rAthet; + lvls=linspace(min(Blines(obj.geomweight(:,:,1)>0)),max(Blines(obj.geomweight(:,:,1)>0)),300); + Blines(obj.geomweight(:,:,1)<0)=NaN; + lvls(isnan(lvls))=[]; + + % We obtain the magnetic field lines coordinates for each + % level in r and z + contpoints=contourc(obj.zgrid,obj.rgrid,Blines,lvls(1:end)); + [x,y,zcont]=C2xyz(contpoints); + + % memory allocation for spped + + potfinal=zeros(numel(cell2mat(x)),length(timestep)); + Pot=cell(1,size(x,2)-1); + rathet=cell(1,size(x,2)-1); + [z,r]=ndgrid(obj.zgrid,obj.rgrid); + + % for each timestep we calculate the well + for i=1:length(timestep)+~isempty(id) + locPhi=Phi(:,:,i); + % We delete points outside of the domain + locPhi(obj.geomweight(:,:,1)<0)=0; + + + locPhi=griddedInterpolant(z,r,locPhi','makima'); + % For each field line we remove the lowest maximum + for j=1:size(x,2) + %for i=1:size(pot,1) + xloc=x{j}; + yloc=y{j}; + + rathet{j}=zcont(j)*ones(1,length(xloc)); + + if(length(xloc)>=3) + + Pot{j}=locPhi(xloc,yloc); + locpot=Pot{j}; + %locpot=locpot-min(locpot); + % along the given field line j we calculate the + % minimum on the left and right side of the + % position k and calculate the local well depth + for k=2:length(locpot)-1 + left=max(locpot(1:k-1)); + right=max(locpot(k+1:end)); + Pot{j}(k)=locpot(k)-min(left,right); + end + Pot{j}(1)=0; + Pot{j}(end)=0; + else + Pot{j}=zeros(size(xloc)); + end + end + potfinal(:,i)=cell2mat(Pot); + end + potfinal(potfinal>0)=NaN; + model.z=cell2mat(x); + model.r=cell2mat(y); + model.pot=-potfinal; + model.rathet=cell2mat(rathet); + end + + function [pot] = PotentialWell(obj,fieldstep) + %PotentialWell Computes the potential well at the given timestep on the FEM grid points + % interpolates the model data on rgrid and zgrid + model=obj.potentialwellmodel(fieldstep); + z=model.z; + r=model.r; + modpot=model.pot; + [Zmesh,Rmesh]=meshgrid(obj.zgrid,obj.rgrid); + pot=zeros(length(obj.zgrid),length(obj.rgrid),length(fieldstep)); + for i=1:length(fieldstep) + pot(:,:,i)=griddata(z,r,modpot(:,i),Zmesh,Rmesh)'; + end + end + + function Epar = Epar(obj,fieldstep) + % Computes the electric field component parallel to the magnetic field line + Epar=obj.Er(:,:,fieldstep).*(obj.Br./obj.B)' + (obj.Bz./obj.B)'.*obj.Ez(:,:,fieldstep); + end + + function Eperp = Eperp(obj,fieldstep) + % Computes the electric field component perpendicular to the magnetic field line + Eperp=obj.Er(:,:,fieldstep).*(obj.Bz./obj.B)' - (obj.Br./obj.B)'.*obj.Ez(:,:,fieldstep); + end + + function Ekin = Ekin(obj,varargin) + %Ekin Computes the classical kinetic energy of particles indices{1} at + % time obj.tpart(indices{2}) in Joules + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + if strcmp(indices{1},':') + p=1:obj.R.nparts; + else + p=indices{1}; + end + if strcmp(indices{2},':') + t=1:length(obj.tpart); + else + t=indices{2}; + end + % if track is true we look at specific particles with their + % index and follow them in time + % if it is false we just care about the distribution function + % and specific particles can have different positions in the + % resulting array for each timestep + if size(indices,1)>2 + track=indices{3}; + else + track=false; + end + Vr=obj.VR(p,t,track); + Vthet= obj.VTHET(p,t,track); + Vz=obj.VZ(p,t,track); + Ekin=0.5*obj.msim/obj.weight*(Vr.^2+Vthet.^2+Vz.^2); + end + + function sig=sigio(obj,E,init) + %sigio returns the total ionisation cross-section in m^2 + % at energy E[eV] + % init is only used during the loading of the h5 file + if nargin <3 + init=false; + end + sig=zeros(size(E)); + + if(~init &&( ~obj.neutcol.present || isempty(obj.neutcol.io_cross_sec))) + sig=zeros(size(E)); + return + end + for i=1:length(E(:)) + if(E(i)>obj.neutcol.Eion) + sig(ind2sub(size(E),i))=obj.fit_cross_sec(E(ind2sub(size(E),i)),obj.neutcol.io_cross_sec); + end + end + end + + function sig=sigmio(obj,E) + %sigmio returns the total ionisation cross-section for momentum exchange for the incoming electron in m^2 + % at energy E[eV] + sig=zeros(size(E)); + if(~obj.neutcol.present || isempty(obj.neutcol.iom_cross_sec)) + return + end + for i=1:length(E(:)) + if(E(i)>obj.neutcol.Eion) + sig(ind2sub(size(E),i))=obj.fit_cross_sec(E(ind2sub(size(E),i)),obj.neutcol.iom_cross_sec); + end + end + end + + function sigm=sigmela(obj,E) + %sigmela returns the elastic collision cross-section for momentum exchange for the incoming electron in m^2 + % at energy E[eV] + sigm=zeros(size(E)); + if(~obj.neutcol.present || isempty(obj.neutcol.ela_cross_sec)) + return + end + for i=1:length(E(:)) + sigm(ind2sub(size(E),i))=obj.fit_cross_sec(E(ind2sub(size(E),i)),obj.neutcol.ela_cross_sec); + end + end + + function sig=sigela(obj,E) + %sigmela returns the elastic collision cross-section for the incoming electron in m^2 + % at energy E[eV] + % if used this will give the frequency of elastic collisions + E0=obj.neutcol.E0; + chi=E./(0.25*E0+E); + sig=(2*chi.^2)./((1-chi).*((1+chi).*log((1+chi)./(1-chi))-2*chi)).*obj.sigmela(E); + end + + function [Forces, Density]=Forcespline(obj,it,fdens,getmean) + %Forcesplie calculates the fluid force terms in each direction + %at time obj.t2d(it) + % if fdens return the force density in N/m^3 othewise give + % the force in N + % if getmean return only the time averaged quanties over + % time samples[it(1)...it(end] + + if strcmp(it,':') + it=floor(0.95*size(obj.t2d)):size(obj.t2d)-1; + end + if nargin<3 + fdens=true; + end + if nargin <4 + getmean=false; + end + + % To be able to calculate the centered finite difference in + % time, we remove the first and last time indices + it(it<2)=[]; + it(it>length(obj.t2d)-1)=[]; + + m_e=obj.msim/obj.weight; + q_e=obj.qsim/obj.weight; + n=obj.N(:,:,it); + [r,~]=meshgrid(obj.rgrid,obj.zgrid); + Rinv=1./r'; + Rinv(isinf(Rinv))=0; + % get inverse of density to get the force in N + Density.N=n; + invn=1./n; + invn(isnan(invn) | isinf(invn))=0; + + + % Calculate electric forces + Eforcer=q_e*obj.Er(:,:,it); + Eforcez=q_e*obj.Ez(:,:,it); + + + + Dragforcer=zeros(size(n,1),size(n,2),size(n,3)); + Dragforcethet=zeros(size(n,1),size(n,2),size(n,3)); + Dragforcez=zeros(size(n,1),size(n,2),size(n,3)); + + time=obj.t2d(it); + Forces.it=it; + Forces.time=time; + + if getmean + if ~fdens + n=ones(size(n)); + end + % Electric forces + Forces.Eforcer=mean(n.*q_e.*obj.Er(:,:,it),3); + Forces.Eforcez=mean(n.*q_e.*obj.Ez(:,:,it),3); + + % Magnetic forces + Forces.Bforcer=mean(q_e.*obj.fluidUTHET(:,:,it).*obj.Bz'.*n,3); + Forces.Bforcethet=mean(q_e.*(obj.fluidUZ(:,:,it).*obj.Br'-obj.fluidUR(:,:,it).*obj.Bz').*n,3); + Forces.Bforcez=mean(-q_e.*obj.fluidUTHET(:,:,it).*obj.Br'.*n,3); + + % Inertial forces + Forces.inertforcer=mean(-m_e.*n.*(-obj.fluidUTHET(:,:,it).^2.*Rinv... + +obj.fluidUR(:,:,it).*obj.fluidUR.der(:,:,it,[1 0])... + +obj.fluidUZ(:,:,it).*obj.fluidUR.der(:,:,it,[0 1])),3); + + Forces.inertforcethet=mean(-m_e*n.*(obj.fluidUR(:,:,it).*obj.fluidUTHET(:,:,it).*Rinv... + +obj.fluidUR(:,:,it).*obj.fluidUTHET.der(:,:,it,[1 0])... + +obj.fluidUZ(:,:,it).*obj.fluidUTHET.der(:,:,it,[0 1])),3); + + Forces.inertforcez=mean(-m_e*n.*(obj.fluidUR(:,:,it).*obj.fluidUZ.der(:,:,it,[1 0])... + +obj.fluidUZ(:,:,it).*obj.fluidUZ.der(:,:,it,[0 1])),3); + + % Pressure forces + Forces.Pressforcer=mean(-n.*( squeeze(obj.Presstens.der(1,:,:,it,[1 0]))... + + squeeze(obj.Presstens(1,:,:,it) - obj.Presstens(4,:,:,it)).*Rinv... + + squeeze(obj.Presstens.der(3,:,:,it,[0 1])))... + .*invn,3); + + Forces.Pressforcethet=mean(-n.*( squeeze(obj.Presstens.der(2,:,:,it,[1 0]))... + + squeeze(obj.Presstens.der(5,:,:,it,[0 1])) ... + + 2*squeeze(obj.Presstens(2,:,:,it)).*Rinv ... + ).*invn,3); + + Forces.Pressforcez=mean(-n.*( squeeze(obj.Presstens.der(3,:,:,it,[1 0]))... + + squeeze(obj.Presstens(3,:,:,it)).*Rinv... + + squeeze(obj.Presstens.der(6,:,:,it,[0 1])) )... + .*invn,3); + + % ellastic coll drag forces + if( obj.neutcol.present) + Ek=squeeze(obj.fluidEkin(1,:,:,it)+obj.fluidEkin(2,:,:,it)+obj.fluidEkin(3,:,:,it)); + sigm=obj.sigmela(Ek/obj.qe)+obj.sigio(Ek/obj.qe)+obj.sigmio(Ek/obj.qe); + dragfreq=obj.neutcol.neutdens.*sigm.*sqrt(2*obj.weight/obj.msim*Ek); + Forces.Dragforcer=mean(-m_e*n.*dragfreq.*obj.fluidUR(:,:,it),3); + Forces.Dragforcethet=mean(-m_e*n.*dragfreq.*obj.fluidUTHET(:,:,it),3); + Forces.Dragforcez=mean(-m_e*n.*dragfreq.*obj.fluidUZ(:,:,it),3); + else + Forces.Dragforcer=0; + Forces.Dragforcethet=0; + Forces.Dragforcez=0; + end + + % effective drag frequency due to the maxwellian source + if( obj.maxwellsrce.present) + dragfreqsrc=obj.maxwellsrce.frequency*obj.weight/(pi*diff(obj.maxwellsrce.zlim)*(obj.maxwellsrce.rlim(2)^2-obj.maxwellsrce.rlim(1)^2)).*invn; + dragfreqsrc(isinf(dragfreqsrc))=0; + Forces.Dragforcer=Forces.Dragforcer+mean(-n.*m_e.*dragfreqsrc.*obj.fluidUR(:,:,it),3); + Forces.Dragforcethet=Forces.Dragforcethet+mean(-n.*m_e.*dragfreqsrc.*obj.fluidUTHET(:,:,it),3); + Forces.Dragforcez=Forces.Dragforcez+mean(-n.*m_e.*dragfreqsrc.*obj.fluidUZ(:,:,it),3); + end + + % Time derivative for fluid accelleration + cdt=(obj.t2d(it+1)-obj.t2d(it-1)); + cdt=reshape(cdt,1,1,[]); + Forces.durdt=mean(m_e*(obj.fluidUR(:,:,it+1)-obj.fluidUR(:,:,it-1))./cdt,3); + Forces.duthetdt=mean(m_e*(obj.fluidUTHET(:,:,it+1)-obj.fluidUTHET(:,:,it-1))./cdt,3); + Forces.duzdt=mean(m_e*(obj.fluidUZ(:,:,it+1)-obj.fluidUZ(:,:,it-1))./cdt,3); + + else + % Allocate memory + Bforcer=zeros(size(n,1),size(n,2),size(n,3)); + Bforcez=zeros(size(n,1),size(n,2),size(n,3)); + Bforcethet=zeros(size(n,1),size(n,2),size(n,3)); + inertforcer=zeros(size(n,1),size(n,2),size(n,3)); + inertforcez=zeros(size(n,1),size(n,2),size(n,3)); + inertforcethet=zeros(size(n,1),size(n,2),size(n,3)); + Pressforcer=zeros(size(n,1),size(n,2),size(n,3)); + Pressforcethet=zeros(size(n,1),size(n,2),size(n,3)); + Pressforcez=zeros(size(n,1),size(n,2),size(n,3)); + durdt=zeros(size(n,1),size(n,2),size(n,3)); + duthetdt=zeros(size(n,1),size(n,2),size(n,3)); + duzdt=zeros(size(n,1),size(n,2),size(n,3)); + fluiduThet=obj.fluidUTHET(:,:,it); + Density.fluiduThet=fluiduThet; + for j=1:size(n,3) + % Magnetic forces + Bforcer(:,:,j)=q_e.*fluiduThet(:,:,j).*obj.Bz'; + Bforcethet(:,:,j)=q_e.*(obj.fluidUZ(:,:,it(j)).*obj.Br'-obj.fluidUR(:,:,it(j)).*obj.Bz'); + Bforcez(:,:,j)=-q_e.*fluiduThet(:,:,j).*obj.Br'; + + % Inertial forces + inertforcer(:,:,j)=-m_e.*(-fluiduThet(:,:,j).^2.*Rinv... + +obj.fluidUR(:,:,it(j)).*obj.fluidUR.der(:,:,it(j),[1 0])... + +obj.fluidUZ(:,:,it(j)).*obj.fluidUR.der(:,:,it(j),[0 1])); + + inert1=obj.fluidUR(:,:,it(j)).*fluiduThet(:,:,j).*Rinv; + inert2=obj.fluidUR(:,:,it(j)).*obj.fluidUTHET.der(:,:,it(j),[1 0]); + inert3=obj.fluidUZ(:,:,it(j)).*obj.fluidUTHET.der(:,:,it(j),[0 1]); + + inertforcethet(:,:,j)=-m_e.*(inert1... + +inert2... + +inert3); + + inertforcez(:,:,j)=-m_e.*(obj.fluidUR(:,:,it(j)).*obj.fluidUZ.der(:,:,it(j),[1 0])... + +obj.fluidUZ(:,:,it(j)).*obj.fluidUZ.der(:,:,it(j),[0 1])); + + % Pressure forces + Pr1=squeeze(obj.Presstens.der(1,:,:,it(j),[1 0])); + + Pr2=squeeze(obj.Presstens(1,:,:,it(j)) - obj.Presstens(4,:,:,it(j))).*Rinv; + + Pr3=squeeze(obj.Presstens.der(3,:,:,it(j),[0 1])); + + Pressforcer(:,:,j)=-( Pr1... + + Pr2... + + Pr3 )... + .*invn(:,:,j); + + Pthet1=squeeze(obj.Presstens.der(2,:,:,it(j),[1 0])); + Pthet2=squeeze(obj.Presstens.der(5,:,:,it(j),[0 1])); + Pthet3=2*squeeze(obj.Presstens(2,:,:,it(j))).*Rinv; + + Pressforcethet(:,:,j)=-( Pthet1... + + Pthet2 ... + + Pthet3 ... + ).*invn(:,:,j); + + Pz1=squeeze(obj.Presstens.der(3,:,:,it(j),[1 0])); + Pz2=squeeze(obj.Presstens(3,:,:,it(j))).*Rinv; + Pz3=squeeze(obj.Presstens.der(6,:,:,it(j),[0 1])); + Pressforcez(:,:,j)=-( Pz1... + + Pz2... + + Pz3 )... + .*invn(:,:,j); + + % ellastic coll drag forces + if( obj.neutcol.present) + Ek=squeeze(obj.fluidEkin(1,:,:,it(j))+obj.fluidEkin(2,:,:,it(j))+obj.fluidEkin(3,:,:,it(j))); + sigm=obj.sigmela(Ek/obj.qe)+obj.sigio(Ek/obj.qe)+obj.sigmio(Ek/obj.qe); + dragfreq=obj.neutcol.neutdens.*sigm.*sqrt(2*obj.weight/obj.msim*Ek); + Dragforcer(:,:,j)=-m_e*dragfreq.*obj.fluidUR(:,:,it(j)); + Dragforcethet(:,:,j)=-m_e*dragfreq.*obj.fluidUTHET(:,:,it(j)); + Dragforcez(:,:,j)=-m_e*dragfreq.*obj.fluidUZ(:,:,it(j)); + end + + % effective drag frequency due to the maxwellian source + if( obj.maxwellsrce.present) + dragfreqsrc=obj.maxwellsrce.frequency*obj.weight/(pi*diff(obj.maxwellsrce.zlim)*(obj.maxwellsrce.rlim(2)^2-obj.maxwellsrce.rlim(1)^2))*invn(:,:,j); + dragfreqsrc(isinf(dragfreqsrc))=0; + Dragforcer(:,:,j)=Dragforcer(:,:,j)+-m_e*dragfreqsrc.*obj.fluidUR(:,:,it(j)); + Dragforcethet(:,:,j)=Dragforcethet(:,:,j)+-m_e*dragfreqsrc.*obj.fluidUTHET(:,:,it(j)); + Dragforcez(:,:,j)=Dragforcez(:,:,j)+-m_e*dragfreqsrc.*obj.fluidUZ(:,:,it(j)); + end + + % Time derivative + cdt=(obj.t2d(it(j)+1)-obj.t2d(it(j)-1)); + durdt(:,:,j)=m_e*(obj.fluidUR(:,:,it(j)+1)-obj.fluidUR(:,:,it(j)-1))/cdt; + duthetdt(:,:,j)=m_e*(obj.fluidUTHET(:,:,it(j)+1)-obj.fluidUTHET(:,:,it(j)-1))/cdt; + duzdt(:,:,j)=m_e*(obj.fluidUZ(:,:,it(j)+1)-obj.fluidUZ(:,:,it(j)-1))/cdt; + end + if(~fdens) + Forces.Eforcer=Eforcer; + Forces.Eforcez=Eforcez; + Forces.Bforcer=Bforcer; + Forces.Bforcethet=Bforcethet; + Forces.Bforcez=Bforcez; + Forces.inertforcer=inertforcer; + Forces.inertforcethet=inertforcethet; + Forces.inertforcez=inertforcez; + Forces.Pressforcer=Pressforcer; + Forces.Pressforcethet=Pressforcethet; + Forces.Pressforcez=Pressforcez; + Forces.durdt=durdt; + Forces.duthetdt=duthetdt; + Forces.duzdt=duzdt; + Forces.Dragforcer=Dragforcer; + Forces.Dragforcethet=Dragforcethet; + Forces.Dragforcez=Dragforcez; + else + % multiply by density to have force density + Forces.Eforcer=Eforcer.*n; + Forces.Eforcez=Eforcez.*n; + Forces.Bforcer=Bforcer.*n; + Forces.Bforcethet=Bforcethet.*n; + Forces.Bforcez=Bforcez.*n; + Forces.inertforcer=inertforcer.*n; + Forces.inertforcethet=inertforcethet.*n; + Forces.inertforcez=inertforcez.*n; + Forces.Pressforcer=Pressforcer.*n; + Forces.Pressforcethet=Pressforcethet.*n; + Forces.Pressforcez=Pressforcez.*n; + Forces.durdt=durdt.*n; + Forces.duthetdt=duthetdt.*n; + Forces.duzdt=duzdt.*n; + Forces.Dragforcer=Dragforcer.*n; + Forces.Dragforcethet=Dragforcethet.*n; + Forces.Dragforcez=Dragforcez.*n; + + end + end + end + + function [lr,rb,lz,zb]= clouddims(obj,it,zpos,fracn) + % clouddims return the cloud axial and radial limit at time it + % and axial position zpos + % fracn defines the fraction of the maximum density below which + % we consider to have a vacuum + if nargin<4 + fracn=0.1; + end + % get the density + n=obj.N(:,:,it); + lr=cell(1,length(it)); + lz=lr; + rb=lr; + zb=rb; + for i=1:size(n,3) + nthresh=fracn*max(max(n(:,:,i))); + % find the points outside of the cloud + outside=find(n(:,zpos,i)2) + rmpos=outside(j); + rppos=outside(j+1); + lr{i}(k)=obj.rgrid(rppos-1)-obj.rgrid(rmpos+1); + rb{i}(:,k)=[max(rmpos+1,1) min(rppos-1,sum(obj.nnr))]; + k=k+1; + end + end + maxgap=2; + k=1; + for I=rmpos+1:rppos-1 + outside=find(n(I,:,i)maxgap) + maxgap=zgap(j); + zmpos=outside(j); + zppos=outside(j+1); + lz{i}(k)=obj.zgrid(zppos-1)-obj.zgrid(zmpos+1); + zb{i}(:,k)=[max(zmpos+1,1) min(zppos-1,obj.nz)]; + k=k+1; + end + end + end + + end + end + + %------------------------------------------ + % Functions for plotting evolving quantities + + function displaysplbound(obj,ax,rescale) + %displaysplbound display on axis ax the boundary of the + %simulation domain and the Dirichlet and Neumann walls defined + %with spline curves + if nargin<2 + ax=gca; + end + if nargin<3 + rescale=1; + end + hold on + for i=1:obj.spl_bound.nbsplines + knots=obj.spl_bound.boundary(i).knots(1:end); + coeffs=obj.spl_bound.boundary(i).coefs'*rescale; + pp=spmak(knots,coeffs); + sizec=size(coeffs,2); + order=length(knots)-sizec; + s=linspace(knots(order),knots(sizec+1),1000); + fittedpos=fnval(pp,s); + plot(fittedpos(1,:),fittedpos(2,:),'-') + plot(coeffs(1,:),coeffs(2,:),'rx','markersize',14) + end + end + + function displayraddim(obj,it,zpos,fracn) + %displayraddim display the evolution of the radial dimension of the cloud in + %time to find if the cloud size get below a critical radial + %size at which the ionisation is not sufficient to compensate + %the losses + % also plot the well radial dimensions in time + if nargin<3 + zpos=floor(length(obj.zgrid)/2); + end + if nargin<4 + fracn=0.1; + end + [lr,rb,lz,zb]=obj.clouddims(it,zpos,fracn); + + t=obj.t2d(it); + Lr=zeros(size(lr)); + er=obj.Er(:,:,it); + r_min=Lr; + r_minpred=r_min; + well_r=Lr; + nb=Lr; + for i=1:length(lr) + if ~isempty(lr{i}) && ~isempty(lz{i}) + [Lr(i),id]=max(lr{i}); + rm=rb{i}(1,id); + rp=rb{i}(2,id); + + nb(i)=mean(obj.N(rm:rp,zpos,it(i))); + + Lp=min(lz{i}); + Lm=mean(lz{i}); + + rpos=rm:rp; + vperp=-er(rpos,zpos,i)./obj.Bz(zpos,rpos)'; + Ek=0.5*obj.me*vperp.^2/obj.qe; + sigio=obj.sigio(Ek); + sigd=obj.sigmela(Ek)+obj.sigmio(Ek)+sigio; + omegap2=obj.qe^2*obj.N(rpos,zpos,it(i))/obj.eps_0/obj.me; + omegac2=(obj.qe*obj.Bz(zpos,rpos)'/obj.me).^2; + ur=er(rpos,zpos,i)*obj.qe./((omegap2-omegac2)*obj.me).*sigd.*vperp*obj.neutcol.neutdens; + r_minpred(i)=mean(obj.N(rp,zpos,it(i))*Lp*ur./(nb(i)*obj.neutcol.neutdens*sigio.*vperp*Lm));%mean(1./(-1/obj.rgrid(rm)+obj.neutcol.neutdens*sigio.*vperp./ur*(Lm/Lp)*nb(i)/obj.N(rp,zpos,it(i)))); + rpos=rp; + vperp=-er(rpos,zpos,i)./obj.Bz(zpos,rpos)'; + Ek=0.5*obj.me*vperp.^2/obj.qe; + sigio=obj.sigio(Ek); + ur=obj.fluidUR(rpos,zpos,it(i)); + r_min(i)=max(obj.N(rp,zpos,it(i))*Lp*ur/(nb(i)*obj.neutcol.neutdens*sigio.*vperp*Lm),0);%max(mean(1./(-1/obj.rgrid(rm)+obj.neutcol.neutdens*sigio.*vperp./ur*(Lm/Lp)*nb(i)/obj.N(rp,zpos,it(i)))),0); + + nb(i)=nb(i)*Lm*2*pi*obj.rgrid(rm)*Lr(i); + else + Lr(i)=NaN; + r_min(i)=NaN; + r_minpred(i)=NaN; + end + potwell=obj.PotentialWell(it(i))'; + outside=find(isnan(potwell(:,zpos))); + gap=diff(outside); + for j=1:length(gap) + if(gap(j)>2) + rmpos=outside(j)+1; + rppos=outside(j+1)-1; + well_r(i)=obj.rgrid(rppos)-obj.rgrid(rmpos); + end + end + + + + end + + f=figure('Name', sprintf('%s rlims B=%f phi=%f',obj.name,obj.B0, obj.phinorm*(obj.potout-obj.potinn))); + plot(t,Lr,'displayname','\Deltar_{cloud}','linewidth',1.3) + hold on + plot(t,r_min,'displayname','\Deltar_{min} (u_r simu)','linewidth',1.3) + plot(t,r_minpred,'displayname','\Deltar_{min} (u_r pred)','linewidth',1.3) + plot(t,well_r,'displayname','\Deltar_{well}','linewidth',1.3) + ylabel('\Delta r [m]') + yyaxis right + plot(t,nb,'--','displayname','N') + legend('location','eastoutside') + xlabel('t [s]') + ylabel('N') + set(gca,'fontsize',12) + + yyaxis left + ylimits=ylim; + %ylim([ylimits(1) 1.1*max(Lr)]) + title(sprintf('cloud radial limits at z=%1.2e[m]',obj.zgrid(zpos))) + obj.savegraph(f,sprintf('%s/%s_%d_rlims',obj.folder,obj.name,zpos),[15 10]); + end + + function displaypsi(obj,deltat) + %% plot the initial and final radial profile at position z=0 and show the normalized enveloppe function Psi + % relevant for Davidson annular distribution function + f=figure('Name', sprintf('%s Psi',obj.name)); + f.Name= sprintf('%s Psi',obj.name); + zpos=floor(length(obj.zgrid)/2); + tinit=1; + tend=length(obj.t2d); + if iscell(deltat) + deltat=cell2mat(deltat); + end + if(obj.R.nt<2) + h0=obj.H0; + p0=obj.P0; + else + h0=mean(H(obj,{1:obj.VR.nparts,obj.VR.nt,false})); + p0=mean(P(obj,{1:obj.VR.nparts,obj.VR.nt,false})); + end + lw=1.5; + Mirrorratio=(obj.Rcurv-1)/(obj.Rcurv+1); + locpot=mean(obj.pot(:,zpos,tend-deltat:tend),3); + psi=1+obj.qe*locpot(:)/h0-1/(2*obj.me*h0)*(p0./obj.rgrid+obj.qe*0.5*obj.B0.*(obj.rgrid-obj.width/pi*Mirrorratio*cos(2*pi*obj.zgrid(zpos)/obj.width)*besseli(1,2*pi*obj.rgrid/obj.width))).^2; + locdens=mean(obj.N(:,zpos,tend-deltat:tend),3); + [maxn,In]=max(locdens);%M.N(:,zpos,tinit)); + plot(obj.rgrid,obj.N(:,zpos,tinit),'bx-','DisplayName',sprintf('t=%1.2f[ns]',obj.t2d(tinit)*1e9),'linewidth',lw) + hold on + plot(obj.rgrid,locdens,'rx-','DisplayName',sprintf('t=[%1.2f-%1.2f] [ns] averaged',obj.t2d(tend-deltat)*1e9,obj.t2d(tend)*1e9),'linewidth',lw) + plot(obj.rgrid(In-2:end),1./obj.rgrid(In-2:end)*maxn*obj.rgrid(In),'DisplayName','N=c*1/r','linewidth',lw) + plot(obj.rgrid(In-2:end),1./obj.rgrid(In-2:end).^2*maxn*obj.rgrid(In)^2,'DisplayName','N=c*1/r^2','linewidth',lw) + plot(obj.rgrid(In-2:end),1./obj.rgrid(In-2:end).^4*maxn*obj.rgrid(In)^4,'DisplayName','N=c*1/r^4','linewidth',lw) + xlabel('r [m]') + ylabel('n_e [m^{-3}]') + I=find(psi>0); + if (length(I)>1) + I=[I(1)-2; I(1)-1; I; I(end)+1; I(end)+2]; + else + I=obj.nnr(1):length(psi); + end + rq=linspace(obj.rgrid(max(I(1),1)),obj.rgrid(I(end)),500); + psiinterp=interp1(obj.rgrid(I),psi(I),rq,'pchip'); + zeroindices=find(diff(psiinterp>=0),2); + maxpsiinterp=max(psiinterp); + plot(rq,maxn*psiinterp/abs(maxpsiinterp),'Displayname','normalized \Psi [a.u.]','linewidth',lw) + ylim([0 inf]) + for i=1:length(zeroindices) + border=plot([rq(zeroindices(i)) rq(zeroindices(i))],[0 obj.rgrid(In)/obj.rgrid(In-2)*maxn],'k--','linewidth',lw); + set(get(get(border,'Annotation'),'LegendInformation'),'IconDisplayStyle','off'); + end + legend + xlim([0 0.02]) + grid on + title(sprintf('Radial density profile at z=%1.2e[m]',obj.zgrid(zpos))) + obj.savegraph(f,sprintf('%sPsi',obj.name),[15 10]); + end + + function f=displayrprofile(obj,t,zpos,init) + %% plot the initial and final radial profile at the axial center of the simulation space + % also plot the azimuthal fluid rotation frequency profile + % t: time index considered + % zpos: axial position index + % init: initial time considered for comparison + f=figure('Name', sprintf('%s Prof',obj.name)); + if nargin < 3 || length(zpos)<1 + zpos=floor(length(obj.zgrid)/2); + end + if nargin<4 + init=false; + end + if(iscell(t)) + t=cell2mat(t); + end + lw=1.5; + if init + tinit=t(1); + t=t(2:end); + end + locdens=mean(obj.N(:,zpos,t),3); + + %inverse of radius + Rinv=1./obj.rgrid; + Rinv(isnan(Rinv))=0; + %azimuthal velocity and azimuthal rotation frequency in m/s and + %1/s + vthet=mean(obj.fluidUTHET(:,zpos,t),3); + omegare=(vthet.*Rinv); + % plot the initial density + if(init) + plot(obj.rgrid,obj.N(:,zpos,tinit),'bx-','DisplayName',sprintf('t=%1.2f[ns]',obj.t2d(tinit)*1e9),'linewidth',lw) + end + hold on + %plot the time averaged current density + plot(obj.rgrid,locdens,'rx-','DisplayName',sprintf('t=[%1.2f-%1.2f] [ns] averaged',obj.t2d(t(1))*1e9,obj.t2d(t(end))*1e9),'linewidth',lw) + xlabel('r [m]') + ylabel('n_e [m^{-3}]') + legend('location','Northwest') + % limit the axis to the simulation domain + if obj.conformgeom + xlim([obj.rgrid(1) obj.rgrid(sum(obj.nnr(1:2)))]) + else + xlim([obj.rgrid(1) obj.rgrid(end)]) + end + grid on + ylimits=ylim(); + % plot the metallic walls for a constant radius coaxial + % configuration + if obj.conformgeom + plot(obj.rgrid(1)*[1 1],ylimits,'k--') + plot(obj.rgrid(end)*[1 1],ylimits,'k--') + else + plot(obj.r_a*[1 1],ylimits,'k--') + if obj.walltype==0 + plot(obj.r_b*[1 1],ylimits,'k--') + elseif obj.walltype==1 + rmax=obj.r_0-obj.r_r*sqrt(1-(obj.zgrid(zpos)-obj.z_0)^2/obj.z_r^2); + plot(rmax*[1 1],ylimits,'k--') + end + end + + yyaxis right + % plot the azimuthal fluid rotation frequency profile + plot(obj.rgrid,omegare,'DisplayName',sprintf('<\\omega_{re}> t=[%1.2f-%1.2f] [ns] averaged',obj.t2d(t(1))*1e9,obj.t2d(t(end))*1e9),'linewidth',lw) + ylabel('\omega_{re} [1/s]') + + title(sprintf('Radial density profile at z=%1.2e[m]',obj.zgrid(zpos))) + obj.savegraph(f,sprintf('%srProf',obj.name),[15 10]); + end + + function displayenergy(obj) + %% Plot the time evolution of the system energy and number of simulated macro particles + tmin=1; + tmax=length(obj.ekin); + f=figure('Name', sprintf('%s Energy',obj.name)); + subplot(2,1,1) + plot(obj.t0d(tmin:tmax),obj.ekin(tmin:tmax),'o-',... + obj.t0d(tmin:tmax),obj.epot(tmin:tmax),'d-',... + obj.t0d(tmin:tmax),obj.etot(tmin:tmax),'h-',... + obj.t0d(tmin:tmax),obj.etot0(tmin:tmax),'h-',... + obj.t0d(tmin:tmax),obj.eerr(tmin:tmax),'x--') + %obj.t0d(tmin:tmax),obj.ekin(tmin:tmax)-obj.epot(tmin:tmax),'--', + legend('E_{kin}', 'E_{pot}', 'E_{tot}','E_{ref}','E_{err}') + xlabel('Time [s]') + ylabel('Energies [J]') + grid on + xlimits=xlim(); + + subplot(2,1,2) + try + semilogy(obj.t0d(tmin:tmax),abs(obj.eerr(tmin:tmax)./obj.etot0(tmin:tmax)),'h-') + catch + semilogy(obj.t0d(tmin:tmax),abs(obj.eerr(tmin:tmax)/obj.etot(2)),'h-') + end + xlabel('t [s]') + ylabel('E_{err}/E_{tot}') + xlim(xlimits) + grid on + try + yyaxis right + plot(obj.t0d(tmin:tmax),abs(obj.npart(tmin:tmax)./obj.npart(1)*100),'d--') + ylabel('Nparts %') + %ylim([0 110]) + catch + end + obj.savegraph(f,sprintf('%s/%sEnergy',obj.folder,obj.name)); + end + + function f=displaycharge(obj,f,linelegend) + %% Plot the time evolution of the system charge of electrons + % f: figure handle if you want to stack several such curves + % on the same figure + % linelegend: legend for this charge evolution + tmin=1; + tmax=length(obj.ekin); + if nargin<2 + f=figure('Name', sprintf('%s Charge',obj.name)); + end + if nargin<3 + linelegend=''; + end + ax=f.CurrentAxes; + if isempty(ax) + ax=axes(f); + end + try + plot(ax,obj.t0d(tmin:tmax),abs(obj.npart(tmin:tmax)*obj.qsim),'linewidth',1.5,'displayname',linelegend) + hold on + ylabel(ax,'Total charge [C]') + xlabel(ax,'t [s]') + grid on + if(nargin>2) + legend + end + set(ax,'fontsize',12) + catch + end + if nargin < 2 + obj.savegraph(f,sprintf('%s/%scharge',obj.folder,obj.name)); + end + end + + function displaySimParticles(obj) + %% Plot the time evolution of the number of simulated markers in the main specie + f=figure('Name', sprintf('%s Trapped particles',obj.name)); + plot(obj.t0d,obj.npart,'linewidth',1.5) + xlabel('t [s]') + ylabel('N particles') + set(gca,'fontsize',12) + obj.savegraph(f,sprintf('%s/%sntrapped',obj.folder,obj.name),[10 12]); + end + + function displayLarmorRad(obj,time2d) + if nargin<2 + time2d=length(obj.t2d); + end + % Plot the larmor radius for created particles with low energy + % the larmor radius is calculated by considering that the + % initial perpendicular velocity \approx the ExB velocity + rl=abs(obj.me/obj.qe*(-obj.Er(:,:,time2d).*obj.Bz'+obj.Ez(:,:,time2d).*obj.Br')./(obj.B.^3)'); + figure + rl(obj.geomweight(:,:,1)<0)=0; + contourf(obj.zgrid,obj.rgrid,rl) + hold on + contour(obj.zgrid,obj.rgrid,obj.geomweight(:,:,1),[0 0],'r-','linewidth',3) + n=obj.N(:,:,time2d); + maxN=max(n (:)); + n=n/maxN*mean(rl(:)); + contour(obj.zgrid,obj.rgrid,n,linspace(0,1,6)*mean(rl(:)),'r:','linewidth',3) + c=colorbar; + xlabel('z [m]') + ylabel('r [m]') + c.Label.String='r_L [m]'; + end + + function displayHP(obj,tstart) + % Plot the histogramm of the total energy and canonical angular momentum at time tstart and + % end time of the simulation over the full simulation space for the main specie + if(iscell(tstart)) + tstart=cell2mat(tstart); + end + if(obj.R.nt>=2) + tstart=obj.R.nt; + f=figure('Name', sprintf('%s HP',obj.name)); + legtext=sprintf("t=%2.1f - %2.1f [ns]",obj.tpart(tstart)*1e9,obj.tpart(end)*1e9); + subplot(1,2,1) + partsmax=min(obj.nbparts(end),obj.R.nparts); + Hloc=H(obj,{1:obj.nbparts(1),1,false}); + h1=histogram(Hloc,20,'BinLimits',[min(Hloc(:)) max(Hloc(:))],'DisplayName',sprintf("t=%2.3d [ns]",obj.tpart(1)*1e9)); + hold on + Hloc=H(obj,{1:partsmax,obj.R.nt,false}); + %,'Binwidth',h1.BinWidth + h1=histogram(Hloc,20,'BinLimits',[min(Hloc(:)) max(Hloc(:))],'DisplayName',legtext); + ylabel('counts') + xlabel('H [J]') + legend + + subplot(1,2,2) + Ploc=P(obj,{1:obj.nbparts(1),1,false}); + h2=histogram(Ploc,50,'BinLimits',[min(Ploc(:)) max(Ploc(:))],'DisplayName',sprintf("t=%2.3d [ns]",obj.tpart(1)*1e9)); + hold on + Ploc=P(obj,{1:partsmax,obj.R.nt,false}); + histogram(Ploc,50,'BinLimits',[min(Ploc(:)) max(Ploc(:))],'DisplayName',legtext); + ylabel('counts') + xlabel('P [kg\cdotm^2\cdots^{-1}]') + %clear P + %clear H + legend + %xlim([0.95*h2.BinLimits(1) 1.05*h2.BinLimits(2)]) + obj.savegraph(f,sprintf('%s/%sParts_HP',obj.folder,obj.name)); + end + end + + function displayaveragetemp(obj) + % Computes and show the particles average temperature as a function of time + f=figure('Name',sprintf('%s potinn=%f part temperature',obj.name,obj.potinn*obj.phinorm)); + vr2=obj.VR(:,:,false); + vr2=mean(vr2.^2,1)-mean(vr2,1).^2; + vz2=obj.VZ(:,:,false); + vz2=mean(vz2.^2,1)-mean(vz2,1).^2; + vthet2=obj.VTHET(:,:,false); + vthet2=mean(vthet2.^2,1)-mean(vthet2,1).^2; + plot(obj.tpart,0.5*obj.me*vr2/obj.qe,'displayname','T_r') + hold on + plot(obj.tpart,0.5*obj.me*vz2/obj.qe,'displayname','T_z') + plot(obj.tpart,0.5*obj.me*vthet2/obj.qe,'displayname','T_{thet}') + xlabel('time [s]') + ylabel('T [eV]') + title(sprintf('\\phi_a=%.1f kV \\phi_b=%.1f kV R=%.1f',obj.potinn*obj.phinorm/1e3,obj.potout*obj.phinorm/1e3,obj.Rcurv)) + legend + grid + obj.savegraph(f,sprintf('%s/%s_partstemp',obj.folder,obj.name)); + + end + + function displayCurrentsevol(obj,timesteps) + % Computes and display the time evolution of the outgoing currents on each domain boundary + % at timesteps timesteps + if nargin<2 + timesteps=1:length(obj.t2d); + end + currents=obj.OutCurrents(timesteps); + f=figure('Name',sprintf('%s Currents',obj.name)); + if(obj.B(1,1)>obj.B(end,1)) + lname='HFS'; + rname='LFS'; + else + lname='LFS'; + rname='HFS'; + end + plot(obj.t2d(timesteps),currents(1,:),'Displayname',lname,'linewidth',1.8); + hold on + plot(obj.t2d(timesteps),currents(2,:),'Displayname',rname,'linewidth',1.8); + plot(obj.t2d(timesteps),currents(3,:),'Displayname','outer cylinder','linewidth',1.8); + plot(obj.t2d(timesteps),currents(4,:),'Displayname','inner cylinder','linewidth',1.8); + if size(currents,1)>=5 + plot(obj.t2d(timesteps),currents(5,:),'Displayname','ellipse','linewidth',1.8); + end + legend('location','Northeast') + xlabel('time [s]') + ylabel('I [A]') + grid on + set(gca,'fontsize',12) + title(sprintf('\\phi_b-\\phi_a=%.2g kV, R=%.1f',(obj.potout-obj.potinn)*obj.phinorm/1e3,obj.Rcurv)) + obj.savegraph(f,sprintf('%s/%s_outCurrents',obj.folder,obj.name),[16 12]); + end + + function displayChargeLossevol(obj,timesteps,toptitle,scalet,dens) + % Computes and display the time evolution of the outgoing currents on each domain boundary + % at time obj.t2d(timesteps) + %scalet=true scales the time by the ellastic collision + %frequency + %dens = true plot the time evolution of the maximum electron + %density in the simulation domain otherwise plot the total + %number of electrons in the domain + if nargin<2 + timesteps=1:length(obj.t2d); + end + if nargin<4 + scalet=true; + end + if nargin <5 + dens=true; + end + + if scalet + if obj.neutcol.present + vexb0=(obj.Ez(:,:,1).*obj.Br'-obj.Er(:,:,1).*obj.Bz')./(obj.B'.^2); + vexb0(obj.geomweight(:,:,1)<=0)=0; + E=0.5*obj.msim/obj.weight*mean(abs(vexb0(:)))^2/obj.qe; + taucol=1/(obj.neutcol.neutdens*mean(abs(vexb0(:)))*(obj.sigio(E)+obj.sigmela(E)+obj.sigmio(E))); + try + Sio_S=1e17*(obj.neutcol.neutdens*mean(abs(vexb0(:)))*obj.sigio(E))/(obj.maxwellsrce.frequency*obj.weight/(pi*(obj.maxwellsrce.rlim(2)^2-obj.maxwellsrce.rlim(1)^2)*diff(obj.maxwellsrce.zlim))) + catch + end + tlabel='t/\tau_d [-]'; + else + taucol=2*pi/obj.omece; + tlabel='t/\tau_ce [-]'; + end + else + taucol=1e-9; + tlabel='t [ns]'; + end + + if dens + N=obj.N(:,:,timesteps); + geomw=obj.geomweight(:,:,1); + geomw(geomw<0)=0; + geomw(geomw>0)=1; + N=N.*geomw; + + nmax=squeeze(max(max(N,[],1),[],2)); + tn=(obj.t2d(timesteps)); + nlabel='n_{e,max} [m^{-3}]'; + ndlabel='n_{e,max}'; + else + t0dst=find(obj.t0d>=obj.t2d(timesteps(1)),1,'first'); + t0dlst=find(obj.t0d<=obj.t2d(timesteps(end)),1,'last'); + tn=obj.t0d(t0dst:t0dlst); + nmax=obj.npart(t0dst:t0dlst)*obj.weight; + nlabel='Nb e^-'; + ndlabel='Nb e^-'; + end + + + [currents,pos]=obj.OutCurrents(timesteps); + P=obj.neutcol.neutdens*obj.kb*300/100;% pressure at room temperature in mbar + currents=currents/P; + f=figure('Name',sprintf('%s Charges',obj.name)); + % Plot the evolution of nb of particles + yyaxis right + p=plot(tn/taucol,nmax,'b-.','linewidth',1.8,'Displayname',ndlabel); + ylabel(nlabel) + ax=gca; + ax.YAxis(2).Color=p.Color; + ylim([0 inf]) + + if(obj.B(1,1)>obj.B(end,1)) + lname='HFS'; + rname='LFS'; + else + lname='LFS'; + rname='HFS'; + end + + yyaxis left + mincurr=max(currents(:))*5e-3; + if (max(currents(1,:)>mincurr)) + plot(obj.t2d(timesteps)/taucol,currents(1,:),'r:','Displayname',lname,'linewidth',1.8); + end + hold on + if (max(currents(2,:)>mincurr)) + plot(obj.t2d(timesteps)/taucol,currents(2,:),'r--','Displayname',rname,'linewidth',1.8); + end + if (max(currents(3,:)>mincurr)) + plot(obj.t2d(timesteps)/taucol,currents(3,:),'r-','Displayname','outer cylinder','linewidth',1.8); + end + if (max(currents(4,:)>mincurr)) + plot(obj.t2d(timesteps)/taucol,currents(4,:),'Displayname','inner cylinder','linewidth',1.8); + end + if (size(currents,1)>=5 && max(currents(5,:)>mincurr)) + plot(obj.t2d(timesteps)/taucol,currents(5,:),'r-','Displayname','ellipse','linewidth',1.8); + end + xlabel(tlabel) + ylabel('I/p_n [A/mbar]') + grid on + set(gca,'fontsize',12) + ax.YAxis(1).Color='red'; + + legend('Orientation','horizontal','location','south','numcolumns',3) + + if nargin <3 + title(sprintf('\\phi_b-\\phi_a=%.2g kV, B=%f T',(obj.potout-obj.potinn)*obj.phinorm/1e3,max(obj.B(:)))) + elseif ~isempty(toptitle) + title(toptitle) + end + obj.savegraph(f,sprintf('%s/%s_ChargeEvol%i%i',obj.folder,obj.name,scalet,dens),[16 14]); + end + + + function displaytotcurrevol(obj,timesteps,toptitle,scalet,dens,subdiv,nmean) + % Computes and display the time evolution of the outgoing + % currents at time obj.t2d(timesteps) + %scalet=true scales the time by the ellastic collision + %frequency + %dens = true plot the time evolution of the maximum electron + %density in the simulation domain otherwise plot the total + %number of electrons in the domain + % also plot in a subplot the color coded boundary corresponding + % to each current + if nargin<2 + timesteps=1:length(obj.t2d); + end + if nargin<3 + scalet=true; + end + if nargin <4 + dens=true; + end + if nargin<5 + subdiv=1; + end + if nargin<6 + nmean=1; + end + + if scalet + if obj.neutcol.present + vexb0=(obj.Ez(:,:,1).*obj.Br'-obj.Er(:,:,1).*obj.Bz')./(obj.B'.^2); + vexb0(obj.geomweight(:,:,1)<=0)=0; + E=0.5*obj.msim/obj.weight*mean(abs(vexb0(:)))^2/obj.qe; + taucol=1/(obj.neutcol.neutdens*mean(abs(vexb0(:)))*(obj.sigio(E)+obj.sigmela(E)+obj.sigmio(E))); + try + Sio_S=1e17*(obj.neutcol.neutdens*mean(abs(vexb0(:)))*obj.sigio(E))/(obj.maxwellsrce.frequency*obj.weight/(pi*(obj.maxwellsrce.rlim(2)^2-obj.maxwellsrce.rlim(1)^2)*diff(obj.maxwellsrce.zlim))) + catch + end + tlabel='t/\tau_d [-]'; + else + taucol=2*pi/obj.omece; + tlabel='t/\tau_ce [-]'; + end + else + taucol=1e-9; + tlabel='t [ns]'; + end + + if dens + N=obj.N(:,:,timesteps); + geomw=obj.geomweight(:,:,1); + geomw(geomw<0)=0; + geomw(geomw>0)=1; + N=N.*geomw; + %[~,idl]=max(N(:,:,end),[],'all','linear'); + %[ir,iz]=ind2sub(size(geomw),idl); + nmax=squeeze(max(max(N,[],1),[],2)); + %nmax=squeeze(N(ir,iz,:)); + tn=(obj.t2d(timesteps)); + nlabel='n_{e,max} [m^{-3}]'; + ndlabel='n_{e,max}'; + else + t0dst=find(obj.t0d>=obj.t2d(timesteps(1)),1,'first'); + t0dlst=find(obj.t0d<=obj.t2d(timesteps(end)),1,'last'); + tn=obj.t0d(t0dst:t0dlst); + nmax=obj.npart(t0dst:t0dlst)*obj.weight; + nlabel='Nb e^-'; + ndlabel='Nb e^-'; + end + + + [currents,pos]=obj.OutCurrents(timesteps,subdiv); + P=obj.neutcol.neutdens*obj.kb*300/100;% pressure at room temperature in mbar + currents=currents/P; + f=figure('Name',sprintf('%s Charges',obj.name)); + tiledlayout(2,1) + nexttile + % Plot the evolution of nb of particles + yyaxis right + p=plot(tn/taucol,nmax,'b-.','linewidth',1.8,'Displayname',ndlabel); + ylabel(nlabel) + axl=gca; + axl.YAxis(2).Color=p.Color; + ylim([0 inf]) + + if(obj.B(1,1)>obj.B(end,1)) + lname='HFS'; + rname='LFS'; + else + lname='LFS'; + rname='HFS'; + end + + yyaxis( 'left'); + map=colormap(lines); + set(axl,'linestyleorder',{'-',':','--','*','+'},... + 'ColorOrder',map(2:7,:), 'NextPlot','replacechildren') + p(1)=plot(axl,obj.t2d(timesteps)/taucol,movmean(currents(1,:),nmean),'Displayname',lname,'linewidth',1.8); + hold on + p(2)=plot(axl,obj.t2d(timesteps)/taucol,movmean(currents(2,:),nmean),'Displayname',rname,'linewidth',1.8); + % Plot the currents + for i=3:size(currents,1) + p(i)=plot(axl,obj.t2d(timesteps)/taucol,movmean(currents(i,:),nmean),'Displayname',sprintf('border %i',i-2),'linewidth',1.8); + end + plot(axl,obj.t2d(timesteps)/taucol,movmean(sum(currents(:,:),1,'omitnan'),nmean),'k-','Displayname','total','linewidth',1.8); + xlabel(tlabel) + ylabel('I/p_n [A/mbar]') + grid on + set(gca,'fontsize',12) + ax.YAxis(1).Color='black'; + + %legend('Orientation','horizontal','location','north','numcolumns',3) + + if ~isempty(toptitle) + title(toptitle) + end + + ax2=nexttile; + geomw=obj.geomweight(:,:,1); + geomw(geomw<=0)=0; + geomw(geomw>0)=NaN; + [c1,hContour]=contourf(ax2,obj.zgrid*1000,obj.rgrid*1000,geomw, [0 0]); + hold on + drawnow; + grid on; + + for i=1:length(pos) + plot(ax2,pos{i}(1,:)*1000,pos{i}(2,:)*1000,'linestyle',p(i+2).LineStyle,... + 'color',p(i+2).Color,'marker',p(i+2).Marker,... + 'displayname',sprintf('border %i',i),'linewidth',1.8) + hold on + end + title('Domain') + plot(ax2,ones(size(obj.rgrid))*obj.zgrid(1)*1000,obj.rgrid*1000,'linestyle',p(1).LineStyle,... + 'color',p(1).Color,'marker',p(1).Marker,... + 'displayname',lname,'linewidth',1.8) + plot(ax2,ones(size(obj.rgrid))*obj.zgrid(end)*1000,obj.rgrid*1000,'linestyle',p(2).LineStyle,... + 'color',p(2).Color,'marker',p(2).Marker,... + 'displayname',rname,'linewidth',1.8) + xlabel('z [mm]') + ylabel('r [mm]') + grid on + set(gca,'fontsize',12) + hFills=hContour.FacePrims; + [hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' + try + hFills(1).ColorData = uint8([150;150;150;255]); + for idx = 2 : numel(hFills) + hFills(idx).ColorData(4) = 0; % default=255 + end + catch + end + %legend('Orientation','horizontal','location','north','numcolumns',4) + + + fprintf('mean total current: %f [A/mbar]\n',mean(sum(currents(:,max(1,size(currents,2)-30):end),1,'omitnan'))); + + %if nargin <3 + % sgtitle(sprintf('\\phi_b-\\phi_a=%.2g kV, B=%f T',(obj.potout-obj.potinn)*obj.phinorm/1e3,mean(obj.B(:)))) + %elseif ~isempty(toptitle) + % sgtitle(toptitle) + %end + if length(subdiv)>1 + obj.savegraph(f,sprintf('%s/%s_totIEvol%i%i_div',obj.folder,obj.name,scalet,dens),[16 14]); + else + obj.savegraph(f,sprintf('%s/%s_totIEvol%i%i',obj.folder,obj.name,scalet,dens),[16 14]); + end + + end + + function display2Dpotentialwell(obj,timestep,rcoord,clims) + % Display the 2D potential well at time obj.t2d(timestep) + % if rcoord is true, the potential is evaluated at grid points in r,z coordinates + % if false, the potential is evaluated at grid points in magnetic field line coordinates + % clims are the values limits in eV for the color coding of the + % potential well + if iscell(timestep) + timestep=cell2mat(timestep); + end + if nargin <3 + rcoord=true; + end + if nargin<4 + clims=[-inf inf]; + end + f=figure('Name',sprintf('%s Potential well',obj.name)); + ax1=gca; + model=obj.potentialwellmodel(timestep); + z=model.z; + r=model.r; + Pot=model.pot; + rathet=model.rathet; + if (timestep==0) + title(sprintf('Potential well Vacuum')) + else + title(sprintf('Potential well t=%1.2f [ns]',obj.t2d(timestep)*1e9)) + end + N0=obj.N(:,:,1); + id=find(timestep==0); + timestep(id)=[]; + Nend=obj.N(:,:,timestep); + if(~isempty(id)) + N0=zeros(obj.N.nr,obj.N.nz); + Nend=cat(3,Nend(:,:,1:id-1),N0,Nend(:,:,id:end)); + end + Nend=mean(Nend,3); + geomw=obj.geomweight(:,:,1); + %z(isnan(Pot))=[]; + %r(isnan(Pot))=[]; + %Pot(isnan(Pot))=[]; + if rcoord + [Zmesh,Rmesh]=meshgrid(obj.zgrid,obj.rgrid); + Pot=griddata(z,r,Pot,Zmesh,Rmesh,'natural'); + %Pot(obj.geomweight(:,:,1)<=0)=NaN; + contourf(obj.zgrid(1:end)*1e3,obj.rgrid(1:end)*1e3,Pot(1:end,1:end),40,'edgecolor','none','Displayname','Well') + xlabel('z [mm]') + ylabel('r [mm]') + xlim([obj.zgrid(1) obj.zgrid(end)]*1e3) + ylim([obj.rgrid(1) obj.rgrid(end)]*1e3) + hold(gca, 'on') + + rdisp=obj.rgrid; + + %% Magnetic field lines + Blines=obj.rAthet; + levels=linspace(min(Blines(obj.geomweight(:,:,1)>0)),max(Blines(obj.geomweight(:,:,1)>0)),20); + Blines(obj.geomweight(:,:,1)<0)=NaN; + [~,h1]=contour(obj.zgrid*1000,obj.rgrid*1000,Blines,real(levels),'k-.','linewidth',1,'Displayname','Magnetic field lines'); + + else + rdisp=sort(obj.rAthet(:,end)); + [Zmesh,Rmesh]=meshgrid(obj.zgrid,rdisp); + Pot=griddata(z,rathet,Pot,Zmesh,Rmesh); + [Zinit,~]=meshgrid(obj.zgrid,obj.rAthet(:,1)); + % if isempty(obj.maxwellsrce) + % end + N0=griddata(Zinit,obj.rAthet,N0,Zmesh,Rmesh); + Nend=griddata(Zinit,obj.rAthet,Nend,Zmesh,Rmesh); + geomw=griddata(Zinit,obj.rAthet,geomw,Zmesh,Rmesh); + Pot(geomw<=0)=0; + surface(obj.zgrid(1:end),rdisp,Pot(1:end,1:end),'edgecolor','none') + ylabel('rA_\theta [Tm^2]') + xlabel('z [m]') + xlim([obj.zgrid(1) obj.zgrid(end)]) + ylim([min(rdisp) max(rdisp)]) + hold(gca, 'on') + + end + maxdensend=max(Nend(:)); + contourscale=0.1; + Nend=(Nend-contourscale*maxdensend)/maxdensend; + maxdens0=max(N0(:)); + contourscale=0.1; + N0=(N0-contourscale*maxdens0)/maxdens0; + contour(obj.zgrid*1e3,rdisp*1e3,Nend,linspace(0,1-contourscale,5),'k--','linewidth',1.5,'Displayname','Cloud Boundaries'); + contour(obj.zgrid*1e3,rdisp*1e3,N0,[0 0],'k-.','linewidth',1.5,'Displayname','Source boundaries'); + %contour(obj.zgrid*1e3,rdisp*1e3,geomw,[0 0],'-','linecolor',[0.5 0.5 0.5],'linewidth',1.5,'Displayname','Vessel Boundaries'); + c=colorbar; + colormap('jet'); + + + + % Grey outline showing the metalic walls + geomw(obj.geomweight(:,:,1)>0)=-1; + geomw(obj.geomweight(:,:,1)<=0)=1; + [c1,hContour]=contourf(ax1,obj.zgrid*1000,obj.rgrid*1000,geomw, [0 0]); + + drawnow; + xlim(ax1,[obj.zgrid(1)*1000 obj.zgrid(end)*1000]) + if(obj.conformgeom) + ylim([ax1 ],[obj.rgrid(1)*1000 obj.rgrid(rgridend)*1000]) + else + ylim([ax1],[obj.rgrid(1)*1000 obj.rgrid(end)*1000]) + end + %ylim(ax1,[0.05*1000 obj.rgrid(end)*1000]) + %xlim([obj.zgrid(1) 0.185]*1e3) + xlabel(ax1,'z [mm]') + ylabel(ax1,'r [mm]') + view(ax1,2) + c.Label.String= 'depth [eV]'; + f.PaperUnits='centimeters'; + caxis(clims) + grid on; + hFills=hContour.FacePrims; + [hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' + try + drawnow + hFills(1).ColorData = uint8([150;150;150;255]); + for idx = 2 : numel(hFills) + hFills(idx).ColorData(4) = 0; % default=255 + end + catch + end + + % add central and external metallic walls if we have a coaxial + % configuration + if( obj.walltype >=2 && obj.walltype<=4) + rectangle('Position',[obj.zgrid(1) obj.r_b obj.zgrid(end)-obj.zgrid(1) 0.001]*1e3,'FaceColor',[150 150 150]/255,'Edgecolor','none') + ylimits=ylim; + ylim([ylimits(1),ylimits(2)+1]) + end + if sum(obj.geomweight(:,1,1))==0 + rectangle('Position',[obj.zgrid(1) obj.r_a-0.001 obj.zgrid(end)-obj.zgrid(1) 0.001]*1e3,'FaceColor',[150 150 150]/255,'Edgecolor','none') + ylimits=ylim; + ylim([ylimits(1)-1,ylimits(2)]) + end + + %axis equal + [max_depth,id]=max(abs(Pot(:))); + [idr,idz]=ind2sub(size(Pot),id); + fprintf('Maximum potential wel depth: %f eV\n',max_depth) + fprintf('at location r=%f z=%f [mm]\n',obj.rgrid(idr)*1e3, obj.zgrid(idz)*1e3) + papsize=[14 8]; + if rcoord + obj.savegraph(f,sprintf('%s/%s_wellr%i',obj.folder,obj.name,floor(mean(timestep))),papsize); + else + obj.savegraph(f,sprintf('%s/%s_wellpsi%i',obj.folder,obj.name,floor(mean(timestep))),papsize); + end + end + + function display1Dpotentialwell(obj,timestep,rpos) + % Display the potential well along the magentic field line + % passing by rgrid(rpos) at the center of the simulation space + if iscell(timestep) + timestep=cell2mat(timestep); + end + f=figure('Name',sprintf('%s 1D Potential well',obj.name)); + model=obj.potentialwellmodel(timestep); + z=model.z; + r=model.r; + Pot=model.pot; + rathet=model.rathet; + if (mod(rpos, 1) ~= 0) + [~,rpos]=min(abs(M.rgrid-rpos)); + end + crpos=obj.rgrid(rpos); + id=find(timestep==0); + timestep(id)=[]; + n=obj.N(:,:,timestep); + if(~isempty(timestep==0)) + N0=zeros(obj.N.nr+1,obj.N.nz+1); + n=cat(3,n(:,:,1:id-1),N0,n(:,:,id:end)); + end + n=mean(n,3); + linepot=zeros(length(obj.zgrid),length(timestep)); + rathetpos=obj.rAthet(rpos,ceil(length(obj.zgrid)/2)); + F=scatteredInterpolant(z',rathet',Pot(:,1)); + for i=1:length(timestep) + F=scatteredInterpolant(z',rathet',Pot(:,i)); + linepot(:,i)=F(obj.zgrid,rathetpos*ones(size(obj.zgrid))); + %linepot(:,i)=griddata(z,rathet,pot(:,i),obj.zgrid,rathetpos); + end + linepot=mean(linepot,2); + [Zinit,~]=meshgrid(obj.zgrid,obj.rAthet(:,1)); + n=griddata(Zinit,obj.rAthet,n,obj.zgrid,rathetpos); + + plot(obj.zgrid,linepot) + ylabel('Potentiel [eV]') + xlabel('z [m]') + xlim([obj.zgrid(1) obj.zgrid(end)]) + hold(gca, 'on') + yyaxis right + plot(obj.zgrid,n) + ylabel('n [m^{-3}]') + if length(timestep)==1 + title(sprintf('Potential well t=%1.2f [ns] r=%1.2f [mm]',obj.t2d(timestep)*1e9,1e3*crpos)) + else + title(sprintf('Potential well t=[%1.2f-%1.2f] [ns] r=%1.2f [mm]',obj.t2d(timestep(1))*1e9,obj.t2d(timestep(end))*1e9,1e3*crpos)) + end + + obj.savegraph(f,sprintf('%s/%s_well1Dr_%d',obj.folder,obj.name,rpos)); + end + + function displayVdistribRThetZ(obj,timestep, rpos, zpos) + %displayVdistribRThetZ plot the velocity distribution function + % in m/s + %extracted from the markers at position window from rpos(1) + %rpos(end) and zpos(1) to zpos(end) + % and at time obj.tpart(timestep) + %rpos and zpos are given as grid indices + if(obj.R.nt>=2) + if nargin<2 + timesteppart=length(obj.tpart); + else + timesteppart=timestep; + end + if nargin<3 || isempty(rpos) + rpos=1:length(obj.rgrid); + rspan=[obj.rgrid(1) obj.rgrid(end)]; + else + r=[obj.rgrid(1);(obj.rgrid(1:end-1)+obj.rgrid(2:end))*0.5;obj.rgrid(end)]; + rspan=[r(rpos) r(rpos+1)]; + end + if nargin<4 || isempty(zpos) + zpos=1:length(obj.zgrid); + zspan=[obj.zgrid(1) obj.zgrid(end)]; + else + z=[obj.zgrid(1);(obj.zgrid(1:end-1)+obj.zgrid(2:end))*0.5;obj.zgrid(end)]; + zspan=[z(zpos) z(zpos+1)]; + end + + + nbp=min(obj.nbparts(1),obj.R.nparts); + R=obj.R(1:nbp,1,false); + Z=obj.Z(1:nbp,1,false); + Vr=obj.VR(1:nbp,1,false); + Vz=obj.VZ(1:nbp,1,false); + Vthet=obj.VTHET(1:nbp,1,false); + ids=R>=rspan(1) & R<=rspan(2) & Z>=zspan(1) & Z<=zspan(2); + + Vr=Vr(ids); + Vz=Vz(ids); + Vthet=Vthet(ids); + vTr=std(Vr,1); + vTz=std(Vz,1); + vTthet=std(Vthet,1); + + nbp=min(obj.nbparts(timesteppart),obj.R.nparts); + Rend=obj.R(1:nbp,timesteppart,false); + Zend=obj.Z(1:nbp,timesteppart,false); + Vrend=obj.VR(1:nbp,timesteppart,false); + Vzend=obj.VZ(1:nbp,timesteppart,false); + Vthetend=obj.VTHET(1:nbp,timesteppart,false); + ids=Rend>=rspan(1) & Rend<=rspan(2) & Zend>=zspan(1) & Zend<=zspan(2); + nbtot=sum(ids) + Vrend=Vrend(ids); + Vzend=Vzend(ids); + Vthetend=Vthetend(ids); + vTrend=std(Vrend,1); + vTzend=std(Vzend,1); + vTthetend=std(Vthetend,1); + + binwidth=abs(max(Vrend)-min(Vrend))/sqrt(length(Vrend)); + f=figure('Name',sprintf("%s vrz distrib",obj.file)); + + [~,time2did]=min(abs(obj.t2d-obj.tpart(timestep))); + + + subplot(1,4,1); + obj.dispV(Vr,Vrend,'V_r [m/s]',[1,timesteppart]) + + [~,time2did]=min(abs(obj.t2d-obj.tpart(timestep))); + if length(rpos)==1 + vexb=-obj.Er(rpos,zpos,time2did)/obj.Bz(zpos,rpos)'; + vexb=mean(vexb(:)); + + if ~isempty(obj.neutcol.ela_cross_sec) % plot the radial drift velocity as nu_dE_r/(B\Omega_c) + vdr=obj.neutcol.neutdens*obj.sigmela(vexb^2*obj.me*0.5/obj.qe)*vexb*-obj.Er(rpos,zpos,time2did)... + ./(obj.B(zpos,rpos)'.*obj.B(zpos,rpos)'*obj.qe/obj.me); + vdr=mean(vdr(:)); + ylimits=ylim; + plot(vdr*[1 1],ylimits,'k--','displayname',sprintf('V_{d,pred}=%1.2g [m/s]',vdr)) + end + end + + + subplot(1,4,2); + obj.dispV(Vthet,Vthetend,'V\theta [m/s]',[1,timesteppart]) + hold on + drawnow + ylimits=ylim; + if length(rpos)==1 + if ~isempty(obj.Erxt) + vexbext=-obj.Erxt(rpos,zpos)/obj.Bz(zpos,rpos)'; + plot(vexbext*[1 1],ylimits,'k--','displayname',sprintf('V_{ExB,ext}=%1.2g [m/s]',vexbext)) + end + + plot(vexb*[1 1],ylimits,'k-.','displayname',sprintf('V_{ExB,tot}=%1.2g [m/s]',vexb)) + end + + subplot(1,4,3); + obj.dispV(Vz,Vzend,'Vz [m/s]',[1,timesteppart]) + + subplot(1,4,4); + obj.dispV(sqrt(Vr.^2+(Vthet).^2+Vz.^2),sqrt(Vrend.^2+(Vthetend).^2+Vzend.^2),'Vtot [m/s]',[1,timesteppart],'maxwell') + + + sgtitle(sprintf('t=%1.2e[ns] r=[%2.1f, %2.1f] [mm] z=[%2.1f, %2.1f] [mm]',obj.tpart(timestep)*1e9, rspan*1e3, zspan*1e3)) + obj.savegraph(f,sprintf('%s/%sParts_V_RZ',obj.folder,obj.name),[25 14]); + + + end + + end + + function displayEkin(obj,timestep, rpos, zpos) + %displayEkin plot the kinetic energy distribution function in + %eV + %extracted from the markers at position window from rpos(1) + %rpos(end) and zpos(1) to zpos(end) + % and at time obj.tpart(timestep) + %rpos and zpos are given as grid indices + if(obj.R.nt>=2) + if nargin<2 + timesteppart=[1 length(obj.tpart)]; + else + if length(timestep)<2 + timesteppart=[1 timestep]; + else + timesteppart=[timestep(1) timestep(end)]; + end + end + if nargin<3 || isempty(rpos) + rspan=[obj.rgrid(1) obj.rgrid(end)]; + else + r=[obj.rgrid(1);(obj.rgrid(1:end-1)+obj.rgrid(2:end))*0.5;obj.rgrid(end)]; + rspan=[r(rpos) r(rpos+1)]; + end + if nargin<4 || isempty(zpos) + zspan=[obj.zgrid(1) obj.zgrid(end)]; + else + z=[obj.zgrid(1);(obj.zgrid(1:end-1)+obj.zgrid(2:end))*0.5;obj.zgrid(end)]; + zspan=[z(zpos) z(zpos+1)]; + end + + + nbp=min(obj.nbparts(timesteppart(1)),obj.R.nparts); + R=obj.R(1:nbp,timesteppart(1),false); + Z=obj.Z(1:nbp,timesteppart(1),false); + Ekin=obj.Ekin(1:nbp,timesteppart(1),false); + ids=R>=rspan(1) & R<=rspan(2) & Z>=zspan(1) & Z<=zspan(2); + Ekin=Ekin(ids)/obj.qe; + + nbp=min(obj.nbparts(timesteppart(2)),obj.R.nparts); + Rend=obj.R(1:nbp,timesteppart(2),false); + Zend=obj.Z(1:nbp,timesteppart(2),false); + Ekinend=obj.Ekin(1:nbp,timesteppart(2),false); + ids=Rend>=rspan(1) & Rend<=rspan(2) & Zend>=zspan(1) & Zend<=zspan(2); + Ekinend=Ekinend(ids)/obj.qe; + + f=figure('Name',sprintf("%s E_k distrib",obj.file)); + + obj.dispV(Ekin,Ekinend,'E_k',timesteppart,'maxwell') + + + + sgtitle(sprintf('dt=%1.2e[ns] r=[%2.1f, %2.1f] [mm] z=[%2.1f, %2.1f] [mm]',obj.dt*1e9, rspan*1e3, zspan*1e3)) + obj.savegraph(f,sprintf('%s/%sParts_E_kin',obj.folder,obj.name)); + + + end + + end + + function displayVdistribParPer(obj,timestep, rpos, zpos, gcs) + %displayVdistribParPer plot the velocity distribution function + % in m/s for the parallel and perpendicular velocity + %extracted from the markers at position window from rpos(1) + %rpos(end) and zpos(1) to zpos(end) + % and at time obj.tpart(timestep) + %rpos and zpos are given as grid indices + % gcs define if you give the perpendicular velocity in the + % guiding center frame or in the laboratory frame + if(obj.R.nt>=2) + if nargin<2 + timesteppart=length(obj.tpart); + else + timesteppart=timestep; + end + if nargin<3 || isempty(rpos) + rspan=[obj.rgrid(1) obj.rgrid(end)]; + else + r=[obj.rgrid(1);(obj.rgrid(1:end-1)+obj.rgrid(2:end))*0.5;obj.rgrid(end)]; + rspan=[r(rpos) r(rpos+1)]; + end + if nargin<4 || isempty(zpos) + zspan=[obj.zgrid(1) obj.zgrid(end)]; + else + z=[obj.zgrid(1);(obj.zgrid(1:end-1)+obj.zgrid(2:end))*0.5;obj.zgrid(end)]; + zspan=[z(zpos) z(zpos+1)]; + end + if nargin<5 + gcs=false; % define if we look in the guiding center system + end + + nbp=min(obj.nbparts(1),obj.R.nparts); + R=obj.R(1:nbp,1,false); + Z=obj.Z(1:nbp,1,false); + ids=R>=rspan(1) & R<=rspan(2) & Z>=zspan(1) & Z<=zspan(2); + Vperp=obj.Vperp(1:nbp,1,false,gcs); + Vpar=obj.Vpar(1:nbp,1,false); + Vperp=Vperp(ids); + Vpar=Vpar(ids); + + nbp=min(obj.nbparts(timesteppart),obj.R.nparts); + R=obj.R(1:nbp,timesteppart,false); + Z=obj.Z(1:nbp,timesteppart,false); + ids=R>=rspan(1) & R<=rspan(2) & Z>=zspan(1) & Z<=zspan(2); + Vperpend=obj.Vperp(1:nbp,timesteppart,false,gcs); + Vparend=obj.Vpar(1:nbp,timesteppart,false); + Vperpend=Vperpend(ids); + Vparend=Vparend(ids); + + %binwidth=abs(max(Vparend)-min(Vparend))/500; + f=figure('Name',sprintf("%s v parper distrib",obj.file)); + subplot(1,2,1) + if gcs + lgd='v_\perp gcs [m/s]'; + else + lgd='v_\perp [m/s]'; + end + obj.dispV(Vperp,Vperpend,lgd,[1,timesteppart], 'maxwell') + + subplot(1,2,2) + obj.dispV(abs(Vpar),abs(Vparend),'v_{par} [m/s]',[1,timesteppart],'None') + + sgtitle(sprintf('t=%1.2e[ns] r=[%2.1f, %2.1f] [mm] z=[%2.1f, %2.1f] [mm]',obj.tpart(timestep)*1e9, rspan*1e3, zspan*1e3)) + obj.savegraph(f,sprintf('%s/%sParts_V_parper',obj.folder,obj.name)); + if gcs + obj.savegraph(f,sprintf('%s/%sParts_V_parpergcs',obj.folder,obj.name)); + else + obj.savegraph(f,sprintf('%s/%sParts_V_parper',obj.folder,obj.name)); + end + + end + + end + + function display2DVdistrib(obj,timestep, rpos, zpos, gcs) + %display2DVdistrib plot the velocity distribution function + % in m/s for the parallel and perpendicular velocity + % and for the radial azimuthal velocity + % as a 2D contour plot the show the velocity phase space distribution + %extracted from the markers at position window from rpos(1) + %rpos(end) and zpos(1) to zpos(end) + % and at time obj.tpart(timestep) + %rpos and zpos are given as grid indices + % gcs define if you give the perpendicular velocity in the + % guiding center frame or in the laboratory frame + if(obj.R.nt>=2) + if nargin<2 + timesteppart=length(obj.tpart); + else + timesteppart=timestep; + end + if nargin<3 || isempty(rpos) + rspan=[obj.rgrid(1) obj.rgrid(end)]; + else + r=[obj.rgrid(1);(obj.rgrid(1:end-1)+obj.rgrid(2:end))*0.5;obj.rgrid(end)]; + rspan=[r(rpos(1)) r(rpos(end)+1)]; + end + if nargin<4 || isempty(zpos) + zspan=[obj.zgrid(1) obj.zgrid(end)]; + else + z=[obj.zgrid(1);(obj.zgrid(1:end-1)+obj.zgrid(2:end))*0.5;obj.zgrid(end)]; + zspan=[z(zpos(1)) z(zpos(end)+1)]; + end + if nargin<5 + gcs=false; % define if we look in the guiding center system + end + + nbp=min(obj.nbparts(timesteppart),obj.R.nparts); + R=obj.R(1:nbp,timesteppart,false); + Z=obj.Z(1:nbp,timesteppart,false); + ids=R>=rspan(1) & R<=rspan(2) & Z>=zspan(1) & Z<=zspan(2); + + Vperp=obj.Vperp(1:nbp,timesteppart,false,gcs); + Vpar=obj.Vpar(1:nbp,timesteppart,false); + Vr=obj.VR(1:nbp,timesteppart,false); + Vthet=obj.VTHET(1:nbp,timesteppart,false); + Vper=Vperp(ids); + Vpar=Vpar(ids); + + Vr=Vr(ids); + Vthet=Vthet(ids); + nbp=sum(ids(:)); + + f=figure('Name',sprintf("%s v parper distrib",obj.file)); + subplot(2,1,1) + [N,Xedges,Yedges] = histcounts2(Vpar,Vper,20); + Xedges=(Xedges(1:end-1)+Xedges(2:end))/2; + Yedges=(Yedges(1:end-1)+Yedges(2:end))/2; + contourf(Xedges,Yedges,N') + xlabel('v_{par} [m/s]') + ylabel('v_{\perp} [m/s]') + c=colorbar; + c.Label.String='Counts'; + + subplot(2,1,2) + [N,Xedges,Yedges] = histcounts2(Vthet,Vr,20); + Xedges=(Xedges(1:end-1)+Xedges(2:end))/2; + Yedges=(Yedges(1:end-1)+Yedges(2:end))/2; + contourf(Xedges,Yedges,N') + %histogram2(Vthet,Vr,'displaystyle','tile','binmethod','auto') + %scatter(Vthet,Vr) + xlabel('v_\theta [m/s]') + ylabel('v_r [m/s]') + c=colorbar; + c.Label.String='Counts'; + + sgtitle(sprintf('t=%1.2e[ns] r=[%2.1f, %2.1f] [mm] z=[%2.1f, %2.1f] [mm] N=%3i',mean(obj.tpart(timestep))*1e9, rspan*1e3, zspan*1e3,nbp)) + mkdir(sprintf('%s/vdist',obj.folder)) + if gcs + obj.savegraph(f,sprintf('%s/vdist/%sParts_V_2dparpergcs_r%iz%it%i',obj.folder,obj.name,floor(mean(rpos)),floor(mean(zpos)),floor(mean(timestep)))); + else + obj.savegraph(f,sprintf('%s/vdist/%sParts_V_2dparper_r%iz%it%i',obj.folder,obj.name,floor(mean(rpos)),floor(mean(zpos)),floor(mean(timestep)))); + end + + end + + end + + function [p, maxnb, c]=displayPhaseSpace(obj,type,partsstep, Rindex, Zindex,legendtext, figtitle, f, maxnb, c, gcs) + if nargin<8 + f=figure; + f=gca; + end + if nargin<7 + figtitle=sprintf('r=%1.2f [mm] z=%1.2f [mm] \\Delta\\phi=%1.1f[kV] R=%1.1f',obj.rgrid(Rindex)*1e3,obj.zgrid(Zindex)*1e3,(obj.potout-obj.potinn)*obj.phinorm/1e3,obj.Rcurv); + end + if nargin <6 + legendtext=sprintf('t=%1.3g [s]',obj.tpart(partsstep)); + end + fieldstep=find(obj.tpart(partsstep(end))==obj.t2d,1); + + if nargin>=10 + ctemp=c; + n=zeros(length(c{1}),length(c{2})); + else + nbins=15; + n=zeros(nbins); + end + if nargin <11 + gcs=true; + end + + + for i=1:length(partsstep) + odstep=find(obj.tpart(partsstep(i))==obj.t0d); + nbp=min(obj.R.nparts,obj.nbparts(partsstep(i))); + Rp=obj.R(1:nbp,partsstep(i),false); + Zp=obj.Z(1:nbp,partsstep(i),false); + deltar=obj.dr(2)/2; + deltarm=obj.rgrid(Rindex)-sqrt(obj.rgrid(Rindex)^2-deltar^2-2*obj.rgrid(Rindex)*deltar); + deltaz=obj.dz/2; + Indices=Rp>=obj.rgrid(Rindex)-deltarm & Rp=obj.zgrid(Zindex)-deltaz & Zp0)=maxdens; + dens(geomw<=0)=0; + geomw(geomw>0)=NaN; + + + + f=figure('Name', sprintf('%s fields',obj.name)); + ax1=gca; + title(sprintf('Configuration')) + %dens(dens<=1e13)=NaN; + %% electron density + h=contourf(ax1,obj.zgrid*1000,obj.rgrid*1000,dens,50,'Displayname','n_e [m^{-3}]', 'linestyle','none'); + hold on; + colormap(flipud(hot)); + %% Magnetic field lines + Blines=obj.rAthet; + levels=linspace(min(Blines(obj.geomweight(:,:,1)>0)),max(Blines(obj.geomweight(:,:,1)>0)),20); + [~,h1]=contour(ax1,obj.zgrid*1000,obj.rgrid*1000,Blines,real(levels),'-.','color','k','linewidth',1.5,'Displayname','Magnetic field lines'); + + %% Equipotential lines + Pot=mean(obj.pot(:,:,fieldstart:fieldend),3); + Pot(obj.geomweight(:,:,1)<0)=NaN; + %levels=8;%[-3.4 -5 -10 -15 -20 -25];%7; + potcolor='b';%[0.3660 0.6740 0.1880]; + [c1,h2]=contour(ax1,obj.zgrid*1000,obj.rgrid*1000,Pot,'--','color',potcolor,'ShowText','on','linewidth',1.2,'Displayname','Equipotentials [kV]'); + clabel(c1,h2,'Color',potcolor) + + % Grey outline shows metallic parts + [c1,hContour]=contourf(ax1,obj.zgrid*1000,obj.rgrid*1000,geomw, [0 0]); + + drawnow; + + % set the axia limits + xlim(ax1,[obj.zgrid(1)*1000 obj.zgrid(end)*1000]) + if(obj.conformgeom) + ylim([ax1 ],[obj.rgrid(1)*1000 obj.rgrid(rgridend)*1000]) + else + ylim([ax1],[obj.rgrid(1)*1000 obj.rgrid(end)*1000]) + end + legend([h1,h2],{'Magnetic field lines','Equipotentials [V]'},'location','northeast') + xlabel(ax1,'z [mm]') + ylabel(ax1,'r [mm]') + + c = colorbar(ax1); + c.Label.String= 'n[m^{-3}]'; + view(ax1,2) + + grid on; + hFills=hContour.FacePrims; + [hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' + try + hFills(1).ColorData = uint8([150;150;150;255]); + for idx = 2 : numel(hFills) + hFills(idx).ColorData(4) = 0; % default=255 + end + catch + end + [~, name, ~] = fileparts(obj.file); + + % with this you could show the outline of the maxwellian source + % if obj.maxwellsrce.present + % rlen=diff(obj.maxwellsrce.rlim); + % zlen=diff(obj.maxwellsrce.zlim); + % rectangle('Position',[obj.maxwellsrce.zlim(1) obj.maxwellsrce.rlim(1) zlen rlen]*1000,'Edgecolor','g','Linewidth',2,'Linestyle','--') + % end + + % in case of coaxial configuration, extend the display domain + % and add grey rectangles to show metallic boundaries + if( obj.walltype >=2 && obj.walltype<=4) + rectangle('Position',[obj.zgrid(1) obj.r_b obj.zgrid(end)-obj.zgrid(1) 0.001]*1e3,'FaceColor',[150 150 150]/255,'Edgecolor','none') + ylimits=ylim; + ylim([ylimits(1),ylimits(2)+1]) + end + if sum(obj.geomweight(:,1,1))==0 + rectangle('Position',[obj.zgrid(1) obj.r_a-0.001 obj.zgrid(end)-obj.zgrid(1) 0.001]*1e3,'FaceColor',[150 150 150]/255,'Edgecolor','none') + ylimits=ylim; + ylim([ylimits(1)-1,ylimits(2)]) + end + f.PaperUnits='centimeters'; + %axis equal + + papsize=[14 5 ]; + + + obj.savegraph(f,sprintf('%s/%sFields',obj.folder,obj.name),papsize); + end + + function displaymagfield(obj) + %displaymagfield display the magnetic field lines and the + %amplitude of the magnetic field using a contour + % also show the domain boundaries + + B=obj.B'; + + f=figure('Name', sprintf('%s B field',obj.name)); + B(obj.geomweight(:,:,1)<0)=NaN; + ax1=gca; + title(sprintf('Configuration')) + h=contourf(ax1,obj.zgrid*1000,obj.rgrid*1000,B,linspace(min(B(:)),max(B(:)),50),'Displayname','B [T]', 'linestyle','none'); + hold on; + + %% Magnetic field lines + Blines=obj.rAthet; + levels=linspace(min(Blines(obj.geomweight(:,:,1)>0)),max(Blines(obj.geomweight(:,:,1)>0)),50); + Blines(obj.geomweight(:,:,1)<0)=NaN; + [~,h1]=contour(ax1,obj.zgrid*1000,obj.rgrid*1000,Blines,real(levels),'r-.','linewidth',1.5,'Displayname','Magnetic field lines'); + + colormap(ax1,'parula') + + % Grey outline + geomw=obj.geomweight(:,:,1); + geomw(geomw>0)=NaN; + geomw(geomw<0)=min(B(:)); + [c1,hContour]=contourf(ax1,obj.zgrid*1000,obj.rgrid*1000,geomw, [0 0]); + + drawnow; + xlim(ax1,[obj.zgrid(1)*1000 obj.zgrid(end)*1000]) + if(obj.conformgeom) + ylim([ax1 ],[obj.rgrid(1)*1000 obj.rgrid(rgridend)*1000]) + else + ylim([ax1],[obj.rgrid(1)*1000 obj.rgrid(end)*1000]) + end + legend([h1],{'Magnetic field lines'},'location','northwest') + xlabel(ax1,'z [mm]') + ylabel(ax1,'r [mm]') + %title(ax1,sprintf('Density t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) + c = colorbar(ax1); + c.Label.String= 'B [T]'; + view(ax1,2) + %set(h,'edgecolor','none'); + grid on; + hFills=hContour.FacePrims; + [hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' + %caxis([min(B(:)) max(B(:))]) + try + hFills(1).ColorData = uint8([150;150;150;255]); + for idx = 2 : numel(hFills) + hFills(idx).ColorData(4) = 0; % default=255 + end + catch + end + [~, name, ~] = fileparts(obj.file); + if( obj.walltype >=2 && obj.walltype<=4) + rectangle('Position',[obj.zgrid(1) obj.r_b obj.zgrid(end)-obj.zgrid(1) 0.001]*1e3,'FaceColor',[150 150 150]/255,'Edgecolor','none') + ylimits=ylim; + ylim([ylimits(1),ylimits(2)+1]) + end + if(isempty(obj.spl_bound)) + rectangle('Position',[obj.zgrid(1) obj.r_a-0.001 obj.zgrid(end)-obj.zgrid(1) 0.001]*1e3,'FaceColor',[150 150 150]/255,'Edgecolor','none') + ylimits=ylim; + ylim([ylimits(1)-1,ylimits(2)]) + end + f.PaperUnits='centimeters'; + %axis equal + + papsize=[14 5 ]; + pos=f.Position; + pos(3)=floor(1.3*pos(3)); + f.Position=pos; + + obj.savegraph(f,sprintf('%s/%s_Bfield',obj.folder,obj.name),papsize); + end + + function displaySurfFluxes(obj,timesteps, ids) + %displaySurfFluxes plot the time evolution of the current + %densities on the domain boundaries for times t2d(timesteps) + %also plot the different boundaries to see which flux belong to + %which boundary + + mflux= obj.Metallicflux(timesteps); + lflux= -squeeze(obj.Axialflux(timesteps,1))'; + rflux= squeeze(obj.Axialflux(timesteps,length(obj.zgrid)))'; + + time=obj.t2d(timesteps); + + if nargin<3 + ids=1:length(mflux); + end + + %% + P=obj.neutcol.neutdens*obj.kb*300/100;% pressure at room temperature in mbar + f=figure('name','fluxevol'); + tiledlayout('flow') + j=1 + for i=1:length(mflux.p) + if(find(i+2==ids)) + ax(j)=nexttile; + j=j+1; + if issorted(mflux.p{i}(1,:),'strictascend') + contourf(mflux.p{i}(1,:)*100,time*1e9,mflux.gamma{i}'*obj.qe/(100^2)/P,'linestyle','none') + xlabel('z [cm]') + else + contourf(linspace(0,1,length(mflux.p{i}(1,:))),time*1e9,mflux.gamma{i}'*obj.qe/(100^2)/P,'linestyle','none') + xlabel('s [-]') + end + title(sprintf('Wall %i',i)) + + c=colorbar; + c.Label.String= 'j\cdotn [A/(cm^2 mbar)]'; + end + end + + if(find(1==ids)) + ax(j+1)=nexttile; + contourf(obj.rgrid*100,time*1e9,lflux*obj.qe/(100^2)/P,'linestyle','none') + title('left') + xlabel('r [cm]') + c=colorbar; + c.Label.String= 'j\cdotn [A/(cm^2 mbar)]'; + end + + if(find(2==ids)) + ax(j+2)=nexttile; + contourf(obj.rgrid*100,time*1e9,rflux*obj.qe/(100^2)/P,'linestyle','none') + title('right') + xlabel('r [cm]') + c=colorbar; + c.Label.String= 'j\cdotn [A/(cm^2 mbar)]'; + end + + + ylabel(ax,'t [ns]') + + nexttile; + for i=1:length(mflux.p) + plot(mflux.p{i}(1,:)*100,mflux.p{i}(2,:)*100,'displayname',sprintf('Wall %i',i),'linewidth',2) + hold on + end + legend('location','eastoutside') + title('Domain') + plot(ones(size(obj.rgrid))*obj.zgrid(1)*100,obj.rgrid*100,'displayname','left','linewidth',2) + plot(ones(size(obj.rgrid))*obj.zgrid(end)*100,obj.rgrid*100,'displayname','right','linewidth',2) + xlabel('z [cm]') + ylabel('r [cm]') + %xlim([obj.zgrid(1) obj.zgrid(end)]*100) + %ylim([obj.rgrid(1) obj.rgrid(end)]*100) + + obj.savegraph(f,sprintf('%s/%s_surfFluxEvol',obj.folder,obj.name),[16 14]); + end + + function displaySurfFlux(obj,timestep, subdiv) + %displaySurfFlux plot the current densities + %on the domain boundaries for time t2d(timestep) + %directly on the boundaries themselves + %make it easier to see where the currents are collected + + if nargin<3 + subdiv=1; + end + mflux= obj.Metallicflux(timestep,subdiv); + lflux= -squeeze(obj.Axialflux(timestep,1))'; + rflux= squeeze(obj.Axialflux(timestep,length(obj.zgrid)))'; + + time=obj.t2d(timestep); + + if nargin<3 + ids=1:length(mflux); + end + + %% + P=obj.neutcol.neutdens*obj.kb*300/100;% pressure at room temperature in mbar + f=figure('name','fluxevol'); + linew=5; + %obj.displaysplbound(gca,1e3); + contour(obj.zgrid*1e3,obj.rgrid*1e3,obj.geomweight(:,:,1),[0 0],'b-','linewidth',1.5); + hold on + for i=1:length(mflux.p) + x=mflux.p{i}(1,:)*1000; + y=mflux.p{i}(2,:)*1000; + y(end)=NaN; + c=mflux.gamma{i}'*obj.qe/(100^2)/P; + c(c<=0)=NaN; + patch(x,y,c,'EdgeColor','interp','LineWidth',linew); + hold on + end + + x=obj.zgrid(1)*ones(size(obj.rgrid))*1000; + y=obj.rgrid*1000; + y(end)=NaN; + c=lflux*obj.qe/(100^2)/P; + c(c<=0)=NaN; + patch(x,y,c,'EdgeColor','interp','LineWidth',linew); + + + x=obj.zgrid(end)*ones(size(obj.rgrid))*1e3; + y=obj.rgrid*1000; + y(end)=NaN; + c=rflux*obj.qe/(100^2)/P; + c(c<=0)=NaN; + patch(x,y,c,'EdgeColor','interp','LineWidth',linew); + + title(sprintf('t=%4.2f [ns]',time*1e9)) + + c=colorbar; + c.Label.String= 'j\cdotn [A/(cm^2 mbar)]'; + xlabel('z [mm]') + ylabel('r [mm]') + colormap(jet) + set(gca,'colorscale','log') + + %% Magnetic field lines + Blines=obj.rAthet; + levels=linspace(min(Blines(obj.geomweight(:,:,1)>0)),max(Blines(obj.geomweight(:,:,1)>0)),20); + Blines(obj.geomweight(:,:,1)<0)=NaN; + [~,h1]=contour(obj.zgrid*1000,obj.rgrid*1000,Blines,real(levels),'m-.','linewidth',1.5,'Displayname','Magnetic field lines'); + %axis equal + + obj.savegraph(f,sprintf('%s/%s_surfFlux_it2d_%i',obj.folder,obj.name,timestep),[16 14]); + end + + % interactive window to display the terms of the pressure tensor + dispespicPressure(obj,logdensity,showgrid,fixed,temperature) + + % interactive window to display the electron density, magnetic + % field lines, electric potential and field at given time steps + dispespicFields(obj,logdensity,showgrid,fixed,parper) + + + function displaycollfreq(obj) + %displaycollfreq plot the collision frequencies in Hz/mbar for a range of + %electron kinetic energies in eV for the different collision + %processes considered: ionisation and elastic collisions + E=logspace(1,4,1000); + + v=sqrt(2/obj.msim*obj.weight*E*obj.qe); + P=obj.neutcol.neutdens*obj.kb*300/100;% pressure at room temperature in mbar + tauio=P./(obj.neutcol.neutdens*obj.sigio(E).*v); + tauiom=P./(obj.neutcol.neutdens*obj.sigmio(E).*v); + + tauelam=P./(obj.neutcol.neutdens*obj.sigmela(E).*v); + + f=figure('name','t scales coll'); + loglog(E,1./tauio,'displayname','ionisation','linewidth',1.5) + hold on + loglog(E,1./tauiom,'displayname','ionisation momentum','linewidth',1.5) + loglog(E,1./tauelam,'displayname','elastic','linewidth',1.5) + loglog(E,1./tauio+1./tauiom+1./tauelam,'displayname','total drag','linewidth',1.5) + + loglog([E(1) E(end)],1./(2*pi/obj.omece)* [1 1],'--','displayname','cyclotronic','linewidth',1.5) + + xlabel('Electron kinetic energy [eV]') + ylabel('\nu [Hz/mbar]') + legend('location','southeast') + grid on + + obj.savegraph(f,sprintf('%s/collfreqscales',obj.folder),[14 12]); + end + + function displaycrosssec(obj) + %displaycrosssec plot the collision crosssections in m^2 for a range of + %electron kinetic energies in eV for the different collision + %processes considered: ionisation and elastic collisions + E=logspace(1,4,1000); + + + sig_io=obj.sigio(E); + sig_iom=obj.sigmio(E); + + sig_elam=obj.sigmela(E); + + f=figure('name','t scales coll'); + loglog(E,sig_io,'displayname','ionisation','linewidth',1.5) + hold on + loglog(E,sig_iom,'displayname','ionisation momentum','linewidth',1.5) + loglog(E,sig_elam,'displayname','elastic','linewidth',1.5) + loglog(E,sig_io+sig_elam+sig_iom,'displayname','total drag','linewidth',1.5) + + + xlabel('Energy [eV]') + ylabel('\sigma [m^{2}]') + legend('location','southeast') + grid on + + obj.savegraph(f,sprintf('%s/coll_cross_sec_scales',obj.folder),[14 12]); + end + + %------------------------------------------ + % Helper functions needed for other functions + + function [zpos,rpos]=getpos(obj,tstep) + % interactive window to return an specific axial and radial + % position picked from the cloud density + if nargin<2 + tstep=length(obj.t2d); + end + n=obj.N(:,:,tstep); + n(obj.geomweight(:,:,1)<0)=NaN; + + figure + contourf(obj.zgrid,obj.rgrid,n); + xlabel('z [m]') + ylabel('r [m]') + [x,y]=ginput(1); + zpos=find(x>obj.zgrid,1,'last'); + rpos=find(y>obj.rgrid,1,'last'); + + hold on + plot(obj.zgrid(zpos),obj.rgrid(rpos),'rx') + + fprintf('zpos=%i rpos=%i z=%1.4f r=%1.4f\n',zpos,rpos,obj.zgrid(zpos),obj.rgrid(rpos)) + + end + + function changed=ischanged(obj) + %ischanged Check if the file has been changed since the initial loading of the file + %and if some data must be reloaded + try + filedata=dir(obj.fullpath); + checkedtimestamp=filedata.date; + if (max(checkedtimestamp > obj.timestamp) ) + changed=true; + return + end + changed=false; + return + catch + changed=true; + return + end + end + + function dispV(obj,V,Vend,label,t, dist, vd) + %dispV generic functio to plot the velocity distribution and + %comapare two timesteps V and Vend at time t(1) and t(2) + if nargin<6 + dist='gaussian'; + end + if nargin<7 + vd=0; + end + vmean=mean(V(~isnan(V))); + vtherm=std(V(~isnan(V)),1); + vmeanend=mean(Vend(~isnan(Vend))); + vthermend=std(Vend(~isnan(Vend)),1); + + if(length(V)>1) + [Counts,edges]=histcounts(V,'binmethod','scott'); + binwidth=mean(diff(edges)); + plot([edges(1) 0.5*(edges(2:end)+edges(1:end-1)) edges(end)],[0 Counts 0],'DisplayName',sprintf("t=%2.3d [ns]",obj.tpart(t(1))*1e9)); + hold on + end + hold on + [Counts,edges]=histcounts(Vend,'binmethod','scott'); + plot([edges(1) 0.5*(edges(2:end)+edges(1:end-1)) edges(end)],[0 Counts 0],'DisplayName',sprintf("t=%2.3d [ns]",obj.tpart(t(2))*1e9)); + + if strcmp(dist,'maxwell') + vfit=linspace(0,edges(end),300); + a=vmeanend/sqrt(2); + dist=sqrt(2/pi)*vfit.^2.*exp(-((vfit).^2-vd^2)/2/a^2)/a^3; + dist=dist/max(dist); + plot(vfit,max(Counts)*dist,'displayname',sprintf('Maxw mu=%2.2g sigma=%2.2g',vmeanend,vthermend)) + elseif strcmp(dist,'gaussian') + vfit=linspace(edges(1),edges(end),300); + dist=exp(-(vfit-vmeanend).^2/2/vthermend^2); + plot(vfit,max(Counts)*dist,'displayname',sprintf('gauss mu=%2.2g sigma=%2.2g',vmeanend,vthermend)) + end + ylabel('counts') + xlabel(label) + grid on + legend('location','southoutside','orientation','vertical') + end + + function cross_sec=fit_cross_sec(obj,energy,crosssec_table) + %Interpolate the cross-section at the given energy using the + %crosssec_table and an exponential fitting + cross_sec=0; + if (energy<=0 || isnan(energy) || isinf(energy)) + return + end + id=find(energy>crosssec_table(:,1),1,'last'); + if(isempty(id)) + id=1; + end + id=min(size(crosssec_table,1)-1,id); + id=max(1,id); + + cross_sec=crosssec_table(id,2)*(energy/crosssec_table(id,1))^crosssec_table(id,3); + end + + function fighandle=savegraph(obj, fighandle, name, papsize) + %% Saves the given figure as a pdf a .fig and an eps using export_fig + fighandle.PaperUnits='centimeters'; + if (nargin < 4) + papsize=[14 16]; + end + fighandle.PaperSize=papsize; + print(fighandle,name,'-dpdf','-fillpage') + + savefig(fighandle,name) + set(fighandle, 'Color', 'w'); + export_fig(fighandle,name,'-eps') + end + + function sig=dsigmaio(obj,Ekin, Ebar, Ei, E0, chi, gamma) + % calculates the integrand used for the ionisation collision + % cross section for momentum exchange for the incoming electron + % it is only used by obj.sigmiopre + gamma=reshape(gamma,1,[],1); + chi=reshape(chi,1,1,[]); + + siggamma=sin(gamma).*(E0^2+8*(1-chi)*(Ekin-Ei)*E0)./(E0+4*(1-chi)*(Ekin-Ei)-4*(1-chi)*(Ekin-Ei).*cos(gamma)).^2/2; + + sigchi=(Ekin-Ei)./(Ebar*atan((Ekin-Ei)/(2*Ebar)).*(1+(chi*(Ekin-Ei)/Ebar).^2)); + + dp=1- trapz(gamma,sqrt((1-chi).*(1-Ei/Ekin)).*cos(gamma).*siggamma,2);%- trapz(gamma,sqrt((1-chi).*(1-Ei/Ekin)).*cos(gamma).*siggamma,2); + sig=sigchi.*dp; + end + + function sigm=sigmiopre(obj,E, init) + % returns the precalculated values used for the interpolation + % of the ionisation collision cross-section for momentum + % exchange for the incoming electron + if nargin <3 + init=false; + end + if(~init &&( ~obj.neutcol.present || isempty(obj.neutcol.io_cross_sec))) + sigm=zeros(size(E)); + return + end + Ebar=obj.neutcol.scatter_fac; + Ei=obj.neutcol.Eion; + E0=obj.neutcol.E0; + nE=numel(E); + + nchi=300; + ngamma=300; + gamma=linspace(0,pi,ngamma); + chi=linspace(0,0.5,nchi); + %sigm2=zeros(nE,nchi); + sigm=zeros(size(E)); + + for i=1:nE + if(E(i)>=Ei) + sigm2=zeros(nchi,1); + for j=1:nchi + %sigm2(j)=trapz(alpha,trapz(gamma,obj.dsigmaio(E(i),Ebar,Ei,E0,chi(j),alpha,gamma),2),1); + sigm2(j)=obj.dsigmaio(E(i),Ebar,Ei,E0,chi(j),gamma); + end + sigm(i)=trapz(chi,sigm2)*obj.sigio(E(i),init); + %sigm(i)=trapz(chi,trapz(alpha,trapz(gamma,dsigmaio(obj,E(i),Ebar,Ei,E0,chi,alpha,gamma),2),1),3)*obj.sigio(E(i),init); + end + end + end + + end +end diff --git a/matlab/Analytic_stpe_dens_Efield.m b/matlab/Analytic_stpe_dens_Efield.m new file mode 100644 index 0000000..31143a9 --- /dev/null +++ b/matlab/Analytic_stpe_dens_Efield.m @@ -0,0 +1,30 @@ +function [Phi,Er] = Analytic_stpe_dens_Efield(a,b,deltaphi,rm,rp,n,r) +%Analytic_stpe_dens_Efield Computes the electric field and potential for a +% step density in a biased coaxial geometry +% + +q=1.602176620000000e-19; +eps0=8.854187817620000e-12; +phia=-deltaphi; +phib=0; + + +phip=1/log(b/a)*(q*n/2/eps0*rm^2*log(b/rp)*log(rp/rm)+(phia + q*n/2/eps0*(rp^2-rm^2)*(log(rp/a)-0.5))*log(b/rp)); +phim=1/log(b/a)*(-q*n/2/eps0*rm^2*log(rm/a)*log(rp/rm)+( q*n/2/eps0*(rp^2-rm^2)*(log(b/rp)+0.5))*log(rm/a)+phia*log(b/rm)); + +Phi=zeros(size(r)); +Er=zeros(size(r)); + +Phi(r>=a & r=a & r=rm & r=rm & r=rm & r=rp & r<=b)=((phib-phip)*log(r(r>=rp & r<=b))+phip*log(b)-phib*log(rp))/log(b/rp); + +Er(r>=a & r=a & r=rm & r=rm & r=rm & r=rp & r<=b)=-(phib-phip)./r(r>=rp & r<=b)/log(b/rp); + +end + diff --git a/matlab/CheckAxialConfinement.m b/matlab/CheckAxialConfinement.m new file mode 100644 index 0000000..448cd07 --- /dev/null +++ b/matlab/CheckAxialConfinement.m @@ -0,0 +1,113 @@ +t2dlength=size(M.t2d,1); +tstep=t2dlength-1; + +model=M.potentialwellmodel(tstep); +model2=M.potentialwellmodel(0); +N=M.N(:,:,tstep); +N(M.geomweight(:,:,1)<0)=0; + +% figure +% contourf(M.zgrid,M.rgrid,N); +% xlabel('z [m]') +% ylabel('r [m]') +% [x,y]=ginput(1); +% zpos=find(x>M.zgrid,1,'last'); +% rpos=find(y>M.rgrid,1,'last'); +rpos=68; +zpos=285; +rAthet_pos=M.rAthet(rpos,zpos); + +%% +Psieval=rAthet_pos*ones(M.nz+1,1); +Zeval=M.zgrid; +%vpar0=sqrt(M.kb*22000/M.msim*M.weight); +%vpar0=M.fluidUZ(rpos,zpos,t2dlength); +vpar0=sqrt(2/M.me*M.fluidEkin(3,rpos,zpos,tstep)); +vperp0=sqrt(2/M.me*M.fluidEkin(2,rpos,zpos,tstep)); + + +%rdisp=sort(unique([M.rAthet(:,end);M.rAthet(end,:)])); +[Zmesh,Rmesh]=meshgrid(M.zgrid,M.rgrid); +pot=griddata(model.z,model.rathet,model.pot,Zeval,Psieval); +potxt=griddata(model2.z,model2.rathet,model2.pot,Zeval,Psieval); + +[Zinit,~]=meshgrid(M.zgrid,M.rAthet(:,1)); +N0=griddata(Zinit,M.rAthet,N,Zeval,Psieval); +pot=pot;%+min(pot); +potxt=potxt;%+min(pot); + +% Magnetic field mirror ratio at each grid position +% compared to local position +R=griddata(Zmesh,M.rAthet,M.B',Zeval,Psieval,'natural')/M.B(zpos,rpos); +R=(R-1)*3+1; + +% Electrostatic potential on magnetic field line +% coordinates +phis=griddata(Zmesh,M.rAthet,M.pot(:,:,tstep),Zeval,Psieval,'natural')-M.pot(rpos,zpos,tstep); + +rposline=griddata(Zmesh,M.rAthet,Rmesh,Zeval,Psieval,'natural'); + +%% +f=figure('Name','Axial conf'); +subplot(2,1,1) +contourf(M.zgrid*1e3,M.rgrid*1e3,N,'edgecolor','none') +hold on +plot(M.zgrid(zpos)*1e3,M.rgrid(rpos)*1e3,'rx') +plot(Zeval*1e3,rposline*1e3,'r--') +contour(M.zgrid*1e3,M.rgrid*1e3,M.geomweight(:,:,1),[0 0],'w-.','linewidth',1.5,'Displayname','Vessel Boundaries'); + +totpot=M.pot(:,:,tstep); +totpot(M.geomweight(:,:,1)<0)=0; +[c1,h2]=contour(M.zgrid*1e3,M.rgrid*1e3,totpot./1e3,'m--','ShowText','On','linewidth',1.2,'Displayname','Equipotentials [kV]'); +%clabel(c1,h2,'Color','white'); +clabel(c1,h2,'Color','white'); +%ylim([0.077 0.081]) +xlabel('z [m]') +ylabel('r [m]') +c=colorbar; +c.Label.String='n [m^{-3}]'; +set(gca,'fontsize',12); +drawnow; +texth=h2.TextPrims; +for i=1:length(texth) + a=texth(i).String; + y=char(sprintf('%1.2fkV',str2double(a))); + h2.TextPrims(i).String=y; +end + + +% subplot(3,1,2) +% plot(M.zgrid*1e3,pot-pot(zpos),'displayname','total well') +% hold on +% plot(M.zgrid*1e3,potxt-potxt(zpos),'displayname','external well') +% ylabel('-e\Delta\phi [eV]') +% yyaxis right +% plot(M.zgrid,N0,'displayname','density') +% ylabel('n [m^{-3}]') +% xlabel('z [m]') +% grid on +% set(gca,'fontsize',12); +% legend('location','northwest') + +subplot(2,1,2) +%EparB=0.5*M.msim/M.weight*((M.Ez(rpos,zpos,tstep)*M.Br(zpos,rpos)-M.Er(rpos,zpos,tstep)*M.Bz(zpos,rpos))/M.B(zpos,rpos)^2)^2*(1-R)/M.qe; +EparB=0.5*M.msim/M.weight*(vperp0)^2*(1-R)/M.qe; +Eparphi=-M.qsim/M.weight*phis/M.qe; +Epar=EparB+Eparphi+0.5*M.msim/M.weight/M.qe*vpar0^2; +plot(M.zgrid,Epar,'displayname','E_{par,tot}') +hold on +plot(M.zgrid,EparB,'displayname','\DeltaE_{par,mag}') +plot(M.zgrid,Eparphi,'displayname','\DeltaE_{par,elec}') +plot(M.zgrid,0.5*M.msim/M.weight*(vpar0^2)/M.qe*ones(size(M.zgrid)),':','displayname','E_{par,0}') +grid on +xlabel('z [m]') +ylabel('E_{par} [eV]') +legend('location','north','NumColumns',3) +yyaxis right +plot(M.zgrid,R,'displayname','R') +ylabel('R=B(s)/B(0)') +%ylim(1000*[-1 1]) + +set(gca,'fontsize',12); + +M.savegraph(f,sprintf('%s/%s_Ax_confR%dZ%d_tweaked',M.folder,M.name,rpos,zpos),[12,20]) \ No newline at end of file diff --git a/matlab/CheckDiocStability.m b/matlab/CheckDiocStability.m new file mode 100644 index 0000000..f99145d --- /dev/null +++ b/matlab/CheckDiocStability.m @@ -0,0 +1,59 @@ +t2dlength=size(M.t2d,1); +fieldstart=max(1,t2dlength-500);%floor(0.95*t2dlength); +deltat=t2dlength-fieldstart; +maxl=50; +nbsave=200; + +%[~, rgridend]=min(abs(M.rgrid-0.045)); +rgridend=sum(M.nnr(1:2)); +[~, name, ~] = fileparts(M.file); + +dens=mean(M.N(:,:,fieldstart:end),3); +[R,Z]=meshgrid(M.rgrid,M.zgrid); +Rinv=1./R; +Rinv(isnan(Rinv))=0; +VTHET=mean(M.fluidUTHET(:,:,fieldstart:end),3); +omegare=(VTHET.*Rinv'); +Er=mean(M.Er(:,:,fieldstart:end),3); +Ez=mean(M.Ez(:,:,fieldstart:end),3); +vdrift=(M.Br'.*Ez-Er.*M.Bz')./M.B'.^2; +omegadrift=(vdrift.*Rinv'); +nbzid=9; +zindices=linspace(length(M.zgrid)/6,length(M.zgrid)*5/6,nbzid); + +for zid=1:nbzid%1:length(zindices)%;%floor(length(M.zgrid)/2); + zindex=floor(zindices(zid)); + rindex=M.geomweight(:,zindex,1)>=0; + + n=dens(rindex,zindex); + omegar=omegadrift(rindex,zindex); + rgrid=M.rgrid(rindex); + Bz=mean(M.Bz(zindex,rindex),2); + zfolder=sprintf('diocz_%03d',zindex); + if ~isfolder(zfolder) + mkdir(zfolder) + end + wce=abs(M.qsim/M.msim*Bz); + wpe2=M.qsim^2/(M.msim*M.eps_0*M.weight)*n; + wnum=complex(zeros(nbsave,maxl)); + rnumlength=max(length(rgrid)+2,4003); + phinum=complex(zeros(nbsave,rnumlength,maxl)); + rnum=zeros(rnumlength,maxl); + parfor l=1:maxl + [w, phin, r] = diocotronstab(l, rgrid, n, omegar, wce, 1024+512); + + % different profiles of dphi + [~,ind]=sort(abs(imag(w)),'descend'); + + wnum(:,l)=w(ind(1:nbsave)); + phinum(:,:,l)=padarray(phin(:,ind(1:nbsave))',[0 rnumlength-size(phin,1)], 0, 'post'); + rnum(:,l)=padarray(r,[0 rnumlength-length(r(:))],0, 'post')'; + sprintf('l=%02d, zid=%02d done',l,zid) + end + lsteps=1:maxl; + savecase(zfolder,rnum,wnum,phinum,rgrid,wce,Er,Ez,n,omegare,vdrift,omegadrift,M.geomweight,rgrid(1),rgrid(end),lsteps); +end + +function savecase(folder,r,w,phinum,rgrid,wce,Er,Ez,n,omegare,vdrift,omegadrift,geomweight,a,b,lsteps) + save(sprintf('%s/results',folder),'r','w','phinum','a','b','lsteps','rgrid','wce','Er','Ez','n','omegare', 'vdrift','omegadrift','geomweight','phinum') +end \ No newline at end of file diff --git a/matlab/CheckDiocStability_post.m b/matlab/CheckDiocStability_post.m new file mode 100644 index 0000000..6d524f7 --- /dev/null +++ b/matlab/CheckDiocStability_post.m @@ -0,0 +1,126 @@ +nbzid=15; +zindices=linspace(length(M.zgrid)/6,length(M.zgrid)*5/6,nbzid); +zconsid=1:nbzid; + +for zid=zconsid%1:length(zindices)%;%floor(length(M.zgrid)/2); + zindex=floor(zindices(zid)); + zfolder=sprintf('diocz_%03d',zindex); + if ~isfolder(zfolder) || ~isfile(sprintf('%s/results.mat',zfolder)) + fprintf('Warning: result %s doesn''t exist\n',zfolder) + continue + end + result=load(sprintf('%s/results.mat',zfolder)); + if ~isfield(result,'geomweight') + result.geomweight=M.geomweight(:,:,1); + end + rindex=M.geomweight(:,zindex,1)>=0; + + for l=result.lsteps + + r=result.r(:,l); + phin=result.phinum(:,:,l); + w=result.w(:,l); + n=result.n; + omegar=result.omegadrift; + rgrid=result.rgrid; + omegar=omegar(rindex,zindex); + dn=(n(3:end)-n(1:end-2))./(rgrid(3:end)-rgrid(1:end-2)); + a=min(rgrid); + b=max(rgrid); + idmin=find(r==a); + idmax=find(r==b); + wd=(M.qsim/M.weight)^2*n/M.eps_0/(M.msim/M.weight)/2/result.wce; + f=figure; + subplot(4,1,1) + hold on + xlabel('R [m]') + ylabel('n [m^{-3}]') + plots=gobjects(2,1); + plots(1)=plot(rgrid,n,'displayname','n'); + yyaxis right + plots(2)=plot(rgrid,omegar,'displayname','\omega_{re}'); + hold on + %plots(3)=plot(rgrid,wd,'displayname','\omega_{d}'); + legend(plots,{'n','\omega_r','\omega_d'},'location','northwest') + ylabel('\omega_{re} [rad\cdots^{-1}]') + grid minor + + ax=gca; + ax.LineStyleOrder={'-','--','-.',':'}; + subplot(4,1,[2,3]) + hold on + p=gobjects(); + j=1; + for i=1:size(phin,1) + if imag(w(i))/abs(real(w(i)))>1e-4 + freq=w(i); + disphinum=phin(i,:); + disphinum=disphinum/max(abs(disphinum)); + yyaxis left + p(j)=plot(r(idmin:idmax),[0 real(disphinum(idmin:idmax-2)) 0],'displayname',sprintf('\\omega=%1.3g%+1.3gi',real(freq),imag(freq))); + j=j+1; + yyaxis right + plot(r(idmin:idmax),[0 imag(disphinum(idmin:idmax-2)) 0],'displayname',sprintf('\\omega=%1.3g%+1.3gi',real(freq),imag(freq))) + end + end + subplot(4,1,1) + if (abs(imag(w(1))/real(w(1)))>1e-10) + title(sprintf('Unstable case l=%d, \\omega_d=%1.3g, z=%1.3f [mm]',l,max(wd),M.zgrid(zindex)*1e3)) + else + title(sprintf('Stable case l=%d, \\omega_d=%1.3g z=%1.3f [mm]',l,max(wd),M.zgrid(zindex)*1e3)) + end + subplot(4,1,[2,3]) + yyaxis left + ylim([-1 1]) + xlabel('R [m]') + ylabel('real(\delta\phi) [a.u.]') + grid on + yyaxis right + ylim([-1 1]) + grid minor + ylabel('imag(\delta\phi) [a.u.]') + f.PaperUnits='centimeters'; + f.PaperSize=[16,20]; + legend('location','northwest') + if length(p)>=1 && all(ishghandle(p)) + legend(p) + end + subplot(4,1,4) + plot(real(w),imag(w),'x') + xlabel('\omega_r [rad\cdots^{-1}]') + ylabel('\omega_i [rad\cdots^{-1}]') + subplot(4,1,1) + ax1=gca; + subplot(4,1,[2,3]) + ax2=gca; + drawnow + posit1=get(ax1,'position'); + posit2=get(ax2,'position'); + posit2(3)=posit1(3); + set(ax2,'position',posit2); + print(f,sprintf('%s/%s_dioc_l%d',zfolder,M.name,l),'-dpdf','-fillpage') + savefig(f,sprintf('%s/%s_dioc_l%d',zfolder,M.name,l)) + close(f) + f=figure; + subplot(2,1,1) + hold on + plot(rgrid,n/max(n),'displayname','n') + plot(rgrid(2:end-1),dn/max(abs(dn)),'displayname','dn') + plot(rgrid,(real(w(1))-l*omegar)/max(abs((real(w(1))-l*omegar))),'displayname','w-lwre') + legend + grid on + xlabel('r [m]') + ylabel('Normalized quantity a.u.') + subplot(2,1,2) + disphinum=phin(1,:); + disphinum=disphinum/max(abs(disphinum)); + p(j)=plot(r(idmin:idmax),[0 real(disphinum(idmin:idmax-2)) 0],'displayname',sprintf('\\omega=%1.3g%+1.3gi',real(freq),imag(freq))); + xlabel('r [m]') + ylabel('Re(\delta\phi) a.u.') + f.PaperUnits='centimeters'; + f.PaperSize=[12,6]; + print(f,sprintf('%s/%s_maxw_l%d',zfolder,M.name,l),'-dpdf','-fillpage') + savefig(f,sprintf('%s/%s_maxw_l%d',zfolder,M.name,l)) + close(f) + end +end \ No newline at end of file diff --git a/matlab/CheckDiocStability_post_brd.m b/matlab/CheckDiocStability_post_brd.m new file mode 100644 index 0000000..67a9570 --- /dev/null +++ b/matlab/CheckDiocStability_post_brd.m @@ -0,0 +1,92 @@ +nbzid=15; +zindices=linspace(length(M.zgrid)/6,length(M.zgrid)*5/6,nbzid); +zconsid=2:2:nbzid-1; + + +dens=mean(M.N(:,:,fieldstart:fieldend),3); +wd=M.qe*dens./(2*M.eps_0*M.B'); + +wd=max(wd(:)); +f=figure('Name', sprintf('%s dioc_stab',M.name)); +fieldstart=max(1,length(M.t2d)-500); +fieldend=length(M.t2d); +ax1=subplot(2,1,1); +geomw=M.geomweight(:,:,1); +dens=mean(M.N(:,:,fieldstart:fieldend),3); +dens(geomw<=0)=0; +[C,h]=contourf(ax1,M.zgrid*1e3,M.rgrid*1e3,dens,'LineColor','none'); +set(h,'LineColor','none') +hold on +contour(ax1,M.zgrid*1e3,M.rgrid*1e3,M.geomweight(:,:,1),[0 0],'w-.','linewidth',1.5,'Displayname','Boundaries'); +if(M.conformgeom) + ylim(ax1,[M.rgrid(1) M.rgrid(sum(M.nnr(1:2)))]*1e3) +else + ylim(ax1,[M.rgrid(1) M.rgrid(end)]*1e3) +end +xlim(ax1,[M.zgrid(1) M.zgrid(end)]*1e3) +xlabel(ax1,'z [mm]') +ylabel(ax1,'r [mm]') +c = colorbar(ax1); +c.Label.String= 'n[m^{-3}]'; +view(ax1,2) +grid on; +hold on; + +zpos=zeros(size(zconsid)); +wrslt=zeros(size(zconsid)); +lmax=zeros(size(zconsid)); +for zid=1:length(zconsid)%1:length(zindices)%;%floor(length(M.zgrid)/2); + zindex=floor(zindices(zconsid(zid))); + plot(ax1,M.zgrid(zindex)*[1 1]*1e3,[M.rgrid(1) M.rgrid(end)]*1e3,'r--') + zpos(zid)=M.zgrid(zindex)*1e3; + zfolder=sprintf('diocz_%03d',zindex); + if ~isfolder(zfolder) || ~isfile(sprintf('%s/results.mat',zfolder)) + fprintf('Warning: result %s doesn''t exist\n',zfolder) + continue + end + result=load(sprintf('%s/results.mat',zfolder)); + if ~isfield(result,'geomweight') + result.geomweight=M.geomweight(:,:,1); + end + rindex=M.geomweight(:,zindex,1)>=0; + + for l=result.lsteps + + r=result.r(:,l); + phin=result.phinum(:,:,l); + w=result.w(:,l); + [~,idwmax]=max(imag(w)); + if(imag(w(idwmax))>imag(wrslt(zid))) + wrslt(zid)=w(idwmax); + lmax(zid)=l; + end + end +end +ax2=subplot(2,1,2); +semilogy(zpos,real(wrslt)/wd,'bx','displayname','Real') +xlabel('z [mm]') +hold on +semilogy(zpos,imag(wrslt)/wd,'b+','displayname','Imag') +xlim([M.zgrid(1) M.zgrid(end)]*1e3) +ylabel('\omega/\omega_d') +ylim([1e-2 2]) +yyaxis right +plot(zpos,lmax,'d','displayname','l') +ylabel('most unstable l') +grid on +title('Most unstable modes') +legend('location','east') + + +drawnow +posit1=get(ax1,'position'); +posit2=get(ax2,'position'); +posit2(3)=posit1(3); +set(ax2,'position',posit2); + +f.PaperOrientation='landscape'; +f.PaperUnits='centimeters'; +papsize=[10 12]; +f.PaperSize=papsize; +print(f,sprintf('%sdioc_stab',M.name),'-dpdf','-fillpage') +savefig(f,sprintf('%sdioc_stab',M.name)) \ No newline at end of file diff --git a/matlab/Continuityspline.m b/matlab/Continuityspline.m index 5ea04a3..d648de6 100644 --- a/matlab/Continuityspline.m +++ b/matlab/Continuityspline.m @@ -1,132 +1,111 @@ function [Continuity]=Continuityspline(M,it,norm,log,contourscale, zlims) %ForceBalance Show the radial force balance % Plot the three radial forces for the given time-step it or averaged % over the range of time steps defined in it -if it==':' +if strcmp(it,':') it=floor(0.95*size(M.t2d)):size(M.t2d); end switch nargin case 2 - contourscale=0.6; + contourscale=0.2; norm=false; log=false; zlims=[-inf inf]; case 3 log=false; - contourscale=0.6; + contourscale=0.2; zlims=[-inf inf]; case 4 - contourscale=0.6; + contourscale=0.2; zlims=[-inf inf]; case 5 zlims=[-inf inf]; case 6 otherwise error("Invalid number of arguments") end contourcolor=[0 0 0];%[255 20 147]/255; +if(it(1)==1) + it=it(2:end); +end +if(it(end)==length(M.t2d)) + it=it(1:end-1); +end + N=M.N(:,:,it); -densitycontour=contour(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); +maxdens=max(N(:)); +densitycontour=contourc(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); rgridmax=sum(M.nnr(1:2)); %% Continuity -f3=figure(); t=M.t2d(it); -dndt=zeros(size(N,1),size(N,2),size(N,3)-1); -ndivu=zeros(size(N,1),size(N,2),size(N,3)-1); -ugradn=zeros(size(N,1),size(N,2),size(N,3)-1); -parfor i=1:size(N,3)-1 - dndt(:,:,i)=(N(:,:,i+1)-N(:,:,i))./(t(i+1)-t(i)); +dndt=zeros(size(N,1),size(N,2),size(N,3)); +S=zeros(size(N,1),size(N,2),size(N,3)); +ndivu=zeros(size(N,1),size(N,2),size(N,3),3); +ugradn=zeros(size(N,1),size(N,2),size(N,3),2); +[R,~]=meshgrid(M.rgrid,M.zgrid); +R=R'; +Rinv=1./R; +Rinv(isinf(Rinv))=0; +geommask=M.geomweight(:,:,1)<0; +for i=1:size(N,3) + ddt=(M.N(:,:,it(i)+1)-M.N(:,:,it(i)-1))./(M.t2d(it(i)+1)-M.t2d(it(i)-1)); + ddt(geommask)=0; + + dndt(:,:,i)=ddt; + + ndivu1=N(:,:,i).*(M.fluidUR.der(:,:,it(i),[1 0])); + ndivu1(geommask)=0; + ndivu(:,:,i,1)=ndivu1; - ndivu(:,:,i)=N(:,:,i).*(M.fluidUR.der(:,:,it(i),[1 0])... - + M.fluidUR(:,:,it(i)).*Rinv... - + M.fluidUZ.der(:,:,it(i),[0 1])); + ndivu2=N(:,:,i).*(M.fluidUR(:,:,it(i)).*Rinv); + ndivu2(geommask)=0; + ndivu(:,:,i,2)=ndivu2; - ugradn(:,:,i)= M.fluidUR(:,:,it(i)).*M.N.der(:,:,it(i),[1 0]) ... - + M.fluidUZ(:,:,it(i)).*M.N.der(:,:,it(i),[0 1]); + ndivu3=N(:,:,i).*(M.fluidUZ.der(:,:,it(i),[0 1])); + ndivu3(geommask)=0; + ndivu(:,:,i,3)=ndivu3; + + ugradn1= M.fluidUR(:,:,it(i)).*M.N.der(:,:,it(i),[1 0]); + ugradn1(geommask)=0; + ugradn(:,:,i,1)=ugradn1; + ugradn2= M.fluidUZ(:,:,it(i)).*M.N.der(:,:,it(i),[0 1]); + ugradn2(geommask)=0; + ugradn(:,:,i,2)=ugradn2; + + if M.neutcol.present && ~isempty(M.neutcol.io_cross_sec) + % average kinetic energy in each direction +% Er=M.fluidEkin(1,:,:,it(i)); +% Ethet=M.fluidEkin(2,:,:,it(i)); +% Ez=M.fluidEkin(3,:,:,it(i)); +% Ek=squeeze((Er+Ethet+Ez)/M.qe); +% % % corresponding particle velocity +% U=squeeze(sqrt(2*M.weight/M.msim*(Er+Ethet+Ez))); + U=-M.Er(:,:,it(i))./M.Bz'; + Ek=0.5*M.msim/M.weight/M.qe*U.^2; + Ek(N(:,:,i)<=0)=0; + % ionisation cross section per cell + sigio=M.sigio(Ek); + Si=M.neutcol.neutdens.*N(:,:,i).*sigio.*U; + end + + if M.maxwellsrce.present + Si=Si+M.maxwellsrce.frequency*M.weight/(pi*diff(M.maxwellsrce.zlim)*(M.maxwellsrce.rlim(2)^2-M.maxwellsrce.rlim(1)^2)); + end + Si(geommask)=0; + S(:,:,i)=Si; end +Continuity.N=N; Continuity.dndt=dndt; Continuity.ndivu=ndivu; Continuity.ugradn=ugradn; +Continuity.maxdens=maxdens; +Continuity.it=it; - -ax7=subplot(4,1,1); -plotvalue(ax7,mean(Continuity.dndt,3),'\partialn/\partialt','\partialn/\partialt [m^{-3}s^{-1}]',true) - - -zlim=[max([min(min(mean(Continuity.ndivu(2:end,:,:),3))),min(min(mean(Continuity.ugradn(2:end,:,:),3)))]) min([max(max(mean(Continuity.ndivu(2:end,:,:),3))),max(max(mean(Continuity.ugradn(2:end,:,:),3)))])]; - -ax8=subplot(4,1,2); -plotvalue(ax8,mean(Continuity.ndivu,3),'n\nabla(u)','n\nabla(u)[m^{-3}s^{-1}]',true,zlim) - -ax9=subplot(4,1,3); -plotvalue(ax9,mean(Continuity.ugradn,3),'u\nabla(n)','u\nabla(n)[m^{-3}s^{-1}]',true,zlim) - -ax10=subplot(4,1,4); -plotvalue(ax10,mean(Continuity.ndivu+Continuity.ugradn+Continuity.dndt,3),'Total','total[m^{-3}s^{-1}]',true,zlim) - -sgtitle(sprintf('Continuity t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -f3.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f3.PaperUnits='centimeters'; -papsize=[20 19]; -f3.PaperSize=papsize; -print(f3,sprintf('%scontinuity',name),'-dpdf','-fillpage') - - - function plotvalue(axis,matrix,titl, clab, rm1strow, zlims) - if nargin < 5 - rm1strow=false; - end - if nargin>5 - zmin=zlims(1); - zmax=zlims(2); - else - zmin=-inf; - zmax=inf; - end - if rm1strow - if log - h=surface(axis,M.zgrid,M.rgrid(2:end),abs(matrix(2:end,:))); - else - h=surface(axis,M.zgrid,M.rgrid(2:end),matrix(2:end,:)); - end - else - if log - h=surface(axis,M.zgrid,M.rgrid,abs(matrix)); - else - h=surface(axis,M.zgrid,M.rgrid,matrix); - end - end - set(h,'edgecolor','none'); - hold on; - if log - zpos=interp2(M.zgrid, M.rgrid, abs(matrix), densitycontour(1,2:end), densitycontour(2,2:end)); - else - zpos=interp2(M.zgrid, M.rgrid, matrix, densitycontour(1,2:end), densitycontour(2,2:end)); - end - plot3(axis,densitycontour(1,2:end),densitycontour(2,2:end),zpos,'-.','linewidth',2,'color',contourcolor) - xlabel(axis,'z [m]') - ylabel(axis,'r [m]') - xlim(axis,[M.zgrid(1) M.zgrid(end)]) - ylim(axis,[M.rgrid(1) M.rgrid(rgridmax)]) - colormap(axis,'jet') - c = colorbar(axis); - c.Label.String=clab; - caxis(axis,[zmin zmax]) - if norm - caxis(axis,[Fminr Fmaxr]); - end - if log - set(axis,'colorscale','log') - end - title(axis,titl) - grid(axis, 'on') - view(axis,2) - end +Continuity.S=S; end diff --git a/matlab/DensityVideosaving.m b/matlab/DensityVideosaving.m new file mode 100644 index 0000000..3c37950 --- /dev/null +++ b/matlab/DensityVideosaving.m @@ -0,0 +1,114 @@ + +initfieldstep=1; + +maxN=3e17; +tend=4700;%length(M.t2d);%min(60000,length(M.t2d)); +%tend=length(M.t2d); + +filename=[M.name,'_dens_log.avi']; +videowriterobj=VideoWriter([M.folder,'/',filename]); +videowriterobj.FrameRate=20; +videowriterobj.Quality=90; +open(videowriterobj); + +step=5; + + +%% Fields +f=figure('Name', sprintf('%s fields',M.name),'Position',[0 0 1600 900]); +ax1=gca; + +dens=M.N(:,:,initfieldstep); +dispdens=dens; +colormap(flipud(hot)); +geomw=M.geomweight(:,:,1); +%geomw(geomw>0)=1; +%geomw(geomw<=0)=0; +dispdens(geomw<=0)=0; +Nlvls=logspace( log10(1e12), log10(maxN),50); +[h,curve]=contourf(ax1,M.zgrid*1000,M.rgrid*1000,dispdens,Nlvls,'Displayname','n_e [m^{-3}]'); +set(curve,'linecolor','none'); +colormap(flipud(hot)); +hold on; +pot=M.pot(:,:,initfieldstep); +pot(geomw<1e-4)=NaN; + + +Blines=M.rAthet; +levels=linspace(min(Blines(M.geomweight(:,:,1)>0)),max(Blines(M.geomweight(:,:,1)>0)),20); +Blines(M.geomweight(:,:,1)<0)=NaN; +[~,h1]=contour(ax1,M.zgrid*1000,M.rgrid*1000,Blines,real(levels),'-.','color','k','linewidth',1.5,'Displayname','Magnetic field lines'); + +pot(geomw<0)=NaN; +potcolor='b'; +[c1,h2]=contour(ax1,M.zgrid*1000,M.rgrid*1000,pot/1e3,0:-5:-20,'--','color',potcolor,'ShowText','on','linewidth',1.2,'Displayname','Equipotentials [kV]'); +clabel(c1,h2,'Color',potcolor,'fontsize',14) + +%contour(ax1,M.zgrid*1000,M.rgrid*1000,M.geomweight(:,:,1),[0 0],'w-','linewidth',1.5); + +[c1,hContour]=contourf(ax1,M.zgrid*1000,M.rgrid*1000,-geomw,[0,0],'linewidth',1.5); +drawnow; +xlim(ax1,[M.zgrid(1)*1000 M.zgrid(end)*1000]) +if(M.conformgeom) + ylim(ax1,[M.rgrid(1)*1000 M.rgrid(rgridend)*1000]) +else + ylim(ax1,[M.rgrid(1)*1000 M.rgrid(end)*1000]) +end +legend([h1,h2],{'Magnetic field lines','Equipotentials [kV]'},'location','southwest') +xlabel(ax1,'z [mm]') +ylabel(ax1,'r [mm]') +%title(ax1,sprintf('Density t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) +c = colorbar(ax1); +c.Label.String= 'n[m^{-3}]'; +view(ax1,2) +%set(h,'edgecolor','none'); +grid on; +set(ax1,'fontsize',14); + +hFills=hContour.FacePrims; +[hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' +try + hFills(end).ColorData = uint8([150;150;150;255]); + for idx = 1 : numel(hFills)-1 + hFills(idx).ColorData(4) = 0; % default=255 + end +catch +end +%rectangle('Position',[M.zgrid(1) M.rgrid(1)-2e-4 M.zgrid(end)-M.zgrid(1) 2e-4]*1000,'FaceColor',[150 150 150]/255) +%rectangle('Position',[M.zgrid(1) M.rgrid(end) M.zgrid(end)-M.zgrid(1) 2e-4]*1000,'FaceColor',[150 150 150]/255) +sgtitle(f,sprintf('t= %1.2f [ns]',M.t2d(initfieldstep)*1e9)) +% if isfield(M.maxwellsrce,'time_start') && M.maxwellsrce.time_start<=M.t2d(1) +% rectangle('Position',[M.maxwellsrce.zlim(1) M.maxwellsrce.rlim(1) diff(M.maxwellsrce.zlim) diff(M.maxwellsrce.rlim)]*1000,'linestyle','--') +% end + +set(ax1,'ColorScale','log') +ylim([M.rgrid(1)-2e-4 M.rgrid(end)+2e-4]*1000) + +caxis([Nlvls(1) Nlvls(end)]); + +for i=initfieldstep:step:tend + sgtitle(f,sprintf('t= %1.2f [ns]',M.t2d(i)*1e9)) + dens=M.N(:,:,i); + dispdens=dens; + dispdens(geomw<=0)=0; + pot=M.pot(:,:,i)./1e3; + pot(geomw<1e-4)=NaN; + + + curve.ZData=dispdens; + h2.ZData=pot; + caxis(ax1,[Nlvls(1) Nlvls(end)]); + %Curve2.YData=pot(rAthetpos,1:end); + drawnow; + hFills=hContour.FacePrims; + [hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' + try + hFills(1).ColorData = uint8([150;150;150;255]); + for idx = 2 : numel(hFills) + hFills(idx).ColorData(4) = 0; % default=255 + end + catch + end + writeVideo(videowriterobj,getframe(f)); +end +close(videowriterobj); \ No newline at end of file diff --git a/matlab/DisplayContinuityspline.m b/matlab/DisplayContinuityspline.m new file mode 100644 index 0000000..963a1a8 --- /dev/null +++ b/matlab/DisplayContinuityspline.m @@ -0,0 +1,217 @@ +function DisplayContinuityspline(M,Continuity,norm,log,contourscale, zlims) +%ForceBalance Show the radial force balance +% Plot the three radial forces for the given time-step it or averaged +% over the range of time steps defined in it + +switch nargin + case 2 + contourscale=0.2; + norm=false; + log=false; + zlims=[-inf inf]; + case 3 + log=false; + contourscale=0.2; + zlims=[-inf inf]; + case 4 + contourscale=0.2; + zlims=[-inf inf]; + case 5 + zlims=[-inf inf]; + case 6 + + otherwise + error("Invalid number of arguments") +end + +contourcolor=[0 0 0];%[255 20 147]/255; +it=Continuity.it; + +N=mean(Continuity.N,3); +maxdens=max(N(:)); +densitycontour=contourc(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); +rgridmax=sum(M.nnr(1:2)); + +S=1*mean(Continuity.S,3); + + +%% +f3=figure(); +ax7=subplot(5,1,1); +zlim=[-1 1]*1e8; +plotvalue(ax7,mean(Continuity.dndt,3)./N,'\partialn/\partialt','\partialn/\partialt [m^{-3}s^{-1}]',true,zlim) + + +%zlim=[max([min(min(mean(sum(Continuity.ndivu(2:end,:,:),4),3))),min(min(mean(sum(Continuity.ugradn(2:end,:,:),4),3)))]) min([max(max(mean(sum(Continuity.ndivu(2:end,:,:),4),3))),max(max(mean(sum(Continuity.ugradn(2:end,:,:),4),3)))])]; + +ax8=subplot(5,1,2); +plotvalue(ax8,mean(sum(Continuity.ndivu,4),3)./N,'n\nabla(u)','n\nabla(u)[m^{-3}s^{-1}]',true,zlim) + +ax9=subplot(5,1,3); +plotvalue(ax9,mean(sum(Continuity.ugradn,4),3)./N,'u\nabla(n)','u\nabla(n)[m^{-3}s^{-1}]',true,zlim) + + +ax10=subplot(5,1,4); +plotvalue(ax10,mean(Continuity.S,3)./N,'S_{io}','S_{io} [m^{-3}s^{-1}]',true,zlim) + +ax11=subplot(5,1,5); +plotvalue(ax11,(mean(Continuity.dndt,3)+mean(sum(Continuity.ndivu,4)+sum(Continuity.ugradn,4),3)-S)./N,'Total','total[m^{-3}s^{-1}]',true,zlim) + +linkaxes([ax7,ax8,ax9,ax10,ax11]) +xlim([ax7,ax8,ax9,ax10,ax11],[1e-3 12e-3]) +ylim([ax7,ax8,ax9,ax10,ax11],[0.078 0.0792]) + +sgtitle(sprintf('Continuity t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) +f3.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f3.PaperUnits='centimeters'; +papsize=[20 19]; +f3.PaperSize=papsize; +print(f3,sprintf('%scontinuity%d',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f3,sprintf('%scontinuity%d',name,floor(mean(it)))) + + +%% +f3=figure(); +ax7=subplot(5,1,1); +plotvalue(ax7,mean(Continuity.dndt,3)./N,'\partialn/\partialt','\partialn/\partialt [m^{-3}s^{-1}]',true,zlim) + + +%zlim=[max([min(min(mean(sum(Continuity.ndivu(2:end,:,:),4),3))),min(min(mean(sum(Continuity.ugradn(2:end,:,:),4),3)))]) min([max(max(mean(sum(Continuity.ndivu(2:end,:,:),4),3))),max(max(mean(sum(Continuity.ugradn(2:end,:,:),4),3)))])]; + +ax8=subplot(5,1,2); +plotvalue(ax8,mean(sum(Continuity.ndivu,4),3)./N,'n\nabla(u)','n\nabla(u)[m^{-3}s^{-1}]',true,zlim) + +ax9=subplot(5,1,3); +plotvalue(ax9,mean(sum(Continuity.ugradn,4),3)./N,'u\nabla(n)','u\nabla(n)[m^{-3}s^{-1}]',true,zlim) + + +ax10=subplot(5,1,4); +plotvalue(ax10,mean(Continuity.S,3)./N,'S_{io}','S_{io} [m^{-3}s^{-1}]',true,zlim) + +ax11=subplot(5,1,5); +plotvalue(ax11,(-mean(sum(Continuity.ndivu,4)+sum(Continuity.ugradn,4),3)+S)./N,'S-\nabla(nu)','S-\nabla(nu) [m^{-3}s^{-1}]',true,zlim) + +linkaxes([ax7,ax8,ax9,ax10,ax11]) +xlim([ax7,ax8,ax9,ax10,ax11],[1e-3 12e-3]) +ylim([ax7,ax8,ax9,ax10,ax11],[0.078 0.0792]) + +sgtitle(sprintf('Continuity t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) +f3.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f3.PaperUnits='centimeters'; +papsize=[20 19]; +f3.PaperSize=papsize; +print(f3,sprintf('%scontinuity%d',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f3,sprintf('%scontinuity%d',name,floor(mean(it)))) + + +%% +f3=figure; +ax6=subplot(4,1,1); +plotvalue(ax6,squeeze(mean(sum(Continuity.ndivu(:,:,:,:),4),3)),'ndivu er','radial ndivu [m^{-3}s^{-1}]',true,zlim) + +ax7=subplot(4,1,2); +plotvalue(ax7,squeeze(mean(Continuity.ndivu(:,:,:,1),3)),'ndivu er1','radial 1 ndivu [m^{-3}s^{-1}]',true,zlim) + +ax8=subplot(4,1,3); +plotvalue(ax8,squeeze(mean(Continuity.ndivu(:,:,:,2),3)),'ndivu er2','radial 2 ndivu [m^{-3}s^{-1}]',true,zlim) + +ax9=subplot(4,1,4); +plotvalue(ax9,squeeze(mean(Continuity.ndivu(:,:,:,3),3)),'ndivu ez','axial ndivu [m^{-3}s^{-1}]',true,zlim) + + + +linkaxes([ax6 ax7,ax8,ax9]) +xlim([ax6 ax7,ax8,ax9],[1e-3 12e-3]) +ylim([ax6 ax7,ax8,ax9],[0.0785 0.0792]) + +sgtitle(sprintf('Continuity ndivu t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) +f3.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f3.PaperUnits='centimeters'; +papsize=[20 19]; +f3.PaperSize=papsize; +print(f3,sprintf('%scontinuityndivu%d',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f3,sprintf('%scontinuityndivu%d',name,floor(mean(it)))) +linkaxes([ax7,ax8,ax9]) + + +%% +f3=figure; +ax7=subplot(2,1,1); +plotvalue(ax7,squeeze(mean(Continuity.ugradn(:,:,:,1),3)),'ugradn er','radial ugradn [m^{-3}s^{-1}]',true,zlim) + +ax8=subplot(2,1,2); +plotvalue(ax8,squeeze(mean(Continuity.ugradn(:,:,:,2),3)),'ugradn ez','axial ugradn [m^{-3}s^{-1}]',true,zlim) + + +linkaxes([ax7,ax8]) +xlim([ax7,ax8],[1e-3 12e-3]) +ylim([ax7,ax8],[0.0785 0.0792]) + +sgtitle(sprintf('Continuity ugradn t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) +f3.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f3.PaperUnits='centimeters'; +papsize=[20 19]; +f3.PaperSize=papsize; +print(f3,sprintf('%scontinuityugradn%d',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f3,sprintf('%scontinuityugradn%d',name,floor(mean(it)))) +linkaxes([ax7,ax8]) + + + function plotvalue(axis,matrix,titl, clab, rm1strow, zlims) + if nargin < 5 + rm1strow=false; + end + if nargin>5 + zmin=zlims(1); + zmax=zlims(2); + else + zmin=-inf; + zmax=inf; + end + if rm1strow + if log + h=surface(axis,M.zgrid,M.rgrid(2:end),abs(matrix(2:end,:))); + else + h=surface(axis,M.zgrid,M.rgrid(2:end),matrix(2:end,:)); + end + else + if log + h=surface(axis,M.zgrid,M.rgrid,abs(matrix)); + else + h=surface(axis,M.zgrid,M.rgrid,matrix); + end + end + %set(h,'edgecolor','none'); + hold on; + if log + zpos=interp2(M.zgrid, M.rgrid, abs(matrix), densitycontour(1,2:end), densitycontour(2,2:end)); + else + zpos=interp2(M.zgrid, M.rgrid, matrix, densitycontour(1,2:end), densitycontour(2,2:end)); + end + plot3(axis,densitycontour(1,2:end),densitycontour(2,2:end),zpos,'-.','linewidth',2,'color',contourcolor) + xlabel(axis,'z [m]') + ylabel(axis,'r [m]') + xlim(axis,[M.zgrid(1) M.zgrid(end)]) + if M.conformgeom + ylim(axis,[M.rgrid(1) M.rgrid(rgridmax)]) + end + colormap(axis,'jet') + c = colorbar(axis); + c.Label.String=clab; + caxis(axis,[zmin zmax]) + if norm + caxis(axis,[Fminr Fmaxr]); + end + if log + set(axis,'colorscale','log') + end + title(axis,titl) + grid(axis, 'on') + view(axis,2) + end +end + diff --git a/matlab/Eval_geometry.m b/matlab/Eval_geometry.m new file mode 100644 index 0000000..556fa32 --- /dev/null +++ b/matlab/Eval_geometry.m @@ -0,0 +1,281 @@ +%Mtest=espic2dhdf5('test_fst_80_2d_websurfw.h5',false); +%M=espic2dhdf5('/misc/spltestdwn.h5',false); +Mtest=M; + +if length(Mtest.r_a)<1 + r_a=3.e-3; + r_b=0.152; + z_0=0; + r_0=0.158; + z_r=0.26; + r_r=0.02; + Lr=0.12; + Lz=0.96; + above=1; + Interior=-1; +else + r_a=Mtest.r_a; + r_b=Mtest.r_b; + z_0=Mtest.z_0; + r_0=Mtest.r_0; + z_r=Mtest.z_r; + r_r=Mtest.r_r; + Lr=Mtest.L_r; + Lz=Mtest.L_z; + above=double(Mtest.above1); + Interior=double(Mtest.interior); +end +if isempty(Lr) +Lr=0.12; +Lz=0.96; +end +invr_r=1/r_r; +invr_z=1/z_r; + +nbcont=24; + +[z,r]=meshgrid(Mtest.zgrid,Mtest.rgrid); + +%wgeom=geom_weight(z,r,r_a,above,z_0,r_0,invr_z,invr_r,Interior); +wgeom=Mtest.geomweight(:,:,1);%wgeom(:,:,1); + +z0=0.5*(M.zgrid(end)+M.zgrid(1)); +r0=0.5*(M.rgrid(end)+M.rgrid(1)); + +potth=(sin(pi*(z-z0)/Lz).*sin(pi*(r-r0)/Lr)+2); +Erth=-(sin(pi*(z-z0)/Lz).*cos(pi*(r-r0)/Lr)*(pi/Lr)); +Ezth=-(cos(pi*(z-z0)/Lz).*sin(pi*(r-r0)/Lr)*(pi/Lz)); + +% potnum=Mtest.pot(:,:,1)/Mtest.phinorm; +% Er=Mtest.Er(:,:,1)/Mtest.phinorm; +% Ez=Mtest.Ez(:,:,1)/Mtest.phinorm; +potnum=Mtest.potxt/Mtest.phinorm; +Er=Mtest.Erxt/Mtest.phinorm; +Ez=Mtest.Ezxt/Mtest.phinorm; + + +potnum(wgeom<0)=NaN; +potth(wgeom<0)=NaN; +Erth(wgeom<0)=NaN; +Ezth(wgeom<0)=NaN; +Er(wgeom<0)=NaN; +Ez(wgeom<0)=NaN; +invpotth=1./potth; +%invpotth(isinf(invpotth))=0; +%poterr=abs(poterr./max(potth(:))); +inverth=1./Erth; +inverth(isinf(inverth))=0; +%Ererr=abs(Er-Erth)./max(Erth(:)); +%Ezerr=abs(Ez-Ezth)./max(Ezth(:)); +Ererr=abs((Er-Erth)./max(Erth(:))); +Ezerr=abs((Ez-Ezth)./max(Ezth(:))); +poterr=abs((potnum-potth)./max(potth(:))); +zerr=z; +rerr=r; +Mtest.name; +L2errpot=norm(potth-potnum,2)/norm(potth,2) +L2errEr=norm(Erth-Er,2)/norm(Erth,2) +L2errEz=norm(Ezth-Ez,2)/norm(Ezth,2) +maxerr=max(max(abs((potth-potnum)./potth))) +gridsize=sprintf(" (nz=%i,nr=%i)",Mtest.nz,sum(Mtest.nnr)); + + +%% numerical potential solution +f=figure(); +lvls=linspace(-1,1,nbcont)*max(abs(potth(:))); +subplot(2,1,1) +contourf(Mtest.zgrid, Mtest.rgrid,potnum,lvls) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +caxis([min(potth(:)) max(potth(:))]) +title(['pot num',gridsize]) +xlabel('z [m]') +ylabel('r [m]') +c = colorbar; +c.Label.String='\phi_{num} [V]'; +caxis([min(potth(:)) max(potth(:))]) + +subplot(2,1,2) +contourf(Mtest.zgrid,Mtest.rgrid,potth,lvls) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +c = colorbar; +c.Label.String='\phi_{th} [V]'; +caxis([min(potth(:)) max(potth(:))]) +title('pot th') +xlabel('z') +ylabel('r') +linkaxes + +colormap jet +Mtest.savegraph(f,sprintf('%s/%s_potnum',Mtest.folder,Mtest.name)); + + +%% numerical radial electric field +figure +lvls=linspace(-1,1,nbcont)*max(abs(Erth(:))); +subplot(2,1,1) +contourf(Mtest.zgrid,Mtest.rgrid,Er,lvls) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +colorbar +title('E_r') +xlabel('z') +ylabel('r') +subplot(2,1,2) +contourf(Mtest.zgrid,Mtest.rgrid,Erth,lvls) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +colorbar +linkaxes +title('E_r th') +xlabel('z') +ylabel('r') + +colormap jet + +%% numerical axial electric field +figure +lvls=linspace(-1,1,nbcont)*max(abs(Ezth(:))); +subplot(2,1,1) +contourf(Mtest.zgrid,Mtest.rgrid,Ez,lvls) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +colorbar +title('E_z') +xlabel('z') +ylabel('r') +subplot(2,1,2) +contourf(Mtest.zgrid,Mtest.rgrid,Ezth,lvls) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +colorbar +linkaxes +title('E_z th') +xlabel('z') +ylabel('r') + +colormap jet + + +%% relative error on potential +f=figure(); +poterr(Mtest.geomweight(:,:,1)<0)=0; + +contourf(zerr,rerr,poterr,logspace(-8,1,nbcont)) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +set(gca,'ColorScale','log') +%set(gca,'ZScale','log') +title(['Err \phi web',gridsize]) +xlabel('z [m]') +ylabel('r [m]') +[poterrmax, id]=max(poterr(2:end-1,2:end-1),[],'all','linear'); +[idr,idz]=ind2sub(size(poterr(2:end-1,2:end-1)),id); +fprintf('poterrmax= %1.5f, at z=%1.4f r=%1.4f\n',poterrmax,Mtest.zgrid(idz+1),Mtest.rgrid(idr+1)) +c = colorbar; +c.Label.String='relative error'; + +colormap jet +Mtest.savegraph(f,sprintf('%s/%s_pot_error',Mtest.folder,Mtest.name)); + +%% relative error on Er +f=figure(); +Ererr(Mtest.geomweight(:,:,1)<0)=0; +contourf(zerr,rerr,Ererr,logspace(-8,1,nbcont)) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +set(gca,'ColorScale','log') +%set(gca,'ZScale','log') +title(['Err Er web',gridsize]) +xlabel('z [m]') +ylabel('r [m]') +[Ererrmax, id]=max(Ererr(2:end-1,2:end-1),[],'all','linear'); +[idr,idz]=ind2sub(size(poterr(2:end-1,2:end-1)),id); +fprintf('Errmax= %1.5f, at z=%1.4f r=%1.4f\n',Ererrmax,Mtest.zgrid(idz+1),Mtest.rgrid(idr+1)) +c = colorbar; +c.Label.String='relative error'; + +colormap jet +Mtest.savegraph(f,sprintf('%s/%s_Er_error',Mtest.folder,Mtest.name)); + +%% relative error on Ez +f=figure(); +Ezerr(Mtest.geomweight(:,:,1)<0)=0; +contourf(zerr,rerr,Ezerr,logspace(-8,1,nbcont)) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +set(gca,'ColorScale','log') +%set(gca,'ZScale','log') +title(['Err Ez web',gridsize]) +xlabel('z [m]') +ylabel('r [m]') +[Ezerrmax, id]=max(Ezerr(2:end-1,2:end-1),[],'all','linear'); +[idr,idz]=ind2sub(size(poterr(2:end-1,2:end-1)),id); +fprintf('Ezrmax= %1.5f, at z=%1.4f r=%1.4f\n',Ezerrmax,Mtest.zgrid(idz+1),Mtest.rgrid(idr+1)) +c = colorbar; +c.Label.String='relative error'; +colormap jet +Mtest.savegraph(f,sprintf('%s/%s_Ez_error',Mtest.folder,Mtest.name)); + + + +%% analytical potential solution +f=figure(); +contourf(Mtest.zgrid, Mtest.rgrid,potth) +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'r-','linewidth',1.2) +title('pot th') +xlabel('z [m]') +ylabel('r [m]') +c = colorbar; +c.Label.String='\phi_{Ana} [V]'; +colormap jet +Mtest.savegraph(f,sprintf('%s/%s_potAna',Mtest.folder,Mtest.name)); + +% %% class of the cells +% zclass=(M.zgrid(1:end-1)+M.zgrid(2:end))/2; +% rclass=(M.rgrid(1:end-1)+M.rgrid(2:end))/2; +% [zclassgrid, rclassgrid]=meshgrid(zclass,rclass); +% gridclass=M.geomweight(:,:,1);%geom_weight(zclassgrid,rclassgrid,r_a,above,z_0,r_0,invr_z,invr_r,Interior); +% +% f=figure(); +% imagesc(zclass,rclass,gridclass(:,:,1)) +% hold on +% contour(M.zgrid,M.rgrid,wgeom,[0 0],'r-','linewidth',1.2) + +%% geometric weight and boundaries +f=figure(); +contourf(Mtest.zgrid,Mtest.rgrid,wgeom,'color','none') +hold on +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'m--','linewidth',2.2) +contour(Mtest.zgrid,Mtest.rgrid,wgeom,max(wgeom(:))*[1 1],'k--','linewidth',2.2) +title(['weight',gridsize]) +c = colorbar; +c.Label.String='w(x)'; +xlabel('z [m]') +ylabel('r [m]') +%caxis([min(wgeom(:)) 1]) +colormap jet +Mtest.savegraph(f,sprintf('%s/%s_weight',Mtest.folder,Mtest.name)); + + +%% cells lines +f=figure(); + +for i=1:length(Mtest.rgrid) + plot([Mtest.zgrid(1) Mtest.zgrid(end)],Mtest.rgrid(i)*[1 1],'k-','linewidth',0.3) + hold on +end +for j=1:length(Mtest.zgrid) + pl2=plot(Mtest.zgrid(j)*[1 1],[Mtest.rgrid(1) Mtest.rgrid(end)],'k-','linewidth',0.3); +end +contour(Mtest.zgrid,Mtest.rgrid,wgeom,[0 0],'m--','linewidth',2.2) +M.displaysplbound +%legend([f.Children(1).Children(1),pl2],{'Boundaries','Grid lines'},'location','east') +xlabel('z[m]') +ylabel('r[m]') +axis equal + +Mtest.savegraph(f,sprintf('%s/%s_geomgrid',Mtest.folder,Mtest.name)); + diff --git a/matlab/FBspline.m b/matlab/FBspline.m index 605258d..4c1608b 100644 --- a/matlab/FBspline.m +++ b/matlab/FBspline.m @@ -1,254 +1,575 @@ -function [Forces, Continuity]=FBspline(M,it,Forces, Continuity, Density,norm,log,contourscale, zlims) -%ForceBalance Show the radial force balance -% Plot the three radial forces for the given time-step it or averaged -% over the range of time steps defined in it - -if it==':' - it=floor(0.95*size(M.t2d)):size(M.t2d); -end -switch nargin - case 5 - contourscale=0.6; - norm=false; - log=false; - zlims=[-inf inf]; - case 6 - log=false; - contourscale=0.6; - zlims=[-inf inf]; - case 7 - contourscale=0.6; - zlims=[-inf inf]; - case 8 - zlims=[-inf inf]; - case 9 - - otherwise - error("Invalid number of arguments") -end - -contourcolor=[0 0 0];%[255 20 147]/255; -N=Density.N; - -maxdens=max(N(:)); - -densitycontour=contour(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); - -Fmaxr=max([max(Forces.Eforcer(:)),max(Forces.Bforcer(:)),max(Forces.inertforce(:)),max(Forces.Pressforcer(:))]); -Fmaxz=max([max(Forces.Eforcez(:)),max(Forces.Bforcez(:)),max(Forces.Pressforcez(:))]); -if log - Er=Forces.Eforcer(Forces.Eforcer~=0); - Br=Forces.Bforcer(Forces.Bforcer~=0); - inert=Forces.inertforce(Forces.inertforce~=0); - pressr=Forces.Pressforcer(Forces.Pressforcer~=0); - Fminr=min([min(abs(Er(:))),min(abs(Br(:))),min(abs(inert(:))),min(abs(pressr(:)))]); -else - Fminr=min([min(Forces.Eforcer(:)),min(Forces.Bforcer(:)),min(Forces.inertforce(:)),min(Forces.Pressforcer(:))]); -end -rgridmax=sum(M.nnr(1:2)); - -%% Radial forces -f=figure(); - -ax1=subplot(4,1,1); -plotvalue(ax1,mean(Forces.Eforcer,3),'Electric force density', 'F_{Er} [Nm^{-3}]',true); - -%zlim=[Fmin Fmax]; -zlim=[-inf inf]; - -ax2=subplot(4,1,2); -plotvalue(ax2,mean(Forces.Bforcer,3),'Magnetic force density','F_{Br} [Nm^{-3}]',true,zlim) - -ax3=subplot(4,1,3); -plotvalue(ax3,mean(Forces.inertforce,3),'Centrifugal force density','F_{i} [Nm^{-3}]',true,zlim) - -ax4=subplot(4,1,4); -plotvalue(ax4,mean(Forces.Pressforcer,3),'Pressure force density','F_{pr} [Nm^{-3}]',true,zlim) - -linkaxes([ax1 ax2 ax3 ax4],'xy') -sgtitle(sprintf('Radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) - -f.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f.PaperUnits='centimeters'; -papsize=[20 29]; -f.PaperSize=papsize; -print(f,sprintf('%sforce_balancer',name),'-dpdf','-fillpage') -savefig(f,sprintf('%sforce_balancer',name)) - -%% Radial relative forces -f=figure(); -ax1=subplot(4,1,1); -color_limits=[-1 1]; -reference=Forces.Bforcer; -plotvalue(ax1,mean(Forces.Eforcer./reference,3),'Relative Electric force density', 'F_{Er}/F_{Br}',true,color_limits); - -%zlim=[Fmin Fmax]; -zlim=[-inf inf]; - -ax2=subplot(4,1,2); -plotvalue(ax2,mean(Forces.Bforcer./reference,3),'Relative Magnetic force density','F_{Br}/F_{Br}',true,color_limits) - -ax3=subplot(4,1,3); -plotvalue(ax3,mean(Forces.inertforce./reference,3),'Relative Centrifugal force density','F_i/F_{Br}',true,color_limits) - -ax4=subplot(4,1,4); -plotvalue(ax4,mean(Forces.Pressforcer./reference,3),'Relative Pressure force density','F_{pr}/F_{Br}',true,color_limits) - -linkaxes([ax1 ax2 ax3 ax4],'xy') -sgtitle(sprintf('Relative radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) - -f.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f.PaperUnits='centimeters'; -papsize=[20 29]; -f.PaperSize=papsize; -print(f,sprintf('%s_relatforce_balance',name),'-dpdf','-fillpage') -savefig(f,sprintf('%s_relatforce_balance',name)) - -%% Axial forces -f=figure(); -ax1=subplot(3,1,1); -plotvalue(ax1,mean(Forces.Eforcez,3),'Electric force density', 'F_{Ez} [Nm^{-3}]',true); - -zlim=[-inf inf]; - -ax2=subplot(3,1,2); -plotvalue(ax2,mean(Forces.Bforcez,3),'Magnetic force density','F_{Bz} [Nm^{-3}]',true,zlim) - -ax3=subplot(3,1,3); -plotvalue(ax3,mean(Forces.Pressforcez,3),'Pressure force density','F_{pz} [Nm^{-3}]',true,zlim) - -linkaxes([ax1 ax2 ax3],'xy') -sgtitle(sprintf('Axial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) - -f.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f.PaperUnits='centimeters'; -papsize=[20 29]; -f.PaperSize=papsize; -print(f,sprintf('%sforce_balancez',name),'-dpdf','-fillpage') -savefig(f,sprintf('%sforce_balancez',name)) - -%% Forces sums -f2=figure(); -ax4=subplot(3,1,1); -totforcer=mean(Forces.Eforcer+Forces.Bforcer+Forces.inertforce+Forces.Pressforcer,3); -Forcemaxr=cat(3,mean(Forces.Eforcer,3),mean(Forces.Bforcer,3),mean(Forces.inertforce,3),mean(Forces.Pressforcer,3)); -Forcemaxr=max(Forcemaxr,[],3); -plotvalue(ax4,totforcer,'Total radial force density','F [Nm^{-3}]',true,zlims) - -ax5=subplot(3,1,2); -totforcez=mean(Forces.Eforcez+Forces.Bforcez+Forces.Pressforcez,3); -Forcemaxz=cat(3,mean(Forces.Eforcez,3),mean(Forces.Bforcez,3),mean(Forces.Pressforcez,3)); -Forcemaxz=max(Forcemaxz,[],3); -plotvalue(ax5,totforcez,'Total axial force density','F [Nm^{-3}]',true,zlims) - -ax6=subplot(3,1,3); -surface(ax6,M.zgrid,M.rgrid,mean(N,3),'edgecolor','none'); -hold on; -zlevel=interp2(M.zgrid, M.rgrid, mean(N,3), densitycontour(1,2:end), densitycontour(2,2:end)); -plot3(ax6,densitycontour(1,2:end),densitycontour(2,2:end),zlevel,'-.','linewidth',2,'color',contourcolor) -xlim(ax6,[M.zgrid(1) M.zgrid(end)]) -ylim(ax6,[M.rgrid(1) M.rgrid(rgridmax)]) -colormap(ax6,'parula') -xlabel(ax6,'z [m]') -ylabel(ax6,'r [m]') -title(ax6,'Density') -c = colorbar(ax6); -c.Label.String= 'n[m^{-3}]'; -view(ax6,2) - -sgtitle(sprintf('Forces sum t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -f2.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f2.PaperUnits='centimeters'; -papsize=[20 29]; -f2.PaperSize=papsize; -print(f2,sprintf('%sforce_dens',name),'-dpdf','-fillpage') -savefig(f2,sprintf('%sforce_dens',name)) - - -%% Continuity -if( ~isempty(Continuity)) -f3=figure(); -t=M.t2d(it); - -ax7=subplot(4,1,1); -plotvalue(ax7,mean(Continuity.dndt,3),'\partialn/\partialt','\partialn/\partialt [m^{-3}s^{-1}]',true) - - -zlim=[max([min(min(mean(Continuity.ndivu(2:end,:,:),3))),min(min(mean(Continuity.ugradn(2:end,:,:),3)))]) min([max(max(mean(Continuity.ndivu(2:end,:,:),3))),max(max(mean(Continuity.ugradn(2:end,:,:),3)))])]; - -ax8=subplot(4,1,2); -plotvalue(ax8,mean(Continuity.ndivu,3),'n\nabla(u)','n\nabla(u)[m^{-3}s^{-1}]',true,zlim) - -ax9=subplot(4,1,3); -plotvalue(ax9,mean(Continuity.ugradn,3),'u\nabla(n)','u\nabla(n)[m^{-3}s^{-1}]',true,zlim) - -ax10=subplot(4,1,4); -plotvalue(ax10,mean(Continuity.ndivu+Continuity.ugradn+Continuity.dndt,3),'Total','total[m^{-3}s^{-1}]',true,zlim) - -sgtitle(sprintf('Continuity t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -f3.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f3.PaperUnits='centimeters'; -papsize=[20 19]; -f3.PaperSize=papsize; -print(f3,sprintf('%scontinuity',name),'-dpdf','-fillpage') -savefig(f,sprintf('%scontinuity',name)) -end - - function plotvalue(axis,matrix,titl, clab, rm1strow, zlims) - if nargin < 5 - rm1strow=false; - end - if nargin>5 - zmin=zlims(1); - zmax=zlims(2); - else - zmin=-inf; - zmax=inf; - end - if rm1strow - if log - h=surface(axis,M.zgrid,M.rgrid(12:end),abs(matrix(12:end,:))); - else - h=surface(axis,M.zgrid,M.rgrid(12:end),matrix(12:end,:)); - end - else - if log - h=surface(axis,M.zgrid,M.rgrid,abs(matrix)); - else - h=surface(axis,M.zgrid,M.rgrid,matrix); - end - end - set(h,'edgecolor','none'); - hold on; - if log - zpos=interp2(M.zgrid, M.rgrid, abs(matrix), densitycontour(1,2:end), densitycontour(2,2:end)); - else - zpos=interp2(M.zgrid, M.rgrid, matrix, densitycontour(1,2:end), densitycontour(2,2:end)); - end - plot3(axis,densitycontour(1,2:end),densitycontour(2,2:end),zpos,'-.','linewidth',2,'color',contourcolor) - xlabel(axis,'z [m]') - ylabel(axis,'r [m]') - xlim(axis,[M.zgrid(1) M.zgrid(end)]) - ylim(axis,[M.rgrid(1) M.rgrid(rgridmax)]) - colormap(axis,'jet') - c = colorbar(axis); - c.Label.String=clab; - caxis(axis,[zmin zmax]) - if norm - caxis(axis,[Fminr Fmaxr]); - end - if log - set(axis,'colorscale','log') - caxis(axis,'auto') - end - title(axis,titl) - grid(axis, 'on') - view(axis,2) - end -end - +function [Forces, Continuity]=FBspline(M,Forces, Continuity, Density,norm,log,parper,contourscale, zlims, rlims, clims) +%ForceBalance Show the radial force balance +% Plot the three radial forces for the given time-step it or averaged +% over the range of time steps defined in it + +it=Forces.it; +switch nargin + case 4 + parper=false; + contourscale=0.6; + norm=false; + log=false; + zlims=[-inf inf]; + case 5 + parper=false; + log=false; + contourscale=0.6; + zlims=[-inf inf]; + case 6 + parper=false; + contourscale=0.6; + zlims=[-inf inf]; + case 7 + contourscale=0.6; + zlims=[-inf inf]; + case 8 + zlims=[-inf inf]; + case 9 + rlims=[]; + case 10 +end +if nargin<10 + rlims=[]; +end + +if nargin<11 + climsr=[-230000 190000]; + climsz=[-15000 13000]; + climsthet=[-1100 1800]; +else + climsr=clims(1,:); + climsthet=clims(2,:); + climsz=clims(3,:); +end + +contourcolor=[0 0 0];%[255 20 147]/255; +N=Density.N; +n=mean(N,3); + +maxdens=max(N(:)); + +densitycontour=contourc(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); + +geometriccontour=contourc(M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0]); + +Fmaxr=max([max(Forces.Eforcer(:)),max(Forces.Bforcer(:)),max(Forces.inertforcer(:)),max(Forces.Pressforcer(:))]); +Fmaxz=max([max(Forces.Eforcez(:)),max(Forces.Bforcez(:)),max(Forces.Pressforcez(:)),max(Forces.inertforcez(:))]); +FEr=mean(Forces.Eforcer,3); +FBr=mean(Forces.Bforcer,3); +FPr=mean(Forces.Pressforcer,3); +FIr=mean(Forces.inertforcer,3); +Fdr=mean(Forces.Dragforcer,3); + +FBthet=mean(Forces.Bforcethet,3); +%FPthet=zeros(size(FBthet));%mean(Forces.Pressforcethet,3); +FPthet=mean(Forces.Pressforcethet,3); +FIthet=mean(Forces.inertforcethet,3); +Fdthet=mean(Forces.Dragforcethet,3); + +FEz=mean(Forces.Eforcez,3); +FBz=mean(Forces.Bforcez,3); +FPz=mean(Forces.Pressforcez,3); +FIz=mean(Forces.inertforcez,3); +Fdz=mean(Forces.Dragforcez,3); + +durdt=mean(Forces.durdt,3); +duthetdt=mean(Forces.duthetdt,3); +duzdt=mean(Forces.duzdt,3); + +totforcer=(FEr+FBr+FPr+FIr+Fdr-durdt); +totforcethet=(FBthet+FPthet+FIthet+Fdthet-duthetdt); +totforcez=(FEz+FBz+FPz+FIz+Fdz-duzdt); +if log + Er=Forces.Eforcer(Forces.Eforcer~=0); + Br=Forces.Bforcer(Forces.Bforcer~=0); + inertr=Forces.inertforcer(Forces.inertforcer~=0); + pressr=Forces.Pressforcer(Forces.Pressforcer~=0); + Fminr=min([min(abs(Er(:))),min(abs(Br(:))),min(abs(inertr(:))),min(abs(pressr(:)))]); +else + Fminr=min([min(Forces.Eforcer(:)),min(Forces.Bforcer(:)),min(Forces.inertforcer(:)),min(Forces.Pressforcer(:))]); +end +rgridmax=sum(M.nnr(1:2)); + +if parper + costhet=(M.Br./M.B)'; + sinthet=(M.Bz./M.B)'; + FErd=FEr.*sinthet-FEz.*costhet; + FBrd=FBr.*sinthet-FBz.*costhet; + FPrd=FPr.*sinthet-FPz.*costhet; + FIrd=FIr.*sinthet-FIz.*costhet; + Fdrd=Fdr.*sinthet-Fdz.*costhet; + durd=durdt.*sinthet-duzdt.*costhet; + + FBthetd=FBthet; + FPthetd=FPthet; + FIthetd=FIthet; + Fdthetd=Fdthet; + duthetd=duthetdt; + + FEzd=FEr.*costhet+FEz.*sinthet; + FBzd=FBr.*costhet+FBz.*sinthet; + FPzd=FPr.*costhet+FPz.*sinthet; + FIzd=FIr.*costhet+FIz.*sinthet; + Fdzd=Fdr.*costhet+Fdz.*sinthet; + duzd=durdt.*costhet+duzdt.*sinthet; + + totforcerd=totforcer.*sinthet-totforcez.*costhet; + totforcethetd=totforcethet; + totforcezd=totforcer.*costhet+totforcez.*sinthet; +else + FErd=FEr; + FBrd=FBr; + FPrd=FPr; + FIrd=FIr; + Fdrd=Fdr; + durd=durdt; + + FBthetd=FBthet; + FPthetd=FPthet; + FIthetd=FIthet; + Fdthetd=Fdthet; + duthetd=duthetdt; + + FEzd=FEz; + FBzd=FBz; + FPzd=FPz; + FIzd=FIz; + Fdzd=Fdz; + duzd=duzdt; + + totforcerd=totforcer; + totforcethetd=totforcethet; + totforcezd=totforcez; +end + + + +%% Radial forces +f=figure('Name','radial'); + + +ax0=subplot(4,2,1); +plotvalue(ax0,durd,'Mass flux time derivative', 'mn\partialu_{r}/\partialt [Nm^{-3}]',true,climsr,zlims,rlims); + +ax1=subplot(4,2,2); +plotvalue(ax1,FErd,'Electric force density', 'F_{E,r} [Nm^{-3}]',true,climsr,zlims,rlims); + +ax2=subplot(4,2,3); +plotvalue(ax2,FBrd,'Magnetic force density','F_{B,r} [Nm^{-3}]',true,climsr,zlims,rlims); + +ax3=subplot(4,2,4); +plotvalue(ax3,FIrd,'Inertial force density','F_{i,r} [Nm^{-3}]',true,climsr,zlims,rlims); + +ax4=subplot(4,2,5); +plotvalue(ax4,FPrd,'Pressure force density','F_{p,r} [Nm^{-3}]',true,climsr,zlims,rlims); + +ax5=subplot(4,2,6); +plotvalue(ax5,Fdrd,'Drag force density','F_{d,r} [Nm^{-3}]',true,climsr,zlims,rlims); + +ax6=subplot(4,2,[7,8]); +plotvalue(ax6,totforcerd,'Tot force density','F_{tot,r} [Nm^{-3}]',true,climsr,zlims,rlims); + + +linkaxes([ax0 ax1 ax2 ax3 ax4 ax5 ax6],'xy') +if parper + sgtitle(f,sprintf('Perp forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +else +sgtitle(f,sprintf('Radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +end +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +papsize=[20 29]; +f.PaperSize=papsize; +print(f,sprintf('%sforce_balancer_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f,sprintf('%sforce_balancer_%i',name,floor(mean(it)))) + +%% Radial relative forces +f=figure('Name','radial relative'); + +color_limits=[-1 1]*2; +referencer=FErd; + +ax1=subplot(3,2,1); +plotvalue(ax1,FErd./referencer,'Relative Electric force density', 'F_{Er}/F_{Er}',true,color_limits,zlims,rlims); + +%zlim=[Fmin Fmax]; +zlim=[-inf inf]; + +ax2=subplot(3,2,2); +plotvalue(ax2,FBrd./referencer,'Relative Magnetic force density','F_{Br}/F_{Er}',true,color_limits,zlims,rlims); + +ax3=subplot(3,2,3); +plotvalue(ax3,FIrd./referencer,'Relative Inertial force density','F_i/F_{Er}',true,color_limits,zlims,rlims); + +ax4=subplot(3,2,4); +plotvalue(ax4,FPrd./referencer,'Relative Pressure force density','F_{pr}/F_{Er}',true,color_limits,zlims,rlims); + +ax5=subplot(3,2,5); +plotvalue(ax5,Fdrd./referencer,'Relative Drag force density','F_{dr}/F_{Er}',true,color_limits,zlims,rlims); + +ax6=subplot(3,2,6); +plotvalue(ax6,totforcerd./referencer,'Total force density','F_{totr}/F_{Er}',true,color_limits,zlims,rlims); + + +linkaxes([ax1 ax2 ax3 ax4 ax5 ax6],'xy') +if parper + sgtitle(sprintf('Relative perp forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +else +sgtitle(sprintf('Relative radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +end +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +papsize=[20 29]; +f.PaperSize=papsize; +print(f,sprintf('%s_relatforce_balancer_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f,sprintf('%s_relatforce_balancer_%i',name,floor(mean(it)))) + + +%% Azimuthal relative forces +f=figure('Name','azimuthal relative'); +color_limits=[-1 1]*2; +referencethet=FBthetd; + +ax1=subplot(3,2,1); +plotvalue(ax1,duthetd./referencethet,'Mass flux time derivartive','mn\partialu_\theta/\partialt/F_{B,\theta}',true,color_limits,zlims,rlims); + +ax2=subplot(3,2,2); +plotvalue(ax2,FBthetd./referencethet,'Magnetic force density','F_{B,\theta}/F_{B,\theta}',true,color_limits,zlims,rlims); + +ax3=subplot(3,2,3); +plotvalue(ax3,FIthetd./referencethet,'Inertial force density','F_{i,\theta}/F_{B,\theta}',true,color_limits,zlims,rlims); + +ax4=subplot(3,2,4); +plotvalue(ax4,FPthetd./referencethet,'Pressure force density','F_{p,\theta}/F_{B,\theta}',true,color_limits,zlims,rlims); + +ax5=subplot(3,2,5); +plotvalue(ax5,Fdthetd./referencethet,'Drag force density','F_{d,\theta}/F_{B,\theta}',true,color_limits,zlims,rlims); + +ax6=subplot(3,2,6); +plotvalue(ax6,totforcethetd./referencethet,'Total force density','F_{tot,\theta}/F_{B,\theta}',true,color_limits,zlims,rlims); + + + +linkaxes([ax1 ax2 ax3 ax4 ax5 ax6],'xy') + +sgtitle(sprintf('Relative azimuthal forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +papsize=[20 29]; +f.PaperSize=papsize; +print(f,sprintf('%srelatforce_balancethet_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f,sprintf('%srelatforce_balancethet_%i',name,floor(mean(it)))) + + +%% Azimuthal forces +f=figure('Name','azimuthal'); + + + +ax1=subplot(3,2,1); +plotvalue(ax1,duthetd,'Mass flux time derivartive','mn\partialu_\theta/\partialt [Nm^{-3}]',true,climsthet,zlims,rlims); + +ax2=subplot(3,2,2); +plotvalue(ax2,FBthetd,'Magnetic force density','F_{B,\theta} [Nm^{-3}]',true,climsthet,zlims,rlims); + +ax3=subplot(3,2,3); +plotvalue(ax3,FIthetd,'Inertial force density','F_{i,\theta} [Nm^{-3}]',true,climsthet,zlims,rlims); + +ax4=subplot(3,2,4); +plotvalue(ax4,FPthetd,'Pressure force density','F_{p,\theta} [Nm^{-3}]',true,climsthet,zlims,rlims); + +ax5=subplot(3,2,5); +plotvalue(ax5,Fdthetd,'Drag force density','F_{d,\theta} [Nm^{-3}]',true,climsthet,zlims,rlims); + +ax6=subplot(3,2,6); +plotvalue(ax6,totforcethetd,'Total force density','F_{tot,\theta} [Nm^{-3}]',true,climsthet,zlims,rlims); + + +linkaxes([ax1 ax2 ax3 ax4 ax5 ax6],'xy') + +sgtitle(sprintf('Azimuthal forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +papsize=[20 29]; +f.PaperSize=papsize; +print(f,sprintf('%sforce_balancethet_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f,sprintf('%sforce_balancethet_%i',name,floor(mean(it)))) + +%% Axial forces +f=figure('Name','axial'); +%zlim=[Fmin Fmax]; + +%zlim=[-15 15]; + +ax0=subplot(4,2,1); +plotvalue(ax0,duzd,'Mass flux time derivartive','mn\partialu_z/\partialt [Nm^{-3}]',true,climsz,zlims,rlims); + +ax1=subplot(4,2,2); +plotvalue(ax1,FEzd,'Electric force density', 'F_{E,z} [Nm^{-3}]',true,climsz,zlims,rlims); + +ax2=subplot(4,2,3); +plotvalue(ax2,FBzd,'Magnetic force density','F_{B,z} [Nm^{-3}]',true,climsz,zlims,rlims); + +ax3=subplot(4,2,4); +plotvalue(ax3,FIzd,'Inertial force density','F_{i,z} [Nm^{-3}]',true,climsz,zlims,rlims); + +ax4=subplot(4,2,5); +plotvalue(ax4,FPzd,'Pressure force density','F_{p,z} [Nm^{-3}]',true,climsz,zlims,rlims); + +ax5=subplot(4,2,6); +plotvalue(ax5,Fdzd,'Drag force density','F_{d,z} [Nm^{-3}]',true,climsz,zlims,rlims); + +ax6=subplot(4,2,[7,8]); +plotvalue(ax6,totforcezd,'Tot force density','F_{tot,z} [Nm^{-3}]',true,climsz,zlims,rlims); + +linkaxes([ax0 ax1 ax2 ax3 ax4 ax5 ax6],'xy') + +if parper + sgtitle(sprintf('Parallel forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +else +sgtitle(sprintf('Axial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +end + +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +papsize=[20 29]; +f.PaperSize=papsize; +print(f,sprintf('%sforce_balancez_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f,sprintf('%sforce_balancez_%i',name,floor(mean(it)))) + +%% Axial relative forces +f=figure('Name','axial relative'); +ax1=subplot(3,2,1); +color_limits=[-1 1]*2; +referencez=FEzd; +plotvalue(ax1,FEzd./referencez,'Relative Electric force density', 'F_{Ez}/F_{Ez}',true,color_limits,zlims,rlims); + +%zlim=[Fmin Fmax]; +zlim=[-inf inf]; + +ax2=subplot(3,2,2); +plotvalue(ax2,FBzd./referencez,'Relative Magnetic force density','F_{Bz}/F_{Ez}',true,color_limits,zlims,rlims); + +ax3=subplot(3,2,3); +plotvalue(ax3,FIzd./referencez,'Relative Inertial force density','F_{Iz}/F_{Ez}',true,color_limits,zlims,rlims); + +ax4=subplot(3,2,4); +plotvalue(ax4,FPzd./referencez,'Relative Pressure force density','F_{Pz}/F_{Ez}',true,color_limits,zlims,rlims); + +ax5=subplot(3,2,5); +plotvalue(ax5,Fdzd./referencez,'Relative Drag force density','F_{dz}/F_{Ez}',true,color_limits,zlims,rlims); + +ax6=subplot(3,2,6); +plotvalue(ax6,totforcezd./referencez,'Total force density','F_{totz}/F_{Ez}',true,color_limits,zlims,rlims); + + +linkaxes([ax1 ax2 ax3 ax4 ax5 ax6],'xy') + +if parper + sgtitle(sprintf('Relative Parallel forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +else +sgtitle(sprintf('Relative axial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +end +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +papsize=[20 29]; +f.PaperSize=papsize; +print(f,sprintf('%s_relatforce_balancez_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f,sprintf('%s_relatforce_balancez_%i',name,floor(mean(it)))) + +%% Forces sums +f2=figure('Name','total'); +ax4=subplot(4,1,1); +maxforcerd=1;%cat(3,FErd,FBrd,FPrd,FIrd); +%maxforcerd=max(maxforcerd,[],3); +if parper +plotvalue(ax4,totforcerd./maxforcerd,'Total Perp force density','F [Nm^{-3}]',true,climsr,zlims,rlims); +else + plotvalue(ax4,totforcerd./maxforcerd,'Total radial force density','F [Nm^{-3}]',true,climsr,zlims,rlims); +end + +ax5=subplot(4,1,2); +maxforcethetd=1;%cat(3,FEzd,FBzd,FPzd,FIzd); +%maxforcezd=max(maxforcezd,[],3); +%zlims=[-15 15]; +plotvalue(ax5,totforcethetd./maxforcethetd,'Total azimuthal force density','F [Nm^{-3}]',true,climsthet,zlims,rlims); + + +ax6=subplot(4,1,3); +maxforcezd=1;%cat(3,FEzd,FBzd,FPzd,FIzd); +%maxforcezd=max(maxforcezd,[],3); +%zlims=[-15 15]; +if parper + plotvalue(ax6,totforcezd./maxforcezd,'Total par force density','F [Nm^{-3}]',true,climsz,zlims,rlims); +else + plotvalue(ax6,totforcezd./maxforcezd,'Total axial force density','F [Nm^{-3}]',true,climsz,zlims,rlims); +end +ax7=subplot(4,1,4); +surface(ax7,M.zgrid*1e3,M.rgrid*1e3,mean(N,3),'edgecolor','none'); +hold on; +zlevel=interp2(M.zgrid*1e3, M.rgrid*1e3, mean(N,3), densitycontour(1,2:end)*1e3, densitycontour(2,2:end)*1e3); +plot3(ax7,densitycontour(1,2:end)*1e3,densitycontour(2,2:end)*1e3,zlevel,'-.','linewidth',2,'color',contourcolor) +leng=size(geometriccontour,2); +strt=1; +while leng>1 + span=geometriccontour(2,strt); + plot(ax7,geometriccontour(1,strt+1:strt+span)*1e3,geometriccontour(2,strt+1:strt+span)*1e3,'--','linewidth',2,'color','red') + leng=leng-span-1; + strt=strt+span+1; +end + + +colormap(ax7,'parula') +xlabel(ax7,'z [m]') +ylabel(ax7,'r [m]') +title(ax7,'Density') +c = colorbar(ax7); +c.Label.String= 'n[m^{-3}]'; +view(ax7,2) +linkaxes([ax4 ax5 ax6 ax7]) + +if (nargin>8 && ~isempty(zlims)) + xlim(ax7,zlims*1e3) +else + xlim(ax7,[M.zgrid(1) M.zgrid(end)]*1e3) +end +if (nargin>9 && ~isempty(rlims)) + ylim(ax7,rlims*1e3) +else + if M.conformgeom + ylim(ax7,[M.rgrid(1) M.rgrid(rgridmax)]*1e3) + else + ylim(ax7,[M.rgrid(1) M.rgrid(end)]*1e3) + end +end + +sgtitle(sprintf('Forces sum t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +f2.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f2.PaperUnits='centimeters'; +papsize=[20 29]; +f2.PaperSize=papsize; +print(f2,sprintf('%sforce_dens_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f2,sprintf('%sforce_dens_%i',name,floor(mean(it)))) + + +%% Continuity +if( ~isempty(Continuity)) +f3=figure('Name','Continuity'); +it=Continuity.it; +t=M.t2d(it); + +ax7=subplot(4,1,1); +plotvalue(ax7,mean(Continuity.dndt,3),'\partialn/\partialt','\partialn/\partialt [m^{-3}s^{-1}]',true) + + +zlim=[max([min(min(mean(Continuity.ndivu(2:end,:,:),3))),min(min(mean(Continuity.ugradn(2:end,:,:),3)))]) min([max(max(mean(Continuity.ndivu(2:end,:,:),3))),max(max(mean(Continuity.ugradn(2:end,:,:),3)))])]; + +ax8=subplot(4,1,2); +plotvalue(ax8,mean(sum(Continuity.ndivu,4),3),'n\nabla(u)','n\nabla(u)[m^{-3}s^{-1}]',true,zlim) + +ax9=subplot(4,1,3); +plotvalue(ax9,mean(sum(Continuity.ugradn,4),3),'u\nabla(n)','u\nabla(n)[m^{-3}s^{-1}]',true,zlim) + +ax10=subplot(4,1,4); +plotvalue(ax10,mean(sum(Continuity.ndivu,4)+sum(Continuity.ugradn,4)+Continuity.dndt,3),'Total','total[m^{-3}s^{-1}]',true,zlim) + +sgtitle(sprintf('Continuity t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3} Ns=%d',M.t2d(min(it)),M.t2d(max(it)),double(maxdens),length(it))) +f3.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f3.PaperUnits='centimeters'; +papsize=[20 19]; +f3.PaperSize=papsize; +print(f3,sprintf('%scontinuity_%i',name,floor(mean(it))),'-dpdf','-fillpage') +savefig(f,sprintf('%scontinuity_%i',name,floor(mean(it)))) +end + + function plotvalue(axis,matrix,titl, clab, rm1strow, clims, zlims, rlims) + if nargin < 5 + rm1strow=false; + end + if nargin>5 + cmin=clims(1); + cmax=clims(2); + else + cmin=-inf; + cmax=inf; + end + matrix(M.geomweight(:,:,1)<=0)=NaN; + if rm1strow + if log + h=surface(axis,M.zgrid*1e3,M.rgrid(1:end)*1e3,abs(matrix(1:end,:))); + else + h=surface(axis,M.zgrid*1e3,M.rgrid(1:end)*1e3,matrix(1:end,:)); + end + else + if log + h=surface(axis,M.zgrid*1e3,M.rgrid*1e3,abs(matrix)); + else + h=surface(axis,M.zgrid*1e3,M.rgrid*1e3,matrix); + end + end + set(h,'edgecolor','none'); + hold on; + if log + zpos=interp2(M.zgrid*1e3, M.rgrid*1e3, abs(matrix), densitycontour(1,2:end)*1e3, densitycontour(2,2:end)*1e3); + else + zpos=interp2(M.zgrid*1e3, M.rgrid*1e3, matrix, densitycontour(1,2:end)*1e3, densitycontour(2,2:end)*1e3); + end + plot3(axis,densitycontour(1,2:end)*1e3,densitycontour(2,2:end)*1e3,zpos,'-.','linewidth',2,'color',contourcolor) + + + if log + zpos=interp2(M.zgrid*1e3, M.rgrid*1e3, abs(matrix), geometriccontour(1,2:end)*1e3, geometriccontour(2,2:end)*1e3); + else + zpos=interp2(M.zgrid*1e3, M.rgrid*1e3, matrix, geometriccontour(1,2:end)*1e3, geometriccontour(2,2:end)*1e3); + end + leng=size(geometriccontour,2); + strt=1; + while leng>1 + span=geometriccontour(2,strt); + plot(axis,geometriccontour(1,strt+1:strt+span)*1e3,geometriccontour(2,strt+1:strt+span)*1e3,'--','linewidth',2,'color','red') + leng=leng-span-1; + strt=strt+span+1; + end + xlabel(axis,'z [mm]') + ylabel(axis,'r [mm]') + if (nargin>6 && ~isempty(zlims)) + xlim(axis,zlims*1e3) + else + xlim(axis,[M.zgrid(1) M.zgrid(end)]*1e3) + end + if (nargin>7 && ~isempty(rlims)) + ylim(axis,rlims*1e3) + else + if M.conformgeom + ylim(axis,[M.rgrid(1) M.rgrid(rgridmax)]*1e3) + else + ylim(axis,[M.rgrid(1) M.rgrid(end)]*1e3) + end + end + + colormap(axis,'jet') + c = colorbar(axis); + c.Label.String=clab; + caxis(axis,[cmin cmax]) + if norm + caxis(axis,[Fminr Fmaxr]); + end + if log + set(axis,'colorscale','log') + caxis(axis,'auto') + end + title(axis,titl) + grid(axis, 'on') + view(axis,2) + end +end + diff --git a/matlab/FBspline_article.m b/matlab/FBspline_article.m new file mode 100644 index 0000000..1578b46 --- /dev/null +++ b/matlab/FBspline_article.m @@ -0,0 +1,330 @@ +function [Forces, Continuity]=FBspline_article(M,Forces, Continuity, Density,norm,log,parper,contourscale, zlims, rlims, clims) +%ForceBalance Show the radial force balance +% Plot the three radial forces for the given time-step it or averaged +% over the range of time steps defined in it + +it=Forces.it; +switch nargin + case 4 + parper=false; + contourscale=0.2; + norm=false; + log=false; + zlims=[-inf inf]; + case 5 + parper=false; + log=false; + contourscale=0.2; + zlims=[-inf inf]; + case 6 + parper=false; + contourscale=0.2; + zlims=[-inf inf]; + case 7 + contourscale=0.2; + zlims=[-inf inf]; + case 8 + zlims=[-inf inf]; + case 9 + rlims=[]; + case 10 +end +if nargin<10 + rlims=[]; +end + +if nargin<11 + climsr=[-230000 190000]; + climsz=[-15000 13000]; + climsthet=[-1100 1800]; +else + climsr=clims(1,:); + climsthet=clims(2,:); + climsz=clims(3,:); +end + +folder='Article_Forces'; + +[status, msg, msgID] = mkdir(folder); + +contourcolor=[0 0 0];%[255 20 147]/255; +N=Density.N; +n=mean(N,3); + +maxdens=max(N(:)); + +densitycontour=contourc(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); + +geometriccontour=contourc(M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0]); + +Fmaxr=max([max(Forces.Eforcer(:)),max(Forces.Bforcer(:)),max(Forces.inertforcer(:)),max(Forces.Pressforcer(:))]); +Fmaxz=max([max(Forces.Eforcez(:)),max(Forces.Bforcez(:)),max(Forces.Pressforcez(:)),max(Forces.inertforcez(:))]); +FEr=mean(Forces.Eforcer,3); +FBr=mean(Forces.Bforcer,3); +FPr=mean(Forces.Pressforcer,3); +FIr=mean(Forces.inertforcer,3); +Fdr=mean(Forces.Dragforcer,3); + +FBthet=mean(Forces.Bforcethet,3); +%FPthet=zeros(size(FBthet));%mean(Forces.Pressforcethet,3); +FPthet=mean(Forces.Pressforcethet,3); +FIthet=mean(Forces.inertforcethet,3); +Fdthet=mean(Forces.Dragforcethet,3); + +FEz=mean(Forces.Eforcez,3); +FBz=mean(Forces.Bforcez,3); +FPz=mean(Forces.Pressforcez,3); +FIz=mean(Forces.inertforcez,3); +Fdz=mean(Forces.Dragforcez,3); + +durdt=mean(Forces.durdt,3); +duthetdt=mean(Forces.duthetdt,3); +duzdt=mean(Forces.duzdt,3); + +totforcer=(FEr+FBr+FPr+FIr+Fdr-durdt); +totforcethet=(FBthet+FPthet+FIthet+Fdthet-duthetdt); +totforcez=(FEz+FBz+FPz+FIz+Fdz-duzdt); +if log + Er=Forces.Eforcer(Forces.Eforcer~=0); + Br=Forces.Bforcer(Forces.Bforcer~=0); + inertr=Forces.inertforcer(Forces.inertforcer~=0); + pressr=Forces.Pressforcer(Forces.Pressforcer~=0); + Fminr=min([min(abs(Er(:))),min(abs(Br(:))),min(abs(inertr(:))),min(abs(pressr(:)))]); +else + Fminr=min([min(Forces.Eforcer(:)),min(Forces.Bforcer(:)),min(Forces.inertforcer(:)),min(Forces.Pressforcer(:))]); +end +rgridmax=sum(M.nnr(1:2)); + +if parper + costhet=(M.Br./M.B)'; + sinthet=(M.Bz./M.B)'; + FErd=FEr.*sinthet-FEz.*costhet; + FBrd=FBr.*sinthet-FBz.*costhet; + FPrd=FPr.*sinthet-FPz.*costhet; + FIrd=FIr.*sinthet-FIz.*costhet; + Fdrd=Fdr.*sinthet-Fdz.*costhet; + durd=durdt.*sinthet-duzdt.*costhet; + + FBthetd=FBthet; + FPthetd=FPthet; + FIthetd=FIthet; + Fdthetd=Fdthet; + duthetd=duthetdt; + + FEzd=FEr.*costhet+FEz.*sinthet; + FBzd=FBr.*costhet+FBz.*sinthet; + FPzd=FPr.*costhet+FPz.*sinthet; + FIzd=FIr.*costhet+FIz.*sinthet; + Fdzd=Fdr.*costhet+Fdz.*sinthet; + duzd=durdt.*costhet+duzdt.*sinthet; + + totforcerd=totforcer.*sinthet-totforcez.*costhet; + totforcethetd=totforcethet; + totforcezd=totforcer.*costhet+totforcez.*sinthet; +else + FErd=FEr; + FBrd=FBr; + FPrd=FPr; + FIrd=FIr; + Fdrd=Fdr; + durd=durdt; + + FBthetd=FBthet; + FPthetd=FPthet; + FIthetd=FIthet; + Fdthetd=Fdthet; + duthetd=duthetdt; + + FEzd=FEz; + FBzd=FBz; + FPzd=FPz; + FIzd=FIz; + Fdzd=Fdz; + duzd=duzdt; + + totforcerd=totforcer; + totforcethetd=totforcethet; + totforcezd=totforcez; +end + +papsize=[12 17]; + +%% Radial forces +f=figure('Name','radial'); + + +ax0=subplot(3,2,1); +plotvalue(ax0,durd,'mn\partial_tu_{r}', 'F_{a,r} [N/m^3]',true,climsr,zlims,rlims); + +ax1=subplot(3,2,2); +plotvalue(ax1,FErd,'qnE_r', 'F_{E,r} [N/m^3]',true,climsr,zlims,rlims); + +ax2=subplot(3,2,3); +plotvalue(ax2,FBrd,'qn(u_\thetaB_z)','F_{B,r} [N/m^3]',true,climsr,zlims,rlims); + +ax3=subplot(3,2,4); +plotvalue(ax3,FIrd,'-mn(\bf{u\bullet\nabla})u_r','F_{i,r} [N/m^3]',true,climsr,zlims,rlims); + +ax4=subplot(3,2,5); +plotvalue(ax4,FPrd,'-(\nablaP)_r','F_{p,r} [N/m^3]',true,climsr,zlims,rlims); + +ax5=subplot(3,2,6); +plotvalue(ax5,Fdrd,'-mnn_n<\sigma_d v>u_r','F_{d,r} [N/m^3]',true,climsr,zlims,rlims); + + +linkaxes([ax0 ax1 ax2 ax3 ax4 ax5],'xy') + + +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; + + +M.savegraph(f,sprintf('%s/%sforce_balancer_%i_art',folder,name,floor(mean(it))),papsize) + + +%% Azimuthal forces +f=figure('Name','azimuthal'); + + +ax0=subplot(3,2,1); +plotvalue(ax0,duzd,'mn\partial_tu_{\theta}','F_{a,\theta} [N/m^3]',true,climsthet,zlims,rlims); + +ax1=subplot(3,2, 2); +plotvalue(ax1,ones(size(FEzd)),'qnE_\theta', 'F_{E,\theta} [N/m^3]',true,climsthet,zlims,rlims); + +ax2=subplot(3,2,3); +plotvalue(ax2,FBthetd,'qn(u_zB_r-u_rB_z)','F_{B,\theta} [N/m^3]',true,climsthet,zlims,rlims); + +ax3=subplot(3,2,4); +plotvalue(ax3,FIthetd,'-mn(\bf{u\bullet\nabla})u_\theta','F_{i,\theta} [N/m^3]',true,climsthet,zlims,rlims); + +ax4=subplot(3,2,5); +plotvalue(ax4,FPthetd,'-(\nablaP)_\theta','F_{p,\theta} [N/m^3]',true,climsthet,zlims,rlims); + +ax5=subplot(3,2,6); +plotvalue(ax5,Fdthetd,'-mnn_n<\sigma_d v>u_\theta','F_{d,\theta} [N/m^3]',true,climsthet,zlims,rlims); + + + +linkaxes([ax0 ax1 ax2 ax3 ax4 ax5],'xy') + +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +f.PaperSize=papsize; + +M.savegraph(f,sprintf('%s/%sforce_balancethet_%i_art',folder,name,floor(mean(it))),papsize) + +%% Axial forces +f=figure('Name','axial'); +%zlim=[Fmin Fmax]; + +%zlim=[-15 15]; + +ax0=subplot(3,2,1); +plotvalue(ax0,duzd,'mn\partial_tu_{z}','F_{a,z} [N/m^3]',true,climsz,zlims,rlims); + +ax1=subplot(3,2,2); +plotvalue(ax1,FEzd,'qnE_z', 'F_{E,z} [N/m^3]',true,climsz,zlims,rlims); + +ax2=subplot(3,2,3); +plotvalue(ax2,FBzd,'-qn(u_\thetaB_r)','F_{B,z} [N/m^3]',true,climsz,zlims,rlims); + +ax3=subplot(3,2,4); +plotvalue(ax3,FIzd,'-mn(\bf{u\bullet\nabla})u_z','F_{i,z} [N/m^3]',true,climsz,zlims,rlims); + +ax4=subplot(3,2,5); +plotvalue(ax4,FPzd,'-(\nablaP)_z','F_{p,z} [N/m^3]',true,climsz,zlims,rlims); + +ax5=subplot(3,2,6); +plotvalue(ax5,Fdzd,'-mnn_n<\sigma_d v>u_z','F_{d,z} [N/m^3]',true,climsz,zlims,rlims); + +linkaxes([ax0 ax1 ax2 ax3 ax4 ax5],'xy') + +f.PaperOrientation='portrait'; +[~, name, ~] = fileparts(M.file); +f.PaperUnits='centimeters'; +f.PaperSize=papsize; + +M.savegraph(f,sprintf('%s/%sforce_balancez_%i_art',folder,name,floor(mean(it))),papsize) + + + function plotvalue(ax,matrix,titl, clab, rm1strow, clims, zlims, rlims) + if nargin < 5 + rm1strow=false; + end + if nargin>5 + cmin=clims(1); + cmax=clims(2); + else + cmin=-inf; + cmax=inf; + end + matrix(M.geomweight(:,:,1)<=0)=NaN; + if rm1strow + if log + h=contourf(ax,M.zgrid*1e3,M.rgrid(1:end)*1e3,abs(matrix(1:end,:)),30,'linecolor','none'); + else + h=contourf(ax,M.zgrid*1e3,M.rgrid(1:end)*1e3,matrix(1:end,:),30,'linecolor','none'); + end + else + if log + h=contourf(ax,M.zgrid*1e3,M.rgrid*1e3,abs(matrix),30,'linecolor','none'); + else + h=contourf(ax,M.zgrid*1e3,M.rgrid*1e3,matrix,30,'linecolor','none'); + end + end + %set(h,'edgecolor','none'); + hold on; + if log + zpos=interp2(M.zgrid*1e3, M.rgrid*1e3, abs(matrix), densitycontour(1,2:end)*1e3, densitycontour(2,2:end)*1e3); + else + zpos=interp2(M.zgrid*1e3, M.rgrid*1e3, matrix, densitycontour(1,2:end)*1e3, densitycontour(2,2:end)*1e3); + end + %plot3(ax,densitycontour(1,2:end)*1e3,densitycontour(2,2:end)*1e3,zpos,'-.','linewidth',2,'color',contourcolor) + contour(M.zgrid*1e3,M.rgrid*1e3,mean(N,3),[contourscale contourscale]*maxdens,'-.','linewidth',2,'color',contourcolor); + + contour(M.zgrid*1e3,M.rgrid*1e3,M.geomweight(:,:,1),[0 0],'--','linewidth',2,'color','red'); + + %plot(ax,geometriccontour(1,2:end)*1e3,geometriccontour(2,2:end)*1e3,'--','linewidth',2,'color','red') + xlabel(ax,'z [mm]') + ylabel(ax,'r [mm]') + + if (nargin>6 && ~isempty(zlims)) + xlim(ax,zlims*1e3) + else + xlim(ax,[M.zgrid(1) M.zgrid(end)]*1e3) + end + if (nargin>7 && ~isempty(rlims)) + ylim(ax,rlims*1e3) + else + if M.conformgeom + ylim(ax,[M.rgrid(1) M.rgrid(rgridmax)]*1e3) + else + ylim(ax,[M.rgrid(1) M.rgrid(end)]*1e3) + end + end + + colormap(ax,'jet') + c = colorbar(ax); + c.Label.String='[N/m^{3}]'; + caxis(ax,[cmin cmax]) + if norm + caxis(ax,[Fminr Fmaxr]); + end + if log + set(ax,'colorscale','log') + caxis(ax,'auto') + end + title(ax,clab(1:end-8)) + grid(ax, 'on') + view(ax,2) +% pos=ax.Position; +% pos(3)=0.98*pos(3); +% pos(4)=0.95*pos(4); +% ax.Position=pos; + + end +end + diff --git a/matlab/ForceBalance.m b/matlab/ForceBalance.m deleted file mode 100644 index 9793f4b..0000000 --- a/matlab/ForceBalance.m +++ /dev/null @@ -1,285 +0,0 @@ -function ForceBalance(M,it,norm,log,contourscale, zlims) -%ForceBalance Show the radial force balance -% Plot the three radial forces for the given time-step it or averaged -% over the range of time steps defined in it - -if it==':' - it=1:size(M.t2d); -end -switch nargin - case 2 - contourscale=0.6; - norm=false; - log=false; - zlims=[-inf inf]; - case 3 - log=false; - contourscale=0.6; - zlims=[-inf inf]; - case 4 - contourscale=0.6; - zlims=[-inf inf]; - case 5 - zlims=[-inf inf]; - case 6 - - otherwise - error("Invalid number of arguments") -end -r=(M.rgrid(1:end-1)+M.rgrid(2:end))/2; -z=(M.zgrid(1:end-1)+M.zgrid(2:end))/2; -[Zc,Rc]=meshgrid(z,r); -[Z,R]=meshgrid(M.zgrid,M.rgrid); - -contourcolor=[0 0 0];%[255 20 147]/255; - -avgpresstens=M.Presstens(:,1:end-1,1:end-1,it); -uThet=M.fluidUTHET(1:end-1,1:end-1,it); -Ntmp=M.N(1:end-1,1:end-1,it); -dim3=size(Ntmp,3); -presstens=zeros(6,size(M.rgrid,1),size(M.zgrid,1),dim3); -fluiduThet=zeros(size(M.rgrid,1),size(M.zgrid,1),dim3); -N=zeros(size(M.rgrid,1),size(M.zgrid,1),dim3); -for j=1:size(avgpresstens,4) - for i=1:6 - presstens(i,:,:,j)=interp2(Zc,Rc,squeeze(avgpresstens(i,:,:,j)),Z,R); - end - fluiduThet(:,:,j)=interp2(Zc,Rc,uThet(:,:,j),Z,R); - N(:,:,j)=interp2(Zc,Rc,Ntmp(:,:,j),Z,R); -end -presstens(isnan(presstens))=0; -fluiduThet(isnan(fluiduThet))=0; -N(isnan(N))=0; -dr=[(M.rgrid(3)-M.rgrid(1))/2;(M.rgrid(3:end)-M.rgrid(1:end-2))/2]; -dz=[(M.zgrid(3)-M.zgrid(1))/2;(M.zgrid(3:end)-M.zgrid(1:end-2))/2]; -[dz, dr]=meshgrid(dz,dr); - -Rinv=1./R; -%Rinv=padarray(Rinv, [1,1], 0, 'post'); -Rinv(1,:)=0; - -dpr=zeros(size(N,1)-1,size(N,2)-1,dim3); -dpz=zeros(size(N,1)-1,size(N,2)-1,dim3); -for j=1:size(presstens,4) - dpr(:,:,j)=squeeze(presstens(1,2:end,1:end-1,j)-presstens(1,1:end-1,1:end-1,j))./dr; - dpz(:,:,j)=squeeze(presstens(4,1:end-1,2:end,j)-presstens(4,1:end-1,1:end-1,j))./dz; -end -dpr=padarray(dpr, [1,1,0], 0, 'post'); -dpz=padarray(dpz, [1,1,0], 0, 'post'); -maxdens=max(N(:)); - -densitycontour=contour(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); - - -Eforce=-M.qe*N.*M.Er(:,:,it); -Bforce=zeros(size(N,1),size(N,2),size(N,3)); -inertforce=zeros(size(N,1),size(N,2),size(N,3)); -Pressforce=zeros(size(N,1),size(N,2),size(N,3)); -for j=1:size(N,3) - Bforce(:,:,j)=-M.qe*N(:,:,j).*fluiduThet(:,:,j).*M.Bz'; - inertforce(:,:,j)=M.me*N(:,:,j).*fluiduThet(:,:,j).^2.*Rinv; - Pressforce(:,:,j)=-( dpr(:,:,j)... - + squeeze(presstens(1,:,:,j)).*Rinv + squeeze(-presstens(4,:,:,j)).*Rinv... - + dpz(:,:,j) ); -end - -Fmax=max([max(Eforce(:)),max(Bforce(:)),max(inertforce(:)),max(Pressforce(:))]); -if log - E=Eforce(Eforce~=0); - B=Bforce(Bforce~=0); - inert=inertforce(inertforce~=0); - press=Pressforce(Pressforce~=0); - Fmin=min([min(abs(E(:))),min(abs(B(:))),min(abs(inert(:))),min(abs(press(:)))]); -else - Fmin=min([min(Eforce(:)),min(Bforce(:)),min(inertforce(:)),min(Pressforce(:))]); -end -rgridmax=sum(M.nnr(1:2)); -f=figure(); - -ax1=subplot(4,1,1); -plotvalue(ax1,mean(Eforce,3),'Electric force density', 'F_E [Nm^{-3}]',false); - -%zlim=[Fmin Fmax]; -zlim=[-inf inf]; - -ax2=subplot(4,1,2); -plotvalue(ax2,mean(Bforce,3),'Magnetic force density','F_B [Nm^{-3}]',false,zlim) - -ax3=subplot(4,1,3); -plotvalue(ax3,mean(inertforce,3),'Centrifugal force density','F_i [Nm^{-3}]',false,zlim) - -ax4=subplot(4,1,4); -plotvalue(ax4,mean(Pressforce,3),'Pressure force density','F_p [Nm^{-3}]',false,zlim) - -linkaxes([ax1 ax2 ax3 ax4],'xy') -sgtitle(sprintf('Radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) - -f.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f.PaperUnits='centimeters'; -papsize=[20 29]; -f.PaperSize=papsize; -print(f,sprintf('%sforce_balance',name),'-dpdf','-fillpage') - -%% Radial relative forces -f=figure(); -old_log=log; -log=true; -ax1=subplot(4,1,1); -plotvalue(ax1,mean(Eforce./Bforce,3),'Relative Electric force density', 'F_{Er}/F_{Br} [Nm^{-3}]',false); - -%zlim=[Fmin Fmax]; -zlim=[-inf inf]; - -ax2=subplot(4,1,2); -plotvalue(ax2,mean(Bforce./Bforce,3),'Relative Magnetic force density','F_{Br}/F_{Br} [Nm^{-3}]',false) - -ax3=subplot(4,1,3); -plotvalue(ax3,mean(inertforce./Bforce,3),'Relative Centrifugal force density','F_i/F_{Br} [Nm^{-3}]',false) - -ax4=subplot(4,1,4); -plotvalue(ax4,mean(Pressforce./inertforce,3),'Relative Pressure force density','F_{pr}/F_{Br} [Nm^{-3}]',false) - -linkaxes([ax1 ax2 ax3 ax4],'xy') -sgtitle(sprintf('Relative radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) - -f.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f.PaperUnits='centimeters'; -papsize=[20 29]; -f.PaperSize=papsize; -print(f,sprintf('%s_relatforce_balance',name),'-dpdf','-fillpage') -log=old_log; - -f2=figure(); -ax5=subplot(2,1,1); -totforce=mean(Eforce+Bforce+inertforce+Pressforce,3); -Forcemax=cat(3,mean(Eforce,3),mean(Bforce,3),mean(inertforce,3),mean(Pressforce,3)); -Forcemax=max(Forcemax,[],3); -plotvalue(ax5,totforce./Forcemax,'Total force density','F [Nm^{-3}]',false,zlims) - -ax6=subplot(2,1,2); -surface(ax6,M.zgrid,M.rgrid,mean(N,3),'edgecolor','none'); -hold on; -zlevel=interp2(M.zgrid, M.rgrid, mean(N,3), densitycontour(1,2:end), densitycontour(2,2:end)); -plot3(ax6,densitycontour(1,2:end),densitycontour(2,2:end),zlevel,'-.','linewidth',2,'color',contourcolor) -xlim(ax6,[M.zgrid(1) M.zgrid(end)]) -ylim(ax6,[M.rgrid(1) M.rgrid(rgridmax)]) -colormap(ax6,'parula') -xlabel(ax6,'z [m]') -ylabel(ax6,'r [m]') -title(ax6,'Density') -c = colorbar(ax6); -c.Label.String= 'n[m^{-3}]'; -view(ax6,2) - -sgtitle(sprintf('Radial forces sum t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -f2.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f2.PaperUnits='centimeters'; -papsize=[20 29]; -f2.PaperSize=papsize; -print(f2,sprintf('%sforce_dens',name),'-dpdf','-fillpage') - -f3=figure(); -subN=M.N(:,:,it); -t=M.t2d(it); -UR=M.fluidUR(:,:,it); -UZ=M.fluidUZ(:,:,it); -dndt=zeros(size(subN,1)-1,size(subN,2)-1,size(subN,3)-1); -ndivu=zeros(size(subN,1)-1,size(subN,2)-1,size(subN,3)-1); -ugradn=zeros(size(subN,1)-1,size(subN,2)-1,size(subN,3)-1); -for i=1:size(subN,3)-1 - dndt(:,:,i)=(subN(1:end-1,1:end-1,i+1)-subN(1:end-1,1:end-1,i))./(t(i+1)-t(i)); - - ndivu(:,:,i)=subN(1:end-1,1:end-1,i).*(UR(2:end,1:end-1,i)-UR(1:end-1,1:end-1,i))./dr... - + UR(1:end-1,1:end-1,i).*subN(1:end-1,1:end-1,i)./Rc... - + subN(1:end-1,1:end-1,i).*(UZ(1:end-1,2:end,i)-UZ(1:end-1,1:end-1,i))./dz; - - ugradn(:,:,i)= UR(1:end-1,1:end-1,i).*(subN(2:end,1:end-1,i)-subN(1:end-1,1:end-1,i))./dr ... - + UZ(1:end-1,1:end-1,i).*(subN(1:end-1,2:end,i)-subN(1:end-1,1:end-1,i))./dz; -end -dndt=padarray(dndt, [1,1], 0, 'post'); -ndivu=padarray(ndivu, [1,1], 0, 'post'); -ugradn=padarray(ugradn, [1,1], 0, 'post'); - -ax7=subplot(4,1,1); -plotvalue(ax7,mean(dndt,3),'\partialn/\partialt','\partialn/\partialt [m^{-3}s^{-1}]',true) - - -zlim=[max([min(min(mean(ndivu(2:end,:,:),3))),min(min(mean(ugradn(2:end,:,:),3)))]) min([max(max(mean(ndivu(2:end,:,:),3))),max(max(mean(ugradn(2:end,:,:),3)))])]; - -ax8=subplot(4,1,2); -plotvalue(ax8,mean(ndivu,3),'n\nabla(u)','n\nabla(u)[m^{-3}s^{-1}]',true,zlim) - -ax9=subplot(4,1,3); -plotvalue(ax9,mean(ugradn,3),'u\nabla(n)','u\nabla(n)[m^{-3}s^{-1}]',true,zlim) - -ax10=subplot(4,1,4); -plotvalue(ax10,mean(ndivu+ugradn+dndt,3),'Total','total[m^{-3}s^{-1}]',true,zlim) - -sgtitle(sprintf('Continuity t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -f3.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -f3.PaperUnits='centimeters'; -papsize=[20 19]; -f3.PaperSize=papsize; -print(f3,sprintf('%scontinuity',name),'-dpdf','-fillpage') - - - function plotvalue(axis,matrix,titl, clab, rm1strow, zlims) - if nargin < 5 - rm1strow=false; - end - if nargin>5 - zmin=zlims(1); - zmax=zlims(2); - elseif log - zmin=0; - zmax=inf; - else - zmin=-inf; - zmax=inf; - end - if rm1strow - if log - h=surface(axis,M.zgrid,M.rgrid(2:end),abs(matrix(2:end,:))); - else - h=surface(axis,M.zgrid,M.rgrid(2:end),matrix(2:end,:)); - end - else - if log - h=surface(axis,M.zgrid,M.rgrid,abs(matrix)); - else - h=surface(axis,M.zgrid,M.rgrid,matrix); - end - end - set(h,'edgecolor','none'); - hold on; - if log - zpos=interp2(M.zgrid, M.rgrid, abs(matrix), densitycontour(1,2:end), densitycontour(2,2:end)); - else - zpos=interp2(M.zgrid, M.rgrid, matrix, densitycontour(1,2:end), densitycontour(2,2:end)); - end - plot3(axis,densitycontour(1,2:end),densitycontour(2,2:end),zpos,'-.','linewidth',2,'color',contourcolor) - xlabel(axis,'z [m]') - ylabel(axis,'r [m]') - xlim(axis,[M.zgrid(1) M.zgrid(end)]) - ylim(axis,[M.rgrid(1) M.rgrid(rgridmax)]) - colormap(axis,'jet') - c = colorbar(axis); - c.Label.String=clab; - caxis(axis,[zmin zmax]) - if norm - caxis(axis,[Fmin Fmax]); - end - if log - set(axis,'colorscale','log') - caxis(axis,'auto') - end - title(axis,titl) - grid(axis, 'on') - view(axis,2) - end -end - diff --git a/matlab/ForceBalancespline.m b/matlab/ForceBalancespline.m deleted file mode 100644 index 7e4261c..0000000 --- a/matlab/ForceBalancespline.m +++ /dev/null @@ -1,249 +0,0 @@ -function [Forces, Density]=ForceBalancespline(M,it,norm,log,contourscale, zlims) -%ForceBalance Show the radial force balance -% Plot the three radial forces for the given time-step it or averaged -% over the range of time steps defined in it - -if it==':' - it=floor(0.95*size(M.t2d)):size(M.t2d); -end -switch nargin - case 2 - contourscale=0.6; - norm=false; - log=false; - zlims=[-inf inf]; - case 3 - log=false; - contourscale=0.6; - zlims=[-inf inf]; - case 4 - contourscale=0.6; - zlims=[-inf inf]; - case 5 - zlims=[-inf inf]; - case 6 - - otherwise - error("Invalid number of arguments") -end - -contourcolor=[0 0 0];%[255 20 147]/255; -fluiduThet=M.fluidUTHET(:,:,it); -Density.fluiduThet=fluiduThet; - -N=M.N(:,:,it); -[R,~]=meshgrid(M.rgrid,M.zgrid); -Rinv=1./R'; -Rinv(isinf(Rinv))=0; -Density.N=N; -maxdens=max(N(:)); - -densitycontour=contour(M.zgrid,M.rgrid,mean(N,3),[contourscale contourscale]*maxdens); - - -Forces.Eforcer=-M.qe*N.*M.Er(:,:,it); -Forces.Eforcez=-M.qe*N.*M.Ez(:,:,it); -Bforcer=zeros(size(N,1),size(N,2),size(N,3)); -Bforcez=zeros(size(N,1),size(N,2),size(N,3)); -inertforce=zeros(size(N,1),size(N,2),size(N,3)); -Pressforcer=zeros(size(N,1),size(N,2),size(N,3)); -Pressforcez=zeros(size(N,1),size(N,2),size(N,3)); -parfor j=1:size(N,3) - Bforcer(:,:,j)=-M.qe*N(:,:,j).*fluiduThet(:,:,j).*M.Bz'; - Bforcez(:,:,j)=M.qe*N(:,:,j).*fluiduThet(:,:,j).*M.Br'; - inertforce(:,:,j)=M.me*N(:,:,j).*fluiduThet(:,:,j).^2.*Rinv; - Pressforcer(:,:,j)=-( squeeze(M.Presstens.der(1,:,:,it(j),[1 0]))... - + squeeze(M.Presstens(1,:,:,it(j)) - M.Presstens(4,:,:,it(j))).*Rinv... - + squeeze(M.Presstens.der(3,:,:,it(j),[0 1])) ); - - Pressforcez(:,:,j)=-( squeeze(M.Presstens.der(3,:,:,it(j),[1 0]))... - + squeeze(M.Presstens(3,:,:,it(j))).*Rinv... - + squeeze(M.Presstens.der(6,:,:,it(j),[0 1])) ); -end -Forces.Bforcer=Bforcer; -Forces.Bforcez=Bforcez; -Forces.inertforce=inertforce; -Forces.Pressforcer=Pressforcer; -Forces.Pressforcez=Pressforcez; - -Fmaxr=max([max(Forces.Eforcer(:)),max(Forces.Bforcer(:)),max(Forces.inertforce(:)),max(Forces.Pressforcer(:))]); -Fmaxz=max([max(Forces.Eforcez(:)),max(Forces.Bforcez(:)),max(Forces.Pressforcez(:))]); -if log - Er=Forces.Eforcer(Eforcer~=0); - Br=Forces.Bforcer(Bforcer~=0); - inert=Forces.inertforce(inertforce~=0); - pressr=Forces.Pressforcer(Pressforcer~=0); - Fminr=min([min(abs(Er(:))),min(abs(Br(:))),min(abs(inert(:))),min(abs(pressr(:)))]); -else - Fminr=min([min(Forces.Eforcer(:)),min(Forces.Bforcer(:)),min(Forces.inertforce(:)),min(Forces.Pressforcer(:))]); -end -rgridmax=sum(M.nnr(1:2)); - -% %% Radial forces -% f=figure(); -% -% ax1=subplot(4,1,1); -% plotvalue(ax1,mean(Forces.Eforcer,3),'Electric force density', 'F_{Er} [Nm^{-3}]',false); -% -% %zlim=[Fmin Fmax]; -% zlim=[-inf inf]; -% -% ax2=subplot(4,1,2); -% plotvalue(ax2,mean(Forces.Bforcer,3),'Magnetic force density','F_{Br} [Nm^{-3}]',false,zlim) -% -% ax3=subplot(4,1,3); -% plotvalue(ax3,mean(Forces.inertforce,3),'Centrifugal force density','F_{i} [Nm^{-3}]',false,zlim) -% -% ax4=subplot(4,1,4); -% plotvalue(ax4,mean(Forces.Pressforcer,3),'Pressure force density','F_{pr} [Nm^{-3}]',false,zlim) -% -% linkaxes([ax1 ax2 ax3 ax4],'xy') -% sgtitle(sprintf('Radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -% -% f.PaperOrientation='portrait'; -% [~, name, ~] = fileparts(M.file); -% f.PaperUnits='centimeters'; -% papsize=[20 29]; -% f.PaperSize=papsize; -% print(f,sprintf('%sforce_balancer',name),'-dpdf','-fillpage') -% -% %% Radial relative forces -% f=figure(); -% ax1=subplot(4,1,1); -% reference=mean(Forces.Bforcer,3); -% plotvalue(ax1,mean(Forces.Eforcer,3)./reference,'Relative Electric force density', 'F_{Er}/F_{Br}',false); -% -% %zlim=[Fmin Fmax]; -% zlim=[-inf inf]; -% -% ax2=subplot(4,1,2); -% plotvalue(ax2,mean(Forces.Bforcer,3)./reference,'Relative Magnetic force density','F_{Br}/F_{Br}',false) -% -% ax3=subplot(4,1,3); -% plotvalue(ax3,mean(Forces.inertforce,3)./reference,'Relative Centrifugal force density','F_i/F_{Br}',false) -% -% ax4=subplot(4,1,4); -% plotvalue(ax4,mean(Forces.Pressforcer,3)./reference,'Relative Pressure force density','F_{pr}/F_{Br}',false) -% -% linkaxes([ax1 ax2 ax3 ax4],'xy') -% sgtitle(sprintf('Relative radial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -% -% f.PaperOrientation='portrait'; -% [~, name, ~] = fileparts(M.file); -% f.PaperUnits='centimeters'; -% papsize=[20 29]; -% f.PaperSize=papsize; -% print(f,sprintf('%s_relatforce_balance',name),'-dpdf','-fillpage') -% -% %% Axial forces -% f=figure(); -% ax1=subplot(3,1,1); -% plotvalue(ax1,mean(Forces.Eforcez,3),'Electric force density', 'F_{Ez} [Nm^{-3}]',false); -% -% zlim=[-inf inf]; -% -% ax2=subplot(3,1,2); -% plotvalue(ax2,mean(Forces.Bforcez,3),'Magnetic force density','F_{Bz} [Nm^{-3}]',false,zlim) -% -% ax3=subplot(3,1,3); -% plotvalue(ax3,mean(Forces.Pressforcez,3),'Pressure force density','F_{pz} [Nm^{-3}]',false,zlim) -% -% linkaxes([ax1 ax2 ax3],'xy') -% sgtitle(sprintf('Axial forces t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -% -% f.PaperOrientation='portrait'; -% [~, name, ~] = fileparts(M.file); -% f.PaperUnits='centimeters'; -% papsize=[20 29]; -% f.PaperSize=papsize; -% print(f,sprintf('%sforce_balancez',name),'-dpdf','-fillpage') -% -% %% Forces sums -% f2=figure(); -% ax4=subplot(3,1,1); -% totforcer=mean(Forces.Eforcer+Forces.Bforcer+Forces.inertforce+Forces.Pressforcer,3); -% Forcemaxr=cat(3,mean(Forces.Eforcer,3),mean(Forces.Bforcer,3),mean(Forces.inertforce,3),mean(Forces.Pressforcer,3)); -% Forcemaxr=max(Forcemaxr,[],3); -% plotvalue(ax4,totforcer./Forcemaxr,'Total radial force density','F [Nm^{-3}]',false,zlims) -% -% ax5=subplot(3,1,2); -% totforcez=mean(Forces.Eforcez+Forces.Bforcez+Forces.Pressforcez,3); -% Forcemaxz=cat(3,mean(Forces.Eforcez,3),mean(Forces.Bforcez,3),mean(Forces.Pressforcez,3)); -% Forcemaxz=max(Forcemaxz,[],3); -% plotvalue(ax5,totforcez./Forcemaxz,'Total axial force density','F [Nm^{-3}]',false,zlims) -% -% ax6=subplot(3,1,3); -% surface(ax6,M.zgrid,M.rgrid,mean(N,3),'edgecolor','none'); -% hold on; -% zlevel=interp2(M.zgrid, M.rgrid, mean(N,3), densitycontour(1,2:end), densitycontour(2,2:end)); -% plot3(ax6,densitycontour(1,2:end),densitycontour(2,2:end),zlevel,'-.','linewidth',2,'color',contourcolor) -% xlim(ax6,[M.zgrid(1) M.zgrid(end)]) -% ylim(ax6,[M.rgrid(1) M.rgrid(rgridmax)]) -% colormap(ax6,'parula') -% xlabel(ax6,'z [m]') -% ylabel(ax6,'r [m]') -% title(ax6,'Density') -% c = colorbar(ax6); -% c.Label.String= 'n[m^{-3}]'; -% view(ax6,2) -% -% sgtitle(sprintf('Radial forces sum t=[%1.2g-%1.2g]s n_e=%1.2g m^{-3}',M.t2d(min(it)),M.t2d(max(it)),double(maxdens))) -% f2.PaperOrientation='portrait'; -% [~, name, ~] = fileparts(M.file); -% f2.PaperUnits='centimeters'; -% papsize=[20 29]; -% f2.PaperSize=papsize; -% print(f2,sprintf('%sforce_dens',name),'-dpdf','-fillpage') - - function plotvalue(axis,matrix,titl, clab, rm1strow, zlims) - if nargin < 5 - rm1strow=false; - end - if nargin>5 - zmin=zlims(1); - zmax=zlims(2); - else - zmin=-inf; - zmax=inf; - end - if rm1strow - if log - h=surface(axis,M.zgrid,M.rgrid(2:end),abs(matrix(2:end,:))); - else - h=surface(axis,M.zgrid,M.rgrid(2:end),matrix(2:end,:)); - end - else - if log - h=surface(axis,M.zgrid,M.rgrid,abs(matrix)); - else - h=surface(axis,M.zgrid,M.rgrid,matrix); - end - end - set(h,'edgecolor','none'); - hold on; - if log - zpos=interp2(M.zgrid, M.rgrid, abs(matrix), densitycontour(1,2:end), densitycontour(2,2:end)); - else - zpos=interp2(M.zgrid, M.rgrid, matrix, densitycontour(1,2:end), densitycontour(2,2:end)); - end - plot3(axis,densitycontour(1,2:end),densitycontour(2,2:end),zpos,'-.','linewidth',2,'color',contourcolor) - xlabel(axis,'z [m]') - ylabel(axis,'r [m]') - xlim(axis,[M.zgrid(1) M.zgrid(end)]) - ylim(axis,[M.rgrid(1) M.rgrid(rgridmax)]) - colormap(axis,'jet') - c = colorbar(axis); - c.Label.String=clab; - caxis(axis,[zmin zmax]) - if norm - caxis(axis,[Fminr Fmaxr]); - end - if log - set(axis,'colorscale','log') - end - title(axis,titl) - grid(axis, 'on') - view(axis,2) - end -end - diff --git a/matlab/Magnetic_Field_GLS_2020/Cryogenic/B_Ellip_10T_DNP.m b/matlab/Magnetic_Field_GLS_2020/Cryogenic/B_Ellip_10T_DNP.m new file mode 100644 index 0000000..34d3837 --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/Cryogenic/B_Ellip_10T_DNP.m @@ -0,0 +1,149 @@ +function [B] = B_Ellip_10T_DNP(mode,magnet,I,r,z) +% +% Call: [B] = B_Ellip_10T_DNP(mode,magnet,r,z) +% Examples: +% 1. Plot axis magnetic field profile: +% +% >> z = linspace(0,1,101); +% >> r = 0*z + 0.0; +% >> I = [61.56 66.45 113.43 108.09 ]; +% >> [B] = B_Ellip_Cryogenic_170('bz','cryogenic',I,r,z); +% >> figure(1); plot(z,B) +% +% 2. Plot field lines (r* aphi = cst) +% +% >> z = linspace(0,1,51); +% >> r = linspace(0,0.2,21); +% >> [Z,R] = meshgrid(z,r); +% >> I = [61.56 66.45 113.43 108.09 ]; +% >> [Aphi] = B_Ellip_Cryogenic_170('aphi','cryogenic',I,R,Z); +% >> figure(2); contour(Z,R,R.*Aphi); +% +% Magnetic field associated with GT170 GHz magnet, with currents Igun and Icav, +% as read on power supplies (i.e. a current -(Icav+Igun) is actually flowing in the +% gun coil. +% The computation is based on the elliptic integral formulae that can be found in: +% Smythe: "Static and Dynamic Electricity", Third edition, McGraw-Hill, p.290. +% +% Version : 1.0 J.-P. Hogge Sep. 16, 2003 +% 1.1 J.-P. Hogge Jan. 12, 2005 Included 165Ghz FZK magnet, other options deactivated +% +% mode : 'bz','br','dbzbz','dbzdr','dbrdz','dbrdr', 'd2bzdz2' or 'aphi' +% magnet : 'fzk_165ghz_oi' only so far +% z : a row vector (in [m]) or a 2D matrix containing the z locations where the field is to be evaluated +% r : a row vector (in [m]) or a 2D matrix containing the r locations where the field is to be evaluated +% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% 10T magnet for DNP gyrotron coils geometry [m] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +switch lower(magnet) + + case 'new' % Geometry of the 10Tesla sup.cond. solenoid with 76 mm bore diameter + + Ncoil = 13; + Nturn = [1670 2117 2960 4995 12793 59 57 56 54 59 57 56 54]; + rmin = [52.592 60.069 76.919 82.502 90.129 103.638 104.187 104.735 105.283 103.638 104.187 104.735 105.283].*10^(-3); + rmax = [60.069 66.051 82.502 90.129 103.638 104.187 104.735 105.283 105.832 104.190 104.735 105.283 105.832].*10^(-3); + zmin = [-145 -145 -150 -150 -150 -150 -150 -150 -150 -150 -150 -150 -150].*10^(-3); + zmax = [145 145 150 150 150 150 150 150 150 150 150 150 150].*10^(-3); + na = 8. *[10 10 10 10 10 10 10 10 10 10 10 10 10]; % Number of subcoils (radial direction + nb = 8. *[10 10 10 10 10 10 10 10 10 10 10 10 10]; % Number of subcoils in longitudinal direction + % I = 31.31 A => B~ 1.7T at flange + % I = 32.35 A => B< 3T at z = 0 => 235 mm from top flange + Current = [ I(1) I(2) I(3) I(4) I(5) I(6) I(7) I(8) I(9) I(10) I(11) I(12) I(13)]; % Current flowing in each power supply + + rcen = (rmin + rmax)/2; + zcen = (zmin + zmax)/2; + dr = rmax - rmin; + dz = zmax - zmin; + coilsurf= dr .* dz; + + JTot = Current .* Nturn; % Total current flowing in each coil + J = JTot./(dr.*dz); % Current density in each coil + Ic1 = JTot ; % Total current flowing in each coil [A] + + otherwise + disp('Unknown magnet') + +end + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Construct array of current loops +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% + +% Number of coils +ncoil = length(rcen); + +% Number of subcoils +ntot = sum(na.*nb); + +% Preallocate space +r2 = zeros(1,ntot); +z2 = zeros(1,ntot); +Ic = zeros(ntot,1); + + +% isub is the index of subcoil + +isub = 0; +for icoil=1:ncoil + for ib=1:nb(icoil) + for ia=1:na(icoil) + isub = isub +1; + r2(isub) = (rcen(icoil) - dr(icoil)/2) + (dr(icoil)/nb(icoil))*(ib-0.5); + z2(isub) = (zcen(icoil) - dz(icoil)/2) + (dz(icoil)/na(icoil))*(ia-0.5); + Ic(isub) = Ic1(icoil) / (na(icoil)*nb(icoil)); + end + end +end + +% printf('isub =',isub) + + +% Transform array of evaluation points in a one-dimensional array +% +r1d = reshape(r,1,prod(size(r))); +z1d = reshape(z,1,prod(size(z))); + +% +% Suppress locations where Green function diverges (i.e. on the sources) +% +for i=1:length(r2), + [I] = find((r1d.*r1d -r2(i)*r2(i) == 0) & (z1d.*z1d - z2(i)^2)==0); + + if ~isempty(I) + r1d(I)=NaN; + z1d(I)=NaN; + end +end + +% Call greenem + +b = greenem_jph(mode,r1d,z1d,r2,z2); + +if(~iscell(mode)) + nbmodes=length({mode}); +else + nbmodes=length(mode); +end +B=zeros(size(r,1),size(r,2),nbmodes); + +% b is a rectangular matrix (length(r1d) x length(r2)) which needs to be multiplied by the +% column vector Ic to get the proper contribution of each source. +for i=1:size(b,3) + temp = b(:,:,i) * Ic; + + B(:,:,i) = reshape(temp,size(r,1),size(r,2)); +end +B(find((B==Inf)|(B==-Inf)))= 0; +B(find(isnan(B))) = 0; + + + +return diff --git a/matlab/Magnetic_Field_GLS_2020/Cryogenic/B_Ellip_Cryogenic_170.m b/matlab/Magnetic_Field_GLS_2020/Cryogenic/B_Ellip_Cryogenic_170.m new file mode 100644 index 0000000..1da5cb2 --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/Cryogenic/B_Ellip_Cryogenic_170.m @@ -0,0 +1,268 @@ + function [B] = B_Ellip_Cryogenic_170(mode,magnet,I,r,z) +% +% Call: [B] = B_Ellip_Cryogenic_170(mode,magnet,r,z) +% Examples: +% 1. Plot axis magnetic field profile: +% +% >> z = linspace(0,1,101); +% >> r = 0*z + 0.0; +% >> I = [61.56 66.45 113.43 108.09 ]; +% >> [B] = B_Ellip_Cryogenic_170('bz','cryogenic',I,r,z); +% >> figure(1); plot(z,B) +% +% 2. Plot field lines (r* aphi = cst) +% +% >> z = linspace(0,1,51); +% >> r = linspace(0,0.2,21); +% >> [Z,R] = meshgrid(z,r); +% >> I = [61.56 66.45 113.43 108.09 ]; +% >> [Aphi] = B_Ellip_Cryogenic_170('aphi','cryogenic',I,R,Z); +% >> figure(2); contour(Z,R,R.*Aphi); +% +% Magnetic field associated with GT170 GHz magnet, with currents Igun and Icav, +% as read on power supplies (i.e. a current -(Icav+Igun) is actually flowing in the +% gun coil. +% The computation is based on the elliptic integral formulae that can be found in: +% Smythe: "Static and Dynamic Electricity", Third edition, McGraw-Hill, p.290. +% +% Version : 1.0 J.-P. Hogge Sep. 16, 2003 +% 1.1 J.-P. Hogge Jan. 12, 2005 Included 165Ghz FZK magnet, other options deactivated +% +% mode : 'bz','br','dbzbz','dbzdr','dbrdz','dbrdr', 'd2bzdz2' or 'aphi' +% magnet : 'fzk_165ghz_oi' only so far +% z : a row vector (in [m]) or a 2D matrix containing the z locations where the field is to be evaluated +% r : a row vector (in [m]) or a 2D matrix containing the r locations where the field is to be evaluated +% + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% 140 GHz coils geometry [m] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + switch lower(magnet) + + case 'cryogenic', % Geometry of the 'as built' Cryogenic magnet for the EU 170GHz 1MW tube (Contract F4E OPE-552) + + Ncoil = 4; + % G1 G2 BC MC + Nturn = [ 959 2068 3898 26667 ]; + rmin = [0.181500 0.181450 0.147700 0.165300 ]; + rmax = [0.211360 0.212000 0.197200 0.240900 ]; + zmin = [0.084000 0.150400 0.207800 0.333110 ]; + zmax = [0.118650 0.185650 0.307850 0.637690 ]; + na = 8. *[ 10 10 10 10 ]; % Number of subcoils (radial direction + nb = 8. *[ 10 10 10 10 ]; % Number of subcoils in longitudinal direction + + Current = [ I(1) -I(2) -I(3) I(4) ]; % Current flowing in each power supply + + rcen = (rmin + rmax)/2; + zcen = (zmin + zmax)/2; + dr = rmax - rmin; + dz = zmax - zmin; + coilsurf= dr .* dz; + + JTot = Current .* Nturn; % Total current flowing in each coil + J = JTot./(dr.*dz); % Current density in each coil + Ic1 = JTot ; % Total current flowing in each coil [A] + + case 'cryogenic_bestfit', % Geometry of the 'best fit' Cryogenic magnet for the EU 170GHz 1MW tube (Contract F4E OPE-552), 2018 05 25 + + Ncoil = 4; + Nturn = [ 959 2068 3898 26667 ]; + rmin = [0.186875 0.186540 0.146717 0.161725 ]; + rmax = [0.217632 0.202334 0.200795 0.242476 ]; + zmin = [0.0809181 0.153461 0.210388 0.332353 ]; + zmax = [0.115717 0.189897 0.304647 0.637898 ]; + na = 8. *[ 10 10 10 10 ]; % Number of subcoils (radial direction + nb = 8. *[ 10 10 10 10 ]; % Number of subcoils in longitudinal direction + + Current = [ I(1) -I(2) -I(3) I(4) ]; % Current flowing in each power supply + + rcen = (rmin + rmax)/2; + zcen = (zmin + zmax)/2; + dr = rmax - rmin; + dz = zmax - zmin; + coilsurf= dr .* dz; + + JTot = Current .* Nturn; % Total current flowing in each coil + J = JTot./(dr.*dz); % Current density in each coil + Ic1 = JTot ; % Total current flowing in each coil [A] + + case 'design', % Geometry which appears in the SC CFT document (Contract EFDA/ 03-961) + + Ncoil = 7; + Nturn = [ 3000 3000 4320 6200 9177 4566 7723 ]; + rmin = [0.135000 0.135000 0.135000 0.135000 0.190000 0.135000 0.170000 ]; + rmax = [0.145000 0.145000 0.200700 0.190000 0.237040 0.170000 0.204370 ]; + zmin = [0.078500 0.138500 0.183900 0.317000 0.317000 0.509000 0.509000 ]; + zmax = [0.098500 0.158500 0.282900 0.490000 0.490000 0.708000 0.708000 ]; + na = 2. *[ 10 10 10 10 10 10 10 ]; % Number of subcoils (radial direction + nb = 2. *[ 10 10 10 10 10 10 10 ]; % Number of subcoils in longitudinal direction + + Current = [ I(1) I(2) -I(3) I(3) I(3) I(3) I(3) ]; % Current flowing in each power supply + + rcen = (rmin + rmax)/2; + zcen = (zmin + zmax)/2; + dr = rmax - rmin; + dz = zmax - zmin; + coilsurf= dr .* dz; + + JTot = Current .* Nturn; % Total current flowing in each coil + J = JTot./(dr.*dz); % Current density in each coil + Ic1 = JTot ; % Total current flowing in each coil [A] + + case 'asg_modified_201206', +% +% Geometry of the magnet as built with 2 layers removed on coils#3 and 3 on +% coil #5 after the problems with the conductor +% Option added on Decemebr 20th, 2006 +% +% Reference current +% [ 9.7 7.7 -96.2499 -96.2499 91.0124 91.0124 87.4723 87.4723 ] = [ I(1) I(2) -I(3) -I(3) I(4) I(4) I(5) I(5)] + + Ncoil = 8; + Nturn = [ 218 218 2528.5 2626 7689 11517.5 6110 9656.5 ]; + rmin = [0.13958 0.13958 0.13710 0.16952 0.13670 0.19101 0.13458 0.17197 ]; + rmax = [0.142694 0.142694 0.16733 0.20005 0.18941 0.23666 0.16972 0.20412 ]; + zmin = [0.07854 0.13854 0.18200 0.18200 0.31778 0.31748 0.50685 0.50685 ]; + zmax = [0.09846 0.15846 0.28350 0.28350 0.48977 0.49017 0.71128 0.71128 ]; + + rcen = (rmin + rmax)/2; + zcen = (zmin + zmax)/2; + dr = rmax - rmin; + dz = zmax - zmin; + coilsurf= dr .* dz; + + a = rcen-dr/2; + b = rcen+dr/2; + l = dz; + + + na = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils (radial direction + nb = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils in longitudinal direction + + Current = [ I(1) I(2) -I(3) -I(3) I(4) I(4) I(5) I(5) ]; % Current flowing in each power supply + + JTot = Current .* Nturn; % Total current flowing in each coil + J = JTot./(dr.*dz); % Current density in each coil + Ic1 = JTot ; % Total current flowing in each coil [A] + + case 'asg_report', +% +% Geometry of the magnet as built with 2 layers removed on coils#3 and 3 on +% coil #5 after the problems with the conductor +% Option added on Decemebr 20th, 2006 +% +% Reference current +% [0 0 6.79541 1.967 88.2799]=[I(1) I(2) I(3) I(4) I(5)] + + + Ncoil = 8; + Nturn = [ 218 218 2528.5 2626 7689 11517.5 6110 9656.5 ]; + rmin = [0.13958 0.13958 0.13710 0.16952 0.13670 0.19101 0.13458 0.17197 ]; + rmax = [0.142694 0.142694 0.16733 0.20005 0.18941 0.23666 0.16972 0.20412 ]; + zmin = [0.07854 0.13854 0.18200 0.18200 0.31778 0.31748 0.50685 0.50685 ]; + zmax = [0.09846 0.15846 0.28350 0.28350 0.48977 0.49017 0.71128 0.71128 ]; + + rcen = (rmin + rmax)/2; + zcen = (zmin + zmax)/2; + dr = rmax - rmin; + dz = zmax - zmin; + coilsurf= dr .* dz; + + a = rcen-dr/2; + b = rcen+dr/2; + l = dz; + + + na = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils (radial direction + nb = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils in longitudinal direction + + Current = [ I(1) I(2) -I(3)-I(5) -I(3)-I(5) I(4)+I(5) I(4)+I(5) I(5) I(5) ]; % Current flowing in each power supply + + JTot = Current .* Nturn; % Total current flowing in each coil + J = JTot./(dr.*dz); % Current density in each coil + Ic1 = JTot ; % Total current flowing in each coil [A] + + otherwise, + disp('Unknown magnet') + + end + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Construct array of current loops +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% + +% Number of coils + ncoil = length(rcen); + +% Number of subcoils + ntot = sum(na.*nb); + +% Preallocate space + r2 = zeros(1,ntot); + z2 = zeros(1,ntot); + Ic = zeros(ntot,1); + + +% isub is the index of subcoil +% Positions of the coils subdivided + isub = 0; + for icoil=1:ncoil + for ib=1:nb(icoil) + for ia=1:na(icoil) + isub = isub +1; + r2(isub) = (rcen(icoil) - dr(icoil)/2) + (dr(icoil)/nb(icoil))*(ib-0.5); + z2(isub) = (zcen(icoil) - dz(icoil)/2) + (dz(icoil)/na(icoil))*(ia-0.5); + Ic(isub) = Ic1(icoil) / (na(icoil)*nb(icoil)); + end + end + end + +% printf('isub =',isub) + + +% Transform array of evaluation points in a one-dimensional array +% + r1d = reshape(r,1,prod(size(r))); + z1d = reshape(z,1,prod(size(z))); + +% +% Suppress locations where Green function diverges (i.e. on the sources) +% +for i=1:length(r2), + [I] = find((r1d.*r1d -r2(i)*r2(i) == 0) & (z1d.*z1d - z2(i)^2)==0); + + if ~isempty(I) + r1d(I)=NaN; + z1d(I)=NaN; + end + end + +% Call greenem + + b = greenem_jph(mode,r1d,z1d,r2,z2); + + if(~iscell(mode)) + nbmodes=length({mode}); + else + nbmodes=length(mode); + end + B=zeros(size(r,1),size(r,2),nbmodes); + +% b is a rectangular matrix (length(r1d) x length(r2)) which needs to be multiplied by the +% column vector Ic to get the proper contribution of each source. + for i=1:size(b,3) + temp = b(:,:,i) * Ic; + + B(:,:,i) = reshape(temp,size(r,1),size(r,2)); + end + B(find((B==Inf)|(B==-Inf)))= 0; + B(find(isnan(B))) = 0; + + + + return diff --git a/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_10T_DNP_geom.m b/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_10T_DNP_geom.m new file mode 100644 index 0000000..b79acae --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_10T_DNP_geom.m @@ -0,0 +1,53 @@ +%geom=importgeometry('../geometry.data'); + +z=linspace(0.225,0.525,2500); +r=linspace(0.00,0.10,1000); +[Z,R] = meshgrid(z,r); + + +% 100 Amperes current in the coils +I = 100*ones(1,13); +res=zeros([size(Z),3]); + +for i=1:size(R,2) +res(:,i,:) = B_Ellip_10T_DNP({'aphi','bz','br'},'new',I,R(:,i),Z(:,i)); +end +Aphi=res(:,:,1); +Bz=res(:,:,2); +Br=res(:,:,3); + + +% Plot the magnetic field lines +f=figure; +% for k=1:size(geom.Z) +% plothandle=plot(geom.Z{k}, geom.R{k},'k-'); +% hold on +% end +axis equal +[~,cont1]=contour(Z,R,R.*Aphi,15,'r:'); +%xlim([min(z) 0.04]) +ylim([0.0 0.085]) +%[~,cont2]=contour(Zphi,Rphi,Phi,20,'b'); + +f.PaperUnits='centimeters'; +f.PaperSize=[12,8]; +xlabel('z [m]') +ylabel('r [m]') +print(f,'phiBprofile_10T','-dpdf','-fillpage') +savefig(f,'phiBprofile_10T') +hold off + + + + +figure; contourf(Z,R,Bz) +hold on +contour(Z,R,R.*Aphi,15,'r:'); + +xlim([min(z) max(z)]) +ylim([min(r) max(r)]) + + +function phi=Phivacuum(r,a,b,phia,phib) + phi=((phib-phia)*log(r)+phia*log(b)-phib*log(a))/log(b/a); +end diff --git a/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_170_geom.m b/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_170_geom.m new file mode 100644 index 0000000..6199473 --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_170_geom.m @@ -0,0 +1,72 @@ +geom=importgeometry('../geometry.data'); + +% z=linspace(-0.012,0.04,100); +% r=linspace(0.063,0.085,50); +% [Z,R] = meshgrid(z,r); +% +% I = [61.56 66.45 113.43 108.09 ]; +% Aphi=zeros(size(Z)); +% Bz=zeros(size(Z)); +% Br=zeros(size(Z)); +% +% for i=1:size(R,1) +% Aphi(i,:) = B_Ellip_Cryogenic_170('aphi','cryogenic',I,R(i,:),Z(i,:)); +% Bz(i,:) = B_Ellip_Cryogenic_170('bz','cryogenic',I,R(i,:),Z(i,:)); +% Br(i,:) = B_Ellip_Cryogenic_170('br','cryogenic',I,R(i,:),Z(i,:)); +% end + +% zphi=linspace(0.005,0.0195,100); +% rphi=linspace(0.06375,0.079,150); +% b=max(rphi); +% a=min(rphi); +% phib=0; +% phia=-30000; +% phi=((phib-phia)*log(rphi)+phia*log(b)-phib*log(a))/log(b/a); +% Er=((phib-phia)./rphi)/log(b/a); +% [Zphi,Rphi] = meshgrid(zphi,rphi); +% Phi=repmat(phi',1,length(zphi)); + + + +f=figure; +for k=1:size(geom.Z) +plothandle=plot(geom.Z{k}, geom.R{k},'k-'); +hold on +end +axis equal +[~,cont1]=contour(Z,R,R.*Aphi,15,'r:'); +xlim([min(z) 0.04]) +ylim([0.055 0.085]) +%[~,cont2]=contour(Zphi,Rphi,Phi,20,'b'); +rectangle('Position',[-0.011, 0.06375, 0.032+0.011, 0.081-0.06375],'EdgeColor','magenta','Linestyle','--') +t=linspace(0,-pi,500); +x=0.028/2*cos(t)+0.012;y=0.004*sin(t)+0.083; +phandle2=plot(x,y,'b-','displayname','Wall approximation 1'); +x=0.028/2*cos(t)+0.012;y=0.012*sin(t)+0.091; +phandle3=plot(x,y,'g--','displayname','Wall approximation 2'); +x=0.028/2*cos(t)+0.012;y=0.006*sin(t)+0.085; +phandle4=plot(x,y,'r--','displayname','Wall approximation 3'); + +legend([plothandle,cont1,phandle2,phandle3],{'Gun geometry', 'Magnetic field lines','Wall approximation'},'location','southwest') +f.PaperUnits='centimeters'; +f.PaperSize=[12,8]; +xlabel('z [m]') +ylabel('r [m]') +print(f,'phiBprofile','-dpdf','-fillpage') +savefig(f,'phiBprofile') +hold off + + + + +figure; contour(Z,R,Bz) +xlim([min(z) max(z)]) +ylim([min(r) max(r)]) + +figure; contour(Z,R,Br) +xlim([min(z) max(z)]) +ylim([min(r) max(r)]) + +function phi=Phivacuum(r,a,b,phia,phib) + phi=((phib-phia)*log(r)+phia*log(b)-phib*log(a))/log(b/a); +end diff --git a/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_170_geom_refurb.m b/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_170_geom_refurb.m new file mode 100644 index 0000000..0a7e3cd --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/Cryogenic/Draw_B_Ellip_170_geom_refurb.m @@ -0,0 +1,200 @@ +% creates the magnetic field h5 file necessary for espic2d +% This uses the geometry of the refurbished 170GHz coaxial gyrotron gun +% The magnetic field is the one created by the asg magnet +% Both the magnet and geometry are definde in the Final report of the +% Development of the european gyrotron CCCGDS6 + +%% Import and calculate the magnetic field +magnet=load('asg_magnet_red.mat'); + + + +%% calculate magnetic field and magnetic vector +idr=1:10:length(magnet.r); +idz=1:10:length(magnet.z); + +% Define the coils currents +%I = [ 9.7 7.7 96.2499 91.0124 87.4723]; +% I = [0 0 4.5 1.967 88.2799]; +% name='phiBprofile_refurbasg_4_5_red'; +% I = [0 0 4.8 1.967 88.2799]; +% name='phiBprofile_refurbasg_well_red'; +I = [0 0 5 1.967 88.2799]; +name='phiBprofile_refurbasg_5_red'; +% I = [0 0 5.15 1.967 88.2799]; +% name='phiBprofile_refurbasg_limdwn_red'; +%I = [0 0 5.35 1.967 88.2799]; +%name='phiBprofile_refurbasg_limup_red'; +% I = [0 0 5.5 1.967 88.2799]; +% name='phiBprofile_refurbasg_5_5_red'; +%I = [0 0 5.6 1.967 88.2799]; +%name='phiBprofile_refurbasg_5_6_red'; +%I = [0 0 5.7 1.967 88.2799]; +%name='phiBprofile_refurbasg_5_7_red'; +% I = [0 0 5.75 1.967 88.2799]; +% name='phiBprofile_refurbasg_5_75_red'; +% I = [0 0 5.8 1.967 88.2799]; +% name='phiBprofile_refurbasg_5_8_red'; +% I = [0 0 5.85 1.967 88.2799]; +% name='phiBprofile_refurbasg_5_85_red'; +% I = [0 0 5.9 1.967 88.2799]; +% name='phiBprofile_refurbasg_5_9_red'; +% I = [0 0 6 1.967 88.2799]; +% name='phiBprofile_refurbasg_6_red'; + +% I = [0 0 6.4 1.967 88.2799]; +% name='phiBprofile_refurbasg_6_4_red'; +% I = [0 0 6.79541 1.967 88.2799]; +% name='phiBprofile_refurbasg_nominal_red'; + + +Icoil=[I(1) I(2) -I(3)-I(5) -I(3)-I(5) I(4)+I(5) I(4)+I(5) I(5) I(5)]; +% Nturn = [ 218 218 2528.5 2626 7689 11517.5 6110 9656.5 ]; +% na = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils (radial direction +% nb = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils in longitudinal direction +% Icoil=Icoil.*Nturn./(na.*nb); + +[Z,R] = meshgrid(magnet.z(idz),magnet.r(idr)); + +Aphi=zeros(length(idr),length(idz)); +Bz=zeros(length(idr),length(idz)); +Br=zeros(length(idr),length(idz)); + +for i=1:size(magnet.subcoils,1) + Aphi=Aphi+Icoil(i)*magnet.subcoils{i,1}(idr,idz); + Bz=Bz+Icoil(i)*magnet.subcoils{i,2}(idr,idz); + Br=Br+Icoil(i)*magnet.subcoils{i,3}(idr,idz); +end + +%% Define the individual boundaries +geom=importrefurb('../refurb_modif.data'); +geomcells={}; +j=1; +i=1; +n=1; +while i<=size(geom.Z,1) + if isnan(geom.Z(i)) + j=j+1; + while isnan(geom.Z(i)) + i=i+1; + end + n=1; + end + geomcells{j}.Z(n)=geom.Z(i)/1e3; + geomcells{j}.R(n)=geom.R(i)/1e3; + i=i+1; + n=n+1; +end +geomcells{1}.name='Cathode'; +geomcells{1}.Dval=-30000; +geomcells{2}.name='Body'; +geomcells{2}.Dval=0; +geomcells{3}.name='Coaxial insert'; +geomcells{3}.Dval=0; +for k=1:length(geomcells) +geomcells{k}.order=3; +geomcells{k}.dim=2; +geomcells{k}.epsce=1e-9; +geomcells{k}.epsge=1e-9; +geomcells{k}.type=0; +geomcells{k}.periodic=0; +end + + +%% Load the original boundary +geomorig=importrefurb('../refurb.data'); +geomcellsorig={}; +j=1; +i=1; +n=1; +while i<=size(geomorig.Z,1) + if isnan(geomorig.Z(i)) + j=j+1; + while isnan(geomorig.Z(i)) + i=i+1; + end + n=1; + end + geomcellsorig{j}.Z(n)=geomorig.Z(i)/1e3; + geomcellsorig{j}.R(n)=geomorig.R(i)/1e3; + i=i+1; + n=n+1; +end +geomcellsorig{1}.name='Cathode'; +geomcellsorig{1}.Dval=-30000; +geomcellsorig{2}.name='Body'; +geomcellsorig{2}.Dval=0; +geomcellsorig{3}.name='Coaxial insert'; +geomcellsorig{3}.Dval=0; +for k=1:length(geomcellsorig) +geomcellsorig{k}.order=3; +geomcellsorig{k}.dim=2; +geomcellsorig{k}.epsce=1e-9; +geomcellsorig{k}.epsge=1e-9; +end + +%% Plots +f=figure; +for k=1:length(geomcellsorig) + plothandleorig=plot(geomcellsorig{k}.Z*1e3, geomcellsorig{k}.R*1e3,'r-','linewidth',2); + hold on +end +for k=1:length(geomcells) + %plothandle=plot(geomcells{k}.Z, geomcells{k}.R,'k-','linewidth',1.5); + hold on + geomcells{k}.points=[geomcells{k}.Z; geomcells{k}.R]; + order=geomcells{k}.order; + knots=linspace(0,1,length(geomcells{k}.Z)-(order-2)); + knots=augknt(knots, order); + sizec=size(geomcells{k}.Z); + order=length(knots)-sizec(end); + coeffs=[geomcells{k}.Z; geomcells{k}.R]; + pp=spmak(knots,coeffs); + s=linspace(0,1,500); + fittedpos=fnval(pp,s); + plot(fittedpos(1,:)*1e3,fittedpos(2,:)*1e3,'--','linewidth',2.2) +end + +%axis equal +raphi=R.*Aphi; +lvls=logspace(-4,log10(max(raphi(:))),50); +[~,cont1]=contour(Z*1e3,R*1e3,raphi,lvls,'b:','linewidth',1.5); +xlim([min(magnet.z) max(magnet.z)]*1e3) +ylim([min(magnet.r) max(magnet.r)]*1e3) +%[~,cont2]=contour(Zphi,Rphi,Phi,20,'b'); +rectangle('Position',[-0.1, 0.045, 0.292, 0.092-0.045]*1e3,'EdgeColor','black','Linestyle','--','linewidth',2) + +%legend([plothandle,cont1],{'Gun geometry', 'Magnetic field lines'},'location','southwest') +f.PaperUnits='centimeters'; +f.PaperSize=[12,8]; +xlabel('z [mm]') +ylabel('r [mm]') + +grid on + +print(f,name,'-dpdf','-fillpage') +savefig(f,name) +set(f, 'Color', 'w'); +export_fig(f,name,'-eps') +hold off + + +%% Save magnetic field and geometry to disk +save=true; +overwrite=true; +if save + idr=1:1:length(magnet.r); +idz=1:1:length(magnet.z); +Aphi=zeros(length(idr),length(idz)); +Bz=zeros(length(idr),length(idz)); +Br=zeros(length(idr),length(idz)); + +for i=1:size(magnet.subcoils,1) + Aphi=Aphi+Icoil(i)*magnet.subcoils{i,1}(idr,idz); + Bz=Bz+Icoil(i)*magnet.subcoils{i,2}(idr,idz); + Br=Br+Icoil(i)*magnet.subcoils{i,3}(idr,idz); +end + savemagtoh5([name,'.h5'],magnet.r,magnet.z,Aphi,Br,Bz,overwrite); + savegeomtoh5('refurb_geom.h5',geomcells,1e-2,overwrite); +end + diff --git a/matlab/Magnetic_Field_GLS_2020/Cryogenic/asg_magnet.m b/matlab/Magnetic_Field_GLS_2020/Cryogenic/asg_magnet.m new file mode 100644 index 0000000..6aa3496 --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/Cryogenic/asg_magnet.m @@ -0,0 +1,148 @@ +% Computes the magnetic field of each submagnet in the asg +% superconducting magnet assembly +% +% The magnetic field is calculated for a unit current at positions +% defined by r and z. +% The fields are then saved in asg_magnet.mat + +% Evaluation grid space + +z=linspace(-0.3,0.3,6000); +r=linspace(0.0,0.1,1000); +% z=linspace(-0.15,0.3,100); +% r=linspace(0.0,0.1,100); +[Z,R] = meshgrid(z,r); + +mode={'aphi','bz','br'}; + + +% Transform array of evaluation points in a one-dimensional array +% +r1d = reshape(R,1,numel(R)); +z1d = reshape(Z,1,numel(Z)); + +% +% Geometry of the asg magnet as built with 2 layers removed on coils#3 and 3 on +% coil #5 after the problems with the conductor +% +% Reference current +% [0 0 6.79541 1.967 88.2799]=[I(1) I(2) I(3) I(4) I(5)] + + +Ncoil = 8; +Nturn = [ 218 218 2528.5 2626 7689 11517.5 6110 9656.5 ]; +rmin = [0.13958 0.13958 0.13710 0.16952 0.13670 0.19101 0.13458 0.17197 ]; +rmax = [0.142694 0.142694 0.16733 0.20005 0.18941 0.23666 0.16972 0.20412 ]; +zmin = [0.07854 0.13854 0.18200 0.18200 0.31778 0.31748 0.50685 0.50685 ]; +zmax = [0.09846 0.15846 0.28350 0.28350 0.48977 0.49017 0.71128 0.71128 ]; + +rcen = (rmin + rmax)/2; +zcen = (zmin + zmax)/2; +dr = rmax - rmin; +dz = zmax - zmin; +coilsurf= dr .* dz; + +a = rcen-dr/2; +b = rcen+dr/2; +l = dz; + + +na = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils (radial direction +nb = 2. *[ 10 10 10 10 10 10 10 10 ]; % Number of subcoils in longitudinal direction + +% Current = [ I(1) I(2) -I(3)-I(5) -I(3)-I(5) I(4)+I(5) I(4)+I(5) I(5) I(5) ]; % Current flowing in each power supply +% +JTot = Nturn; % Total current flowing in each coil +% J = JTot./(dr.*dz); % Current density in each coil +Ic1 = JTot ; % Total current flowing in each coil [A] + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Construct array of current loops +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% + +% Number of coils +ncoil = length(rcen); + +if(~iscell(mode)) + nbmodes=length({mode}); +else + nbmodes=length(mode); +end +subcoils=cell(ncoil,nbmodes); +for icoil=1:ncoil + for imode=1:nbmodes + subcoils{icoil,imode}=zeros(length(r),length(z)); + end +end +for icoil=1:ncoil + %subcoils{icoil}=zeros(length(r),length(z),nbmodes); + B=zeros(length(r),length(z),nbmodes); + parfor ir=1:length(r) + [Z,R] = meshgrid(z,r(ir)); + r1d = reshape(R,1,numel(R)); + z1d = reshape(Z,1,numel(Z)); + + % Number of subcoils + ntot = sum(na(icoil).*nb(icoil)); + + % Preallocate space + r2 = zeros(1,ntot); + z2 = zeros(1,ntot); + Ic = zeros(ntot,1); + + + % isub is the index of subcoil + % Positions of the coils subdivided + isub = 0; + for ib=1:nb(icoil) + for ia=1:na(icoil) + isub = isub +1; + r2(isub) = (rcen(icoil) - dr(icoil)/2) + (dr(icoil)/nb(icoil))*(ib-0.5); + z2(isub) = (zcen(icoil) - dz(icoil)/2) + (dz(icoil)/na(icoil))*(ia-0.5); + Ic(isub) = Ic1(icoil) / (na(icoil)*nb(icoil)); + end + end + + + + + % + % Suppress locations where Green function diverges (i.e. on the sources) + % + for i=1:length(r2) + [I] = find((r1d.*r1d -r2(i)*r2(i) == 0) & (z1d.*z1d - z2(i)^2)==0); + + if ~isempty(I) + r1d(I)=NaN; + z1d(I)=NaN; + end + end + + % Call greenem + + b = greenem_jph(mode,r1d,z1d,r2,z2); + + + temp2=zeros(size(R,2),nbmodes); + + % b is a rectangular matrix (length(r1d) x length(r2)) which needs to be multiplied by the + % column vector Ic to get the proper contribution of each source. + for i=1:size(b,3) + temp = b(:,:,i)*Ic; + temp((temp==Inf)|(temp==-Inf))= 0; + temp(isnan(temp)) = 0; + temp2(:,i) = reshape(temp,size(R,1),size(R,2)); + end + B(ir,:,:)=temp2(:,:); + + end + for imode=1:nbmodes + subcoils{icoil,imode}=B(:,:,imode); + end +end +save('asg_magnet_red.mat','r','z','subcoils','mode','Ncoil','na','nb','Nturn','rmin','rmax','zmin','zmax','-v7.3') + + diff --git a/matlab/Magnetic_Field_GLS_2020/Cryogenic/importgeometry.m b/matlab/Magnetic_Field_GLS_2020/Cryogenic/importgeometry.m new file mode 100644 index 0000000..36dfa4f --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/Cryogenic/importgeometry.m @@ -0,0 +1,108 @@ +function geometry = importgeometry(filename, startRow, endRow) +%IMPORTFILE Import numeric data from a text file as a matrix. +% GEOMETRY = IMPORTFILE(FILENAME) Reads data from text file FILENAME for +% the default selection. +% +% GEOMETRY = IMPORTFILE(FILENAME, STARTROW, ENDROW) Reads data from rows +% STARTROW through ENDROW of text file FILENAME. +% +% Example: +% geometry = importfile('geometry.data', 1, 2398); +% +% See also TEXTSCAN. + +% Auto-generated by MATLAB on 2020/10/01 10:24:26 + +%% Initialize variables. +if nargin<=2 + startRow = 1; + endRow = inf; +end + +%% Read columns of data as text: +% For more information, see the TEXTSCAN documentation. +formatSpec = '%22s%22s%s%[^\n\r]'; + +%% Open the text file. +fileID = fopen(filename,'r'); + +%% Read columns of data according to the format. +% This call is based on the structure of the file used to generate this +% code. If an error occurs for a different file, try regenerating the code +% from the Import Tool. +textscan(fileID, '%[^\n\r]', startRow(1)-1, 'WhiteSpace', '', 'ReturnOnError', false); +dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, 'Delimiter', '', 'WhiteSpace', '', 'TextType', 'string', 'ReturnOnError', false, 'EndOfLine', '\r\n'); +for block=2:length(startRow) + frewind(fileID); + textscan(fileID, '%[^\n\r]', startRow(block)-1, 'WhiteSpace', '', 'ReturnOnError', false); + dataArrayBlock = textscan(fileID, formatSpec, endRow(block)-startRow(block)+1, 'Delimiter', '', 'WhiteSpace', '', 'TextType', 'string', 'ReturnOnError', false, 'EndOfLine', '\r\n'); + for col=1:length(dataArray) + dataArray{col} = [dataArray{col};dataArrayBlock{col}]; + end +end + +%% Close the text file. +fclose(fileID); + +%% Convert the contents of columns containing numeric text to numbers. +% Replace non-numeric text with NaN. +raw = repmat({''},length(dataArray{1}),length(dataArray)-1); +for col=1:length(dataArray)-1 + raw(1:length(dataArray{col}),col) = mat2cell(dataArray{col}, ones(length(dataArray{col}), 1)); +end +numericData = NaN(size(dataArray{1},1),size(dataArray,2)); + +for col=[1,2,3] + % Converts text in the input cell array to numbers. Replaced non-numeric + % text with NaN. + rawData = dataArray{col}; + for row=1:size(rawData, 1) + % Create a regular expression to detect and remove non-numeric prefixes and + % suffixes. + regexstr = '(?.*?)(?([-]*(\d+[\,]*)+[\.]{0,1}\d*[eEdD]{0,1}[-+]*\d*[i]{0,1})|([-]*(\d+[\,]*)*[\.]{1,1}\d+[eEdD]{0,1}[-+]*\d*[i]{0,1}))(?.*)'; + try + result = regexp(rawData(row), regexstr, 'names'); + numbers = result.numbers; + + % Detected commas in non-thousand locations. + invalidThousandsSeparator = false; + if numbers.contains(',') + thousandsRegExp = '^[-/+]*\d+?(\,\d{3})*\.{0,1}\d*$'; + if isempty(regexp(numbers, thousandsRegExp, 'once')) + numbers = NaN; + invalidThousandsSeparator = true; + end + end + % Convert numeric text to numbers. + if ~invalidThousandsSeparator + numbers = textscan(char(strrep(numbers, ',', '')), '%f'); + numericData(row, col) = numbers{1}; + raw{row, col} = numbers{1}; + end + catch + raw{row, col} = rawData{row}; + end + end +end + + +%% Exclude rows with non-numeric cells +I = ~all(cellfun(@(x) (isnumeric(x) || islogical(x)) && ~isnan(x),raw),2); % Find rows with non-numeric cells +ii = [I',0]; +i1 = strfind(ii,[1 0])+1; +i2 = strfind(ii,[0 1]); +if (length(i2) 1 | size(r2,1) > 1 , error('Input must be row vectors'); end + + R1 = repmat(r1', 1 , length(r2) ); % R1 is a matrix length(r1)xlength(r2) + Z1 = repmat(z1', 1 , length(r2) ); % Z1 is a matrix length(r1)xlength(r2) + R2 = repmat(r2 , length(r1), 1 ); % R2 is a matrix length(r1)xlength(r2) + Z2 = repmat(z2 , length(r1), 1 ); % Z2 is a matrix length(r1)xlength(r2) + + + h = Z1 -Z2; + d = sqrt( (R2 - R1).^2 + h.^2 ); + u = sqrt( (R2 + R1).^2 + h.^2 ); + k = sqrt( 4*R1.*R2 ./ u.^2 ); + v = sqrt( R2.^2 + R1.^2 + h.^2); + w = sqrt( R2.^2 - R1.^2 - h.^2); + + + [K,E] = ellipke(k.^2); + + + if(~iscell(mode)) + mode={mode}; + end + b=zeros([size(k),length(mode)]); + for i=1:length(mode) + + switch lower(mode{i}) + + + case 'bz', + + b(:,:,i) = (mu0/2/pi) * (1./u) .* ( (w.^2./d.^2).*E + K ); + + case 'br', + + b(:,:,i) = (mu0/2/pi) * (h./R1./u) .* ( (v.^2./d.^2).*E - K ); + + case 'dbzdz', + + b(:,:,i) = (mu0/2/pi) * (h./d.^2./u.^3) .* ( (-4*v.^2.*w.^2-3*u.^2.*d.^2)./d.^2.*E + w.^2 .* K ); + + case 'dbzdr', + +%JPH 5_11_2002 b = (mu0/2/pi) * (1./R2./d.^2./u.^3) .* ( (v.^2.*d.^2.*u.^2 - h.^2.*(d.^4+k.^2.*u.^4))./d.^2.*E + (h.^2.*v.^2 - u.^2.*d.^2) .* K ); + b(:,:,i) = (mu0/2/pi) * (1./R1./d.^2./u.^3) .* ( (v.^2.*d.^2.*u.^2 - h.^2.*(d.^4+k.^2.*u.^4))./d.^2.*E + (h.^2.*v.^2 - u.^2.*d.^2) .* K ); + + case 'dbrdr', + +% d(br)/dr1 = -d(bz)/dz1 - br/r1 + + b(:,:,i) = - (mu0/2/pi) * (h./d.^2./u.^3) .* ( (-4*v.^2.*w.^2-3*u.^2.*d.^2)./d.^2.*E + w.^2 .* K ) ... + - (mu0/2/pi) * (h./R1./u) .* ( (v.^2./d.^2).*E - K ) ./R1; + + case 'dbrdz', + + b(:,:,i) = (mu0/2/pi) * (h.^2./d.^2./R1./u) .* ( ( -u.^2./d.^2 - d.^2./u.^2 + v.^2./h.^2 +1).*E + (-d.^2./h.^2 + v.^2./u.^2) .* K ); + + case 'd2bzdz2', + + b(:,:,i) = (mu0/2/pi) * (1./d.^4./u.^3) .* ( ( ( -d.^2.*u.^2 + 4*h.^2.*( 2*R1.*R2 + d.^2)) + 8 * h.^2.*u.^2.*w.^2./d.^2 ... + + 2 * ( -u.^2.*w.^2 + 4*h.^2.*( u.^2 + w.^2)) - 2 * d.^2.*( u.^2 + w.^2 ) ... + - h.^2 .* ( -8*d.^2 - (7 - 8*k.^2).*w.^2) ) .* E ... + - ( d.^2.*h.^2 + 4 * h.^2.*w.^2 + ... + d.^2./u.^2 .* ( -u.^2.*w.^2 + 4*h.^2.*( u.^2 + w.^2)) ) .* K ); + + + case 'aphi' + + b(:,:,i) = (mu0/pi) * sqrt(R2./(k.^2.*R1)) .* ( (1-k.^2/2).*K - E); + otherwise, + disp('Unknown mode') + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + end + end + + +return diff --git a/matlab/Magnetic_Field_GLS_2020/refurb.data b/matlab/Magnetic_Field_GLS_2020/refurb.data new file mode 100644 index 0000000..63a6d1b --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/refurb.data @@ -0,0 +1,218 @@ +# z [mm] +# r [mm] +# Cathode +cthP101 -96.945839 97.528159 +cthP100 -96.945839 90.821586 +cthP94 -82.846074 90.020801 +cthP97 -82.44608 95.025706 +cthP95 -82.846074 85.015895 +cthP94 -82.846074 90.020801 +cthP93 -93.945889 90.020801 +cthP92 -93.945889 80.010989 +cthP91 -77.522162 80.010989 +cthEP0 -19.9797 63.1981 +cthEP1 -17.4845 63.0446 +cthEP2 -14.9889 62.8954 +cthEP3 -12.3933 62.7449 +cthEP4 -9.8972 62.6048 +cthEP5 -7.40087 62.4694 +cthEP6 -4.90429 62.3388 +cthEP7 -2.40746 62.213 +cthEP8 0.189511 62.0874 +cthEP9 2.68684 61.9718 +cthEP10 5.1844 61.8615 +cthEP11 7.6822 61.7565 +cthEP12 10.2801 61.6531 +cthEP13 12.7784 61.5594 +cthEP14 15.2768 61.4714 +cthEP15 17.7755 61.3892 +cthEP16 20.2743 61.313 +cthEP17 22.8733 61.2403 +cthEP18 25.3725 61.1768 +cthEP19 27.8718 61.1197 +cthEP20 30.3713 61.0691 +cthEP21 32.8709 61.0253 +cthEP22 35.4707 60.987 +cthEP23 37.9705 60.9572 +cthEP24 40.4704 60.9346 +cthEP25 42.9703 60.9193 +cthEP26 45.4703 60.9114 +cthEP27 48.0703 60.9112 +cthEP28 50.5703 60.9189 +cthEP29 53.0703 60.9343 +cthEP30 55.5701 60.9576 +cthEP31 58.1699 60.9903 +cthEP32 60.6696 61.03 +cthEP33 63.1692 61.0778 +cthEP34 65.6685 61.1338 +cthEP35 68.1677 61.1979 +cthEP36 70.7666 61.2733 +cthEP37 73.2653 61.354 +cthEP38 75.7637 61.4427 +cthEP39 78.2619 61.5392 +cthEP40 80.7597 61.6434 +cthEP41 83.3571 61.7595 +cthEP42 85.8543 61.8784 +cthEP43 88.3511 62.0038 +cthEP44 90.8477 62.1353 +cthEP45 93.4438 62.2778 +cthEP46 95.9397 62.4195 +cthEP47 98.4355 62.565 +cthEP48 100.931 62.7131 +cthEP49 103.526 62.8685 +cthP2 105.72253 63 +cthP4 106.72253 63 +cthP5 109.57202 62.864366 +cthP6 112.39575 62.458692 +cthP7 115.16818 61.786644 +cthP8 117.86425 60.854301 +emtrP2 120.25 59.9 +emtrP1 124.75 58.1 +cthP13 131.1696 55.53216 +cthP14 132.70522 54.781069 +cthP15 134.11253 53.81064 +cthP16 135.36045 52.642314 +cthP17 136.42138 51.301909 +cthP18 137.27189 49.819043 +cthP19 137.89319 48.226481 +cthP20 138.27155 46.559414 +cthP21 138.39861 44.854678 +cthP24 138.2641 43.469046 +cthP25 137.86576 42.155894 +cthP26 137.21889 40.945686 +cthP27 136.34835 39.884931 +cthP28 135.2876 39.014391 +cthP29 134.07739 38.367521 +cthP30 132.76424 37.969181 +cthP31 131.39861 37.834678 +cthP11 122.5 37.834678 +cthP32 -100 57.5 +cthP40 -154.21892 64.40626 +cthP42 -158.26726 65.357779 +cthP44 -162.03129 67.125946 +cthP46 -165.34829 69.634314 +cthP48 -168.07484 72.77443 +cthP52 -168.90702 74.602138 +cthP54 -169.02759 76.606762 +cthP56 -168.42046 78.521037 +cthP58 -167.16659 80.089745 +cthP60 -165.43313 81.103738 +cthP62 -163.45121 81.427828 +cthP64 -161.48505 81.018804 +cthP78 -159.79681 79.9312 +cthP66 -149.8737 79.9312 +cthP67 -149.8737 89.9172 +cthP68 -161.0308 89.9172 +cthP69 -161.0308 84.9248 +cthP72 -161.4323 94.91 +cthP68 -161.0308 89.9172 +cthP76 -146.8508 90.716 +cthP77 -146.8508 94.91 + +# Body +crnP40 42.053679 93.524235 +crnP39 42.053679 90.821586 +crnP33 27.953913 90.020801 +crnP36 27.55392 95.025706 +crnP34 27.953913 85.015895 +crnP33 27.953913 90.020801 +crnP32 39.053729 90.020801 +crnP31 39.053729 82.012951 +crnP24 24 82 +crnP20 23.543174 82.83962 +crnP16 22.590076 82.91212 +crnP12 22.011506 82.151259 +crnP8 22.336075 81.2522 +crnP4 24.365737 79.996789 +crnP0 26.7 79.5 +crnEP1 28.0992 79.4536 +crnEP2 30.1982 79.3891 +crnEP3 32.3974 79.3284 +crnEP4 34.4968 79.2771 +crnEP5 36.5963 79.2325 +crnEP6 38.696 79.1947 +crnEP7 40.7957 79.164 +crnEP8 42.9956 79.1394 +crnEP9 45.0955 79.1235 +crnEP10 47.1955 79.1151 +crnEP11 49.2955 79.1145 +crnEP12 51.3955 79.1218 +crnEP13 53.5954 79.1382 +crnEP14 55.6953 79.1623 +crnEP15 57.795 79.195 +crnEP16 59.8946 79.2364 +crnEP17 61.994 79.2868 +crnEP18 64.1931 79.3494 +crnEP19 66.292 79.4189 +crnEP20 68.3905 79.4979 +crnEP21 70.4886 79.5869 +crnEP22 72.6861 79.6908 +crnEP23 74.7833 79.8007 +crnEP24 76.8798 79.921 +crnEP25 78.9757 80.0522 +crnEP26 81.0709 80.1943 +crnEP27 83.265 80.3552 +crnEP28 85.3585 80.5204 +crnEP29 87.451 80.6973 +crnEP30 89.6421 80.8952 +crnEP31 91.7324 81.0964 +crnEP32 93.8216 81.3098 +crnEP33 96.0088 81.5466 +crnEP34 98.0952 81.7854 +crnEP35 100.18 82.0368 +crnEP36 102.363 82.3139 +crnEP37 104.444 82.5915 +crnEP38 106.524 82.8822 +crnEP39 108.701 83.2007 +crnEP40 110.777 83.5181 +crnEP41 112.949 83.8648 +crnEP42 115.021 84.2092 +crnEP43 117.188 84.5842 +crnEP44 119.255 84.9557 +crnEP45 121.418 85.3589 +crnEP46 123.48 85.7572 +crnEP47 125.637 86.1882 +crnEP48 127.792 86.6332 +crnEP49 129.846 87.0707 +crnEP50 131.995 87.5419 +andP2 133.91839 87.820665 +andP12 156.41106 78.8236 +andP4 190 43.3 +andP1 196 40.4 +andP11 203 37.3 +andP13 300 36.3 + +# Coaxial insert +insP6 300 8.4 +insP5 250 8.4 +insP4 250 12 +insP3 217.3 18.7 +insP71 200 18.7 +insP71 180 18.7 +insP71 170 18.7 +insP71 160 18.7 +insP71 150 18.7 +insP71 140 18.7 +insP71 130 18.7 +insP71 120 18.7 +insP72 110 18.7 +insP8 92.5184 18.7 +insP81 80 19.8121 +insP82 38.75 23.4768 +insP83 -2.5 27.1415 +insP84 -43.75 30.8062 +insP85 -85 34.4708 +insP86 -126.25 38.1355 +insP87 -167.5 41.8002 +insP88 -208.75 45.4648 +insP89 -250 49.1295 +insP9 -259.5 49.9735 +insP10 -296.9384 49.9742 +insP11 -296.9384 69.9453 +insP12 -282.884 69.9453 +insP13 -282.884 89.9172 +insP14 -271.6998 89.9172 +insP15 -271.6998 84.9242 +insP17 -271.2767 94.9119 +insP14 -271.6998 89.9172 +insP24 -300 90.722 diff --git a/matlab/Magnetic_Field_GLS_2020/refurb_modif.data b/matlab/Magnetic_Field_GLS_2020/refurb_modif.data new file mode 100644 index 0000000..f09b79c --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/refurb_modif.data @@ -0,0 +1,242 @@ +# z [mm] +# r [mm] +# Cathode +cthP99 -75 110 +cthP98 -75 100 +cthP97 -75 95 +cthP96 -75 90.020801 +cthP95 -80 90.020801 +cthP94 -82.846074 90.020801 +cthP93 -93.945889 90.020801 +cthP92 -93.945889 80.010989 +cthP911 -90 80.010989 +cthP911 -85 80.010989 +cthP91 -77.522162 80.010989 +cthEP0 -19.9797 63.1981 +cthEP1 -17.4845 63.0446 +cthEP2 -14.9889 62.8954 +cthEP3 -12.3933 62.7449 +cthEP4 -9.8972 62.6048 +cthEP5 -7.40087 62.4694 +cthEP6 -4.90429 62.3388 +cthEP7 -2.40746 62.213 +cthEP8 0.189511 62.0874 +cthEP9 2.68684 61.9718 +cthEP10 5.1844 61.8615 +cthEP11 7.6822 61.7565 +cthEP12 10.2801 61.6531 +cthEP13 12.7784 61.5594 +cthEP14 15.2768 61.4714 +cthEP15 17.7755 61.3892 +cthEP16 20.2743 61.313 +cthEP17 22.8733 61.2403 +cthEP18 25.3725 61.1768 +cthEP19 27.8718 61.1197 +cthEP20 30.3713 61.0691 +cthEP21 32.8709 61.0253 +cthEP22 35.4707 60.987 +cthEP23 37.9705 60.9572 +cthEP24 40.4704 60.9346 +cthEP25 42.9703 60.9193 +cthEP26 45.4703 60.9114 +cthEP27 48.0703 60.9112 +cthEP28 50.5703 60.9189 +cthEP29 53.0703 60.9343 +cthEP30 55.5701 60.9576 +cthEP31 58.1699 60.9903 +cthEP32 60.6696 61.03 +cthEP33 63.1692 61.0778 +cthEP34 65.6685 61.1338 +cthEP35 68.1677 61.1979 +cthEP36 70.7666 61.2733 +cthEP37 73.2653 61.354 +cthEP38 75.7637 61.4427 +cthEP39 78.2619 61.5392 +cthEP40 80.7597 61.6434 +cthEP41 83.3571 61.7595 +cthEP42 85.8543 61.8784 +cthEP43 88.3511 62.0038 +cthEP44 90.8477 62.1353 +cthEP45 93.4438 62.2778 +cthEP46 95.9397 62.4195 +cthEP47 98.4355 62.565 +cthEP48 100.931 62.7131 +cthEP49 103.526 62.8685 +cthP2 105.72253 63 +cthP4 106.72253 63 +cthP5 109.57202 62.864366 +cthP6 112.39575 62.458692 +cthP7 115.16818 61.786644 +cthP8 117.86425 60.854301 +emtrP2 120.25 59.9 +emtrP1 124.75 58.1 +cthP13 131.1696 55.53216 +cthP14 132.70522 54.781069 +cthP15 134.11253 53.81064 +cthP16 135.36045 52.642314 +cthP17 136.42138 51.301909 +cthP18 137.27189 49.819043 +cthP19 137.89319 48.226481 +cthP20 138.27155 46.559414 +cthP21 138.39861 44.854678 +cthP24 138.2641 43.469046 +cthP25 137.86576 42.155894 +cthP26 137.21889 40.945686 +cthP27 136.34835 39.884931 +cthP28 135.2876 39.014391 +cthP29 134.07739 38.367521 +cthP30 132.76424 37.969181 +cthP31 131.39861 37.834678 +cthP11 122.5 37.834678 +cthP32 -100 57.5 +cthP40 -154.21892 64.40626 +cthP42 -158.26726 65.357779 +cthP44 -162.03129 67.125946 +cthP46 -165.34829 69.634314 +cthP48 -168.07484 72.77443 +cthP52 -168.90702 74.602138 +cthP54 -169.02759 76.606762 +cthP56 -168.42046 78.521037 +cthP58 -167.16659 80.089745 +cthP60 -165.43313 81.103738 +cthP62 -163.45121 81.427828 +cthP64 -161.48505 81.018804 +cthP78 -159.79681 79.9312 +cthP66 -149.8737 79.9312 +cthP67 -149.8737 89.9172 +cthP68 -161.0308 89.9172 +cthP69 -161.0308 95 +cthP70 -161.0308 100 +cthP71 -161.0308 110 + + +# Body +andP13 300 36.3 +andP11 203 37.3 +andP1 196 40.4 +andP4 190 43.3 +andP5 187.60079 45.8374 +andP5 185.20158 48.3748 +andP5 182.80237 50.9122 +andP5 180.40316 53.4496 +andP5 178.00395 55.9870 +andP5 175.60474 58.5244 +andP5 173.20553 61.0618 +andP5 170.80632 63.5992 +andP5 168.40711 66.1366 +andP5 166.00790 68.6740 +andP5 163.60869 71.2114 +andP5 161.20948 73.7488 +andP5 158.81027 76.2862 +andP12 156.41106 78.8236 +andP13 151.912526 80.623013 +andP14 147.413992 82.422426 +andP15 142.915458 84.221839 +andP16 138.416924 86.021252 +andP2 133.91839 87.820665 +crnEP50 131.995 87.5419 +crnEP49 129.846 87.0707 +crnEP48 127.792 86.6332 +crnEP47 125.637 86.1882 +crnEP46 123.48 85.7572 +crnEP45 121.418 85.3589 +crnEP44 119.255 84.9557 +crnEP43 117.188 84.5842 +crnEP42 115.021 84.2092 +crnEP41 112.949 83.8648 +crnEP40 110.777 83.5181 +crnEP39 108.701 83.2007 +crnEP38 106.524 82.8822 +crnEP37 104.444 82.5915 +crnEP36 102.363 82.3139 +crnEP35 100.18 82.0368 +crnEP34 98.0952 81.7854 +crnEP33 96.0088 81.5466 +crnEP32 93.8216 81.3098 +crnEP31 91.7324 81.0964 +crnEP30 89.6421 80.8952 +crnEP29 87.451 80.6973 +crnEP28 85.3585 80.5204 +crnEP27 83.265 80.3552 +crnEP26 81.0709 80.1943 +crnEP25 78.9757 80.0522 +crnEP24 76.8798 79.921 +crnEP23 74.7833 79.8007 +crnEP22 72.6861 79.6908 +crnEP21 70.4886 79.5869 +crnEP20 68.3905 79.4979 +crnEP19 66.292 79.4189 +crnEP18 64.1931 79.3494 +crnEP17 61.994 79.2868 +crnEP16 59.8946 79.2364 +crnEP15 57.795 79.195 +crnEP14 55.6953 79.1623 +crnEP13 53.5954 79.1382 +crnEP12 51.3955 79.1218 +crnEP11 49.2955 79.1145 +crnEP10 47.1955 79.1151 +crnEP9 45.0955 79.1235 +crnEP8 42.9956 79.1394 +crnEP7 40.7957 79.164 +crnEP6 38.696 79.1947 +crnEP5 36.5963 79.2325 +crnEP4 34.4968 79.2771 +crnEP3 32.3974 79.3284 +crnEP2 30.1982 79.3891 +crnEP1 28.0992 79.4536 +crnP0 26.7 79.5 +crnP4 24.365737 79.996789 +crnP8 22.336075 81.2522 +crnP12 22.011506 82.151259 +crnP16 22.590076 82.91212 +crnP20 23.543174 82.83962 +crnP24 24 82 +crnP24 28 82 +crnP24 32 82 +crnP24 36 82 +crnP31 39.053729 82 +crnP311 39.053729 84 +crnP312 39.053729 86 +crnP313 39.053729 88 +crnP32 39.053729 90.020801 +crnP321 36 90.020801 +crnP322 32 90.020801 +crnP33 28 90.020801 +crnP32 27.953913 90.020801 +crnP38 27.953913 100 +crnP39 27.953913 110 + + +# Coaxial insert +cthP71 -271.6998 110 +cthP70 -271.6998 100 +cthP69 -271.6998 95 +insP14 -271.6998 89.9172 +insP13 -282.884 89.9172 +insP12 -282.884 69.9453 +insP11 -296.9384 69.9453 +insP10 -296.9384 49.9742 +insP9 -259.5 49.9735 +insP89 -250 49.1295 +insP88 -208.75 45.4648 +insP87 -167.5 41.8002 +insP86 -126.25 38.1355 +insP85 -85 34.4708 +insP84 -43.75 30.8062 +insP83 -2.5 27.1415 +insP82 38.75 23.4768 +insP81 80 19.8121 +insP8 92.5184 18.7 +insP72 110 18.7 +insP71 120 18.7 +insP71 130 18.7 +insP71 140 18.7 +insP71 150 18.7 +insP71 160 18.7 +insP71 170 18.7 +insP71 180 18.7 +insP71 200 18.7 +insP3 217.3 18.7 +insP4 250 12 +insP5 250 8.4 +insP6 300 8.4 \ No newline at end of file diff --git a/matlab/Magnetic_Field_GLS_2020/test_slanted_geom.data b/matlab/Magnetic_Field_GLS_2020/test_slanted_geom.data new file mode 100644 index 0000000..7bda935 --- /dev/null +++ b/matlab/Magnetic_Field_GLS_2020/test_slanted_geom.data @@ -0,0 +1,42 @@ +# z [mm] +# r [mm] +# slanted rectangle +andP3 139.8686 43.3 +andP3 150 43.3 +andP3 160 43.3 +andP3 170 43.3 +andP3 180 43.3 +andP4 190 43.3 +andP5 187.60079 45.8374 +andP5 185.20158 48.3748 +andP5 182.80237 50.9122 +andP5 180.40316 53.4496 +andP5 178.00395 55.9870 +andP5 175.60474 58.5244 +andP5 173.20553 61.0618 +andP5 170.80632 63.5992 +andP5 168.40711 66.1366 +andP5 166.00790 68.6740 +andP5 163.60869 71.2114 +andP5 161.20948 73.7488 +andP5 158.81027 76.2862 +andP12 156.41106 78.8236 +andP12 146.41106 78.8236 +andP12 136.41106 78.8236 +andP12 126.41106 78.8236 +andP12 116.41106 78.8236 +andP13 106.2797 78.8236 +andP14 108.67890 76.2862 +andP14 111.07811 73.7488 +andP14 113.47732 71.2114 +andP14 115.87652 68.6740 +andP14 118.27573 66.1366 +andP14 120.67494 63.5992 +andP14 123.07415 61.0618 +andP14 125.47335 58.5244 +andP14 127.87256 55.9870 +andP14 130.27177 53.4496 +andP14 132.67097 50.9122 +andP14 135.07018 48.3748 +andP14 137.46939 45.8374 +andP3 139.8686 43.3 \ No newline at end of file diff --git a/matlab/PlotEspic2dgriddata.m b/matlab/PlotEspic2dgriddata.m deleted file mode 100644 index 1d13c5b..0000000 --- a/matlab/PlotEspic2dgriddata.m +++ /dev/null @@ -1,49 +0,0 @@ -function PlotEspic2dgriddata(fig,M,fieldstep) -%PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep -sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,M.t2d(fieldstep))) - - -ax1=subplot(2,2,1,'Parent',fig); -pcolor(ax1,M.zgrid,M.rgrid,M.N(:,:,fieldstep)); -xlim(ax1,[M.zgrid(1) M.zgrid(end)]) -ylim(ax1,[M.rgrid(1) M.rgrid(end)]) -xlabel(ax1,'Z [m]') -ylabel(ax1,'R [m]') -title(ax1,'Position') -c = colorbar(ax1); -c.Label.String= 'N'; -view(ax1,2) -%set(ax1,'colorscale','log') - - -ax2=subplot(2,2,2,'Parent',fig); -contourf(ax2,M.zgrid,M.rgrid,M.pot(:,:,fieldstep)'); -%plot(ax2,M.zgrid,data.pot(:,5)) -xlabel(ax2,'Z [m]') -ylabel(ax2,'R [m]') -colormap(ax2,'jet') -c = colorbar(ax2); -c.Label.String= '\Phi [V]'; -title(ax2,'Es potential') -grid(ax2, 'on') - - -ax3=subplot(2,2,3,'Parent',fig); -contourf(ax3,M.zgrid,M.rgrid,M.Er(:,:,fieldstep)') -xlabel(ax3,'Z [m]') -ylabel(ax3,'R [m]') -c = colorbar(ax3); -c.Label.String= 'E_r [V/m]'; -title(ax3,'Radial Electric field') -grid(ax3, 'on') - -ax4=subplot(2,2,4,'Parent',fig); -contourf(ax4,M.zgrid,M.rgrid,M.Ez(:,:,fieldstep)') -xlabel(ax4,'Z [m]') -ylabel(ax4,'R [m]') -c = colorbar(ax4); -c.Label.String= 'E_z [V/m]'; -title(ax4,'Axial Electric field') -grid(ax4, 'on') -end - diff --git a/matlab/PlotEspic2dpartdata.m b/matlab/PlotEspic2dpartdata.m deleted file mode 100644 index f6ff72c..0000000 --- a/matlab/PlotEspic2dpartdata.m +++ /dev/null @@ -1,51 +0,0 @@ -function PlotEspic2dpartdata(fig,M,fieldstep) -%PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep -sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,M.t2d(fieldstep))) - -ax1=subplot(3,2,1,'Parent',fig); -plot(ax1,M.Z(:,fieldstep),M.R(:,fieldstep),'.'); -xlim(ax1,[M.zgrid(1) M.zgrid(end)]) -ylim(ax1,[M.rgrid(1) M.rgrid(end)]) -xlabel(ax1,'Z [m]') -ylabel(ax1,'R [m]') -title(ax1,'Position') -grid(ax1, 'on') - - -ax2=subplot(3,2,2,'Parent',fig); -plot(ax2,M.VZ(:,fieldstep),M.VR(:,fieldstep),'.'); -xlabel(ax2,'VZ [m/s]') -ylabel(ax2,'VR [m/s]') -axis(ax2, 'equal') -grid(ax2, 'on') - - -ax3=subplot(3,2,3,'Parent',fig); -plot(ax3,M.Z(:,fieldstep),M.VZ(:,fieldstep),'.'); -xlim(ax3,[M.zgrid(1) M.zgrid(end)]) -xlabel(ax3,'Z [m]') -ylabel(ax3,'VZ [m/s]') -grid(ax3, 'on') - -ax4=subplot(3,2,4,'Parent',fig); -plot(ax4,M.VZ(:,fieldstep),M.R(:,fieldstep),'.') -ylabel(ax4,'R [m]') -xlabel(ax4,'VZ [m/s]') -ylim(ax4,[M.rgrid(1) M.rgrid(end)]) -grid(ax4, 'on') - -ax5=subplot(3,2,5,'Parent',fig); -plot(ax5,M.Z(:,fieldstep),M.VR(:,fieldstep),'.'); -xlim(ax5,[M.zgrid(1) M.zgrid(end)]) -xlabel(ax5,'Z [m]') -ylabel(ax5,'VR [m/s]') -grid(ax5, 'on') - -ax6=subplot(3,2,6,'Parent',fig); -plot(ax6,M.VR(:,fieldstep),M.R(:,fieldstep),'.'); -ylim(ax6,[M.rgrid(1) M.rgrid(end)]) -ylabel(ax6,'R [m]') -xlabel(ax6,'VR [m/s]') -grid(ax6, 'on') -end - diff --git a/matlab/PlotParticleTrajectory.m b/matlab/PlotParticleTrajectory.m index 28cf962..de19670 100644 --- a/matlab/PlotParticleTrajectory.m +++ b/matlab/PlotParticleTrajectory.m @@ -1,73 +1,105 @@ function PlotParticleTrajectory(M,partnumber,t,text) %UNTITLED3 Summary of this function goes here % Detailed explanation goes here if (nargin<4) text=''; else text=['_',text]; end + +if isa(M,'h5parts') + geomstruct=M.parent; +else + geomstruct=M; +end + f=figure(); %time=((t(1):t(end))-1)*M.dt*double(M.it2); time=M.tpart(t(1):t(end)); %sgtitle(sprintf('Particles %d',partnumber)); ax1=subplot(1,2,1); hold(ax1); +Blines=geomstruct.rAthet; +levels=linspace(min(min(Blines(:,2:end))),max(max(Blines(:,:))),10); +[~,h1]=contour(ax1,geomstruct.zgrid*1000,geomstruct.rgrid*1000,Blines,real(levels),'r-.','linewidth',1.5,'Displayname','Magnetic field lines'); + ax2=subplot(1,2,2); hold(ax2); Msize=8; -Z=M.Z(partnumber,t,true); -R=M.R(partnumber,t,true); +Z=M.Z(partnumber,t,true)*1000; +R=M.R(partnumber,t,true)*1000; THET=M.THET(partnumber,t,true); for i=1:length(partnumber) -p1(i)=plot(ax1,Z(i,:),R(i,:),'x-.','Linewidth',1.1,... + Zl=Z(i,R(i,:)>0); + Rl=R(i,R(i,:)>0); + THETl=THET(i,R(i,:)>0); +p1(i)=plot(ax1,Zl,Rl,'x-.','Linewidth',1.1,... 'Displayname',sprintf('part=%d',partnumber(i)),'MarkerIndices',1,... 'Markersize',Msize); -plot(ax1,Z(i,end),R(i,end),'^','Linewidth',1.1,... +plot(ax1,Zl(end),Rl(end),'^','Linewidth',1.1,... 'Displayname',sprintf('part=%d',partnumber(i)),'MarkerIndices',1,... 'Markersize',Msize,'color',p1(i).Color) -x=R(i,:).*cos(THET(i,:)); -y=R(i,:).*sin(THET(i,:)); +x=Rl.*cos(THETl); +y=Rl.*sin(THETl); %pp=spline(x,y); p2=plot(ax2,x,y,'x-.','Linewidth',1.1,... 'Displayname',sprintf('part=%d',partnumber(i)),'MarkerIndices',1,... 'Markersize',Msize); -plot(ax2,R(i,end).*cos(THET(i,end)),R(i,end).*sin(THET(i,end)),'^',... +plot(ax2,Rl(end).*cos(THETl(end)),Rl(end).*sin(THETl(end)),'^',... 'Linewidth',1.1,'Displayname',sprintf('part=%d',partnumber(i)),'MarkerIndices',1,... 'Markersize',Msize,'color',p2.Color) end -xlabel(ax1,'z [m]') -ylabel(ax1,'r [m]') +xlabel(ax1,'z [mm]') +ylabel(ax1,'r [mm]') sgtitle(sprintf('Position between t=[%3.2f-%3.2f] ns',time(1)*1e9,time(end)*1e9)) -xlim(ax1,[M.zgrid(1) M.zgrid(end)]) +xlim(ax1,[M.zgrid(1) M.zgrid(end)]*1e3) %xlim(ax1,[-.17 .17]) -ylim(ax1,[M.rgrid(1) M.rgrid(end)]) +ylim(ax1,[M.rgrid(1) M.rgrid(end)]*1e3) %ylim(ax1,[0.005 0.025]) -legend(ax1,p1(1:end)) grid(ax1,'on'); -xlabel(ax2,'x [m]') -ylabel(ax2,'y [m]') +xlabel(ax2,'x [mm]') +ylabel(ax2,'y [mm]') t=2*pi*linspace(0,1,100); -xin=M.rgrid(1)*cos(t); -yin=M.rgrid(1)*sin(t); +if(geomstruct.conformgeom) + +xin=geomstruct.rgrid(1)*cos(t)*1e3; +yin=geomstruct.rgrid(1)*sin(t)*1e3; plot(ax2,xin,yin,'k-') -xout=M.rgrid(end)*cos(t); -yout=M.rgrid(end)*sin(t); +xout=geomstruct.rgrid(end)*cos(t)*1e3; +yout=geomstruct.rgrid(end)*sin(t)*1e3; plot(ax2,xout,yout,'k-') +plot(ax1,geomstruct.zgrid*1e3,geomstruct.rgrid(1)*ones(size(geomstruct.zgrid))*1e3,'k-') +plot(ax1,geomstruct.zgrid*1e3,geomstruct.rgrid(end)*ones(size(geomstruct.zgrid))*1e3,'k-') +else + subplot(1,2,1) + contour(geomstruct.zgrid*1e3,geomstruct.rgrid*1e3,geomstruct.geomweight(:,:,1),[0 0],'k-') + [~,rgrid]=meshgrid(geomstruct.zgrid*1e3,geomstruct.rgrid*1e3); + rlims=rgrid(geomstruct.geomweight(:,:,1)<0); + xin=geomstruct.rgrid(1)*cos(t)*1e3; +yin=geomstruct.rgrid(1)*sin(t)*1e3; +plot(ax2,xin,yin,'k-') +xout=min(rlims)*cos(t); +yout=min(rlims)*sin(t); +plot(ax2,xout,yout,'k-') + +end +legend(ax1,p1(1:end)) +legend('location','southwest') axis(ax2,'equal') %legend(ax2) grid(ax2,'on'); % ax=subplot(1,2,2); % xlabel(ax,'t [s]') % ylabel(ax,'VR [m/s]') % title(ax,'Radial velocity') % grid on; f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.fullpath); f.PaperUnits='centimeters'; f.PaperSize=[16 9]; print(f,sprintf('%sTrajectories%s',name,text),'-dpdf','-fillpage') - +savefig(f,sprintf('%sTrajectories%s',name,text)) end diff --git a/matlab/PotentialWell.m b/matlab/PotentialWell.m index de734ba..74da3ad 100644 --- a/matlab/PotentialWell.m +++ b/matlab/PotentialWell.m @@ -1,39 +1,39 @@ function [pot] = PotentialWell(Phi,Atheta,r,z,creategraph) -%PotentialWell Computes and display the potential well given the electrostatic potentila Phi +%PotentialWell Computes and display the potential well given the electrostatic potential Phi % and the magnetic vector potential Atheta if nargin <5 creategraph=true; end [rgrid,~]=meshgrid(r,z); rAthet=(rgrid.*Atheta)'; contpoints=contourc(z,r,rAthet,rAthet(:,1)'); [x,y,zcont]=C2xyz(contpoints); k=1; zdiff=[diff(zcont),0]; for j=1:size(x,2) %for i=1:size(pot,1) xloc=x{j}; yloc=y{j}; xloc=xloc(ylocr(1)); yloc=yloc(ylocr(1)); x{j}=xloc; y{j}=yloc; if(length(xloc)>1) pot{j}=interp2(z,r,Phi,xloc,yloc); pot{j}=pot{j}-min(pot{j}); end k=k+(zdiff(j)~=0); end x=cell2mat(x); y=cell2mat(y); pot=cell2mat(pot); [Z,R]=meshgrid(z,r); pot=griddata(x,y,pot,Z,R); if(creategraph) figure surface(z(2:end),r(2:end),-pot(2:end,2:end),'edgecolor','none') xlabel('r') ylabel('z') colorbar end end diff --git a/matlab/WellVideosaving.m b/matlab/WellVideosaving.m index ad3b9cf..0f9af2b 100644 --- a/matlab/WellVideosaving.m +++ b/matlab/WellVideosaving.m @@ -1,108 +1,139 @@ fieldstep=1; -maxN=5e12; -Minwell=-10; -tend=min(8000,length(M.t2d)); +maxN=1.4e17; +Minwell=-6000; +%tend=min(8000,length(M.t2d)); tend=length(M.t2d); -plot3d=false; +plot3d=true; +logdensity=false; -rAthetpos=25; +fracn=0.1; + +rAthetpos=177; thefig=figure('Position',[0 0 1600 900]); +filename=M.name; +if logdensity + filename=strcat(filename,'_log'); +end if plot3d - filename=[M.name,'_well3D.avi']; + filename=strcat(filename,'_well3D.avi'); else - filename=[M.name,'_well2D.avi']; + filename=strcat(filename,'_well2D.avi'); end videowriterobj=VideoWriter([M.folder,'/',filename]); -videowriterobj.FrameRate=10; +videowriterobj.FrameRate=5; open(videowriterobj); -plotaxes(1)=subplot(1,2,1,'Parent',thefig); -plotaxes(2)=subplot(1,2,2,'Parent',thefig); + if plot3d + plotaxes(1)=subplot(2,1,1,'Parent',thefig); + plotaxes(2)=subplot(2,1,2,'Parent',thefig); sf=surface(plotaxes(1),M.zgrid,M.rgrid,M.N(:,:,fieldstep),'edgecolor','none'); + + hold(plotaxes(1),'on') + contour(plotaxes(1),M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r--') + if logdensity + set(plotaxes(1),'zscale','log') + set(plotaxes(1),'colorscale','log') + end xlim(plotaxes(1),[M.zgrid(1) M.zgrid(end)]) - ylim(plotaxes(1),[M.rgrid(1) M.rgrid(sum(M.nnr(1:2))+5)]) + %ylim(plotaxes(1),[M.rgrid(1) M.rgrid(sum(M.nnr(1:2))+5)]) xlabel(plotaxes(1),'z [m]') ylabel(plotaxes(1),'r [m]') title(plotaxes(1),'Density') c = colorbar(plotaxes(1)); c.Label.String= 'n[m^{-3}]'; - caxis(plotaxes(1),[-Inf maxN]); + %caxis(plotaxes(1),[-Inf maxN]); + + + + well=M.PotentialWell(fieldstep); + sf=surface(plotaxes(2),M.zgrid,M.rgrid,well','edgecolor','none'); + hold(plotaxes(2),'on') + dens=M.N(:,:,fieldstep); + contour(plotaxes(2),M.zgrid,M.rgrid,dens-fracn*max(dens(:)),[0 0],'k--') + contour(plotaxes(2),M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r--') - well=M.PotentialWell(fieldstep,false); - sf=surface(plotaxes(2),M.zgrid(2:end),M.rgrid(2:end),well(2:end,2:end),'edgecolor','none'); xlim(plotaxes(2),[M.zgrid(1) M.zgrid(end)]) - ylim(plotaxes(2),[M.rgrid(1) M.rgrid(sum(M.nnr(1:2))+5)]) + %ylim(plotaxes(2),[M.rgrid(1) M.rgrid(sum(M.nnr(1:2))+5)]) xlabel(plotaxes(2),'z [m]') ylabel(plotaxes(2),'r [m]') title(plotaxes(2),'Well') c = colorbar(plotaxes(2)); c.Label.String= 'depth [V]'; - view(plotaxes(2),3) + view(plotaxes(2),2) colormap(plotaxes(2),'jet'); caxis(plotaxes(2),[Minwell 0]); zlim(plotaxes(2),[Minwell 0]); + axis(plotaxes(1),'equal') + axis(plotaxes(2),'equal') + - for i=fieldstep:5:tend + for i=fieldstep:80:tend sgtitle(thefig,sprintf('t= %1.2f [ns]',M.t2d(i)*1e9)) dens=M.N(:,:,i); + dens(M.geomweight(:,:,1)<0)=NaN; - well=M.PotentialWell(i,false); - well=well(2:end,2:end); + well=M.PotentialWell(i); + well=well'; - plotaxes(1).Children.ZData=dens; - plotaxes(1).Children.CData=dens; + plotaxes(1).Children(end).ZData=dens; + plotaxes(1).Children(end).CData=dens; - plotaxes(2).Children.ZData=well; - plotaxes(2).Children.CData=well; + plotaxes(2).Children(end-1).ZData=dens-fracn*max(dens(:)); + plotaxes(2).Children(end).ZData=well; + plotaxes(2).Children(end).CData=well; writeVideo(videowriterobj,getframe(thefig)); end else dens=M.N(:,:,fieldstep); model=M.potentialwellmodel(fieldstep); z=model.z; r=model.r; pot=model.pot; rathet=model.rathet; [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); dens=griddata(Zmesh,M.rAthet,dens,Zmesh,Rmesh); - plot(plotaxes(1),M.zgrid,dens(rAthetpos,1:end)); + plot(M.zgrid,dens(rAthetpos,1:end)); + plotaxes(1)=gca; xlim(plotaxes(1),[M.zgrid(1) M.zgrid(end)]) ylim(plotaxes(1),[0 maxN]); xlabel(plotaxes(1),'z [m]') ylabel(plotaxes(1),'n [m^{-3}]') - title(plotaxes(1),'Density') - grid(plotaxes, 'on') + grid(plotaxes(1), 'on') pot=griddata(z,rathet,pot,Zmesh,Rmesh); - plot(plotaxes(2),M.zgrid(1:end),pot(rAthetpos,1:end)); - xlim(plotaxes(2),[M.zgrid(1) M.zgrid(end)]) + yyaxis right + + plot(M.zgrid(1:end),pot(rAthetpos,1:end)); + plotaxes(2)=gca; ylim(plotaxes(2),[Minwell 0]); - xlabel(plotaxes(2),'z [m]') ylabel(plotaxes(2),'depth [eV]') - title(plotaxes(2),'Well') - grid on + Curve2=plotaxes(2).Children; + yyaxis left + plotaxes(1)=gca; + Curve1=plotaxes(1).Children; - for i=fieldstep:20:tend + for i=fieldstep:80:tend sgtitle(thefig,sprintf('rA_\\theta=%1.3g [Tm^2] t= %1.2f [ns]',Rmesh(rAthetpos,1),M.t2d(i)*1e9)) dens=M.N(:,:,i); model=M.potentialwellmodel(i); z=model.z; r=model.r; pot=model.pot; rathet=model.rathet; [Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,1)); dens=griddata(Zmesh,M.rAthet,dens,Zmesh,Rmesh); pot=griddata(z,rathet,pot,Zmesh,Rmesh); - plotaxes(1).Children.YData=dens(rAthetpos,1:end); - plotaxes(2).Children.YData=pot(rAthetpos,1:end); + Curve1.YData=dens(rAthetpos,1:end); + Curve2.YData=pot(rAthetpos,1:end); + drawnow; writeVideo(videowriterobj,getframe(thefig)); end end -close(videowriterobj); \ No newline at end of file +close(videowriterobj); diff --git a/matlab/analyse_espicfields.m b/matlab/analyse_espicfields.m index ac9c157..9831748 100644 --- a/matlab/analyse_espicfields.m +++ b/matlab/analyse_espicfields.m @@ -1,342 +1,391 @@ +%% time extent of plotted variables % file='teststablegauss_13_traject2.h5'; % file='unigauss.h5'; % file='Dav_5e14fine_wr+.h5'; % file='stablegauss_8e19fine.h5' % % %file='teststable_Dav1.h5'; % % %close all hidden; % if (~exist('M','var')) % M.file=file; % end % M=load_espic2d(file,M,'all'); t2dlength=size(M.t2d,1) fieldstart=max(1,t2dlength-500);%floor(0.95*t2dlength); +fieldend=t2dlength;%23; deltat=t2dlength-fieldstart; - %[~, rgridend]=min(abs(M.rgrid-0.045)); rgridend=sum(M.nnr(1:2)); [~, name, ~] = fileparts(M.file); M.displayenergy %% Radial profile try - M.displayrprofile(deltat) + M.displayrprofile([1,fieldstart:fieldend],[],true) catch end - +%% %fieldstart=2300; fieldend=2500; -dens=mean(M.N(:,:,fieldstart:end),3); -pot=mean(M.pot(:,:,fieldstart:end),3); -brillratio=2*dens*M.me./(M.eps_0*M.Bz'.^2); +dens=mean(M.N(:,:,fieldstart:fieldend),3); +geomw=M.geomweight(:,:,1); +geomw(geomw<0)=0; +geomw(geomw>0)=max(max(dens)); +dispdens=dens; +dispdens(geomw<=0)=NaN; +pot=mean(M.pot(:,:,fieldstart:fieldend),3); +brillratio=2*dens*M.me./(M.eps_0*M.B'.^2); [R,Z]=meshgrid(M.rgrid,M.zgrid); Rinv=1./R; Rinv(isnan(Rinv))=0; -VTHET=mean(M.fluidUTHET(:,:,fieldstart:end),3); +VTHET=mean(M.fluidUTHET(:,:,fieldstart:fieldend),3); omegare=(VTHET.*Rinv'); maxdens=max(max(dens)); - % f=figure(); % ax3=gca; % surface(ax3,M.zgrid(1:end-1),M.rgrid(1:end-1),(squeeze(mean(M.Presstens(4,:,:,fieldstart:end),4)))*M.vlight) % xlabel(ax3,'z [m]') % ylabel(ax3,'r [m]') % xlim(ax3,[M.zgrid(1) M.zgrid(end)]) % ylim(ax3,[M.rgrid(1) M.rgrid(50)]) % colormap(ax3,'jet') % c = colorbar(ax3); % c.Label.String= 'thermal v_\theta [m/s]'; % %c.Limits=[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]; % %caxis(ax3,[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]) % title(ax3,'Azimuthal velocity') % %set(ax3,'colorscale','log') % grid(ax3, 'on') % view(ax3,2) % f.PaperOrientation='landscape'; % [~, name, ~] = fileparts(M.file); % f.PaperUnits='centimeters'; % papsize=[16 14]; % f.PaperSize=papsize; % print(f,sprintf('%sfluid_thermtheta',name),'-dpdf','-fillpage') %% Density -f=figure('Name', sprintf('%sfluid_dens',name)); -ax1=gca; -h=surface(ax1,M.zgrid,M.rgrid,dens); -set(h,'edgecolor','none'); -ylim(ax1,[M.rgrid(1) M.rgrid(rgridend)]) -xlim(ax1,[M.zgrid(1) M.zgrid(end)]) -xlabel(ax1,'z [m]') -ylabel(ax1,'r [m]') -title(ax1,sprintf('Density t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(end),double(maxdens))) -c = colorbar(ax1); -c.Label.String= 'n[m^{-3}]'; -view(ax1,2) -grid on; -hold on; -f.PaperOrientation='landscape'; -[~, name, ~] = fileparts(M.file); -f.PaperUnits='centimeters'; -papsize=[16 14]; -f.PaperSize=papsize; -print(f,sprintf('%sfluid_dens',name),'-dpdf','-fillpage') -savefig(f,sprintf('%sfluid_dens',name)) +M.displaydensity(fieldstart,fieldend); %% Fieldswide f=figure('Name', sprintf('%s fields',name)); ax1=gca; -h=contourf(ax1,M.zgrid,M.rgrid,dens,'Displayname','n_e [m^{-3}]'); +h=contourf(ax1,M.zgrid,M.rgrid,dispdens,'Displayname','n_e [m^{-3}]'); hold on; %[c1,h]=contour(ax1,M.zgrid,M.rgrid,pot./1e3,5,'m--','ShowText','off','linewidth',1.5,'Displayname','Equipotentials [kV]'); %clabel(c1,h,'Color','white') Blines=zeros(size(M.Athet)); for i=1:length(M.zgrid) Blines(i,:)=M.Athet(i,:).*M.rgrid'; end zindex=floor(length(M.zgrid/2)); levels=logspace( log10(min(min(Blines(:,2:end)))), log10(max(max(Blines(:,:)))),8); contour(ax1,M.zgrid,M.rgrid,Blines',real(levels),'r-.','linewidth',1.5,'Displayname','Magnetic field lines'); +[~, hContour]=contourf(ax1,M.zgrid,M.rgrid,geomw,2); +drawnow; xlim(ax1,[M.zgrid(1) M.zgrid(end)]) ylim(ax1,[M.rgrid(1) M.rgrid(end)]) -legend +legend({'n_e [m^{-3}]','Magnetic field lines'},'location','southwest') xlabel(ax1,'z [m]') ylabel(ax1,'r [m]') -title(ax1,sprintf('Density t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(end),double(maxdens))) +title(ax1,sprintf('Density t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) c = colorbar(ax1); c.Label.String= 'n[m^{-3}]'; view(ax1,2) %set(h,'edgecolor','none'); grid on; +hFills=hContour.FacePrims; +[hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' +hFills(1).ColorData = uint8([255;255;255;255]); +for idx = 2 : numel(hFills) + hFills(idx).ColorData(4) = 0; % default=255 +end %rectangle('position',[M.zgrid(5) M.rgrid(3) M.zgrid(end-5)-M.zgrid(5) (M.rgrid(rgridend)-M.rgrid(1))],'Edgecolor','white','linestyle','--','linewidth',2) f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); f.PaperUnits='centimeters'; papsize=[18 10]; f.PaperSize=papsize; print(f,sprintf('%sFieldswide',name),'-dpdf','-fillpage') savefig(f,sprintf('%sFieldswide',name)) %% Fields f=figure('Name', sprintf('%s fields',name)); ax1=gca; -h=contourf(ax1,M.zgrid,M.rgrid,dens,'Displayname','n_e [m^{-3}]'); +h=contourf(ax1,M.zgrid*1000,M.rgrid*1000,dispdens,'Displayname','n_e [m^{-3}]'); hold on; -[c1,h]=contour(ax1,M.zgrid,M.rgrid,pot./1e3,'m--','ShowText','on','linewidth',1.5,'Displayname','Equipotentials [kV]'); -clabel(c1,h,'Color','white') +pot(M.geomweight(:,:,1)<0)=0; Blines=zeros(size(M.Athet)); for i=1:length(M.zgrid) Blines(i,:)=M.Athet(i,:).*M.rgrid'; end zindex=floor(length(M.zgrid/2)); -levels=logspace( log10(min(min(Blines(:,2:end)))), log10(max(max(Blines(:,:)))),10); -contour(ax1,M.zgrid,M.rgrid,Blines',real(levels),'r-.','linewidth',1.5,'Displayname','Magnetic field lines'); -xlim(ax1,[M.zgrid(1) M.zgrid(end)]) -ylim(ax1,[M.rgrid(1) M.rgrid(rgridend)]) -legend -xlabel(ax1,'z [m]') -ylabel(ax1,'r [m]') -title(ax1,sprintf('Density t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(end),double(maxdens))) +%levels=logspace( log10(min(min(Blines(:,2:end)))), log10(max(max(Blines(:,:)))),10); +levels=linspace(min(min(Blines(:,2:end))),max(max(Blines(:,:))),10); +[~,h1]=contour(ax1,M.zgrid*1000,M.rgrid*1000,Blines',real(levels),'r-.','linewidth',1.5,'Displayname','Magnetic field lines'); +max(abs(pot(:)))/1e3 +[c1,h2]=contour(ax1,M.zgrid*1000,M.rgrid*1000,pot./1e3,'m--','ShowText','on','linewidth',1.2,'Displayname','Equipotentials [kV]'); +clabel(c1,h2,'Color','white') + +%contour(ax1,M.zgrid*1000,M.rgrid*1000,M.geomweight(:,:,1),[0 0],'w-','linewidth',1.5); + +[c1,hContour]=contourf(ax1,M.zgrid*1000,M.rgrid*1000,geomw); +drawnow; +xlim(ax1,[M.zgrid(1)*1000 M.zgrid(end)*1000]) +if(M.conformgeom) + ylim(ax1,[M.rgrid(1)*1000 M.rgrid(rgridend)*1000]) +else + ylim(ax1,[M.rgrid(1)*1000 M.rgrid(end)*1000]) +end +legend([h1,h2],{'Magnetic field lines','Equipotentials [kV]'},'location','southwest') +xlabel(ax1,'z [mm]') +ylabel(ax1,'r [mm]') +%title(ax1,sprintf('Density t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) c = colorbar(ax1); c.Label.String= 'n[m^{-3}]'; view(ax1,2) %set(h,'edgecolor','none'); grid on; +hFills=hContour.FacePrims; +[hFills.ColorType] = deal('truecoloralpha'); % default = 'truecolor' +try +hFills(1).ColorData = uint8([150;150;150;255]); +for idx = 2 : numel(hFills) + hFills(idx).ColorData(4) = 0; % default=255 +end +catch +end f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); +if M.maxwellsrce.present + rlen=diff(M.maxwellsrce.rlim); + zlen=diff(M.maxwellsrce.zlim); +rectangle('Position',[M.maxwellsrce.zlim(1) M.maxwellsrce.rlim(1) zlen rlen]*1000,'Edgecolor','g','Linewidth',3,'Linestyle','--') +end +%rectangle('Position',[-10 73 44 8],'Edgecolor','g','Linewidth',3,'Linestyle','--') f.PaperUnits='centimeters'; -papsize=[18 10]; +papsize=[12 8]; f.PaperSize=papsize; +rectangle('Position',[M.zgrid(1) M.rgrid(1) M.zgrid(end)-M.zgrid(1) 0.064-M.rgrid(1)]*1000,'FaceColor',[150 150 150]/255) print(f,sprintf('%sFields',name),'-dpdf','-fillpage') savefig(f,sprintf('%sFields',name)) %% Brillouin f=figure('Name', sprintf('%s Brillouin',name)); ax1=gca; +brillratio(geomw<=0)=NaN; h=surface(ax1,M.zgrid,M.rgrid,brillratio); set(h,'edgecolor','none'); xlim(ax1,[M.zgrid(1) M.zgrid(end)]) -ylim(ax1,[M.rgrid(1) M.rgrid(rgridend)]) +if(M.conformgeom) + ylim(ax1,[M.rgrid(1) M.rgrid(rgridend)]) +else + ylim(ax1,[M.rgrid(1) M.rgrid(end)]) +end xlabel(ax1,'z [m]') ylabel(ax1,'r [m]') title(ax1,'Position') c = colorbar(ax1); c.Label.String= 'Brillouin Ratio'; view(ax1,2) -caxis([0 1.2]) -title(ax1,sprintf('Brillouin Ratio t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(end),double(maxdens))) +caxis([0 max(1.2,max(brillratio(:)))]) +title(ax1,sprintf('Brillouin Ratio t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); f.PaperUnits='centimeters'; papsize=[16 14]; f.PaperSize=papsize; print(f,sprintf('%sfluid_Brillouin',name),'-dpdf','-fillpage') savefig(f,sprintf('%sfluid_Brillouin',name)) %% Omegar f=figure('Name', sprintf('%s Omegar',name)); ax3=gca; +omegare(geomw<=0)=NaN; surface(ax3,M.zgrid,M.rgrid,omegare,'edgecolor','None') xlabel(ax3,'z [m]') ylabel(ax3,'r [m]') xlim(ax3,[M.zgrid(1) M.zgrid(end)]) -ylim(ax3,[M.rgrid(1) M.rgrid(rgridend)]) +if(M.conformgeom) + ylim(ax3,[M.rgrid(1) M.rgrid(rgridend)]) +else + ylim(ax3,[M.rgrid(1) M.rgrid(end)]) +end colormap(ax3,'jet') c = colorbar(ax3); c.Label.String= '|\omega_\theta| [1/s]'; %c.Limits=[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]; %caxis(ax3,[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]) -title(ax3,sprintf('Azimuthal frequency t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(end),double(maxdens))) +title(ax3,sprintf('Azimuthal frequency t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) %set(ax3,'colorscale','log') grid(ax3, 'on') view(ax3,2) f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); f.PaperUnits='centimeters'; papsize=[16 14]; f.PaperSize=papsize; print(f,sprintf('%sfluid_omegar',name),'-dpdf','-fillpage') savefig(f,sprintf('%sfluid_omegar',name)) %% VTHET f=figure('Name', sprintf('%s Vthet',name)); ax3=gca; +VTHET(geomw<=0)=NaN; surface(ax3,M.zgrid,M.rgrid,VTHET,'edgecolor','None') xlabel(ax3,'z [m]') ylabel(ax3,'r [m]') xlim(ax3,[M.zgrid(1) M.zgrid(end)]) -ylim(ax3,[M.rgrid(1) M.rgrid(rgridend)]) +if(M.conformgeom) + ylim(ax3,[M.rgrid(1) M.rgrid(rgridend)]) +else + ylim(ax3,[M.rgrid(1) M.rgrid(end)]) +end colormap(ax3,'jet') c = colorbar(ax3); c.Label.String= '|V_\theta| [m/s]'; %c.Limits=[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]; %caxis(ax3,[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]) -title(ax3,sprintf('Azimuthal velocity t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(end),double(maxdens))) +title(ax3,sprintf('Azimuthal velocity t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) %set(ax3,'colorscale','log') grid(ax3, 'on') view(ax3,2) f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); f.PaperUnits='centimeters'; papsize=[16 14]; f.PaperSize=papsize; print(f,sprintf('%sfluid_vthet',name),'-dpdf','-fillpage') savefig(f,sprintf('%sfluid_vthet',name)) -%% diff vdrift +%% vdrift f=figure('Name', sprintf('%s V drift',name)); ax3=gca; -vdrift=-mean(M.Er(:,:,fieldstart:end),3).*M.Bz'./(M.B.^2)'; -v=abs(vdrift-VTHET)./VTHET; +vdrift=(mean(M.Ez(:,:,fieldstart:end),3).*M.Br'-mean(M.Er(:,:,fieldstart:end),3).*M.Bz')./(M.B.^2)'; +vdrift(geomw<=0)=NaN; +v=vdrift; surface(ax3,M.zgrid,M.rgrid,v,'edgecolor','None') xlabel(ax3,'z [m]') ylabel(ax3,'r [m]') xlim(ax3,[M.zgrid(1) M.zgrid(end)]) -ylim(ax3,[M.rgrid(1) M.rgrid(rgridend)]) +if(M.conformgeom) + ylim(ax3,[M.rgrid(1) M.rgrid(rgridend)]) +else + ylim(ax3,[M.rgrid(1) M.rgrid(end)]) +end colormap(ax3,'jet') c = colorbar(ax3); set(ax3,'zscale','log') set(ax3,'colorscale','log') -c.Label.String= '(V_\theta-V_E)/V_\theta'; +c.Label.String= 'V_{ExB}'; %c.Limits=[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]; %caxis(ax3,[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]) -title(ax3,sprintf('(V_\\theta-V_E)/V_\\theta velocity t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(end),double(maxdens))) +title(ax3,sprintf('V_{ExB} velocity t=[%1.2g-%1.2g]s n_e=%1.2gm^{-3}',M.t2d(fieldstart),M.t2d(fieldend),double(maxdens))) %set(ax3,'colorscale','log') grid(ax3, 'on') view(ax3,2) f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); f.PaperUnits='centimeters'; papsize=[16 14]; f.PaperSize=papsize; print(f,sprintf('%sfluid_EB',name),'-dpdf','-fillpage') savefig(f,sprintf('%sfluid_EB',name)) %% HP if(M.R.nt>=2) taveragestart=max(floor(0.8*size(M.tpart,1)),2); M.displayHP(taveragestart); end +%% VR, VTHET, VZ +M.displayVdistribRThetZ() + +%% Vperp Vpar +M.displayVdistribParPer() + + %% 0D diagnostics BrillouinRatioth=2*M.omepe^2/M.omece^2 BrillouinMax=max(max(brillratio)) -NpartsTrapped=mean(M.nbparts(end-10:end))*M.msim/M.me -dblengthiter=fieldstart:length(M.t2d); +NpartsTrapped=mean(M.npart(end-10:end))*M.msim/M.me +dblengthiter=fieldstart:fieldend; Pr=squeeze(M.Presstens(1,:,:,dblengthiter)); Pz=squeeze(M.Presstens(6,:,:,dblengthiter)); enddens=M.N(:,:,dblengthiter); meanPr=mean(Pr,3); meanPz=mean(Pz,3); Tr=Pr./enddens; Tz=Pz./enddens; Debye_length=sqrt(abs(min(min(min(mean(Tr(Tr>0)./enddens(Tr>0),3))),min(min(mean(Tz(Tz>0)./enddens(Tz>0),3)))))*M.eps_0/M.qe^2) %% Debye length f=figure('Name', sprintf('%s Debye',name)); subplot(2,1,1) -surface(M.zgrid,M.rgrid,sqrt(mean(abs(Tr./enddens*M.eps_0/M.qe^2),3))) +surface(M.zgrid,M.rgrid,sqrt(mean(abs(Tr./enddens*M.eps_0/M.qe^2),3)),'edgecolor','none') colorbar f.PaperOrientation='landscape'; xlabel('z [m]') ylabel('r [m]') c = colorbar; c.Label.String= '\lambda_r [m]'; subplot(2,1,2) -surface(M.zgrid,M.rgrid,sqrt(mean(abs(Tz./enddens*M.eps_0/M.qe^2),3))) +surface(M.zgrid,M.rgrid,sqrt(mean(abs(Tz./enddens*M.eps_0/M.qe^2),3)),'edgecolor','none') colorbar f.PaperOrientation='landscape'; xlabel('z [m]') ylabel('r [m]') c = colorbar; c.Label.String= '\lambda_z [m]'; [~, name, ~] = fileparts(M.file); f.PaperUnits='centimeters'; papsize=[16 14]; f.PaperSize=papsize; print(f,sprintf('%s_dblength',name),'-dpdf','-fillpage') savefig(f,sprintf('%s_dblength',name)) %% Temperature f=figure('Name', sprintf('%s Temperature',name)); ax1=subplot(2,1,1); title('Radial temperature') surface(M.zgrid,M.rgrid,mean(Tr,3)/M.qe,'edgecolor','none') colormap('jet') c = colorbar; c.Label.String= 'T_{er} [eV]'; templimits1=caxis; ax2=subplot(2,1,2); title('Axial tempreature') surface(M.zgrid,M.rgrid,mean(Tz,3)/M.qe,'edgecolor','none') colormap('jet') c = colorbar; c.Label.String= 'T_{ez} [eV]'; maxtemp=max([templimits1,caxis]); caxis(ax1,[0 maxtemp]) caxis(ax2,[0 maxtemp]) f.PaperOrientation='landscape'; [~, name, ~] = fileparts(M.file); f.PaperUnits='centimeters'; papsize=[16 14]; f.PaperSize=papsize; print(f,sprintf('%s_temp',name),'-dpdf','-fillpage') savefig(f,sprintf('%s_temp',name)) %% Wells -M.displaypotentialwell(t2dlength); +M.display2Dpotentialwell(t2dlength-1); +M.display2Dpotentialwell(t2dlength-1,false); diff --git a/matlab/coaxial_ion_loss_criteria.m b/matlab/coaxial_ion_loss_criteria.m index fdc9696..57b4700 100644 --- a/matlab/coaxial_ion_loss_criteria.m +++ b/matlab/coaxial_ion_loss_criteria.m @@ -1,286 +1,287 @@ %% wr+ H0=3.2e-14; P0=8.66e-25; r0=0.005; qe=1.60217662E-19; me=9.109383E-31; eps_0=8.85418781762e-12; vlight=299792458; -ne=5e15; +ne=6e17; ne=5*logspace(10,19,5000); kb=1.38e-23; T=300; P=1e-9*100000; estimneutraldensity=P/(kb*T) I=40; V=90; alpha=1.3; B0=0.21; L=0.48; z=0.0; % Davidson prefix='Davidson'; phia=-60000; phib=0; R=1.5; B0=0.21; width=0.48; b=0.16; a=0.001; r=0.0079; zmin=0.0; zmax=-width/2; deltar=(R-1)/(R+1)*besseli(1,2*pi*r/width)*(width/2/pi)*(cos(2*pi*zmin/width)-cos(2*pi*zmax/width)); rb_=0.00764; rbplus=0.0842; Te=400; %eV %1rbplus=0.020; % Davidson test prefix='Davidson'; phia=-60000; phib=0; R=1.5; B0=0.21; width=0.48; b=0.16; a=0.001; rb_=0.0076; r=9.7e-3 ; zmin=0.0; zmax=-width/8; deltar=(R-1)/(R+1)*besseli(1,2*pi*r/width)*(width/2/pi)*(cos(2*pi*zmin/width)-cos(2*pi*zmax/width)); rbplus=rb_ + max(deltar,0.0026); rcenter=r+deltar Te=400; %eV %1rbplus=0.020; % rb_=0.01; % rbplus=rb_+0.1; % r=(rb_+rbplus)/2; % r=0.015 % deltar=(R-1)/(R+1)*r; -% ITER -% prefix='ITER' -% R=1.0001; -% phia=-10000; -% phib=0; -% b=0.09; -% a=0.068; -% r=0.085; -% deltar=(R-1)/(R+1)*r; -% rb_=0.075; -% rbplus=0.088; +%ITER +prefix='ITER' +R=1.0001; +phia=-5000; +phib=0; +b=0.0792; +a=0.064; +r=0.080; +deltar=(R-1)/(R+1)*r; +rb_=0.0766; +rbplus=0.079; spcharge=@(ne)-qe*ne/2/eps_0; deltaphicloud=@(ne) Phi(r+deltar,rb_,rbplus,a,b,phia,phib, ne)-Phi(r,rb_,rbplus,a,b,phia,phib, ne); deltaphivacuum=(phib-phia)/log(b/a)*log((r+deltar)/r); -% figure -% rforphi=linspace(0.001,0.16,1000); -% phir=zeros(length(rforphi),1); -% for i=1:length(rforphi) -% phir(i)=Phi(rforphi(i),rb_,rbplus,a,b,phia,phib, 1e15); -% end -% plot(rforphi,phir) -% figure -% plot((rforphi(1:end-1)+rforphi(2:end))/2,diff(phir)./diff(rforphi)) -% +figure +rforphi=linspace(a,b,1000); +phir=zeros(length(rforphi),1); +for i=1:length(rforphi) + phir(i)=Phi(rforphi(i),rb_,rbplus,a,b,phia,phib, 6e17); +end +plot(rforphi,phir) +title('Phi') +figure +plot((rforphi(1:end-1)+rforphi(2:end))/2,-diff(phir)./diff(rforphi)) +title('E_r') Emaxcloud=1/(R-1)*deltaphicloud(ne); Emaxvacuum=1/(R-1)*deltaphivacuum rgrid=linspace(1e-3,16e-2,500); zgrid=linspace(-0.24,0.24,1000); GainedE=zeros(length(rgrid),length(zgrid)); for i=1:length(zgrid) drgrid=(R-1)/(R+1)*besseli(1,2*pi*rgrid/width)*(width/2/pi)*(cos(2*pi*0/width)-cos(2*pi*zgrid(i)/width)); GainedE(:,i)=-(Phivacuum(rgrid,a,b,phia,phib)-Phivacuum(rgrid+drgrid,a,b,phia,phib)); end figure title(sprintf('\\phi_a=%3.2f kV \\phi_b=%3.2f kV R=%3.2f',phia/1e3,phib/1e3, R)) surface(zgrid,rgrid,GainedE); xlabel('z') ylabel('r') c=colorbar; c.Label.String='E [eV]'; meanE=mean(mean(abs(GainedE(20:375,ceil(length(zgrid)/2):floor(5*length(zgrid)/6))),2))/2 - +%% figure('name',sprintf('%s Phi_a=%3.2f kV Phi_b=%3.2f kV r=%3.2f mm R=%3.2f',prefix,phia/1e3,phib/1e3,r*1e3, R)); loglog(ne,abs(Emaxcloud),'displayname','flat top density cloud') hold on loglog([ne(1) ne(end)], Emaxvacuum*[1 1],'displayname','Vacuum') xlabel('electron density [m^{-3}]') ylabel('|E_{min}| [eV]') title(sprintf('%s \\Phi_a=%3.2f kV \\Phi_b=%3.2f kV r=%3.2f mm R=%3.2f',prefix,phia/1e3,phib/1e3,r*1e3, R)) savefig(sprintf('%s_Phia%3.2f_Phib%3.2f_r%3.2f_R%3.2f.fig',prefix,phia/1e3,phib/1e3,r*1e3, R)) figure subplot(2,2,1) sgtitle(sprintf('%s \\Phi_a=%3.2f kV \\Phi_b=%3.2f kV r_0=%3.2f mm z_0=%3.2f mm R=%3.2f',prefix,phia/1e3,phib/1e3,(r+deltar)*1e3,z*1e3, R)) neinter=5e13; vpar=linspace(1,vlight,1000); vper=sqrt(-2*qe/me*deltaphicloud(neinter)/(R-1)+vpar.^2/(R-1))/vlight; vpar=vpar/vlight; vpar=vpar(real(vper)~=0); vper=vper(real(vper)~=0); %Davidson vperp phi0=Phi(r,rb_,rbplus,a,b,phia,phib, neinter); vrz=sqrt(2/me*H0+2/me*qe*phi0-1/me^2*(P0/r+qe*Athet(r,z,B0,width,R))^2); theta=2*pi*linspace(0,1,1000); vthet=(P0/r+qe*Athet(r,z,B0,width,R))/me; vperdav=sqrt((vrz^2*cos(theta).^2+vthet^2))/vlight; vpardav=vrz*sin(theta)/vlight; vpargauss=sqrt(Te*qe/me)*linspace(-1,1,200); vpergauss=sqrt(2*Te*qe/me)*sqrt(1-vpargauss.^2*me/Te/qe); vpargauss=[flip(vpargauss(2:end)) vpargauss]/vlight; vpergauss=[-flip(vpergauss(2:end)) vpergauss]/vlight; plot(vpar,vper,'b-') hold on plot(vpar,-vper,'b-') plot(-vpar,vper,'b-') plot(-vpar,-vper,'b-') xlabel('\beta_z [m/s]') ylabel('\beta_\perp [m/s]') title(sprintf('electrons n_e=%3.2e m^{-3}',neinter)) grid h(1)=plot(vpardav,vperdav,'r--','Displayname',sprintf('Dav H0=%#.2g eV P0=%#.2g kg m^2 s^{-1}',H0/qe,P0)); h(2)=plot(vpargauss,vpergauss,'g--','Displayname',sprintf('Gauss Te=%#.2g eV',Te)); legend(h) legend('location','northoutside') xlim([-1 1]) ylim([-1 1]) subplot(2,2,3) neinter=5e17; vpar=linspace(1,vlight,1000); vper=sqrt(-2*qe/me*deltaphicloud(neinter)/(R-1)+vpar.^2/(R-1))/vlight; vpar=vpar/vlight; vpar=vpar(real(vper)~=0); vper=vper(real(vper)~=0); %Davidson vperp phi0=Phi(r,rb_,rbplus,a,b,phia,phib, neinter); vrz=sqrt(2/me*H0+2/me*qe*phi0-1/me^2*(P0/r+qe*Athet(r,z,B0,width,R))^2); theta=2*pi*linspace(0,1,1000); vthet=(P0/r+qe*Athet(r,z,B0,width,R))/me; vperdav=sqrt((vrz^2*cos(theta).^2+vthet^2))/vlight; vpardav=vrz*sin(theta)/vlight; vpargauss=sqrt(Te*qe/me)*linspace(-1,1,200); vpergauss=sqrt(2*Te*qe/me)*sqrt(1-vpargauss.^2*me/Te/qe); vpargauss=[flip(vpargauss(2:end)) vpargauss]/vlight; vpergauss=[-flip(vpergauss(2:end)) vpergauss]/vlight; plot(vpar,vper,'b-') hold on plot(vpar,-vper,'b-') plot(-vpar,vper,'b-') plot(-vpar,-vper,'b-') xlabel('\beta_z [m/s]') ylabel('\beta_\perp [m/s]') title(sprintf('electrons n_e=%3.2e m^{-3}',neinter)) grid h(1)=plot(vpardav,vperdav,'r--','Displayname',sprintf('Dav H0=%#.2g eV P0=%#.2g kg m^2 s^{-1}',H0/qe,P0)); h(2)=plot(vpargauss,vpergauss,'g--','Displayname',sprintf('Gauss Te=%#.2g eV',Te)); xlim([-1 1]) ylim([-1 1]) subplot(2,2,2) neinter=5e13; vpar=linspace(1,vlight,1000); m=28.0134/1000*6.02214076e23; vper=sqrt(2*qe/m*deltaphicloud(neinter)/(R-1)+vpar.^2/(R-1))/vlight; vpar=vpar/vlight; vpar=vpar(real(vper)~=0); vper=vper(real(vper)~=0); plot(vpar,vper,'b-') hold on plot(vpar,-vper,'b-') plot(-vpar,vper,'b-') plot(-vpar,-vper,'b-') xlabel('\beta_z [m/s]') ylabel('\beta_\perp [m/s]') title(sprintf('N_2^+ ions n_e=%3.2e m^{-3}',neinter)) grid xlim([-1 1]) ylim([-1 1]) subplot(2,2,4) neinter=5e17; vpar=linspace(1,vlight,1000); vper=sqrt(2*qe/m*deltaphicloud(neinter)/(R-1)+vpar.^2/(R-1))/vlight; vpar=vpar/vlight; vpar=vpar(real(vper)~=0); vper=vper(real(vper)~=0); plot(vpar,vper,'b-') hold on plot(vpar,-vper,'b-') plot(-vpar,vper,'b-') plot(-vpar,-vper,'b-') xlabel('\beta_z [m/s]') ylabel('\beta_\perp [m/s]') title(sprintf('N_2^+ ions n_e=%3.2e m^{-3}',neinter)) grid savefig(sprintf('%s_Phia%3.2f_Phib%3.2f_r%3.2f_z%3.2f_R%3.2f_cone.fig',prefix,phia/1e3,phib/1e3,(r+deltar)*1e3,z*1e3, R)) xlim([-1 1]) ylim([-1 1]) %% figure r=double(M.rgrid); r(r<1e-3)=1e-3; r(r>16e-2)=16e-2; phi=zeros(length(r),1); for i=1:length(r) phi(i)=Phi(r(i),14e-2,15e-2,0.001,16e-2,-60000,0,0); end plot(r,phi) phim=phi*ones(1,length(M.zgrid)); function Atheta=Athet(r,z,B0,width,R) Atheta=0.5*B0*(r-width/pi*(R-1)/(R+1)... .*besseli(1,2*pi*r/width).*cos(2*pi*z/width)); end function phi=Phi(r,rbm,rbp,a,b,phia,phib, ne) qe=1.60217662E-19; me=9.109383E-31; eps_0=8.85418781762e-12; q=-qe; -phibp=1/log(b/a)*(-ne/2*q/eps_0*rbm^2*log(b/rbp)*log(rbp/rbm)+(-q*ne/2/eps_0*(rbp^2-rbm^2)*(log(rbp/a)-0.5)+phia)*log(b/rbp)+phib*log(rbp/a)); -phibm=1/log(b/a)*(q/eps_0*ne/2*rbm^2*log(rbm/a)*log(rbp/rbm)+(-q/eps_0*ne/2*(rbp^2-rbm^2)*(log(b/rbp)+0.5)+phib)*log(rbm/a)+phia*log(b/rbm)); +phibp=1/log(b/a)*(ne/2*q/eps_0*rbm^2*log(b/rbp)*log(rbp/rbm)+(q*ne/2/eps_0*(rbp^2-rbm^2)*(log(rbp/a)-0.5)+phia)*log(b/rbp)+phib*log(rbp/a)); +phibm=1/log(b/a)*(-q/eps_0*ne/2*rbm^2*log(rbm/a)*log(rbp/rbm)+(q/eps_0*ne/2*(rbp^2-rbm^2)*(log(b/rbp)+0.5)+phib)*log(rbm/a)+phia*log(b/rbm)); if(r=a) phi=((phibm-phia)*log(r)+phia*log(rbm)-phibm*log(a))/log(rbm/a); elseif(r>=rbm && r<=rbp) - phi=-qe/eps_0*ne/4*(r^2-rbp^2)+phibp+(phibp-phibm+qe/eps_0*ne/4*(rbp^2-rbm^2))*log(r/rbp)/log(rbp/rbm); + phi=-q/eps_0*ne/4*(r^2-rbp^2)+phibp+(phibp-phibm+q/eps_0*ne/4*(rbp^2-rbm^2))*log(r/rbp)/log(rbp/rbm); elseif(r>rbp && r<= b) phi=((phib-phibp)*log(r)+phibp*log(b)-phib*log(rbp))/log(b/rbp); else error('invalid radial position') end end function phi=Phivacuum(r,a,b,phia,phib) phi=((phib-phia)*log(r)+phia*log(b)-phib*log(a))/log(b/a); end \ No newline at end of file diff --git a/matlab/diocotronstab.m b/matlab/diocotronstab.m new file mode 100644 index 0000000..00e5477 --- /dev/null +++ b/matlab/diocotronstab.m @@ -0,0 +1,84 @@ +function [w, phin, r] = diocotronstab(l, R, n, wre, wce, nRmin) +if nargin<6 + nRmin=512; +end +% Necessary physical constants +% electron charge and mass + e = 1.602176634e-19; + me = 9.1093837015e-31; +% vacuum permittivity + eps_0 = 8.854188e-12; + +% declaration of wpe2 (vector for numerical solution, renormalised on wce) + + wpe2 = n*(e^2)/(me*eps_0); + +% scale factor for the frequency + wscale=max(abs(wpe2/(2*wce))); + + % construction of r, new vector of distances (chooses the thinner mesh and + % interpolate all variables on that mesh + + delta = diff(R); + delta=delta(delta>0); + x = unique(delta); + N = numel(x); + count = zeros(N,1); + for k = 1:N + count(k) = sum(delta==x(k)); + end + x(count<2)=[]; + rmin = min(x); + nR = floor((max(R)-min(R))/rmin) + 1; + if nR nin, wrin, wpe2in + wrin = interp1(R(2:end),wre(2:end),r,'spline'); + wpe2in = interp1(R,wpe2,r); + wpe2in=wpe2in/wscale^2; + wrin=wrin/wscale; + wce=wce/wscale; + + % finite difference to compute d/dr(wpe^2) + Dwpe2 = zeros([nR 1]); + Dwpe2(2:(nR-1)) = (wpe2in(3:end)-wpe2in(1:(nR-2)))/(2*rmin); + +% construction of matrices A & B such that A*phi = w*B*phi + + A = zeros(nR,nR); + B = zeros(nR,nR); + + + % finite differences in A & B + other terms of the eigenvalue + % equation + + for i = 2:1:(nR-1) + A(i,i) = l*wrin(i)*(-2/(rmin)^2 - l^2/r(i)^2) - l*Dwpe2(i)/wce/r(i); + A(i,i-1) = l*wrin(i)*(1/(rmin)^2 - 1/(2*r(i)*rmin)); + A(i,i+1) = l*wrin(i)*(1/(rmin)^2 + 1/(2*r(i)*rmin)); + + B(i,i) = - 2/(rmin)^2 - (l^2/r(i)^2); + B(i,i-1) = 1/(rmin)^2 - 1/(2*r(i)*rmin); + B(i,i+1) = 1/(rmin)^2 + 1/(2*r(i)*rmin); + + end + A=A(2:(end-1),2:(end-1)); + B=B(2:(end-1),2:(end-1)); + +% Solution of the eigenvalue problem + [phin,w] = eig(A, B); + +% renormalisation of w and r + w=diag(w); + w = w*wscale; + r=r*b; +end + diff --git a/matlab/dispespicFields.m b/matlab/dispespicFields.m deleted file mode 100644 index 61c9dbf..0000000 --- a/matlab/dispespicFields.m +++ /dev/null @@ -1,195 +0,0 @@ -function dispespicFields(M,logdensity,showgrid,fixed) -%dispespicFields Allows to display the time evolution of the density, electric potential and electric fields -% M is of class espic2dhdf5 and contains the simulation results -fieldstep=1; -if nargin <2 - logdensity=false; - showgrid=false; - fixed=false; -end -if nargin <3 - showgrid=false; - fixed=false; -end -if nargin <4 - fixed=false; -end -fixed=fi(fixed); - -f=uifigure('Name',sprintf('Grid data %s',M.name)); - -mf=uipanel(f,'Position',[5 50 f.Position(3)-10 f.Position(4)-55]); -mf.AutoResizeChildren='off'; -m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); - -sgtitle(mf,sprintf('step=%d t=%0.5e s',fieldstep*M.it1,M.t2d(fieldstep))) - -sld = uislider(m,'Position',[10 30 0.6*m.Position(3) 3]); -sld.Value=fieldstep; -sld.Limits=[1 size(M.t2d,1)]; - -edt = uieditfield(m,'numeric','Limits',[1 size(M.t2d,1)],'Value',1); -edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; -edt.RoundFractionalValues='on'; - -MaxN=0; -Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); -Play=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play'); -Pause=uibutton(m,'Position',[Play.Position(1)+Play.Position(3)+10 5 40 20],'Text', 'Pause'); -%Playbt=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play/Pause'); - -stop=false; - -sld.ValueChangingFcn={@updatefigdata,edt,mf}; -edt.ValueChangedFcn={@updatefigdata,sld,mf}; -Printbt.ButtonPushedFcn={@plotGridButtonPushed}; -Play.ButtonPushedFcn={@plotPlayButtonPushed}; - -Pause.ButtonPushedFcn={@PauseButtonPushed}; -PlotEspic2dgriddata(mf,M,fieldstep); - - function plotPlayButtonPushed(btn,ax) - stop=false; - i=sld.Value; - while ~stop - edt.Value=i; - sld.Value=i; - updatesubplotsdata(i,mf); - pause(0.01) - i=sld.Value; - i=i+10; - if(i>sld.Limits(2)) - stop=true; - end - end - end - function PauseButtonPushed(btn,ax) - stop = true; - end - function PlotEspic2dgriddata(fig,M,fieldstep) - %PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep - sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,M.t2d(fieldstep))) - - - ax1=subplot(2,2,1,'Parent',fig); - sf=surface(ax1,M.zgrid,M.rgrid,M.N(:,:,fieldstep),'edgecolor','none'); - if(showgrid) - set(sf,'edgecolor','black') - end - xlim(ax1,[M.zgrid(1) M.zgrid(end)]) - ylim(ax1,[M.rgrid(1) M.rgrid(end)]) - xlabel(ax1,'z [m]') - ylabel(ax1,'r [m]') - title(ax1,'Position') - c = colorbar(ax1); - c.Label.String= 'n[m^{-3}]'; - %c.Limits=[0 max(M.N(:))]; - if(isboolean(fixed) && fixed) - climits=caxis(ax1); - MaxN=climits(2); - elseif(~isboolean(fixed)) - MaxN=fixed.data; - caxis(ax1,[-Inf MaxN]); - end - view(ax1,2) - - if logdensity - set(ax1,'zscale','log') - set(ax1,'colorscale','log') - end - - ax2=subplot(2,2,2,'Parent',fig); - surface(ax2,M.zgrid,M.rgrid,M.pot(:,:,fieldstep),'edgecolor','none'); - %plot(ax2,M.zgrid,data.pot(:,5)) - xlabel(ax2,'z [m]') - ylabel(ax2,'r [m]') - colormap(ax2,'jet') - c = colorbar(ax2); - c.Label.String= '\Phi [V]'; - title(ax2,'Es potential') - grid(ax2, 'on') - - - ax3=subplot(2,2,3,'Parent',fig); - contourf(ax3,M.zgrid,M.rgrid,M.Epar(fieldstep)) - xlabel(ax3,'z [m]') - ylabel(ax3,'r [m]') - c = colorbar(ax3); - c.Label.String= 'E_{par} [V/m]'; - title(ax3,'Parallel Electric field') - grid(ax3, 'on') - - ax4=subplot(2,2,4,'Parent',fig); - contourf(ax4,M.zgrid,M.rgrid,M.Eperp(fieldstep)) - xlabel(ax4,'z [m]') - ylabel(ax4,'r [m]') - c = colorbar(ax4); - c.Label.String= 'E_{per} [V/m]'; - title(ax4,'Perpendicular Electric field') - grid(ax4, 'on') - - linkaxes([ax1,ax2,ax3,ax4]); - end - - function plotGridButtonPushed(btn,ax) - %UNTITLED2 Summary of this function goes here - % Detailed explanation goes here - f=figure(); - PlotEspic2dgriddata(f,M,sld.Value); - f.PaperOrientation='landscape'; - [~, name, ~] = fileparts(M.file); - print(f,sprintf('%sGrid%d',name,sld.Value),'-dpdf','-fillpage') - end - - function updatefigdata(control, event, Othercontrol, fig) - - - if strcmp(event.EventName,'ValueChanged') - fieldstep=floor(control.Value); - control.Value=fieldstep; - else - fieldstep=floor(event.Value); - end - Othercontrol.Value=fieldstep; - updatesubplotsdata(fieldstep, fig); - end - - function updatesubplotsdata(fieldstep, fig) - - sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,M.t2d(floor(fieldstep)))) - - %% update Position histogram - ax1=fig.Children(end); - dens=M.N(:,:,fieldstep); - - ax1.Children.ZData=dens; - ax1.Children.CData=dens; - if(isboolean(fixed) && fixed) - climits=caxis(ax1); - MaxN=climits(2); - nmax=max(dens(:)); - if(nmax>MaxN) - MaxN=nmax; - end - caxis(ax1,[0 MaxN]); - elseif(~isboolean(fixed)) - MaxN=fixed.data; - caxis(ax1,[-Inf MaxN]); - end - - % view(ax1,2) - %% update ES Potential - fig.Children(end-2).Children(1).ZData=M.pot(:,:,fieldstep); - %% update Radial electric field - fig.Children(end-4).Children(1).ZData=M.Epar(fieldstep); - %% update Axial electric field - fig.Children(end-6).Children(1).ZData=M.Eperp(fieldstep); - drawnow limitrate - - - - end - -end - - diff --git a/matlab/dispespicFluid.m b/matlab/dispespicFluid.m deleted file mode 100644 index 64039e7..0000000 --- a/matlab/dispespicFluid.m +++ /dev/null @@ -1,148 +0,0 @@ -function f=dispespicFluid(M) -fieldstep=1; - -f=uifigure('Name','Fluid data'); - -mf=uipanel(f,'Position',[5 50 f.Position(3)-10 f.Position(4)-55]); -mf.AutoResizeChildren='off'; -m=uipanel(f,'Position',[5 5 f.Position(3)-10 40]); - -sgtitle(mf,sprintf('t=%0.5e s',M.t2d(fieldstep))) - -sld = uislider(m,'Position',[10 30 0.6*m.Position(3) 3]); -sld.Value=fieldstep; -sld.Limits=[1 length(M.t2d)]; - -edt = uieditfield(m,'numeric','Limits',[1 length(M.t2d)],'Value',1); -edt.Position=[sld.Position(1)+sld.Position(3)+25 5 40 20]; -edt.RoundFractionalValues='on'; - - -Printbt=uibutton(m,'Position',[edt.Position(1)+edt.Position(3)+10 5 40 20],'Text', 'Save'); -%Playbt=uibutton(m,'Position',[Printbt.Position(1)+Printbt.Position(3)+10 5 40 20],'Text', 'Play/Pause'); - - -sld.ValueChangingFcn={@updatefigdata,edt,mf}; -edt.ValueChangedFcn={@updatefigdata,sld,mf}; -Printbt.ButtonPushedFcn={@plotGridButtonPushed}; -[R,Z]=meshgrid(M.rgrid,M.zgrid); -Rinv=1./R; -Rinv(:,1)=0; - -PlotEspic2dfluiddata(mf,M,fieldstep); - -function PlotEspic2dfluiddata(fig,M,fieldstep) -%PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep -sgtitle(fig,sprintf('t=%0.5e s',M.t2d(fieldstep))) - - -ax1=subplot(2,2,1,'Parent',fig); -surface(ax1,M.zgrid,M.rgrid,M.N(:,:,fieldstep),'edgecolor','none'); -xlim(ax1,[M.zgrid(1) M.zgrid(end)]) -ylim(ax1,[M.rgrid(1) M.rgrid(end)]) -xlabel(ax1,'z [m]') -ylabel(ax1,'r [m]') -title(ax1,'Position') -c = colorbar(ax1); -c.Label.String= 'n[m^{-3}]'; -%c.Limits=[0 max(M.N(:))]; -%caxis(ax1,[0 max(M.N(:))]); -view(ax1,2) - -%set(ax1,'colorscale','log') - - -ax2=subplot(2,2,2,'Parent',fig); -surface(ax2,M.zgrid,M.rgrid,M.fluidUR(:,:,fieldstep),'edgecolor','none'); -%plot(ax2,M.zgrid,data.pot(:,5)) -xlabel(ax2,'z [m]') -ylabel(ax2,'r [m]') -colormap(ax2,'jet') -c = colorbar(ax2); -%c.Limits=[min(M.fluidUR(:,:,:)) max(M.fluidUR(:,:,:))]; -c.Label.String= 'U_r [m/s]'; -title(ax2,'Radial velocity') -grid(ax2, 'on') -%caxis(ax2,[min(M.fluidUR(:)) max(M.fluidUR(:))]) -view(ax2,2) - -ax3=subplot(2,2,3,'Parent',fig); -surface(ax3,M.zgrid,M.rgrid,abs(M.fluidUTHET(:,:,fieldstep).*Rinv'),'edgecolor','none') - -%mean(M.fluidUTHET(:,:,end).*Rinv') -xlabel(ax3,'z [m]') -ylabel(ax3,'r [m]') -colormap(ax3,'jet') -c = colorbar(ax3); -c.Label.String= '\omega_\theta [1/s]'; -%c.Limits=[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]; -%caxis(ax3,[min(M.fluidUTHET(:)) max(M.fluidUTHET(:))]) -title(ax3,'Azimuthal velocity') -set(ax3,'colorscale','log') -grid(ax3, 'on') -view(ax3,2) - -ax4=subplot(2,2,4,'Parent',fig); -surface(ax4,M.zgrid,M.rgrid,M.fluidUZ(:,:,fieldstep),'edgecolor','none') -xlabel(ax4,'z [m]') -ylabel(ax4,'r [m]') -colormap(ax4,'jet') -c = colorbar(ax4); -%c.Limits=[min(M.fluidUZ(:)) max(M.fluidUZ(:))]; -c.Label.String= 'U_z [m/s]'; -title(ax4,'Axial velocity') -grid(ax4, 'on') -%caxis(ax4,[min(M.fluidUZ(:)) max(M.fluidUZ(:))]) -view(ax4,2) - -linkaxes([ax1 ax2 ax3 ax4],'xy') - -end - -function plotGridButtonPushed(btn,ax) -%UNTITLED2 Summary of this function goes here -% Detailed explanation goes here -f=figure(); -PlotEspic2dgriddata(f,M,sld.Value); -f.PaperOrientation='landscape'; -[~, name, ~] = fileparts(M.file); -print(f,sprintf('%sfluid%d',name,sld.Value),'-dpdf','-fillpage') -end - -function updatefigdata(control, event, Othercontrol, fig) - - - if strcmp(event.EventName,'ValueChanged') - fieldstep=floor(control.Value); - control.Value=fieldstep; - else - fieldstep=floor(event.Value); - end - Othercontrol.Value=fieldstep; - - sgtitle(fig,sprintf('t=%0.5e s',double((fieldstep-1)*M.it1)*M.dt)) - - %% update Position histogram - ax1=fig.Children(end); - ax1.Children(1).CData=M.N(:,:,fieldstep); - ax1.Children(1).ZData=M.N(:,:,fieldstep); - - view(ax1,2) - %% update Radial velocity - fig.Children(end-2).Children(1).CData=M.fluidUR(:,:,fieldstep); - fig.Children(end-2).Children(1).ZData=M.fluidUR(:,:,fieldstep); - %% update Azimuthal velocity - fig.Children(end-4).Children(1).CData=abs(M.fluidUTHET(:,:,fieldstep).*Rinv'); - fig.Children(end-4).Children(1).ZData=abs(M.fluidUTHET(:,:,fieldstep).*Rinv'); - %% update Axial velocity - fig.Children(end-6).Children(1).CData=M.fluidUZ(:,:,fieldstep); - fig.Children(end-6).Children(1).ZData=M.fluidUZ(:,:,fieldstep); - drawnow limitrate - - - -end - -end - - diff --git a/matlab/dispespicParts.m b/matlab/dispespicParts.m index 7eb9970..0c9ef1f 100644 --- a/matlab/dispespicParts.m +++ b/matlab/dispespicParts.m @@ -1,138 +1,237 @@ -function dispespicParts(M) +function dispespicParts(M,fieldstep,parper) %dispespicParts Show on an interactive plot the phase space of the given specie in M % Detailed explanation goes here -fieldstep=1; - +if nargin<2 + fieldstep=1; +end +if nargin<3 + parper=false; +end f2=uifigure('Name','Particles data'); mf2=uipanel(f2,'Position',[5 50 f2.Position(3)-10 f2.Position(4)-55]); mf2.AutoResizeChildren='off'; m2=uipanel(f2,'Position',[5 5 f2.Position(3)-10 40]); sgtitle(mf2,sprintf('step=%d t=%0.5e s',fieldstep*M.it2,M.tpart(fieldstep))) sld2 = uislider(m2,'Position',[10 30 0.6*m2.Position(3) 3]); sld2.Value=fieldstep; sld2.Limits=[1 length(M.tpart)]; edt2 = uieditfield(m2,'numeric','Limits',[1 length(M.tpart)],'Value',1); edt2.Position=[sld2.Position(1)+sld2.Position(3)+25 5 40 20]; edt2.RoundFractionalValues='on'; Printbt2=uibutton(m2,'Position',[edt2.Position(1)+edt2.Position(3)+10 5 40 20],'Text', 'Save'); sld2.ValueChangingFcn={@updatefigpartdata,edt2,mf2}; edt2.ValueChangedFcn={@updatefigpartdata,sld2,mf2}; Printbt2.ButtonPushedFcn={@plotPartButtonPushed}; +set(f2,'KeyPressFcn',{ @onKeyDown,sld2,edt2,mf2}) + PlotEspic2dpartdata(mf2,M,fieldstep); function PlotEspic2dpartdata(fig,M,fieldstep) %PlotEspic2dgriddata Plot the 2d data of espic2d at time step fieldstep - sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it2,M.tpart(fieldstep))) + %sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it2,M.tpart(fieldstep))) + sgtitle(fig,sprintf('t=%0.5e s nbparts=%d',M.tpart(fieldstep),M.nbparts(fieldstep))) + + vmax=4e7; + + Z=M.Z(1:min(M.nbparts(fieldstep),M.Z.nparts),fieldstep); + R=M.R(1:min(M.nbparts(fieldstep),M.R.nparts),fieldstep); + + if parper + vpar=M.Vpar(1:min(M.nbparts(fieldstep),M.Z.nparts),fieldstep); + vper=M.Vperp(1:min(M.nbparts(fieldstep),M.Z.nparts),fieldstep); + else + vpar=M.VZ(1:min(M.nbparts(fieldstep),M.Z.nparts),fieldstep); + vper=M.VR(1:min(M.nbparts(fieldstep),M.Z.nparts),fieldstep); + end ax1=subplot(3,2,1,'Parent',fig); - plot(ax1,M.Z(1:M.nbparts(fieldstep),fieldstep),M.R(1:M.nbparts(fieldstep),fieldstep),'.'); - xlim(ax1,[M.zgrid(1) M.zgrid(end)]) - ylim(ax1,[M.rgrid(1) M.rgrid(end)]) + if isa(M,'h5parts') + contour(ax1,M.zgrid,M.rgrid,M.parent.geomweight(:,:,1),[0 0],'r--') + rindex=M.rindex; + zindex=M.zindex; + else + contour(ax1,M.zgrid,M.rgrid,M.geomweight(:,:,1),[0 0],'r--') + rindex=[1 length(M.rgrid)]; + zindex=[1 length(M.zgrid)]; + end + hold(ax1, 'on') + rectangle(ax1,'Position',[M.zgrid(zindex(1)) M.rgrid(rindex(1))... + M.zgrid(zindex(end))-M.zgrid(zindex(1)) M.rgrid(rindex(end))-M.rgrid(rindex(1))],'linestyle','--') + plot(ax1,Z,R,'.'); + + if isa(M,'h5parts') + xlim(ax1,[M.zgrid(zindex(1)) M.zgrid(zindex(end))]) + ylim(ax1,[M.rgrid(rindex(1)) M.rgrid(rindex(end))]) + end xlabel(ax1,'Z [m]') ylabel(ax1,'R [m]') title(ax1,'Position') grid(ax1, 'on') - ax2=subplot(3,2,2,'Parent',fig); - plot(ax2,M.Vpar(1:M.nbparts(fieldstep),fieldstep),M.Vperp(1:M.nbparts(fieldstep),fieldstep),'.'); - xlabel(ax2,'V_{par} [m/s]') - ylabel(ax2,'V_{perp} [m/s]') + plot(ax2,vpar,vper,'.'); + if parper + xlabel(ax2,'V_{par} [m/s]') + ylabel(ax2,'V_{perp} [m/s]') + else + xlabel(ax2,'V_Z[m/s]') + ylabel(ax2,'V_R [m/s]') + end + ylim(ax2,vmax*[-1 1]) + xlim(ax2,vmax*[-1 1]) axis(ax2, 'equal') grid(ax2, 'on') ax3=subplot(3,2,3,'Parent',fig); - plot(ax3,M.Z(1:M.nbparts(fieldstep),fieldstep),M.VZ(1:M.nbparts(fieldstep),fieldstep),'.'); - xlim(ax3,[M.zgrid(1) M.zgrid(end)]) + plot(ax3,Z,vpar,'.'); xlabel(ax3,'Z [m]') - ylabel(ax3,'VZ [m/s]') + if parper + ylabel(ax3,'V_{par} [m/s]') + else + ylabel(ax3,'V_Z[m/s]') + end grid(ax3, 'on') + xlim(ax3,[M.zgrid(zindex(1)) M.zgrid(zindex(end))]) + ylim(ax3,vmax*[-1 1]) ax4=subplot(3,2,4,'Parent',fig); - plot(ax4,M.VR(1:M.nbparts(fieldstep),fieldstep),M.R(1:M.nbparts(fieldstep),fieldstep),'.') - ylabel(ax4,'R [m]') - xlabel(ax4,'VZ [m/s]') - ylim(ax4,[M.rgrid(1) M.rgrid(end)]) + plot(ax4,R,vpar,'.') + if parper + ylabel(ax4,'V_{par} [m/s]') + else + ylabel(ax4,'V_Z[m/s]') + end + xlabel(ax4,'R [m]') + xlim(ax4,[M.rgrid(rindex(1)) M.rgrid(rindex(end))]) + ylim(ax4,vmax*[-1 1]) grid(ax4, 'on') ax5=subplot(3,2,5,'Parent',fig); - plot(ax5,M.Z(1:M.nbparts(fieldstep),fieldstep),M.VR(1:M.nbparts(fieldstep),fieldstep),'.'); - xlim(ax5,[M.zgrid(1) M.zgrid(end)]) + plot(ax5,Z,vper,'.'); + xlim(ax5,[M.zgrid(zindex(1)) M.zgrid(zindex(end))]) xlabel(ax5,'Z [m]') - ylabel(ax5,'VR [m/s]') + if parper + ylabel(ax5,'V_{perp} [m/s]') + else + ylabel(ax5,'V_R [m/s]') + end grid(ax5, 'on') + ylim(ax5,vmax*[-1 1]) ax6=subplot(3,2,6,'Parent',fig); - plot(ax6,M.VR(1:M.nbparts(fieldstep),fieldstep),M.R(1:M.nbparts(fieldstep),fieldstep),'.'); - ylim(ax6,[M.rgrid(1) M.rgrid(end)]) - ylabel(ax6,'R [m]') - xlabel(ax6,'VR [m/s]') + plot(ax6,R,vper,'.'); + xlim(ax6,[M.rgrid(rindex(1)) M.rgrid(rindex(end))]) + if parper + ylabel(ax6,'V_{perp} [m/s]') + else + ylabel(ax6,'V_R [m/s]') + end + xlabel(ax6,'R [m]') + ylim(ax6,vmax*[-1 1]) grid(ax6, 'on') end + function onKeyDown(src,event,slider,editfield, fig) + direction=0; + if strcmp(event.Key,'leftarrow') + direction=-1; + elseif strcmp(event.Key,'rightarrow') + direction=+1; + elseif strcmp(event.Key,'uparrow') + direction=+10; + elseif strcmp(event.Key,'downarrow') + direction=-10; + end + + if(direction~=0) + currval=slider.Value; + slider.Value=max(slider.Limits(1),min(currval+direction,slider.Limits(2))); + updatefigpartdata(slider, event, editfield ,fig) + end + end + function updatefigpartdata(control, event, Othercontrol, fig) if strcmp(event.EventName,'ValueChanged') fieldstep=floor(control.Value); control.Value=fieldstep; + elseif strcmp(event.EventName,'KeyPress') + fieldstep=floor(control.Value); + control.Value=fieldstep; else fieldstep=floor(event.Value); end Othercontrol.Value=fieldstep; - sgtitle(fig,sprintf('t=%0.5e s',M.tpart(fieldstep))) + sgtitle(fig,sprintf('t=%0.5e s nbparts=%03d',M.tpart(fieldstep),M.nbparts(fieldstep))) + % if isa(M,'espic2dhdf5') + % [~,t0dstep]=min(abs(M.tpart(fieldstep)-M.t0d)); + % else + t0dstep=fieldstep; + % end + + Z=M.Z(1:min(M.nbparts(t0dstep),M.Z.nparts),fieldstep); + R=M.R(1:min(M.nbparts(t0dstep),M.R.nparts),fieldstep); + + if parper + vpar=M.Vpar(1:min(M.nbparts(t0dstep),M.Z.nparts),fieldstep); + vper=M.Vperp(1:min(M.nbparts(t0dstep),M.Z.nparts),fieldstep); + else + vpar=M.VZ(1:min(M.nbparts(t0dstep),M.Z.nparts),fieldstep); + vper=M.VR(1:min(M.nbparts(t0dstep),M.Z.nparts),fieldstep); + end %% update Position plot ax1=fig.Children(end); - ax1.Children.XData=M.Z(1:M.nbparts(fieldstep),fieldstep); - ax1.Children.YData=M.R(1:M.nbparts(fieldstep),fieldstep); + ax1.Children(1).XData=Z; + ax1.Children(1).YData=R; view(ax1,2) - %% update VZ VPERP - fig.Children(end-1).Children(1).XData=M.Vpar(1:M.nbparts(fieldstep),fieldstep); - fig.Children(end-1).Children(1).YData=M.Vperp(1:M.nbparts(fieldstep),fieldstep); + %% update VPAR VPERP + fig.Children(end-1).Children(1).XData=vpar; + fig.Children(end-1).Children(1).YData=vper; axis(fig.Children(end-1),'equal') %% update Z VZ - fig.Children(end-2).Children(1).XData=M.Z(1:M.nbparts(fieldstep),fieldstep); - fig.Children(end-2).Children(1).YData=M.VZ(1:M.nbparts(fieldstep),fieldstep); + fig.Children(end-2).Children(1).XData=Z; + fig.Children(end-2).Children(1).YData=vpar; %% update VZ VR - fig.Children(end-3).Children(1).XData=M.VZ(1:M.nbparts(fieldstep),fieldstep); - fig.Children(end-3).Children(1).YData=M.R(1:M.nbparts(fieldstep),fieldstep); + fig.Children(end-3).Children(1).XData=R; + fig.Children(end-3).Children(1).YData=vpar; %% update R VR - fig.Children(end-4).Children(1).XData=M.Z(1:M.nbparts(fieldstep),fieldstep); - fig.Children(end-4).Children(1).YData=M.VR(1:M.nbparts(fieldstep),fieldstep); + fig.Children(end-4).Children(1).XData=Z; + fig.Children(end-4).Children(1).YData=vper; %% update Z VR - fig.Children(end-5).Children(1).XData=M.VR(1:M.nbparts(fieldstep),fieldstep); - fig.Children(end-5).Children(1).YData=M.R(1:M.nbparts(fieldstep),fieldstep); - drawnow limitrate + fig.Children(end-5).Children(1).XData=R; + fig.Children(end-5).Children(1).YData=vper; + %drawnow limitrate end function plotPartButtonPushed(btn,ax) %UNTITLED2 Summary of this function goes here % Detailed explanation goes here f=figure(); PlotEspic2dpartdata(f,M,sld.Value); f.PaperOrientation='portrait'; [~, name, ~] = fileparts(M.file); print(f,sprintf('%sParts%d',name,sld.Value),'-dpdf','-fillpage') end end diff --git a/matlab/displayParPer.m b/matlab/displayParPer.m new file mode 100644 index 0000000..09554be --- /dev/null +++ b/matlab/displayParPer.m @@ -0,0 +1,68 @@ +%% +timesteppart=length(M.tpart); + +fig=figure; +ax1=gca; + +timestepN=find(M.tpart(end)==M.t2d,1); +Ndistrib=M.N(:,:,timestepN); +h=contourf(ax1,M.zgrid,M.rgrid,Ndistrib); +%set(h, 'EdgeColor', 'none'); +hold on +[r,z]=find(Ndistrib~=0); +xlim(ax1,[M.zgrid(min(z)) M.zgrid(max(z))]) +ylim(ax1,[M.rgrid(min(r)) M.rgrid(max(r))]) +xlabel(ax1,'Z [m]') +ylabel(ax1,'R [m]') +c = colorbar(ax1); +c.Label.String= 'n [m^{-3}]'; +view(ax1,2) +%set(ax1,'colorscale','log') + + +[x,y]=ginput(1); +Zindex=find(x>M.zgrid,1,'last'); +Rindex=find(y>M.rgrid,1,'last'); +% Rindex=155; +% Zindex=344; + +% Rindex=123; +% Zindex=658; +gcs=false; + +f=figure(); +ax1=subplot(1,2,1); +ax2=subplot(1,2,2); +%[p,maxnb,c]=M.displayPhaseSpace(1:2,Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(1)),ax1); +% ax1=subplot(1,2,1); +% hold(ax1,'off') +c=cell(2,1); +c{1}=linspace(-2e7,2e7,21); +c{2}=linspace(-0e6,3e7,51); + + +[p,maxnb,c]=M.displayPhaseSpace('parper',2,Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(2)),ax1,-1,c,gcs); +M.displayPhaseSpace('parper',length(M.tpart)+(0),Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(end)),ax2,-1,c,gcs); +xlimits=xlim(ax1); +xlimits=[min([xlimits,xlim(ax2)]) max([xlimits,xlim(ax2)])]; +ylimits=ylim(ax1); +ylimits=[min([ylimits,ylim(ax2)]) max([ylimits,ylim(ax2)])]; +xlim([ax1,ax2],xlimits) +ylim([ax1,ax2],ylimits) +climitsmax=max([caxis(ax1),caxis(ax2)]); +caxis(ax1,[0 climitsmax]); +caxis(ax2,[0 climitsmax]); +xtickformat(ax1,'%.3g') +ytickformat(ax1,'%.3g') +ax1.YAxis.Exponent = 0; +ax1.XAxis.Exponent = 0; +legend(ax2,'off'); +ax1.Children(5).LineStyle='none'; + +xtickformat(ax2,'%.3g') +ytickformat(ax2,'%.3g') +ax2.YAxis.Exponent = 0; +ax2.XAxis.Exponent = 0; + +sgtitle(sprintf('r=%1.2f [mm] z=%1.2f [mm] \\Delta\\phi=%1.1f[kV] R=%1.1f',M.rgrid(Rindex)*1e3,M.zgrid(Zindex)*1e3,(M.potout-M.potinn)*M.phinorm/1e3,M.Rcurv)) +M.savegraph(f,sprintf('%s/%s_phasespaceR%dZ%dbegendnogcs',M.folder,M.name,Rindex,Zindex),[16,10]); \ No newline at end of file diff --git a/matlab/distribution_function.m b/matlab/distribution_function.m index cc91e1e..6c5aa3c 100644 --- a/matlab/distribution_function.m +++ b/matlab/distribution_function.m @@ -1,339 +1,464 @@ %% Show the particles velocity histogram at the position set using ginput % The histogram is compared between timesteppart and end timeinit=1; timesteppart=length(M.tpart)-1; %timesteppart=1; timestepNinit=find(M.tpart(timeinit)==M.t2d); timestepNend=find(M.tpart(timesteppart)==M.t2d); gcs=false; % use guiding center reference system for Vperp coordinates zleftlim=1; %Rindex=15+28; Rindex=28;%43;%29; Zindex=193; fig=figure; ax1=gca; % get the relevant timesteps between 2d and particles variables timestepN=find(M.tpart(end)==M.t2d,1); % Plot the density at the end of simulation to select the region of interest Ndistrib=M.N(:,:,timestepN); h=contourf(ax1,M.zgrid,M.rgrid,Ndistrib); %set(h, 'EdgeColor', 'none'); hold on [r,z]=find(Ndistrib~=0); xlim(ax1,[M.zgrid(min(z)) M.zgrid(max(z))]) ylim(ax1,[M.rgrid(min(r)) M.rgrid(max(r))]) xlabel(ax1,'Z [m]') ylabel(ax1,'R [m]') c = colorbar(ax1); c.Label.String= 'n [m^{-3}]'; view(ax1,2) -%% Define the points of interest for ploting the velocity distribution -[x,y]=ginput(1); -[~,Zindex]=min(abs(x-M.zgrid)); -[~,Rindex]=min(abs(y-M.rgrid)); +% [x,y]=ginput(1); +% Zindex=find(x>M.zgrid,1,'last'); +% Rindex=find(y>M.rgrid,1,'last'); +% Rindex=155; +% Zindex=344; Z=(M.zgrid(Zindex)); R=(M.rgrid(Rindex)); % Show this point on the density map plot(Z,R,'rx','Markersize',12); % Rindex=16; Zindex=64; % Rindex=28; Zindex=floor(size(M.zgrid,1)/2)+1; -%Load the particles velocities at timestep one for Rindex and Zindex -odstep=find(M.tpart(timeinit)==M.t0d); -Rp=M.R(1:M.nbparts(odstep),timeinit,false); -Zp=M.Z(1:M.nbparts(odstep),timeinit,false); - -% Computation of the relevant radial and axial positions -deltar=M.dr(2)/2; -deltarm=M.rgrid(Rindex)-sqrt(M.rgrid(Rindex)^2-deltar^2-2*M.rgrid(Rindex)*deltar); -deltaz=M.dz; - -%Selection of the subset of particles -Indices=Rp>=M.rgrid(Rindex)-deltarm & Rp=M.zgrid(Zindex)-deltaz & Zp=M.rgrid(Rindex)-deltarm & Rend=M.zgrid(Zindex)-deltaz & Zend=M.rgrid(Rindex) & Rp=M.zgrid(Zindex) & Zp=M.rgrid(Rindex) & Rend=M.zgrid(Zindex) & Zend1) h1=histogram(Vr,'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timeinit)*1e9)); %h1=histfit(ax1,Vr); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); end hold on h1=histogram(Vrend,'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); %h1=histfit(ax1,Vrend); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timesteppart)*1e9)); ylabel('counts') xlabel('v_r [m/s]') grid on ylimits=ylim(); Veb=(-M.Er(Rindex,Zindex,end)*M.Bz(Zindex,Rindex)+M.Ez(Rindex,Zindex,end)*M.Br(Zindex,Rindex))/M.B(Zindex,Rindex)^2; plot(-Veb*[1, 1],ylimits,'--k','DisplayName','-V_{ExB}','linewidth',1.5) plot(Veb*[1, 1],ylimits,'--k','DisplayName','V_{ExB}','linewidth',1.5) ax2=subplot(1,3,2); +binwidth=abs(max(Vthetend)-min(Vthetend))/sqrt(length(Vthetend)); if(length(Vthet)>1) h1=histogram(Vthet(:,1),'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timeinit)*1e9)); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); end %h1=histfit(ax2,Vthet(:,1)); hold on h1=histogram(Vthetend,'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); %h1=histfit(ax2,Vthetend); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timesteppart)*1e9)); ylabel('counts') xlabel('v_\theta [m/s]') legend(ax2,'location','northoutside','orientation','vertical') grid on ylimits=ylim(); Veb=(-M.Er(Rindex,Zindex,end)*M.Bz(Zindex,Rindex)+M.Ez(Rindex,Zindex,end)*M.Br(Zindex,Rindex))/M.B(Zindex,Rindex)^2; plot(Veb*[1, 1],ylimits,'--k','DisplayName','V_{ExB}','linewidth',1.5) plot(-Veb*[1, 1],ylimits,'--k','DisplayName','V_{ExB}','linewidth',1.5) ax3=subplot(1,3,3); -%h1=histogram(Vz(:,1),'Binwidth',binwidth,'Normalization','probability','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timeinit)*1e9)); +%h1=histogram(Vz(:,1),'Binwidth',binwidth,'Normalization','probability','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); +binwidth=abs(max(Vzend)-min(Vzend))/sqrt(length(Vzend)); if(length(Vz)>1) -h2=histfit(ax3,Vz(:,1)); -set(h2,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); -h2(1).FaceAlpha=0.6; -h2(1).FaceColor=[0.00,0.45,0.74]; -h2(2).Color='blue'; +h1=histogram(ax3,Vz(:,1),'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); +set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); end hold on %h1=histogram(Vzend,'Binwidth',binwidth,'Normalization','probability','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); -h2=histfit(ax3,Vzend); -set(h2,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timesteppart)*1e9)); -h2(1).FaceAlpha=0.6; -h2(1).FaceColor=[0.85,0.33,0.10]; -h2(2).Color='red'; +h1=histogram(ax3,Vzend,'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); +set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); ylabel('counts') xlabel('v_z [m/s]') ax3.Children=ax3.Children([1,3,2,4]); grid on f=gcf; sgtitle(sprintf('R=%1.2e[m] Z=%1.2e[m] dt=%1.2e[ns]',M.rgrid(Rindex),M.zgrid(Zindex),M.dt*1e9)) f.PaperOrientation='landscape'; f.PaperUnits='centimeters'; papsize=[16 10]; f.PaperSize=papsize; name=strcat(M.folder,'/',M.name); print(f,sprintf('%sParts_V_RZ_R%dZ%d',name,Rindex,Zindex),'-dpdf','-fillpage') savefig(f,sprintf('%sParts_V_RZ_R%dZ%d',name,Rindex,Zindex)) %% Defines the legends given the selected time steps legendinit=sprintf('t=%1.3g [s]',M.tpart(timeinit)); legendend=sprintf('t=%1.3g [s]',M.tpart(timesteppart)); %% Computes the second moment of the distibution function at Rindex Zindex in the paralle perpendicular velocity space Tpar=std(0.5*M.me*Vpar.^2)/M.qe Tparend=std(0.5*M.me*Vparend.^2)/M.qe Tperp=std(0.5*M.me*Vperp.^2)/M.qe Tperpend=std(0.5*M.me*Vperpend.^2)/M.qe %% Gives the velocity distribution at Rindex Zindex in the space V_perp, V_par , |V| binwidth=abs(max(Vperpend)-min(Vperpend))/sqrt(length(Vperpend)); f=figure('Name',sprintf("%s v parper distrib",M.file)); tstudied=0; legtext=sprintf("t=%2.1f [ns]",M.tpart(end)*1e9); ax1=subplot(1,3,1); if(length(Vr)>1) h1=histogram(Vperp,'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timeinit)*1e9)); %h1=histfit(ax1,Vr); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); end hold on h1=histogram(Vperpend,'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); %h1=histfit(ax1,Vrend); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timesteppart)*1e9)); ylabel('counts') grid on ylimits=ylim(); plot(mean(Vthetend)*[1, 1],ylimits,'--k','DisplayName','','linewidth',1.5) if gcs xlabel('v_{perp}^* [m/s]') plot(mean(Vperpend)*[1, 1],ylimits,'--g','DisplayName','','linewidth',1.5) else xlabel('v_{perp} [m/s]') plot(mean(Vperpend)*[1, 1],ylimits,'--g','DisplayName','','linewidth',1.5) end plot(Veb*[1, 1],ylimits,'--b','DisplayName','V_{ExB}','linewidth',1.5) legend(ax1,'location','northoutside','orientation','vertical') ax2=subplot(1,3,2); if(length(Vpar)>1) h1=histogram(Vpar(:,1),'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timeinit)*1e9)); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); end +b=M.rgrid(end); +a=M.rgrid(1); +vd1=((M.potout-M.potinn)*M.phinorm/M.rgrid(Rindex))/M.Bz(Zindex,Rindex)/log(b/a); +vd1=-M.Er(Rindex,Zindex,1)/M.Bz(Zindex,Rindex); +%vd1=-M.Er(Rindex,Zindex,timestepNend)/M.Bz(Zindex,Rindex); +vinit=sqrt(M.kb*10000/M.me); +%vinit=sqrt(M.qe*80/M.me) +%vinit=sqrt(120*M.qe/M.me) +%vd2=((M.potout-M.potinn)*M.phinorm/M.rgrid(25))/M.Bz(1,25)/log(b/a); +[Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,floor(M.nz/2)+1)); +Rcurv=griddata(Zmesh,M.rAthet,M.B',M.zgrid(zleftlim),M.rAthet(Rindex,Zindex),'natural')/M.B(Zindex,Rindex); +deltaphicomp=-0.5*M.me/M.qe*(vpar2-(vd1+vinit)^2*(1-M.Rcurv)) +ratioparper=vpar2/((vd1+vinit)^2*(1-M.Rcurv)) + +Ndistrib=M.N(:,:,timestepNend); +model=M.potentialwellmodel(timestepNend); +z=model.z; +r=model.r; +pot=model.pot; +rathet=model.rathet; +Zeval=[M.zgrid(zleftlim) M.zgrid(Zindex)]; +Psieval=M.rAthet(Rindex,Zindex)*[1 1]; +phis=griddata(Zmesh,M.rAthet,M.pot(:,:,end),Zeval,Psieval,'natural'); +deltaphi=-diff(phis) + +%deltaphi=-215; +R=griddata(Zmesh,M.rAthet,M.B',M.zgrid(zleftlim),M.rAthet(Rindex,Zindex),'natural')/M.B(Zindex,Rindex); +% if vper is above line then electron is kept axially +vper=sqrt(2*M.qe/M.me*deltaphi/(R-1)+vpar.^2/(R-1))/M.vlight; +vpar=vpar/M.vlight; +vpar=vpar(real(vper)~=0); +vper=vper(real(vper)~=0); +xlimits=xlim; +ylimits=ylim; +if(length(vper)>0) +p(3)=plot(vpar,vper,'b-','displayname','Loss parabolla simul'); hold on -h1=histogram(Vparend,'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); -%h1=histfit(ax2,Vthetend); -set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timesteppart)*1e9)); -ylabel('counts') -xlabel('v_{par} [m/s]') -legend(ax2,'location','northoutside','orientation','vertical') -grid on +plot(-vpar,vper,'b-') +end + +vpar=linspace(1,M.vlight,1000); +vper=sqrt(-2*M.qe/M.me*deltaphicomp/(R-1)+vpar.^2/(R-1))/M.vlight; +vpar=vpar/M.vlight; +vpar=vpar(real(vper)~=0); +vper=vper(real(vper)~=0); +if(length(vper)>0) +p(4)=plot(vpar,vper,'k--','displayname','Loss parabolla prediction'); +hold on +plot(-vpar,vper,'k--') +end +xlim(xlimits) +ylim(ylimits) +legend(p) +axis equal + +% subplot(2,2,2) +% scatter(Zp,Vpar,'.','displayname','Init') +% hold on +% scatter(Zend,Vparend,'.','displayname','End') +% xlabel('z [m]') +% ylabel('\beta_{par}') +% +% subplot(2,2,3) +% scatter(Vperp,Rp,'.','displayname','Init') +% hold on +% scatter(Vperpend,Rend,'.','displayname','End') +% xlabel('\beta_\perp') +% ylabel('R [m]') + +ax1=subplot(1,2,2); +h=contourf(ax1,M.zgrid,M.rgrid,Ndistrib); +hold on +[r,z]=find(Ndistrib~=0); +xlim(ax1,[M.zgrid(min(z)) M.zgrid(max(z))]) +ylim(ax1,[M.rgrid(min(r)) M.rgrid(max(r))]) +xlabel(ax1,'Z [m]') +ylabel(ax1,'R [m]') +c = colorbar(ax1); +c.Label.String= 'n [m^{-3}]'; +Zx=(M.zgrid(Zindex)); +Rx=(M.rgrid(Rindex)); + +plot(ax1,Zx,Rx,'rx','Markersize',12); + +sgtitle(sprintf('r=%1.3g [m] z=%1.3g [m] \\Delta\\phi=%1.3g[kV] R=%1.3g',M.rgrid(Rindex),M.zgrid(Zindex),(M.potout-M.potinn)*M.phinorm,M.Rcurv)) + +M.savegraph(f,sprintf('%s/%s_phasespaceR%dZ%dpos',M.folder,M.name,Rindex,Zindex),[16,12]) + ax2=subplot(1,3,3); if(length(Vpar)>1) h1=histogram(sqrt(Vr.^2+Vthet.^2+Vz.^2),'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timeinit)*1e9)); end hold on h1=histogram(sqrt(Vrend.^2+Vthetend.^2+Vzend.^2),'Binwidth',binwidth,'Normalization','count','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); %h1=histfit(ax2,Vthetend); set(h1,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timesteppart)*1e9)); ylabel('counts') xlabel('|v|') legend(ax2,'location','northoutside','orientation','vertical') grid on % ax3=subplot(1,3,3); % h1=histogram(Vz(:,1),'Binwidth',binwidth,'Normalization','probability','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timeinit)*1e9)); % if(length(Vz)>1) % h2=histfit(ax3,sqrt(Vr.^2+Vthet.^2+Vz.^2),[],'Rayleigh'); % set(h2,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(1)*1e9)); % h2(1).FaceAlpha=0.6; % h2(1).FaceColor=[0.00,0.45,0.74]; % h2(2).Color='blue'; % end % hold on % h1=histogram(Vzend,'Binwidth',binwidth,'Normalization','probability','DisplayName',sprintf("t=%2.3d [ns]",M.tpart(end)*1e9)); % h2=histfit(ax3,sqrt(Vrend.^2+Vthetend.^2+Vzend.^2),[],'Rayleigh'); % set(h2,'DisplayName',sprintf("t=%2.3d [ns]",M.tpart(timesteppart)*1e9)); % h2(1).FaceAlpha=0.6; % h2(1).FaceColor=[0.85,0.33,0.10]; % h2(2).Color='red'; % ylabel('counts') % xlabel('|v| [m/s]') % ax3.Children=ax3.Children([1,3,2,4]); % grid on f=gcf; sgtitle(sprintf('R=%1.2e[m] Z=%1.2e[m] dt=%1.2e[ns]',M.rgrid(Rindex),M.zgrid(Zindex),M.dt*1e9)) f.PaperOrientation='landscape'; f.PaperUnits='centimeters'; papsize=[16 10]; f.PaperSize=papsize; name=strcat(M.folder,'/',M.name); if gcs print(f,sprintf('%sParts_V_perparstarR%dZ%d',name,Rindex,Zindex),'-dpdf','-fillpage') savefig(f,sprintf('%sParts_V_perparstarR%dZ%d',name,Rindex,Zindex)) else print(f,sprintf('%sParts_V_perparR%dZ%d',name,Rindex,Zindex),'-dpdf','-fillpage') savefig(f,sprintf('%sParts_V_perparR%dZ%d',name,Rindex,Zindex)) end +b=M.rgrid(end); +a=M.rgrid(1); +vd1=((M.potout-M.potinn)*M.phinorm/M.rgrid(Rindex))/M.Bz(Zindex,Rindex)/log(b/a); +vd1=-M.Er(Rindex,Zindex,1)/M.Bz(Zindex,Rindex); +%vd1=-M.Er(Rindex,Zindex,timestepNend)/M.Bz(Zindex,Rindex); +vinit=sqrt(M.kb*10000/M.me); +%vinit=sqrt(M.qe*80/M.me) +%vinit=sqrt(120*M.qe/M.me) +%vd2=((M.potout-M.potinn)*M.phinorm/M.rgrid(25))/M.Bz(1,25)/log(b/a); +[Zmesh,Rmesh]=meshgrid(M.zgrid,M.rAthet(:,floor(M.nz/2)+1)); + +% Find the R magnetic ratio between local position and left +Rcurv=griddata(Zmesh,M.rAthet,M.B',M.zgrid(zleftlim),M.rAthet(Rindex,Zindex),'natural')/M.B(Zindex,Rindex); + +deltaphicomp=-0.5*M.me/M.qe*(vpar2-(vd1+vinit)^2*(1-M.Rcurv)) + +ratioparper=vpar2/((vd1+vinit)^2*(1-M.Rcurv)) + +Ndistrib=M.N(:,:,timestepNend); +model=M.potentialwellmodel(timestepNend); +z=model.z; +r=model.r; +pot=model.pot; +rathet=model.rathet; +Zeval=[M.zgrid(zleftlim) M.zgrid(Zindex)]; +Psieval=M.rAthet(Rindex,Zindex)*[1 1]; +phis=griddata(Zmesh,M.rAthet,M.pot(:,:,end),Zeval,Psieval,'natural'); +% calculate the potential difference between left and local +deltaphi=-diff(phis) + +%deltaphi=-215; +R=griddata(Zmesh,M.rAthet,M.B',M.zgrid(zleftlim),M.rAthet(Rindex,Zindex),'natural')/M.B(Zindex,Rindex); +vper=sqrt(2*M.qe/M.me*deltaphi/(R-1)+vpar.^2/(R-1))/M.vlight; +vpar=vpar/M.vlight; +vpar=vpar(real(vper)~=0); +vper=vper(real(vper)~=0); +xlimits=xlim; +ylimits=ylim; +if(length(vper)>0) +p(3)=plot(vpar,vper,'b-','displayname','Simulation'); +hold on +plot(-vpar,vper,'b-') +end + +vpar=linspace(1,M.vlight,1000); +vper=sqrt(-2*M.qe/M.me*deltaphicomp/(R-1)+vpar.^2/(R-1))/M.vlight; +vpar=vpar/M.vlight; +vpar=vpar(real(vper)~=0); +vper=vper(real(vper)~=0); +if(length(vper)>0) +p(4)=plot(vpar,vper,'k--','displayname','Prediction'); +hold on +plot(-vpar,vper,'k--') +end +xlim(xlimits) +ylim(ylimits) +legend(p) +axis equal +legend('location','northeast') + +title(sprintf('r=%1.3g [m] z=%1.3g [m] \\Delta\\phi=%1.3g[kV] R=%1.3g',M.rgrid(Rindex),M.zgrid(Zindex),(M.potout-M.potinn)*M.phinorm,M.Rcurv)) %% Show the phase space at begining and end of the simulation at position (Rindex,Zindex) as a probability density f=figure(); ax1=subplot(1,2,1); ax2=subplot(1,2,2); %[p,maxnb,c]=M.displayPhaseSpace(1:2,Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(1)),ax1); % ax1=subplot(1,2,1); % hold(ax1,'off') c=cell(2,1); c{1}=linspace(-3e6,3e6,21); c{2}=linspace(-0e6,1.3e7,51); [p,maxnb,c]=M.displayPhaseSpace('parper',1:2,Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(1)),ax1,-1,c); M.displayPhaseSpace('parper',length(M.tpart)+(-1:0),Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(end)),ax2,maxnb,c); xlimits=xlim(ax1); xlimits=[min([xlimits,xlim(ax2)]) max([xlimits,xlim(ax2)])]; ylimits=ylim(ax1); ylimits=[min([ylimits,ylim(ax2)]) max([ylimits,ylim(ax2)])]; xlim([ax1,ax2],xlimits) ylim([ax1,ax2],ylimits) climitsmax=max([caxis(ax1),caxis(ax2)]); caxis(ax1,[0 climitsmax]); caxis(ax2,[0 climitsmax]); xtickformat(ax1,'%.3g') ytickformat(ax1,'%.3g') ax1.YAxis.Exponent = 0; ax1.XAxis.Exponent = 0; xtickformat(ax2,'%.3g') ytickformat(ax2,'%.3g') ax2.YAxis.Exponent = 0; ax2.XAxis.Exponent = 0; sgtitle(sprintf('r=%1.2f [mm] z=%1.2f [mm] \\Delta\\phi=%1.1f[kV] R=%1.1f',M.rgrid(Rindex)*1e3,M.zgrid(Zindex)*1e3,(M.potout-M.potinn)*M.phinorm/1e3,M.Rcurv)) M.savegraph(f,sprintf('%s/%s_phasespaceR%dZ%dbegend',M.folder,M.name,Rindex,Zindex),[15,10]); %% Show the phase space at begining and end of the simulation at position (Rindex,Zindex) as a probability density f=figure(); ax1=subplot(1,2,1); ax2=subplot(1,2,2); %[p,maxnb,c]=M.displayPhaseSpace(1:2,Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(1)),ax1); % ax1=subplot(1,2,1); % hold(ax1,'off') c=cell(2,1); c{1}=linspace(-1e7,1e7,21); c{2}=linspace(-1.5e7,1.5e7,51); [p,maxnb,c]=M.displayPhaseSpace('rthet',1:2,Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(1)),ax1,-1,c); M.displayPhaseSpace('rthet',length(M.tpart)+(-1:0),Rindex,Zindex,'',sprintf('t=%1.3g [s]',M.tpart(end)),ax2,maxnb,c); xlimits=xlim(ax1); xlimits=[min([xlimits,xlim(ax2)]) max([xlimits,xlim(ax2)])]; ylimits=ylim(ax1); ylimits=[min([ylimits,ylim(ax2)]) max([ylimits,ylim(ax2)])]; xlim([ax1,ax2],xlimits) ylim([ax1,ax2],ylimits) climitsmax=max([caxis(ax1),caxis(ax2)]); caxis(ax1,[0 climitsmax]); caxis(ax2,[0 climitsmax]); xtickformat(ax1,'%.3g') ytickformat(ax1,'%.3g') ax1.YAxis.Exponent = 0; ax1.XAxis.Exponent = 0; xtickformat(ax2,'%.3g') ytickformat(ax2,'%.3g') ax2.YAxis.Exponent = 0; ax2.XAxis.Exponent = 0; sgtitle(sprintf('r=%1.2f [mm] z=%1.2f [mm] \\Delta\\phi=%1.1f[kV] R=%1.1f',M.rgrid(Rindex)*1e3,M.zgrid(Zindex)*1e3,(M.potout-M.potinn)*M.phinorm/1e3,M.Rcurv)) M.savegraph(f,sprintf('%s/%s_phasespaceR%dZ%dbegendrthet',M.folder,M.name,Rindex,Zindex),[15,10]); diff --git a/matlab/espic2dhdf5.m b/matlab/espic2dhdf5.m deleted file mode 100644 index c0da0cf..0000000 --- a/matlab/espic2dhdf5.m +++ /dev/null @@ -1,1031 +0,0 @@ -classdef espic2dhdf5 - %espic2dhdf5 General class used to treat hdf5 result files of espic2d code - % A result file is loaded with a call to M=espic2dhdf5(filename) where filename is the relative or absolute file path - % after loading, several quantities and composite diagnostics such as moments of the distribution function or individual particles - % quantities can be accessed. - properties - filename - name - folder - fullpath - timestamp - info - t0d - t1d - t2d - tpart - it0 - it1 - it2 - - %% Physical constants - vlight=299792458; - qe=1.60217662E-19; - me=9.109383E-31; - eps_0=8.85418781762E-12; - kb=1.38064852E-23; - - %% Run parameters - dt % simulation time step - nrun % number of time steps simulated - nlres - nlsave - nlclassical % Was the equation of motion solved in the classical framework - nlPhis % Was the self-consistent electric field computed - nz % number of intervals in the z direction for the grid - nnr % number of intervals in the r direction for the grid for each of the 3 mesh regions - lz % physical axial dimension of the simulation space - nplasma % Number of initial macro particles - potinn % Normalized electric potential at the coaxial insert - potout % Normalized electric potential at the cylinder surface - B0 % Normalization for the magnetic field - Rcurv % Magnetic mirror ratio - width % Magnetic mirror length - n0 % Initial particle density in case of old particle loading - temp % Initial particle temperature in case of old particle loading - femorder % finite element method order in z and r direction - ngauss % Order of the Gauss integration method for the FEM - plasmadim % initial dimensions of the plasma for the old particle loading system - radii % Radial limits of the three mesh regions coarse,fine,coarse - H0 % Initial particle Energy for Davidsons distribution function - P0 % Initial particle Angular momentum for Davidsons distribution function - normalized % Are the parts quantities normalized in the h5 file - nbspecies % Number of species simulated - - - %% Frequencies - omepe % Reference plasma frequency used for normalization - omece % Reference cyclotronic frequency for normalization - - %% Normalizations - tnorm % Time normalization - rnorm % Dimension normalization - bnorm % Magnetic field normalization - enorm % Electric field normalization - phinorm % Electric potential normalization - vnorm % Velocity normalization - - %% Grid data - rgrid % Radial grid position points - zgrid % Axial grid position points - dz % Axial grid step - dr % Radial grid step for the three mesh regions - CellVol % Volume of the cell used for density calculation - - %% Magnetic field - Br % Radial magnetic field - Bz % Axial magnetic field - Athet % Azimuthal component of the Magnetic potential vector - rAthet % r*Athet used for the representation of magnetic field lines - B % Magnetic field amplitude - - %% Energies - epot % Time evolution of the particles potential energy - ekin % Time evolution of the particles kinetic energy - etot % Time evolution of the particles total energy - etot0 % Time evolution of the reference particle total energy - eerr % Time evolution of the error on the energy conservation - - %% 2D time data evaluated on grid points - N % main specie Density - fluidUR % main specie radial fluid velocity - fluidUZ % main specie axial fluid velocity - fluidUTHET % main specie azimuthal fluid velocity - pot % Electric potential - Er % Radial electric field - Ez % Axial electric field - Presstens % Pressure tensor - - %% Splines - knotsr % Spline radial knots - knotsz % Spline axial knots - - %% Particle parameters - weight % Macro particle numerical weight of the main specie - qsim % Macro particle charge - msim % Macro particle mass - nbparts % Time evolution of the number of simulated particles - partepot % Electric potential at the particles positions - R % Particles radial position - Z % Particles axial position - Rindex % Particles radial grid index - Zindex % Particles axial grid index - partindex % Particles unique id for tracing trajectories - VR % Particles radial velocity - VZ % Particles axial velocity - VTHET % Particles azimuthal velocity - THET % Particles azimuthal position - species % Array containing the other simulated species - - %% Celldiag - celldiag % Array containing the cell diagnostic data - nbcelldiag % Total number of cell diagnostics - - end - - methods - function file=file(obj) - % returns the h5 file name - file=obj.filename; - end - - function obj = espic2dhdf5(filename,readparts) - % Reads the new result file filename and read the parts data if readparts==true - - - % Try catch are there for compatibility with older simulation files - filedata=dir(filename); - if (isempty(filedata)) - error("File: ""%s"" doesn't exist",filename) - end - obj.folder=filedata.folder; - obj.filename=filename; - [~, obj.name, ~] = fileparts(obj.filename); - obj.fullpath=[obj.folder,'/',obj.filename]; - obj.timestamp=filedata.date; - if nargin==1 - readparts=true; - end - %obj.info=h5info(filename); - - - %% Read the run parameters - obj.dt = h5readatt(obj.fullpath,'/data/input.00/','dt'); - obj.nrun = h5readatt(obj.fullpath,'/data/input.00/','nrun'); - obj.nlres = strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlres'),'y'); - obj.nlsave = strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlsave'),'y'); - obj.nlclassical =strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlclassical'),'y'); - obj.nlPhis =strcmp(h5readatt(obj.fullpath,'/data/input.00/','nlPhis'),'y'); - obj.nz = h5readatt(obj.fullpath,'/data/input.00/','nz'); - obj.nnr = h5read(obj.fullpath,'/data/input.00/nnr'); - obj.lz = h5read(obj.fullpath,'/data/input.00/lz'); - obj.qsim = h5readatt(obj.fullpath,'/data/input.00/','qsim'); - obj.msim = h5readatt(obj.fullpath,'/data/input.00/','msim'); - - try - obj.weight=h5readatt(obj.fullpath,'/data/part/','weight'); - catch - obj.weight=obj.msim/obj.me; - end - obj.nplasma = h5readatt(obj.fullpath,'/data/input.00/','nplasma'); - obj.potinn = h5readatt(obj.fullpath,'/data/input.00/','potinn'); - obj.potout = h5readatt(obj.fullpath,'/data/input.00/','potout'); - obj.B0 = h5readatt(obj.fullpath,'/data/input.00/','B0'); - obj.Rcurv = h5readatt(obj.fullpath,'/data/input.00/','Rcurv'); - obj.width = h5readatt(obj.fullpath,'/data/input.00/','width'); - obj.n0 = h5readatt(obj.fullpath,'/data/input.00/','n0'); - obj.temp = h5readatt(obj.fullpath,'/data/input.00/','temp'); - try - obj.it0 = h5readatt(obj.fullpath,'/data/input.00/','it0d'); - obj.it1 = h5readatt(obj.fullpath,'/data/input.00/','it2d'); - obj.it2 = h5readatt(obj.fullpath,'/data/input.00/','itparts'); - catch - obj.it0 = h5readatt(obj.fullpath,'/data/input.00/','it0'); - obj.it1 = h5readatt(obj.fullpath,'/data/input.00/','it1'); - obj.it1 = h5readatt(obj.fullpath,'/data/input.00/','it2'); - end - try - obj.nbspecies=h5readatt(obj.fullpath,'/data/input.00/','nbspecies'); - obj.normalized=strcmp(h5readatt(obj.fullpath,'/data/input.00/','rawparts'),'y'); - catch - obj.nbspecies=1; - obj.normalized=false; - end - try - obj.nbcelldiag=h5readatt(obj.fullpath,'/data/celldiag/','nbcelldiag'); - catch - obj.nbcelldiag=0; - end - - obj.omepe=sqrt(abs(obj.n0)*obj.qe^2/(obj.me*obj.eps_0)); - obj.omece=obj.qe*obj.B0/obj.me; - - obj.nbparts= h5read(obj.fullpath, '/data/var0d/nbparts'); - try - obj.H0 = h5read(obj.fullpath,'/data/input.00/H0'); - obj.P0 = h5read(obj.fullpath,'/data/input.00/P0'); - catch - obj.H0=3.2e-14; - obj.P0=8.66e-25; - end - - % Normalizations - obj.tnorm=1/obj.omepe; - obj.rnorm=obj.vlight*obj.tnorm; - obj.bnorm=obj.B0; - obj.enorm=obj.vlight*obj.bnorm; - obj.phinorm=obj.enorm*obj.rnorm; - obj.vnorm=obj.vlight; - - % Grid data - obj.rgrid= h5read(obj.fullpath, '/data/var1d/rgrid')*obj.rnorm; - obj.zgrid= h5read(obj.fullpath, '/data/var1d/zgrid')*obj.rnorm; - obj.dz=(obj.zgrid(end)-obj.zgrid(1))/double(obj.nz); - rid=1; - for i=1:length(obj.nnr) - obj.dr(i)=(obj.rgrid(sum(obj.nnr(1:i))+1)-obj.rgrid(rid))/double(obj.nnr(i)); - rid=rid+obj.nnr(i); - end - - Br = h5read(obj.fullpath,'/data/fields/Br')*obj.bnorm; - obj.Br= reshape(Br,length(obj.zgrid),length(obj.rgrid)); - Bz = h5read(obj.fullpath,'/data/fields/Bz')*obj.bnorm; - obj.Bz= reshape(Bz,length(obj.zgrid),length(obj.rgrid)); - try - Atheta = h5read(obj.fullpath,'/data/fields/Athet')*obj.bnorm; - obj.Athet= reshape(Atheta,length(obj.zgrid),length(obj.rgrid)); - [rmeshgrid,~]=meshgrid(obj.rgrid,obj.zgrid); - obj.rAthet=(rmeshgrid.*obj.Athet)'; - catch - end - obj.B=sqrt(obj.Bz.^2+obj.Br.^2); - clear Br Bz - try - obj.t0d=h5read(obj.fullpath,'/data/var0d/time'); - catch - obj.t0d=obj.dt.*double(0:length(obj.epot)-1); - end - - - obj.femorder = h5read(obj.fullpath,'/data/input.00/femorder'); - obj.ngauss = h5read(obj.fullpath,'/data/input.00/ngauss'); - obj.plasmadim = h5read(obj.fullpath,'/data/input.00/plasmadim'); - obj.radii = h5read(obj.fullpath,'/data/input.00/radii'); - - obj.epot = h5read(obj.fullpath,'/data/var0d/epot'); - obj.ekin = h5read(obj.fullpath,'/data/var0d/ekin'); - obj.etot = h5read(obj.fullpath,'/data/var0d/etot'); - try - obj.etot0 = h5read(obj.fullpath,'/data/var0d/etot0'); - obj.eerr = obj.etot-obj.etot0; - catch - obj.eerr = obj.etot-obj.etot(2); - end - - if(obj.normalized) - obj.pot=gridquantity(obj.fullpath,'/data/fields/pot',sum(obj.nnr)+1, obj.nz+1,1); - obj.Er=gridquantity(obj.fullpath,'/data/fields/Er',sum(obj.nnr)+1, obj.nz+1,1); - obj.Ez=gridquantity(obj.fullpath,'/data/fields/Ez',sum(obj.nnr)+1, obj.nz+1,1); - else - obj.pot=gridquantity(obj.fullpath,'/data/fields/pot',sum(obj.nnr)+1, obj.nz+1,obj.phinorm); - obj.Er=gridquantity(obj.fullpath,'/data/fields/Er',sum(obj.nnr)+1, obj.nz+1,obj.enorm); - obj.Ez=gridquantity(obj.fullpath,'/data/fields/Ez',sum(obj.nnr)+1, obj.nz+1,obj.enorm); - end - - try - obj.t2d = h5read(obj.fullpath,'/data/fields/time'); - catch - info=h5info(obj.fullpath,'/data/fields/partdensity'); - obj.t2d=obj.dt*(0:info.objspace.Size(2)-1)*double(obj.it1); - end - - try - info=h5info(obj.fullpath,'/data/fields/moments'); - obj.femorder = h5read(obj.fullpath,'/data/input.00/femorder'); - kr=obj.femorder(2)+1; - obj.knotsr=augknt(obj.rgrid,kr); - - kz=obj.femorder(1)+1; - obj.knotsz=augknt(obj.zgrid,kz); - try - obj.CellVol= reshape(h5read(obj.fullpath,'/data/fields/volume'),length(obj.knotsz)-kz,length(obj.knotsr)-kr); - obj.CellVol=permute(obj.CellVol,[2,1,3])*obj.rnorm^3; - catch - zvol=fnder(spmak(obj.knotsz,ones(1,length(obj.knotsz)-kz)), -1 ); - rvol=fnder(spmak(obj.knotsr,2*pi*[obj.rgrid' 2*obj.rgrid(end)-obj.rgrid(end-1)]), -1 ); - ZVol=diff(fnval(zvol,obj.knotsz)); - RVol=diff(fnval(rvol,obj.knotsr)); - obj.CellVol=RVol(3:end-1)*ZVol(3:end-1)'; - obj.CellVol=padarray(obj.CellVol,[1,1],'replicate','post'); - end - if(obj.normalized) - obj.N=splinedensity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, 1, 1); - else - obj.N=splinedensity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, abs(obj.qsim/obj.qe), 1); - end - obj.fluidUR=splinevelocity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.vnorm, 2); - obj.fluidUTHET=splinevelocity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.vnorm, 3); - obj.fluidUZ=splinevelocity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.vnorm, 4); - if(obj.normalized) - obj.Presstens=splinepressure(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, obj.vnorm^2*obj.me); - else - obj.Presstens=splinepressure(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder, obj.CellVol, obj.vnorm^2*obj.msim); - end - catch - obj.CellVol=(obj.zgrid(2:end)-obj.zgrid(1:end-1))*((obj.rgrid(2:end).^2-obj.rgrid(1:end-1).^2)*pi)'; - obj.CellVol=obj.CellVol'; - obj.N=griddensity(obj.fullpath, '/data/fields/partdensity', sum(obj.nnr)+1, obj.nz+1, obj.CellVol, abs(obj.qsim/obj.qe), true); - obj.fluidUR=gridquantity(obj.fullpath, '/data/fields/fluidur', sum(obj.nnr)+1, obj.nz+1, obj.vnorm, true); - obj.fluidUTHET=gridquantity(obj.fullpath, '/data/fields/fluiduthet', sum(obj.nnr)+1, obj.nz+1, obj.vnorm, true); - obj.fluidUZ=gridquantity(obj.fullpath, '/data/fields/fluiduz', sum(obj.nnr)+1, obj.nz+1, obj.vnorm, true); - end - - if(readparts) - - if(obj.normalized) - obj.R = h5partsquantity(obj.fullpath,'/data/part','R'); - obj.Z = h5partsquantity(obj.fullpath,'/data/part','Z'); - else - obj.R = h5partsquantity(obj.fullpath,'/data/part','R',obj.rnorm); - obj.Z = h5partsquantity(obj.fullpath,'/data/part','Z',obj.rnorm); - end - try - obj.THET = h5partsquantity(obj.fullpath,'/data/part','THET'); - catch - clear obj.THET - end - try - obj.Rindex=h5partsquantity(obj.fullpath,'/data/part','Rindex'); - obj.Zindex=h5partsquantity(obj.fullpath,'/data/part','Zindex'); - catch - clear obj.Rindex obj.Zindex - end - obj.VR = h5partsquantity(obj.fullpath,'/data/part','UR',obj.vnorm); - obj.VZ = h5partsquantity(obj.fullpath,'/data/part','UZ',obj.vnorm); - obj.VTHET= h5partsquantity(obj.fullpath,'/data/part','UTHET',obj.vnorm); - - if(obj.normalized) - obj.partepot = h5partsquantity(obj.fullpath,'/data/part','pot',sign(obj.qsim)*obj.qe); - else - obj.partepot = h5partsquantity(obj.fullpath,'/data/part','pot',sign(obj.qsim)*obj.qe*obj.phinorm); - end - - try - obj.partindex = h5partsquantity(obj.fullpath,'/data/part/','partindex'); - catch - end - end - - try - obj.tpart = h5read(obj.fullpath,'/data/part/time'); - catch - obj.tpart=obj.dt*(0:size(obj.R,2)-1)*double(obj.it2); - end - - if(obj.nbspecies >1) - obj.species=h5parts.empty; - for i=2:obj.nbspecies - nbparts=h5read(obj.fullpath,sprintf('%s/Nparts',sprintf('/data/part/%2d',i))); - if (nbparts(1)>0) - obj.species(i-1)=h5parts(obj.fullpath,sprintf('/data/part/%2d',i),obj); - end - end - end - if(obj.nbcelldiag > 0) - obj.celldiag=h5parts.empty; - for i=1:obj.nbcelldiag - nbparts=h5read(obj.fullpath,sprintf('%s/Nparts',sprintf('/data/celldiag/%02d',i))); - if (nbparts(1)>0) - obj.celldiag(i)=h5parts(obj.fullpath,sprintf('/data/celldiag/%02d',i),obj); - end - end - end - end - - function Atheta=Atheta(obj,R,Z) - halflz=(obj.zgrid(end)+obj.zgrid(1))/2; - Atheta=0.5*obj.B0*(R-obj.width/pi*(obj.Rcurv-1)/(obj.Rcurv+1)... - .*besseli(1,2*pi*R/obj.width).*cos(2*pi*(Z-halflz)/obj.width)); - end - - function quantity=H(obj,indices) - %% computes the total energy for the main specie particle indices{1} at time indices{2} - if indices{1}==':' - p=1:obj.VR.nparts; - else - p=indices{1}; - end - if indices{2}==':' - t=1:length(obj.tpart); - else - t=indices{2}; - end - if size(indices,1)>2 - track=indices{3}; - else - track=false; - end - quantity=0.5*obj.me*(obj.VR(p,t,track).^2+obj.VTHET(p,t,track).^2+obj.VZ(p,t,track).^2)+obj.partepot(p,t,track); - end - - function quantity=P(obj,indices) - %% computes the canonical angular momentum for the main specie particle indices{1} at time indices{2} - if indices{1}==':' - p=1:obj.R.nparts; - else - p=indices{1}; - end - if indices{2}==':' - t=1:length(obj.tpart); - else - t=indices{2}; - end - if size(indices,2)>2 - track=indices{3}; - else - track=false; - end - quantity=obj.R(p,t,track).*(obj.VTHET(p,t,track)*obj.me+sign(obj.qsim)*obj.qe*obj.Atheta(obj.R(p,t,track),obj.Z(p,t,track))); - end - - function quantity=Vpar(obj,varargin) - %Vpar Computes the parallel velocity for the main specie particle indices{1} at time indices{2} - if(~iscell(varargin)) - indices=mat2cell(varargin); - else - indices=varargin; - end - if indices{1}==':' - p=1:obj.R.nparts; - else - p=indices{1}; - end - if indices{2}==':' - t=1:length(obj.tpart); - else - t=indices{2}; - end - if size(indices,2)>2 - track=indices{3}; - else - track=false; - end -% Zind=obj.Zindex(p,t,track)+1; -% Rind=obj.Rindex(p,t,track)+1; - rpos(1,1,:)=obj.rgrid; - zpos(1,1,:)=obj.zgrid; - [~,Rind]=min(abs(obj.R(p,t,track)-rpos),[],3); - [~,Zind]=min(abs(obj.Z(p,t,track)-zpos),[],3); - posind=sub2ind(size(obj.B),Zind,Rind); - costhet=obj.Bz(posind)./obj.B(posind); - sinthet=obj.Br(posind)./obj.B(posind); - quantity=obj.VR(p,t,track).*sinthet+obj.VZ(p,t,track).*costhet; - end - - function quantity=Vperp(obj,varargin) - %Vperp Computes the perpendicular velocity in the guidind center reference frame, - % for the main specie particle indices{1} at time indices{2} - - if(~iscell(varargin)) - indices=mat2cell(varargin); - else - indices=varargin; - end - - if indices{1}==':' - p=1:obj.R.nparts; - else - p=indices{1}; - end - if indices{2}==':' - t=1:length(obj.tpart); - else - t=indices{2}; - end - if size(indices,2)>2 - track=indices{3}; - else - track=false; - end - - if size(indices,2)>3 - gcs=indices{4}; - else - gcs=true; - end - %Zind=obj.Zindex(p,t,track)+1; - rpos(1,1,:)=obj.rgrid; - zpos(1,1,:)=obj.zgrid; - [~,Rind]=min(abs(obj.R(p,t,track)-rpos),[],3); - [~,Zind]=min(abs(obj.Z(p,t,track)-zpos),[],3); - posind=sub2ind(size(obj.B),Zind,Rind); - costhet=obj.Bz(posind)./obj.B(posind); - sinthet=obj.Br(posind)./obj.B(posind); - Vdrift=zeros(size(Zind)); - vr=obj.VR(p,t,track); - vz=obj.VZ(p,t,track); - vthet=obj.VTHET(p,t,track); - if gcs - for j=1:length(t) - [~,tfield]=min(abs(obj.t2d-obj.tpart(t(j)))); - timeEr=obj.Er(:,:,tfield); - timeEz=obj.Ez(:,:,tfield); - posindE=sub2ind(size(timeEr),Rind(:,j),Zind(:,j)); - VdriftE=(timeEz(posindE).*obj.Br(posind(:,j))-timeEr(posindE).*obj.Bz(posind(:,j)))./obj.B(posind(:,j)).^2; - dbr=(obj.B(posind(:,j)+1)-obj.B(posind(:,j)))./(obj.rgrid(Rind(:,j)+1)-obj.rgrid(Rind(:,j))); - dbz=(obj.B(posind(:,j)+1)-obj.B(posind(:,j)))./(obj.zgrid(Zind(:,j)+1)-obj.zgrid(Zind(:,j))); - VdriftB= sign(obj.weight)*obj.me/obj.qe*(vr(:,j).*sinthet(:,j)+vz(:,j).*costhet(:,j)).^2./ ... - obj.B(posind(:,j)).^3.*(obj.Bz(posind(:,j)).*dbr-obj.Br(posind(:,j)).*dbz); - Vdrift(:,j)= VdriftE+VdriftB; - end - end - quantity=sqrt((vthet-Vdrift).^2+(vr.*costhet-vz.*sinthet).^2); - end - - function displaypsi(obj,deltat,Davidson) - %% plot the initial and final radial profile at position z=0 and show the normalized enveloppe function Psi - % relevant for Davidson annular distribution function - f=figure('Name', sprintf('%s Psi',obj.name)); - f.Name= sprintf('%s Psi',obj.name); - zpos=floor(length(obj.zgrid)/2); - tinit=1; - tend=length(obj.t2d); - deltat=cell2mat(deltat); - if(obj.R.nt<2) - H0=obj.H0; - P0=obj.P0; - else - H0=mean(H(obj,{1:obj.VR.nparts,obj.VR.nt,false})); - P0=mean(P(obj,{1:obj.VR.nparts,obj.VR.nt,false})); - end - lw=1.5; - Mirrorratio=(obj.Rcurv-1)/(obj.Rcurv+1); - locpot=mean(obj.pot(:,zpos,tend-deltat:tend),3); - psi=1+obj.qe*locpot(:)/H0-1/(2*obj.me*H0)*(P0./obj.rgrid+obj.qe*0.5*obj.B0.*(obj.rgrid-obj.width/pi*Mirrorratio*cos(2*pi*obj.zgrid(zpos)/obj.width)*besseli(1,2*pi*obj.rgrid/obj.width))).^2; - locdens=mean(obj.N(:,zpos,tend-deltat:tend),3); - [maxn,In]=max(locdens);%M.N(:,zpos,tinit)); - plot(obj.rgrid,obj.N(:,zpos,tinit),'bx-','DisplayName',sprintf('t=%1.2f[ns]',obj.t2d(tinit)*1e9),'linewidth',lw) - hold on - plot(obj.rgrid,locdens,'rx-','DisplayName',sprintf('t=[%1.2f-%1.2f] [ns] averaged',obj.t2d(tend-deltat)*1e9,obj.t2d(tend)*1e9),'linewidth',lw) - plot(obj.rgrid(In-2:end),1./obj.rgrid(In-2:end)*maxn*obj.rgrid(In),'DisplayName','N=c*1/r','linewidth',lw) - xlabel('r [m]') - ylabel('n_e [m^{-3}]') - I=find(psi>0); - if (length(I)>1) - I=[I(1)-2; I(1)-1; I; I(end)+1; I(end)+2]; - else - I=obj.nnr(1):length(psi); - end - rq=linspace(obj.rgrid(max(I(1),1)),obj.rgrid(I(end)),500); - psiinterp=interp1(obj.rgrid(I),psi(I),rq,'pchip'); - zeroindices=find(diff(psiinterp>=0),2); - maxpsiinterp=max(psiinterp); - plot(rq,maxn*psiinterp/abs(maxpsiinterp),'Displayname','normalized \Psi [a.u.]','linewidth',lw) - ylim([0 inf]) - for i=1:length(zeroindices) - border=plot([rq(zeroindices(i)) rq(zeroindices(i))],[0 obj.rgrid(In)/obj.rgrid(In-2)*maxn],'k--','linewidth',lw); - set(get(get(border,'Annotation'),'LegendInformation'),'IconDisplayStyle','off'); - end - legend - xlim([0 0.02]) - grid on - title(sprintf('Radial density profile at z=%1.2e[m]',obj.zgrid(zpos))) - obj.savegraph(f,sprintf('%sPsi',obj.name),[15 10]); - end - - function f=displayrprofile(obj,deltat,zpos) - %% plot the initial and final radial profile at the middle of the simulation space - f=figure('Name', sprintf('%s Prof',obj.name)); - if nargin < 3 - zpos=floor(length(obj.zgrid)/2); - end - tinit=1; - tend=length(obj.t2d); - if(iscell(deltat)) - deltat=cell2mat(deltat); - end - lw=1.5; - locdens=mean(obj.N(:,zpos,tend-deltat:tend),3); - Rinv=1./obj.rgrid; - Rinv(isnan(Rinv))=0; - VTHET=mean(obj.fluidUTHET(:,zpos,tend-deltat:tend),3); - omegare=(VTHET.*Rinv); - plot(obj.rgrid,obj.N(:,zpos,tinit),'bx-','DisplayName',sprintf('t=%1.2f[ns]',obj.t2d(tinit)*1e9),'linewidth',lw) - hold on - plot(obj.rgrid,locdens,'rx-','DisplayName',sprintf('t=[%1.2f-%1.2f] [ns] averaged',obj.t2d(tend-deltat)*1e9,obj.t2d(tend)*1e9),'linewidth',lw) - xlabel('r [m]') - ylabel('n_e [m^{-3}]') - legend - xlim([obj.rgrid(1) obj.rgrid(sum(obj.nnr(1:2)))]) - grid on - - yyaxis right - plot(obj.rgrid,omegare,'DisplayName',sprintf('<\\omega_{re}> t=[%1.2f-%1.2f] [ns] averaged',obj.t2d(tend-deltat)*1e9,obj.t2d(tend)*1e9),'linewidth',lw) - ylabel('\omega_{re} [1/s]') - - title(sprintf('Radial density profile at z=%1.2e[m]',obj.zgrid(zpos))) - obj.savegraph(f,sprintf('%srProf',obj.name),[15 10]); - end - - function changed=ischanged(obj) - %ischanged Check if the file has been changed and some data must be reloaded - try - filedata=dir(obj.fullpath); - checkedtimestamp=filedata.date; - if (max(checkedtimestamp > obj.timestamp) ) - changed=true; - return - end - changed=false; - return - catch - changed=true; - return - end - end - - function displayenergy(obj) - %% Plot the time evolution of the system energy and number of simulated macro particles - tmin=2; - tmax=length(obj.ekin); - f=figure('Name', sprintf('%s Energy',obj.name)); - subplot(2,1,1) - plot(obj.t0d(tmin:tmax),obj.ekin(tmin:tmax),'o-',... - obj.t0d(tmin:tmax),obj.epot(tmin:tmax),'d-',... - obj.t0d(tmin:tmax),obj.etot(tmin:tmax),'h-',... - obj.t0d(tmin:tmax),obj.eerr(tmin:tmax),'x--') - legend('ekin', 'epot', 'etot','eerr') - xlabel('Time [s]') - ylabel('Energies [J]') - grid on - xlimits=xlim(); - - subplot(2,1,2) - try - semilogy(obj.t0d(tmin:tmax),abs(obj.eerr(tmin:tmax)./obj.etot0(tmin:tmax)),'h-') - catch - semilogy(obj.t0d(tmin:tmax),abs(obj.eerr(tmin:tmax)/obj.etot(2)),'h-') - end - xlabel('t [s]') - ylabel('E_{err}/E_{tot}') - xlim(xlimits) - grid on - try - yyaxis right - plot(obj.t0d(tmin:tmax),abs(obj.nbparts(tmin:tmax)./obj.nbparts(1)*100),'d--') - ylabel('Nparts %') - %ylim([0 110]) - catch - end - obj.savegraph(f,sprintf('%s/%sEnergy',obj.folder,obj.name)); - end - - function SimParticles(obj) - %% Plot the time evolution of the number of simulated physical particles in the main specie - f=figure('Name', sprintf('%s Trapped particles',obj.name)); - plot(obj.t0d,obj.nbparts*obj.weight) - xlabel('t [s]') - ylabel('N particles') - obj.savegraph(f,sprintf('%s/%sntrapped',obj.folder,obj.name)); - end - - function fighandle=savegraph(obj, fighandle, name, papsize) - %% Saves the given figure as a pdf and a .fig - fighandle.PaperUnits='centimeters'; - if (nargin < 4) - papsize=[14 16]; - end - fighandle.PaperSize=papsize; - print(fighandle,name,'-dpdf','-fillpage') - savefig(fighandle,name) - end - - function displayHP(obj,tstart) - % Plot the histogramm of the total energy and canonical angular momentum at time tstart and - % end time of the simulation over the full simulation space for the main specie - if(iscell(tstart)) - tstart=cell2mat(tstart); - end - if(obj.R.nt>=2) - tstart=obj.R.nt; - f=figure('Name', sprintf('%s HP',obj.name)); - legtext=sprintf("t=%2.1f - %2.1f [ns]",obj.tpart(tstart)*1e9,obj.tpart(end)*1e9); - subplot(1,2,1) - partsmax=min(obj.nbparts(end),obj.R.nparts); - Hloc=H(obj,{1:obj.nbparts(1),1,false}); - h1=histogram(Hloc,20,'BinLimits',[min(Hloc(:)) max(Hloc(:))],'DisplayName',sprintf("t=%2.3d [ns]",obj.tpart(1)*1e9)); - hold on - Hloc=H(obj,{1:partsmax,obj.R.nt,false}); - %,'Binwidth',h1.BinWidth - h1=histogram(Hloc,20,'BinLimits',[min(Hloc(:)) max(Hloc(:))],'DisplayName',legtext); - ylabel('counts') - xlabel('H [J]') - legend - - subplot(1,2,2) - Ploc=P(obj,{1:obj.nbparts(1),1,false}); - h2=histogram(Ploc,50,'BinLimits',[min(Ploc(:)) max(Ploc(:))],'DisplayName',sprintf("t=%2.3d [ns]",obj.tpart(1)*1e9)); - hold on - Ploc=P(obj,{1:partsmax,obj.R.nt,false}); - histogram(Ploc,50,'BinLimits',[min(Ploc(:)) max(Ploc(:))],'DisplayName',legtext); - ylabel('counts') - xlabel('P [kg\cdotm^2\cdots^{-1}]') - %clear P - %clear H - legend - %xlim([0.95*h2.BinLimits(1) 1.05*h2.BinLimits(2)]) - obj.savegraph(f,sprintf('%s/%sParts_HP',obj.folder,obj.name)); - end - end - - function displayaveragetemp(obj) - % Computes and show the particles average temperature as a function of time - f=figure('Name',sprintf('%s potinn=%f part temperature',obj.name,obj.potinn*obj.phinorm)); - vr2=obj.VR(:,:,false); - vr2=mean(vr2.^2,1)-mean(vr2,1).^2; - vz2=obj.VZ(:,:,false); - vz2=mean(vz2.^2,1)-mean(vz2,1).^2; - vthet2=obj.VTHET(:,:,false); - vthet2=mean(vthet2.^2,1)-mean(vthet2,1).^2; - plot(obj.tpart,0.5*obj.me*vr2/obj.qe,'displayname','T_r') - hold on - plot(obj.tpart,0.5*obj.me*vz2/obj.qe,'displayname','T_z') - plot(obj.tpart,0.5*obj.me*vthet2/obj.qe,'displayname','T_{thet}') - xlabel('time [s]') - ylabel('T [eV]') - title(sprintf('\\phi_a=%.1f kV \\phi_b=%.1f kV R=%.1f',obj.potinn*obj.phinorm/1e3,obj.potout*obj.phinorm/1e3,obj.Rcurv)) - legend - grid - obj.savegraph(f,sprintf('%s/%s_partstemp',obj.folder,obj.name)); - - end - - - function Gamma=Axialflux(obj,timestep,zpos) - % Computes the axial particle flux n*Uz at timestep timestep and axial position zpos - Gamma=obj.fluidUZ(:,zpos,timestep).*obj.N(:,zpos,timestep); - end - - function I=OutCurrents(obj,timestep) - % Computes the Outgoing currens at the simulation axial boundaries at timestep timestep - % This is simply the surface integral of the axial flux - I=zeros(2,length(timestep)); - flux=obj.Axialflux(timestep,[1 obj.nz+1]); - I=squeeze(trapz(obj.rgrid,flux.*obj.rgrid)*2*pi*obj.qsim/obj.weight); - end - - function displayCurrentsevol(obj,timesteps) - % Computes and display the time evolution of the outgoing currents at timesteps timesteps - if nargin<2 - timesteps=1:length(obj.t2d); - end - currents=obj.OutCurrents(timesteps); - f=figure('Name',sprintf('%s Currents',obj.name)); - plot(obj.t2d(timesteps),currents(1,:),'Displayname',sprintf('z=%.2f cm',obj.zgrid(1)*100)); - hold on - plot(obj.t2d(timesteps),-currents(2,:),'Displayname',sprintf('z=%.2f cm',obj.zgrid(end)*100)); - plot(obj.t2d(timesteps),(currents(1,:)-currents(2,:))/2,'Displayname','average'); - legend('location','east') - xlabel('time [s]') - ylabel('I [A]') - grid on - title(sprintf('\\phi_b-\\phi_a=%.2g kV, R=%.1f',(obj.potout-obj.potinn)*obj.phinorm/1e3,obj.Rcurv)) - obj.savegraph(f,sprintf('%s/%s_outCurrents',obj.folder,obj.name)); - end - - function model=potentialwellmodel(obj,timestep) - % Computes the potential well at the given timestep and return the model to be able to - % interpolate either using grid coordinates or magnetic field line coordinates - if iscell(timestep) - timestep=cell2mat(timestep); - end - Phi=-obj.pot(:,:,timestep); - contpoints=contourc(obj.zgrid,obj.rgrid,obj.rAthet,obj.rAthet(:,1)'); - [x,y,zcont]=C2xyz(contpoints); - k=1; - zdiff=[diff(zcont),0]; - potfinal=zeros(numel(cell2mat(x)),length(timestep)); - pot=cell(1,size(x,2)); - rmin=obj.rgrid(1); - rmax=obj.rgrid(end); - rathet=x; - for i=1:length(timestep) - locPhi=Phi(:,:,i); - for j=1:size(x,2) - %for i=1:size(pot,1) - xloc=x{j}; - yloc=y{j}; - % xloc=xloc(ylocrmin); - % yloc=yloc(ylocrmin); - % x{j}=xloc; - % y{j}=yloc; - rathet{j}=zcont(j)*ones(1,length(xloc)); - if(length(xloc)>=1) - pot{j}=interp2(obj.zgrid,obj.rgrid,locPhi,xloc,yloc,'spline'); - pot{j}=pot{j}-max(pot{j}); - end - k=k+(zdiff(j)~=0); - end - potfinal(:,i)=cell2mat(pot); - end - model.z=cell2mat(x); - model.r=cell2mat(y); - model.pot=potfinal; - model.rathet=cell2mat(rathet); - end - - function [pot] = PotentialWell(obj,timestep) - % PotentialWell Computes the potential well at the given timestep on the grid points - model=obj.potentialwellmodel(timestep); - z=model.z; - r=model.r; - modpot=model.pot; - rathet=model.rathet; - [Zmesh,Rmesh]=meshgrid(obj.zgrid,obj.rgrid); - pot=zeros(length(obj.zgrid),length(obj.rgrid),length(fieldstep)); - for i=1:length(fieldstep) - pot(:,:,i)=griddata(z,r,modpot(i,:),Zmesh,Rmesh); - end - end - - function displaypotentialwell(obj,timestep,rcoord) - % Display the potential well at timestep timestep - % if rcoord is true, the potential is evaluated at grid points in r,z coordinates - % if false, the potential is evaluated at grid points in magnetic field line coordinates - if iscell(timestep) - timestep=cell2mat(timestep); - end - if nargin <3 - rcoord=true; - end - f=figure('Name',sprintf('%f Potential well',obj.name)); - model=obj.potentialwellmodel(timestep); - z=model.z; - r=model.r; - pot=model.pot; - rathet=model.rathet; - title(sprintf('Potential well t=%1.2f [ns]',obj.t2d(timestep)*1e9)) - if rcoord - [Zmesh,Rmesh]=meshgrid(obj.zgrid,obj.rgrid); - pot=griddata(z,r,pot,Zmesh,Rmesh); - surface(obj.zgrid(1:end),obj.rgrid(1:end),pot(1:end,1:end),'edgecolor','none') - xlabel('z [m]') - ylabel('r [m]') - xlim([obj.zgrid(1) obj.zgrid(end)]) - ylim([obj.rgrid(1) obj.rgrid(end)]) - else - [Zmesh,Rmesh]=meshgrid(obj.zgrid,obj.rAthet(:,1)); - pot=griddata(z,rathet,pot,Zmesh,Rmesh); - surface(obj.zgrid(1:end),obj.rAthet(:,1),pot(1:end,1:end),'edgecolor','none') - ylabel('rA_\theta [Tm^2]') - xlabel('z [m]') - xlim([obj.zgrid(1) obj.zgrid(end)]) - ylim([obj.rAthet(1,1) obj.rAthet(sum(obj.nnr(1:2)),1)]) - end - c=colorbar; - colormap('jet'); - c.Label.String= 'depth [eV]'; - obj.savegraph(f,sprintf('%s/%s_well',obj.folder,obj.name)); - end - - function Epar = Epar(obj,fieldstep) - % Computes the electric field component parallel to the magnetic field line - Epar=obj.Er(:,:,fieldstep).*(obj.Br./obj.B)' + (obj.Bz./obj.B)'.*obj.Ez(:,:,fieldstep); - end - - function Eperp = Eperp(obj,fieldstep) - % Computes the electric field component perpendicular to the magnetic field line - Eperp=obj.Er(:,:,fieldstep).*(obj.Bz./obj.B)' - (obj.Br./obj.B)'.*obj.Ez(:,:,fieldstep); - end - - function charge=totcharge(obj,fieldstep) - % Integrates the density profile over the full volume to obtain - % the total number of electrons in the volume - N=splinedensity(obj.fullpath, '/data/fields/moments', obj.knotsr, obj.knotsz, obj.femorder,ones(size(obj.CellVol)), 1, 1); - charge=sum(sum(N(:,:,end))); - end - - function [p, maxnb, c]=displayPhaseSpace(obj,type,partsstep, Rindex, Zindex,legendtext, figtitle, f, maxnb, c, gcs) - if nargin<8 - f=figure; - f=gca; - end - if nargin<7 - figtitle=sprintf('r=%1.2f [mm] z=%1.2f [mm] \\Delta\\phi=%1.1f[kV] R=%1.1f',obj.rgrid(Rindex)*1e3,obj.zgrid(Zindex)*1e3,(obj.potout-obj.potinn)*obj.phinorm/1e3,obj.Rcurv); - end - if nargin <6 - legendtext=sprintf('t=%1.3g [s]',obj.tpart(partsstep)); - end - fieldstep=find(obj.tpart(partsstep(end))==obj.t2d,1); - - zleftlim=1; - if nargin>=10 - ctemp=c; - n=zeros(length(c{1}),length(c{2})); - else - nbins=15; - n=zeros(nbins); - end - if nargin <11 - gcs=true; - end - - - for i=1:length(partsstep) - odstep=find(obj.tpart(partsstep(i))==obj.t0d); - Rp=obj.R(1:obj.nbparts(odstep),partsstep(i),false); - Zp=obj.Z(1:obj.nbparts(odstep),partsstep(i),false); - deltar=obj.dr(2)/2; - deltarm=obj.rgrid(Rindex)-sqrt(obj.rgrid(Rindex)^2-deltar^2-2*obj.rgrid(Rindex)*deltar); - deltaz=obj.dz/2; - Indices=Rp>=obj.rgrid(Rindex)-deltarm & Rp=obj.zgrid(Zindex)-deltaz & Zp0 obj.R = h5partsquantity(obj.fullpath,hdf5group,'R'); obj.Z = h5partsquantity(obj.fullpath,hdf5group,'Z'); obj.THET = h5partsquantity(obj.fullpath,hdf5group,'THET'); obj.VR = h5partsquantity(obj.fullpath,hdf5group,'UR',obj.vnorm); obj.VZ = h5partsquantity(obj.fullpath,hdf5group,'UZ',obj.vnorm); obj.VTHET= h5partsquantity(obj.fullpath,hdf5group,'UTHET',obj.vnorm); obj.q= h5readatt(obj.fullpath,hdf5group,'q'); obj.m= h5readatt(obj.fullpath,hdf5group,'m'); obj.weight= h5readatt(obj.fullpath,hdf5group,'weight'); obj.partepot = h5partsquantity(filename,hdf5group,'pot',obj.q); + obj.rindex=1:length(obj.rgrid); + obj.zindex=1:length(obj.zgrid); try obj.Rindex=h5partsquantity(obj.fullpath,hdf5group,'Rindex'); obj.Zindex=h5partsquantity(obj.fullpath,hdf5group,'Zindex'); obj.partindex=h5partsquantity(obj.fullpath,hdf5group,'partindex'); catch end + end % try % obj.partindex = h5read(obj.fullpath,sprintf('%s/partindex',hdf5group)); % partindex=obj.partindex; % partindex(partindex==-1)=NaN; % %[~,Indices]=sort(partindex,'ascend'); % Indices=obj.partindex; % for i=1:size(Indices,2) % obj.H(Indices(:,i),i)=obj.H(:,i); % obj.P(Indices(:,i),i)=obj.P(:,i); % % obj.R(Indices(:,i),i)=obj.R(:,i); % obj.Z(Indices(:,i),i)=obj.Z(:,i); % obj.Rindex(Indices(:,i),i)=obj.Rindex(:,i); % obj.Zindex(Indices(:,i),i)=obj.Zindex(:,i); % obj.VR(Indices(:,i),i)=obj.VR(:,i); % obj.VZ(Indices(:,i),i)=obj.VZ(:,i); % obj.VTHET(Indices(:,i),i)=obj.VTHET(:,i); % obj.THET(Indices(:,i),i)=obj.THET(:,i); % end % catch % end % clear partindex; end - function quantity=H(obj,indices) - if indices{1}==':' + function quantity=H(obj,varargin) + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + if strcmp(indices{1},':') p=1:obj.VR.nparts; else p=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') t=1:length(obj.tpart); else t=indices{2}; end - if size(indices,1)>2 + if size(indices,2)>2 track=indices{3}; else track=false; end quantity=0.5*obj.m*(obj.VR(p,t,track).^2+obj.VTHET(p,t,track).^2+obj.VZ(p,t,track).^2)+obj.partepot(p,t,track); end - function quantity=P(obj,indices) - if indices{1}==':' + function quantity=P(obj,varargin) + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + if strcmp(indices{1},':') p=1:obj.R.nparts; else p=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') t=1:length(obj.tpart); else t=indices{2}; end - if size(indices,1)>2 + if size(indices,2)>2 track=indices{3}; else track=false; end quantity=obj.R(p,t,track).*(obj.VTHET(p,t,track)*obj.m+obj.q*obj.parent.Atheta(obj.R(p,t,track),obj.Z(p,t,track))); end - function quantity=Vpar(obj,indices) + function quantity=Vpar(obj,varargin) %Vpar Computes the parallel velocity for the particle indices{1} at time indices{2} - - if indices{1}==':' + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + if strcmp(indices{1},':') p=1:obj.R.nparts; else p=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') t=1:length(obj.tpart); else t=indices{2}; end if size(indices,2)>2 track=indices{3}; else track=false; end - %Zind=obj.Zindex(p,t,track); - %Rind=obj.Rindex(p,t,track); - rpos(1,1,:)=obj.parent.rgrid; - zpos(1,1,:)=obj.parent.zgrid; - [~,Rind]=min(abs(obj.R(p,t,track)-rpos),[],3); - [~,Zind]=min(abs(obj.Z(p,t,track)-zpos),[],3); - posind=sub2ind(size(obj.parent.B),Zind,Rind); - costhet=obj.parent.Bz(posind)./obj.parent.B(posind); - sinthet=obj.parent.Br(posind)./obj.parent.B(posind); + Zp=obj.Z(p,t,track); + Rp=obj.R(p,t,track); + Bzp=interp2(obj.zgrid,obj.rgrid,obj.parent.Bz',Zp,Rp); + Brp=interp2(obj.zgrid,obj.rgrid,obj.parent.Br',Zp,Rp); + Bp=interp2(obj.zgrid,obj.rgrid,obj.parent.B',Zp,Rp); + costhet=Bzp./Bp; + sinthet=Brp./Bp; quantity=obj.VR(p,t,track).*sinthet+obj.VZ(p,t,track).*costhet; end - function quantity=Vperp(obj,indices) + function quantity=Vperp(obj,varargin) %Vperp Computes the perpendicular velocity in the guidind center reference frame, % for the main specie particle indices{1} at time indices{2} - if indices{1}==':' + if(~iscell(varargin)) + indices=mat2cell(varargin); + else + indices=varargin; + end + + if strcmp(indices{1},':') p=1:obj.R.nparts; else p=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') t=1:length(obj.tpart); else t=indices{2}; end if size(indices,2)>2 track=indices{3}; else track=false; end - if size(indices,2)>3 gcs=indices{4}; else - gcs=true; - end - -% Zind=obj.Zindex(p,t,track)+1; -% Rind=obj.Rindex(p,t,track)+1; - rpos(1,1,:)=obj.parent.rgrid; - zpos(1,1,:)=obj.parent.zgrid; - [~,Rind]=min(abs(obj.R(p,t,track)-rpos),[],3); - [~,Zind]=min(abs(obj.Z(p,t,track)-zpos),[],3); - posind=sub2ind(size(obj.parent.B),Zind,Rind); - costhet=obj.parent.Bz(posind)./obj.parent.B(posind); - sinthet=obj.parent.Br(posind)./obj.parent.B(posind); - Vdrift=zeros(size(Zind)); - vr=obj.VR(p,t,track); - vz=obj.VZ(p,t,track); - vthet=obj.VTHET(p,t,track); + gcs=false; + end + Zp=obj.Z(p,t,track); + Rp=obj.R(p,t,track); + Bzp=interp2(obj.zgrid,obj.rgrid,obj.parent.Bz',Zp,Rp); + Brp=interp2(obj.zgrid,obj.rgrid,obj.parent.Br',Zp,Rp); + Bp=interp2(obj.zgrid,obj.rgrid,obj.parent.B',Zp,Rp); + costhet=Bzp./Bp; + sinthet=Brp./Bp; + Vdrift=zeros(size(Zp)); if gcs - for j=1:length(t) - [~,tfield]=min(abs(obj.parent.t2d-obj.tpart(t(j)))); - timeEr=obj.parent.Er(:,:,tfield); - timeEz=obj.parent.Ez(:,:,tfield); - posindE=sub2ind(size(timeEr),Rind(:,j),Zind(:,j)); - VdriftE=(timeEz(posindE).*obj.parent.Br(posind(:,j))-timeEr(posindE).*obj.parent.Bz(posind(:,j)))./obj.parent.B(posind(:,j)).^2; - dbr=(obj.parent.B(posind(:,j)+1)-obj.parent.B(posind(:,j)))./(obj.parent.rgrid(Rind(:,j)+1)-obj.parent.rgrid(Rind(:,j))); - dbz=(obj.parent.B(posind(:,j)+1)-obj.parent.B(posind(:,j)))./(obj.parent.zgrid(Zind(:,j)+1)-obj.parent.zgrid(Zind(:,j))); - VdriftB= +sign(obj.weight)*obj.m/obj.q*(vr(:,j).*sinthet(:,j)+vz(:,j).*costhet(:,j)).^2./ ... - obj.parent.B(posind(:,j)).^3.*(obj.parent.Bz(posind(:,j)).*dbr-obj.parent.Br(posind(:,j)).*dbz); - Vdrift(:,j)= VdriftE+VdriftB; - end - end - quantity=sqrt((vthet-Vdrift).^2+(vr.*costhet-vz.*sinthet).^2); + for j=1:length(t) + [~, tfield]=min(abs(obj.parent.t2d-obj.tpart(t(j)))); + timeEr=obj.parent.Er(:,:,tfield); + timeEz=obj.parent.Ez(:,:,tfield); + %posindE=sub2ind(size(timeEr),Rind(:,j),Zind(:,j)); + timeErp=interp2(obj.zgrid,obj.rgrid,timeEr,Zp(:,j),Rp(:,j)); + timeEzp=interp2(obj.zgrid,obj.rgrid,timeEz,Zp(:,j),Rp(:,j)); + Vdrift(:,j)=(timeEzp.*Brp(:,j)-timeErp.*Bzp(:,j))./Bp(:,j).^2; + end + end + quantity=sqrt((obj.VTHET(p,t,track)-Vdrift).^2+(obj.VR(p,t,track).*costhet-obj.VZ(p,t,track).*sinthet).^2); end function sref = subsref(obj,s) % obj(i) is equivalent to obj.Data(i) switch s(1).type case '.' if(strcmp(s(1).subs,'H')) sref=H(obj,s(2).subs); elseif(strcmp(s(1).subs,'P')) sref=P(obj,s(2).subs); else sref=builtin('subsref',obj,s); end case '()' sref=builtin('subsref',obj,s); case '{}' error('MYDataClass:subsref',... 'Not a supported subscripted reference') end end end -end \ No newline at end of file +end diff --git a/matlab/h5partsquantity.m b/matlab/helper_classes/h5partsquantity.m similarity index 90% rename from matlab/h5partsquantity.m rename to matlab/helper_classes/h5partsquantity.m index accfdd3..c695689 100644 --- a/matlab/h5partsquantity.m +++ b/matlab/helper_classes/h5partsquantity.m @@ -1,85 +1,87 @@ classdef h5partsquantity properties filename group dataset nparts nt scale end methods function obj=h5partsquantity(filename, group, dataset, scale) obj.filename=filename; obj.dataset=dataset; obj.group=group; obj.nparts=h5info(filename,sprintf('%s/%s',group,dataset)).Dataspace.Size(1); obj.nt=h5info(filename,sprintf('%s/%s',group,dataset)).Dataspace.Size(end); if nargin < 4 obj.scale=1; else obj.scale=scale; end end function ind=end(obj,k,n) switch k case 1 ind=obj.nparts; case 2 ind=obj.nt; case default error('Invalid number of dimensions'); end end function sref = subsref(obj,s) % obj(i) is equivalent to obj.Data(i) switch s(1).type case '.' if(strcmp(s(1).subs,'val')) sref = obj.(s(1).subs)(s(2).subs); else sref=builtin('subsref',obj,s); end case '()' sref = val(obj,s(1).subs); case '{}' error('MYDataClass:subsref',... 'Not a supported subscripted reference') end end function quantity=val(obj,indices) - if indices{1}==':' + if strcmp(indices{1},':') p=1:obj.nparts; else p=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') t=1:obj.nt; else t=indices{2}; end if size(indices,2)>2 track=indices{3}; else track=false; end - quantity=zeros(obj.nparts,length(t)); + if track + quantity=sparse(max(p),length(t)); for i=1:length(t) indices = h5read(obj.filename, sprintf('%s/%s',obj.group,'partindex'),[1 t(i)],[Inf 1]); indices(indices<=0)=max(indices)+1; quantity(indices,i) = h5read(obj.filename, sprintf('%s/%s',obj.group,obj.dataset),[1 t(i)],[Inf 1]); end else + quantity=zeros(length(p),length(t)); for i=1:length(t) - quantity(:,i) = h5read(obj.filename, sprintf('%s/%s',obj.group,obj.dataset),[1 t(i)],[Inf 1]); + quantity(:,i) = h5read(obj.filename, sprintf('%s/%s',obj.group,obj.dataset),[1 t(i)],[length(p) 1]); end end quantity=quantity(p,:)*obj.scale; end end end diff --git a/matlab/h5quantity.m b/matlab/helper_classes/h5quantity.m similarity index 100% rename from matlab/h5quantity.m rename to matlab/helper_classes/h5quantity.m diff --git a/matlab/partquantity.m b/matlab/helper_classes/partquantity.m similarity index 100% rename from matlab/partquantity.m rename to matlab/helper_classes/partquantity.m diff --git a/matlab/splinedensity.m b/matlab/helper_classes/splinedensity.m similarity index 84% rename from matlab/splinedensity.m rename to matlab/helper_classes/splinedensity.m index a13b37e..e862223 100644 --- a/matlab/splinedensity.m +++ b/matlab/helper_classes/splinedensity.m @@ -1,21 +1,21 @@ classdef splinedensity < splinequantity properties invVolume end methods - function obj=splinedensity(filename, dataset, knotsr, knotsz, femorder, Volume, MacropartSize, index) - obj=obj@splinequantity(filename, dataset, knotsr, knotsz, femorder, MacropartSize, index); + function obj=splinedensity(filename, dataset, knotsr, knotsz, femorder, Volume, MacropartSize, postscale, index) + obj=obj@splinequantity(filename, dataset, knotsr, knotsz, femorder, MacropartSize, postscale, index); obj.invVolume=1./Volume; obj.invVolume(isinf(obj.invVolume))=0; end function quantity=coeffs(obj,indices) quantity=coeffs@splinequantity(obj,indices); for i=1:size(quantity,3) quantity(:,:,i)=quantity(:,:,i).*obj.invVolume; end end end end \ No newline at end of file diff --git a/matlab/splinepressure.m b/matlab/helper_classes/splineenergy.m similarity index 61% copy from matlab/splinepressure.m copy to matlab/helper_classes/splineenergy.m index edbbbcb..b84b592 100644 --- a/matlab/splinepressure.m +++ b/matlab/helper_classes/splineenergy.m @@ -1,186 +1,178 @@ -classdef splinepressure < h5quantity +classdef splineenergy < h5quantity properties invVolume knotsr knotsz femorder + postscale end methods (Access=public) - function obj=splinepressure(filename, dataset, knotsr, knotsz, femorder, Volume, vnorm) + function obj=splineenergy(filename, dataset, knotsr, knotsz, femorder, Volume, vnorm, postscale) if nargin<7 vnorm=1; end obj=obj@h5quantity(filename, dataset, length(knotsr)-2*femorder(2), length(knotsz)-2*femorder(1), vnorm); obj.knotsr=knotsr; obj.knotsz=knotsz; obj.femorder=double(femorder); obj.invVolume=1./Volume; obj.invVolume(isinf(obj.invVolume))=0; + if nargin <8 + obj.postscale=1; + else + obj.postscale=postscale; + end end function quantity=coeffs(obj,indices) - if indices{1}==':' - ij=1:6; + if strcmp(indices{1},':') + ij=1:3; else ij=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') r=1:obj.nr+obj.femorder(2)-1; else r=indices{2}; end - if indices{3}==':' + if strcmp(indices{3},':') z=1:obj.nz+obj.femorder(1)-1; else z=indices{3}; end - if indices{4}==':' + if strcmp(indices{4},':') t=1:obj.nt; else t=indices{4}; end - temp=zeros(length(ij),(obj.nz+obj.femorder(1)-1),(obj.nr+obj.femorder(2)-1),length(t)); + quantity=zeros(length(ij),(obj.nr+obj.femorder(2)-1),(obj.nz+obj.femorder(1)-1),length(t)); + %temp=zeros((obj.nz+obj.femorder(1)-1)*(obj.nr+obj.femorder(2)-1),length(t)); for tempi=1:length(ij) - temp(tempi,:,:,:)=reshape(obj.readtensorindex(ij(tempi),t),obj.nz+obj.femorder(1)-1,obj.nr+obj.femorder(2)-1,[]); - for timei=1:size(temp,4) - temp(tempi,:,:,timei)=obj.invVolume'.*squeeze(temp(tempi,:,:,timei)); + temp=obj.readtensorindex(ij(tempi),t); + for timei=1:size(temp,2) + quantity(tempi,:,:,timei)=permute(reshape(temp(:,timei),obj.nz+obj.femorder(1)-1,obj.nr+obj.femorder(2)-1),[2,1,3]); end end - quantity=permute(temp,[1,3,2,4])*obj.scale; + quantity=quantity*obj.scale; end function quantity=val(obj,indices) - if indices{1}==':' - ij=1:6; + if strcmp(indices{1},':') + ij=1:3; else ij=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') r=1:obj.nr; else r=indices{2}; end - if indices{3}==':' + if strcmp(indices{3},':') z=1:obj.nz; else z=indices{3}; end - if indices{4}==':' + if strcmp(indices{4},':') t=1:obj.nt; else t=indices{4}; end count=length(t); temp=obj.coeffs({ij,':',':',t}); - quantity=zeros(length(ij),max(r)-min(r)+1,max(z)-min(z)+1,count); + quantity=zeros(length(ij),length(r),length(z),count); for j=1:size(temp,1) for i=1:size(temp,4) - quantity(j,:,:,i)=fnval(spmak({obj.knotsr,obj.knotsz},squeeze(temp(j,:,:,i))),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); + vali=obj.postscale.*fnval(spmak({obj.knotsr,obj.knotsz},squeeze(temp(j,:,:,i))),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); + quantity(j,:,:,i)=vali(r,z); end end end function quantity=der(obj,indices) - if indices{1}==':' - ij=1:6; + if strcmp(indices{1},':') + ij=1:3; else ij=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') r=1:obj.nr; else r=indices{2}; end - if indices{3}==':' + if strcmp(indices{3},':') z=1:obj.nz; else z=indices{3}; end - if indices{4}==':' + if strcmp(indices{4},':') t=1:obj.nt; else t=indices{4}; end if length(indices)<5 order=[1,1]; else order=indices{5}; end count=length(t); temp=obj.coeffs({ij,':',':',t}); quantity=zeros(length(ij),max(r)-min(r)+1,max(z)-min(z)+1,count); for j=1:size(temp,1) for i=1:size(temp,4) quantity(j,:,:,i)=fnval(fnder(... spmak({obj.knotsr,obj.knotsz},squeeze(temp(j,:,:,i)))... ,order),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); end end end function ind=end(obj,k,n) switch k case 1 ind=6; case 2 ind=obj.nr; case 3 ind=obj.nz; case 4 ind=obj.nt; case default error('Invalid number of dimensions'); end end end methods (Access=private) - function quantity = readtensorindex(obj, ij, t) - if ij==':' || length(ij)>1 + function quantity = readtensorindex(obj, ii, t) + if strcmp(ii,':') || length(ii)>1 error('Unable to read several indices at the same time'); end - if t==':' + if strcmp(t,':') t=1:obj.nt; end - ui=zeros((obj.nz+obj.femorder(1)-1)*(obj.nr+obj.femorder(2)-1),length(t)); - uj=ui; - n=ui; - vij=ui; + vii=zeros((obj.nz+obj.femorder(1)-1)*(obj.nr+obj.femorder(2)-1),length(t)); + n=vii; - switch ij + switch ii case 1 - i=1; - j=1; + id=5; case 2 - i=1; - j=2; + id=8; case 3 - i=1; - j=3; - case 4 - i=2; - j=2; - case 5 - i=2; - j=3; - case 6 - i=3; - j=3; + id=10; case default - error('Invalid Pressure index'); + error('Invalid Energy index'); end for k=1:length(t) - vij(:,k) = h5read(obj.filename, obj.dataset,[ij+4 1 t(k)],[1 Inf 1]); - ui(:,k) = h5read(obj.filename, obj.dataset,[i+1 1 t(k)],[1 Inf 1]); - uj(:,k) = h5read(obj.filename, obj.dataset,[j+1 1 t(k)],[1 Inf 1]); + vii(:,k) = h5read(obj.filename, obj.dataset,[id 1 t(k)],[1 Inf 1]); n(:,k) = h5read(obj.filename, obj.dataset,[1 1 t(k)],[1 Inf 1]); end n=1./n; n(isinf(n))=0; - quantity=vij-ui.*uj.*n; + quantity=vii.*n; end end end \ No newline at end of file diff --git a/matlab/splinepressure.m b/matlab/helper_classes/splinepressure.m similarity index 62% rename from matlab/splinepressure.m rename to matlab/helper_classes/splinepressure.m index edbbbcb..dbf0b74 100644 --- a/matlab/splinepressure.m +++ b/matlab/helper_classes/splinepressure.m @@ -1,186 +1,221 @@ classdef splinepressure < h5quantity properties invVolume knotsr knotsz femorder + postscale + rgrid + zgrid + invdr + invdz + rcenters + zcenters end methods (Access=public) - function obj=splinepressure(filename, dataset, knotsr, knotsz, femorder, Volume, vnorm) + function obj=splinepressure(filename, dataset, knotsr, knotsz, femorder, Volume, vnorm, postscale) if nargin<7 vnorm=1; end obj=obj@h5quantity(filename, dataset, length(knotsr)-2*femorder(2), length(knotsz)-2*femorder(1), vnorm); obj.knotsr=knotsr; obj.knotsz=knotsz; obj.femorder=double(femorder); obj.invVolume=1./Volume; obj.invVolume(isinf(obj.invVolume))=0; + if nargin <8 + obj.postscale=1; + else + obj.postscale=postscale; + end + obj.rgrid=obj.knotsr((1:obj.nr) +obj.femorder(2)); + obj.zgrid=obj.knotsz((1:obj.nz) +obj.femorder(1)); + [dr,dz]=meshgrid(obj.rgrid(3:end)-obj.rgrid(1:end-2),obj.zgrid(3:end)-obj.zgrid(1:end-2)); + obj.invdr=1./dr'; + obj.invdz=1./dz'; + obj.rcenters=movmean(obj.knotsr,femorder(2)+2); + obj.rcenters=obj.rcenters(femorder(2):end-femorder(2)); + obj.zcenters=movmean(obj.knotsz,femorder(1)+2); + obj.zcenters=obj.zcenters(femorder(1):end-femorder(1)); end function quantity=coeffs(obj,indices) - if indices{1}==':' + if strcmp(indices{1},':') ij=1:6; else ij=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') r=1:obj.nr+obj.femorder(2)-1; else r=indices{2}; end - if indices{3}==':' + if strcmp(indices{3},':') z=1:obj.nz+obj.femorder(1)-1; else z=indices{3}; end - if indices{4}==':' + if strcmp(indices{4},':') t=1:obj.nt; else t=indices{4}; end - temp=zeros(length(ij),(obj.nz+obj.femorder(1)-1),(obj.nr+obj.femorder(2)-1),length(t)); + quantity=zeros(length(ij),(obj.nr+obj.femorder(2)-1),(obj.nz+obj.femorder(1)-1),length(t)); + %temp=zeros((obj.nz+obj.femorder(1)-1)*(obj.nr+obj.femorder(2)-1),length(t)); for tempi=1:length(ij) - temp(tempi,:,:,:)=reshape(obj.readtensorindex(ij(tempi),t),obj.nz+obj.femorder(1)-1,obj.nr+obj.femorder(2)-1,[]); - for timei=1:size(temp,4) - temp(tempi,:,:,timei)=obj.invVolume'.*squeeze(temp(tempi,:,:,timei)); + temp=obj.readtensorindex(ij(tempi),t); + for timei=1:size(temp,2) + quantity(tempi,:,:,timei)=permute(reshape(temp(:,timei),obj.nz+obj.femorder(1)-1,obj.nr+obj.femorder(2)-1),[2,1,3]).*obj.invVolume; end end - quantity=permute(temp,[1,3,2,4])*obj.scale; + quantity=quantity*obj.scale; end function quantity=val(obj,indices) - if indices{1}==':' + if strcmp(indices{1},':') ij=1:6; else ij=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') r=1:obj.nr; else r=indices{2}; end - if indices{3}==':' + if strcmp(indices{3},':') z=1:obj.nz; else z=indices{3}; end - if indices{4}==':' + if strcmp(indices{4},':') t=1:obj.nt; else t=indices{4}; end count=length(t); temp=obj.coeffs({ij,':',':',t}); quantity=zeros(length(ij),max(r)-min(r)+1,max(z)-min(z)+1,count); + %[Z,R]=meshgrid(obj.zcenters,obj.rcenters); + %[zg,rg]=meshgrid(obj.zgrid,obj.rgrid); for j=1:size(temp,1) for i=1:size(temp,4) quantity(j,:,:,i)=fnval(spmak({obj.knotsr,obj.knotsz},squeeze(temp(j,:,:,i))),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); + %quantity(j,:,:,i)=interp2(Z,R,squeeze(temp(j,:,:,i)),zg,rg); end end end function quantity=der(obj,indices) - if indices{1}==':' + if strcmp(indices{1},':') ij=1:6; else ij=indices{1}; end - if indices{2}==':' + if strcmp(indices{2},':') r=1:obj.nr; else r=indices{2}; end - if indices{3}==':' + if strcmp(indices{3},':') z=1:obj.nz; else z=indices{3}; end - if indices{4}==':' + if strcmp(indices{4},':') t=1:obj.nt; else t=indices{4}; end if length(indices)<5 order=[1,1]; else order=indices{5}; end count=length(t); temp=obj.coeffs({ij,':',':',t}); quantity=zeros(length(ij),max(r)-min(r)+1,max(z)-min(z)+1,count); for j=1:size(temp,1) for i=1:size(temp,4) - quantity(j,:,:,i)=fnval(fnder(... - spmak({obj.knotsr,obj.knotsz},squeeze(temp(j,:,:,i)))... - ,order),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); + %preder=fnval(... + % spmak({obj.knotsr,obj.knotsz},squeeze(temp(j,:,:,i)))... + % ,{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); + preder=squeeze(obj.val({ij(j),r,z,t(i)})); + if order(1)>0 + preder(2:end-1,2:end-1)=(preder(3:end,2:end-1)-preder(1:end-2,2:end-1)).*obj.invdr; + end + if order(2)>0 + preder(2:end-1,2:end-1)=(preder(2:end-1,3:end)-preder(2:end-1,1:end-2)).*obj.invdz; + end + quantity(j,2:end-1,2:end-1,i)=preder(2:end-1,2:end-1); end end end function ind=end(obj,k,n) switch k case 1 ind=6; case 2 ind=obj.nr; case 3 ind=obj.nz; case 4 ind=obj.nt; case default error('Invalid number of dimensions'); end end end methods (Access=private) function quantity = readtensorindex(obj, ij, t) - if ij==':' || length(ij)>1 + if strcmp(ij,':') || length(ij)>1 error('Unable to read several indices at the same time'); end - if t==':' + if strcmp(t,':') t=1:obj.nt; end ui=zeros((obj.nz+obj.femorder(1)-1)*(obj.nr+obj.femorder(2)-1),length(t)); uj=ui; n=ui; vij=ui; switch ij case 1 i=1; j=1; case 2 i=1; j=2; case 3 i=1; j=3; case 4 i=2; j=2; case 5 i=2; j=3; case 6 i=3; j=3; case default error('Invalid Pressure index'); end for k=1:length(t) vij(:,k) = h5read(obj.filename, obj.dataset,[ij+4 1 t(k)],[1 Inf 1]); ui(:,k) = h5read(obj.filename, obj.dataset,[i+1 1 t(k)],[1 Inf 1]); uj(:,k) = h5read(obj.filename, obj.dataset,[j+1 1 t(k)],[1 Inf 1]); n(:,k) = h5read(obj.filename, obj.dataset,[1 1 t(k)],[1 Inf 1]); end n=1./n; n(isinf(n))=0; - quantity=vij-ui.*uj.*n; + nv2=vij; + nuiuj=ui.*uj.*n; + quantity=squeeze(nv2-nuiuj); end end end \ No newline at end of file diff --git a/matlab/helper_classes/splinequantity.m b/matlab/helper_classes/splinequantity.m new file mode 100644 index 0000000..539c00e --- /dev/null +++ b/matlab/helper_classes/splinequantity.m @@ -0,0 +1,182 @@ +classdef splinequantity < h5quantity + + properties + knotsr + knotsz + femorder + index + postscale + rgrid + zgrid + invdr + invdz + rcenters + zcenters + end + + methods + function obj=splinequantity(filename, dataset, knotsr, knotsz, femorder, scale, postscale, index) + if nargin<6 + scale=1; + end + if nargin<8 + index=-1; + end + obj=obj@h5quantity(filename, dataset, length(knotsr)-2*femorder(2), length(knotsz)-2*femorder(1), scale); + obj.knotsr=knotsr; + obj.knotsz=knotsz; + obj.femorder=double(femorder); + if nargin < 7 + obj.postscale=ones(length(knotsr)-2*femorder(2), length(knotsz)-2*femorder(1)); + else + obj.postscale=postscale; + end + if nargin < 8 + obj.index=-1; + else + obj.index=index; + end + obj.rgrid=obj.knotsr((1:obj.nr) +obj.femorder(2)); + obj.zgrid=obj.knotsz((1:obj.nz) +obj.femorder(1)); + [dz,dr]=meshgrid(obj.zgrid(3:end)-obj.zgrid(1:end-2),obj.rgrid(3:end)-obj.rgrid(1:end-2)); + obj.invdr=1./dr; + obj.invdz=1./dz; + obj.rcenters=movmean(obj.knotsr,femorder(2)+2); + obj.rcenters=obj.rcenters(femorder(2):end-femorder(2)); + obj.zcenters=movmean(obj.knotsz,femorder(1)+2); + obj.zcenters=obj.zcenters(femorder(1):end-femorder(1)); + end + + function quantity=coeffs(obj,indices) + if strcmp(indices{1},':') + r=1:obj.nr+obj.femorder(2)-1; + else + r=indices{1}; + end + if strcmp(indices{2},':') + z=1:obj.nz+obj.femorder(1)-1; + else + z=indices{2}; + end + if strcmp(indices{3},':') + t=1:obj.nt; + else + t=indices{3}; + end + temp=zeros((obj.nz+obj.femorder(1)-1)*(obj.nr+obj.femorder(2)-1),length(t)); + if obj.index ~= -1 + if(length(unique(diff(t))) == 1 && length(t)>1) + stride=t(2)-t(1); + temp = h5read(obj.filename, obj.dataset,[obj.index 1 t(1)],[1 Inf length(t)],[1 1 stride]); + else + for i=1:length(t) + temp(:,i) = h5read(obj.filename, obj.dataset,[obj.index 1 t(i)],[1 Inf 1]); + end + end + else + if(sum(diff(diff(t))) == 0 && length(t)>1) + stride=t(2)-t(1); + temp = h5read(obj.filename, obj.dataset,[1 t(1)],[Inf length(t)],[1 stride]); + else + for i=1:length(t) + temp(:,i) = h5read(obj.filename, obj.dataset,[1 t(i)],[Inf 1]) ; + end + end + end + temp=reshape(squeeze(temp),obj.nz+obj.femorder(1)-1,obj.nr+obj.femorder(2)-1,[]); + temp=permute(temp,[2,1,3]); + quantity=temp(r,z,:)*obj.scale; + end + + function quantity=val(obj,indices) + if strcmp(indices{1},':') + r=1:obj.nr; + else + r=indices{1}; + end + if strcmp(indices{2},':') + z=1:obj.nz; + else + z=indices{2}; + end + if strcmp(indices{3},':') + t=1:obj.nt; + else + t=indices{3}; + end + count=length(t); + temp=obj.coeffs({':',':',t}); + quantity=zeros(length(r),length(z),count); + [Z,R]=meshgrid(obj.zcenters,obj.rcenters); + [zg,rg]=meshgrid(obj.zgrid,obj.rgrid); + for i=1:size(temp,3) + quantity(:,:,i)=obj.postscale(r,z).*fnval(spmak({obj.knotsr,obj.knotsz},temp(:,:,i)),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); + %valued=interp2(Z,R,squeeze(temp(:,:,i)),zg,rg); + %quantity(:,:,i)=valued(r,z); + end + end + + function quantity=posval(obj,indices) + if strcmp(indices{1},':') + r=1:obj.nr; + else + r=indices{1}; + end + if strcmp(indices{2},':') + z=1:obj.nz; + else + z=indices{2}; + end + if strcmp(indices{3},':') + t=1:obj.nt; + else + t=indices{3}; + end + count=length(t); + temp=obj.coeffs({':',':',t}); + if(length(r) ~= length(z)) + error("r and z array must be the same size") + end + quantity=zeros(min(length(r),length(z)),count); + for i=1:size(temp,3) + quantity(:,i)=fnval(spmak({obj.knotsr,obj.knotsz},temp(:,:,i)),[r(:)';z(:)']); + end + + end + + + function quantity=der(obj,indices) + if strcmp(indices{1},':') + r=1:obj.nr; + else + r=indices{1}; + end + if strcmp(indices{2},':') + z=1:obj.nz; + else + z=indices{2}; + end + if strcmp(indices{3},':') + t=1:obj.nt; + else + t=indices{3}; + end + order=indices{4}; + count=length(t); + temp=obj.coeffs({':',':',t}); + quantity=zeros(length(r),length(z),count); + for i=1:size(temp,3) + %f=spmak({obj.knotsr,obj.knotsz},temp(:,:,i)); + %preder=fnval(f,{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); + preder=obj.val({r,z,t(i)});% + if order(1)>0 + preder(2:end-1,2:end-1)=(preder(3:end,2:end-1)-preder(1:end-2,2:end-1)).*obj.invdr; + end + if order(2)>0 + preder(2:end-1,2:end-1)=(preder(2:end-1,3:end)-preder(2:end-1,1:end-2)).*obj.invdz; + end + quantity(2:end-1,2:end-1,i)=preder(2:end-1,2:end-1); + end + end + end +end diff --git a/matlab/splinevelocity.m b/matlab/helper_classes/splinevelocity.m similarity index 77% rename from matlab/splinevelocity.m rename to matlab/helper_classes/splinevelocity.m index 46004a7..5550305 100644 --- a/matlab/splinevelocity.m +++ b/matlab/helper_classes/splinevelocity.m @@ -1,20 +1,21 @@ classdef splinevelocity < splinequantity properties Partspercell end methods - function obj=splinevelocity(filename, dataset, knotsr, knotsz, femorder, vnorm, index) - obj@splinequantity(filename, dataset, knotsr, knotsz, femorder, vnorm, index); - obj.Partspercell=splinequantity(filename, dataset, knotsr, knotsz, femorder, 1, 1); + function obj=splinevelocity(filename, dataset, knotsr, knotsz, femorder, vnorm, postscale, index) + obj@splinequantity(filename, dataset, knotsr, knotsz, femorder, vnorm, postscale, index); + obj.Partspercell=splinequantity(filename, dataset, knotsr, knotsz, femorder, 1, 1, 1); end function quantity=coeffs(obj,indices) scalefactgrid=coeffs@splinequantity(obj.Partspercell,indices); scalefactgrid=1./scalefactgrid; scalefactgrid(isinf(scalefactgrid))=0; + % We divide by the local N top obtain the velocity quantity=coeffs@splinequantity(obj,indices).*scalefactgrid; end end end \ No newline at end of file diff --git a/matlab/load_espic2d.m b/matlab/load_espic2d.m deleted file mode 100644 index fa58269..0000000 --- a/matlab/load_espic2d.m +++ /dev/null @@ -1,308 +0,0 @@ -function Data = load_espic2d( file, Data, type, partstime) -% Old function used to load simulations data from espic2d HDF5 results -% This function does not handle b-splines data apart from the density and load the full file to memory -% This can be problematic for big files -if(nargin==1) - type='all'; -else - filedata=dir(file); - if (isempty(filedata)) - error("File: ""%s"" doesn't exist",file) - end - timestamp=filedata.date; - try - if (strcmp(Data.file,file) && ~max(timestamp > Data.timestamp) && (strcmp(Data.type,type)||strcmp(Data.type,'all')) ) - return - end - catch - end - if(nargin<3) - type='all'; - end - if(nargin<4) - partstime=[0 1]; - end -end -Data.type=type; -Data.file=file; -% Physical constants -Data.vlight=299792458; -Data.qe=1.60217662E-19; -Data.me=9.109383E-31; -Data.eps_0=8.85418781762E-12; -Data.kb=1.38064852E-23; - -%Data.job_time = h5readatt(file,'/data/input.00/','job_time'); -%Data.extra_time = h5readatt(file,'/data/input.00/','extra_time'); -Data.dt = h5readatt(file,'/data/input.00/','dt'); -Data.tmax= h5readatt(file,'/data/input.00/','tmax'); -Data.nrun = h5readatt(file,'/data/input.00/','nrun'); -Data.nlres = strcmp(h5readatt(file,'/data/input.00/','nlres'),'y'); -Data.nlsave = strcmp(h5readatt(file,'/data/input.00/','nlsave'),'y'); -Data.nlclassical =strcmp(h5readatt(file,'/data/input.00/','nlclassical'),'y'); -Data.nlPhis =strcmp(h5readatt(file,'/data/input.00/','nlPhis'),'y'); -Data.nz = h5readatt(file,'/data/input.00/','nz'); -Data.nnr = h5read(file,'/data/input.00/nnr'); -Data.femorder = h5read(file,'/data/input.00/femorder'); -Data.lz = h5read(file,'/data/input.00/lz'); -Data.lr = h5read(file,'/data/input.00/radii'); -Data.qsim = h5readatt(file,'/data/input.00/','qsim'); -Data.msim = h5readatt(file,'/data/input.00/','msim'); -Data.nplasma = h5readatt(file,'/data/input.00/','nplasma'); -Data.potinn = h5readatt(file,'/data/input.00/','potinn'); -Data.potout = h5readatt(file,'/data/input.00/','potout'); -Data.B0 = h5readatt(file,'/data/input.00/','B0'); -Data.Rcurv = h5readatt(file,'/data/input.00/','Rcurv'); -Data.width = h5readatt(file,'/data/input.00/','width'); -Data.n0 = h5readatt(file,'/data/input.00/','n0'); -Data.temp = h5readatt(file,'/data/input.00/','temp'); -try - Data.it0 = h5readatt(file,'/data/input.00/','it0d'); - Data.it1 = h5readatt(file,'/data/input.00/','it2d'); - Data.it2 = h5readatt(file,'/data/input.00/','itparts'); -catch - Data.it0 = h5readatt(file,'/data/input.00/','it0'); - Data.it1 = h5readatt(file,'/data/input.00/','it1'); - Data.it1 = h5readatt(file,'/data/input.00/','it2'); -end - -Data.omepe=sqrt(abs(Data.n0)*Data.qe^2/(Data.me*Data.eps_0)); -Data.omece=Data.qe*Data.B0/Data.me; - - -% Normalizations -Data.tnorm=1/Data.omepe; -Data.rnorm=Data.vlight*Data.tnorm; -Data.bnorm=Data.B0; -Data.enorm=Data.vlight*Data.bnorm; -Data.phinorm=Data.enorm*Data.rnorm; -Data.vnorm=Data.vlight; - -% Grid data -Data.rgrid= h5read(file, '/data/var1d/rgrid')*Data.rnorm; -Data.zgrid= h5read(file, '/data/var1d/zgrid')*Data.rnorm; -Data.dz=(Data.zgrid(end)-Data.zgrid(1))/double(Data.nz); -Data.dr=(Data.lr(2:end)-Data.lr(1:end-1))./double(Data.nnr); - - -Br = h5read(file,'/data/fields/Br')*Data.bnorm; -Data.Br= reshape(Br,length(Data.zgrid),length(Data.rgrid)); -Bz = h5read(file,'/data/fields/Bz')*Data.bnorm; -Data.Bz= reshape(Bz,length(Data.zgrid),length(Data.rgrid)); -try - Atheta = h5read(file,'/data/fields/Athet')*Data.bnorm; - Data.Athet= reshape(Atheta,length(Data.zgrid),length(Data.rgrid)); -catch -end -Data.B=sqrt(Data.Bz.^2+Data.Br.^2); -clear Br Bz - - -Data.femorder = h5read(file,'/data/input.00/femorder'); -Data.ngauss = h5read(file,'/data/input.00/ngauss'); -Data.plasmadim = h5read(file,'/data/input.00/plasmadim'); -Data.radii = h5read(file,'/data/input.00/radii'); - -Data.epot = h5read(file,'/data/var0d/epot'); -Data.ekin = h5read(file,'/data/var0d/ekin'); -Data.etot = h5read(file,'/data/var0d/etot'); -try - Data.etot0 = h5read(file,'/data/var0d/etot0'); - Data.eerr = Data.etot-Data.etot0; -catch - Data.eerr = Data.etot-Data.etot(2); -end - -if(strcmp(type,'all') || strcmp(type,'fields')) - pot = h5read(file,'/data/fields/pot')*Data.phinorm; - Data.pot= reshape(pot,length(Data.zgrid),length(Data.rgrid),size(pot,2)); - clear pot - Data.pot=permute(Data.pot,[2,1,3]); - Er = h5read(file,'/data/fields/Er')*Data.enorm; - Data.Er= reshape(Er,length(Data.zgrid),length(Data.rgrid),size(Er,2)); - clear Er - Data.Er=permute(Data.Er,[2,1,3]); - Ez = h5read(file,'/data/fields/Ez')*Data.enorm; - Data.Ez= reshape(Ez,length(Data.zgrid),length(Data.rgrid),size(Ez,2)); - clear Ez - Data.Ez=permute(Data.Ez,[2,1,3]); - try - dens = h5read(file,'/data/fields/moments',[1 1 1],[1,Inf,Inf]); - kr=Data.femorder(2)+1; - Data.knotsr=augknt(Data.rgrid,kr); - kz=Data.femorder(1)+1; - Data.knotsz=augknt(Data.zgrid,kz); - - zvol=fnder(spmak(Data.knotsz,ones(1,length(Data.zgrid))), -1 ); - rvol=fnder(spmak(Data.knotsr,2*pi*Data.rgrid'), -1 ); - ZVol=diff(fnval(zvol,Data.knotsz)); - RVol=diff(fnval(rvol,Data.knotsr)); - Data.V=ZVol(2:end-1)*RVol(2:end-1)'; - Data.invV=1./Data.V'; - Data.invV(isinf(Data.invV))=0; - - dens = h5read(file,'/data/fields/moments',[1 1 1],[1,Inf,Inf]); - Data.dens=reshape(dens,Data.nz+Data.femorder(1),sum(Data.nnr)+Data.femorder(2),[]); - Data.dens=permute(Data.dens,[2,1,3]); - clear dens - - fluidvr = h5read(file,'/data/fields/moments',[2 1 1],[1,Inf,Inf]); - Data.fluidvr=reshape(fluidvr,Data.nz+Data.femorder(1),sum(Data.nnr)+Data.femorder(2),[]); - clear fluidvr - Data.fluidvr=permute(Data.fluidvr,[2,1,3])*Data.vnorm./Data.dens; - Data.fluidvr(isinf(Data.fluidvr))=0; - - fluidvthet = h5read(file,'/data/fields/moments',[3 1 1],[1,Inf,Inf]); - Data.fluidvthet=reshape(fluidvthet,Data.nz+Data.femorder(1),sum(Data.nnr)+Data.femorder(2),[]); - Data.fluidvthet=permute(Data.fluidvthet,[2,1,3])*Data.vnorm./Data.dens; - Data.fluidvthet(isinf(Data.fluidvthet))=0; - clear fluidvthet - - fluidvz = h5read(file,'/data/fields/moments',[4 1 1],[1,Inf,Inf]); - Data.fluidvz=reshape(fluidvz,Data.nz+Data.femorder(1),sum(Data.nnr)+Data.femorder(2),[]); - Data.fluidvz=permute(Data.fluidvz,[2,1,3])*Data.vnorm./Data.dens; - Data.fluidvz(isinf(Data.fluivz))=0; - clear fluidvz - - vrvr = h5read(file,'/data/fields/moments',[5 1 1],[1,Inf,Inf]); - Data.fluidvz=reshape(vrvr,Data.nz+Data.femorder(1),sum(Data.nnr)+Data.femorder(2),[]); - Data.fluidvz=permute(Data.fluidvz,[2,1,3]); - - %Data.Presstens=zeroes(6,size(Data.zgrid,1),size(Data.rgrid,1), size(moments,3)); - %Data.Presstens(1,:,:,:)=reshape(moments(5,:,:),Data.nz+Data.femorder(1),sum(Data.nnr)+Data.femorder(2),[]) - %Data.Presstens=Data.me*Data.vlight^2*reshape(h5read(file,'/data/fields/presstens'),6,length(Data.zgrid)-1,length(Data.rgrid)-1,[]); - %Data.Presstens=permute(Data.Presstens,[1,3,2,4]); - clear moments; - Data.N=zeros(length(Data.rgrid),length(Data.zgrid),size(Data.dens,3)); - for i=1:size(Data.dens,3) - Data.dens(:,:,i)=Data.dens(:,:,i).*Data.invV*abs(Data.qsim/Data.qe); - Data.N(:,:,i)=fnval(spmak({Data.knotsr,Data.knotsz},Data.dens(:,:,i)),{Data.rgrid,Data.zgrid}); - end - - %Data.moments=reshape(Data.moments,10,length(Data.zgrid),length(Data.rgrid),[]); - %Data.moments=permute(Data.moments,[1,3,2,4]); - catch - N = h5read(file,'/data/fields/partdensity'); - Data.N=reshape(N,length(Data.zgrid)-1,length(Data.rgrid)-1,[]); - Data.N=permute(Data.N,[2,1,3]); - Data.Npartscell=Data.N; - Vcell=(Data.zgrid(2:end)-Data.zgrid(1:end-1))*((Data.rgrid(2:end).^2-Data.rgrid(1:end-1).^2)*pi)'; - for i=1:size(Data.N,3) - Data.N(:,:,i)=abs(Data.qsim/Data.qe)*Data.N(:,:,i)./Vcell'; - end - Data.N=padarray(Data.N,[1,1],0,'post'); - Data.Npartscell=padarray(Data.Npartscell,[1,1],0,'post'); - clear Vcell - clear N - Data.fluidUR=reshape(h5read(file,'/data/fields/fluidur'),length(Data.zgrid)-1,length(Data.rgrid)-1,[]); - Data.fluidUR=permute(Data.fluidUR,[2,1,3])*Data.vnorm; - Data.fluidUR=padarray(Data.fluidUR,[1,1],0,'post'); - Data.fluidUZ=reshape(h5read(file,'/data/fields/fluiduz'),length(Data.zgrid)-1,length(Data.rgrid)-1,[]); - Data.fluidUZ=permute(Data.fluidUZ,[2,1,3])*Data.vnorm; - Data.fluidUZ=padarray(Data.fluidUZ,[1,1],0,'post'); - Data.fluidUTHET=reshape(h5read(file,'/data/fields/fluiduthet'),length(Data.zgrid)-1,length(Data.rgrid)-1,[]); - Data.fluidUTHET=permute(Data.fluidUTHET,[2,1,3])*Data.vnorm; - Data.fluidUTHET=padarray(Data.fluidUTHET,[1,1],0,'post'); - - Data.Presstens=Data.me*Data.vlight^2*reshape(h5read(file,'/data/fields/presstens'),6,length(Data.zgrid)-1,length(Data.rgrid)-1,[]); - Data.Presstens=permute(Data.Presstens,[1,3,2,4]); - Data.Presstens=padarray(Data.Presstens,[0,1,1,0],0,'post'); - for i=1:6 - Data.Presstens(i,:,:,:)=squeeze(Data.Presstens(i,:,:,:)).*Data.N; - end - end - try - Data.t2d = h5read(file,'/data/fields/time'); - catch - Data.t2d=Data.dt*(0:size(Data.N,3)-1)*double(Data.it1); - end - if(strcmp(Data.type,'parts')) - Data.type='all'; - end -end - -if(strcmp(type,'all') || strcmp(type,'parts')) - Data.R = h5read(file,'/data/part/R')*Data.rnorm; - partsbegin=floor(partstime(1)*size(Data.R,2))+1; - partsend=floor(partstime(2)*size(Data.R,2)); - Data.R=Data.R(:,partsbegin:partsend); - Data.Z = h5read(file,'/data/part/Z',[1,partsbegin],[size(Data.R,1) partsend])*Data.rnorm; - try - Data.THET = h5read(file,'/data/part/THET',[1,partsbegin],[size(Data.R,1) partsend]); - catch - clear Data.THET - end - try - Data.Rindex=h5read(file,'/data/part/Rindex',[1,partsbegin],[size(Data.R,1) partsend]); - Data.Zindex=h5read(file,'/data/part/Zindex',[1,partsbegin],[size(Data.R,1) partsend]); - catch - clear Data.Rindex Data.Zindex - end - UR = h5read(file,'/data/part/UR',[1,partsbegin],[size(Data.R,1) partsend]); - UZ = h5read(file,'/data/part/UZ',[1,partsbegin],[size(Data.R,1) partsend]); - UTHET= h5read(file,'/data/part/UTHET',[1,partsbegin],[size(Data.R,1) partsend]); - if(Data.nlclassical) - Data.VR = UR*Data.vnorm; - clear UR - Data.VZ = UZ*Data.vnorm; - clear UZ - Data.VTHET = UTHET*Data.vnorm; - clear UTHET - else - GAMM = sqrt(1+UR.^2+UZ.^2+UTHET.^2); - Data.VR = UR./GAMM*Data.vnorm; - clear UR - Data.VZ = UZ./GAMM*Data.vnorm; - clear UZ - Data.VTHET = UTHET./GAMM*Data.vnorm; - clear UTHET - clear GAMM - end - %Data.VPERP = sqrt(Data.VR.*Data.VR+Data.VTHET.*Data.VTHET); - partepot = sign(Data.qsim)*h5read(file,'/data/part/pot',[1,partsbegin],[size(Data.R,1) partsend])*Data.phinorm*Data.qe; - Data.H=0.5*Data.me*(Data.VR.^2+Data.VTHET.^2+Data.VZ.^2)+partepot; - clear partepot - Data.P=Data.R.*(Data.VTHET*Data.me+sign(Data.qsim)*Data.qe*Athet(Data.R,Data.Z)); - try - partindex = h5read(file,'/data/part/partindex',[1,partsbegin],[size(Data.R,1) partsend]); - [~,Indices]=sort(partindex); - for i=1:size(Indices,2) - Data.H(:,i)=Data.H(Indices(:,i),i); - Data.P(:,i)=Data.P(Indices(:,i),i); - Data.R(:,i)=Data.R(Indices(:,i),i); - Data.Z(:,i)=Data.Z(Indices(:,i),i); - Data.Rindex(:,i)=Data.Rindex(Indices(:,i),i); - Data.Zindex(:,i)=Data.Zindex(Indices(:,i),i); - Data.VR(:,i)=Data.VR(Indices(:,i),i); - Data.VZ(:,i)=Data.VZ(Indices(:,i),i); - Data.VTHET(:,i)=Data.VTHET(Indices(:,i),i); - Data.THET(:,i)=Data.THET(Indices(:,i),i); - end - catch - end - clear partindex; - - try - Data.tpart = h5read(file,'/data/part/time'); - catch - Data.tpart=Data.dt*(0:size(Data.R,2)-1)*double(Data.it2); - end - if(strcmp(Data.type,'fields')) - Data.type='all'; - end -end - -Data.t0d=Data.dt.*double(0:length(Data.epot)-1); - - -filedata=dir(file); -Data.timestamp=filedata.date; -disp('M reloaded') - - function Atheta=Athet(R,Z) - Atheta=0.5*Data.B0*(R-Data.width/pi*(Data.Rcurv-1)/(Data.Rcurv+1)... - .*besseli(1,2*pi*R/Data.width).*cos(2*pi*Z/Data.width)); - end - -end - diff --git a/matlab/muEconserv.m b/matlab/muEconserv.m new file mode 100644 index 0000000..11bb902 --- /dev/null +++ b/matlab/muEconserv.m @@ -0,0 +1,99 @@ +part=M.species(1); +time=part.tpart; +track=true; +gcs=true; +rpos(1,1,:)=M.rgrid; +zpos(1,1,:)=M.zgrid; +partindices=M.species(1).partindex(1:100,end); +partindices=partindices(partindices>0); +timeindices=(-200:1:0)+length(part.tpart); +timeindices(timeindices<1)=[]; +pR=part.R(partindices,timeindices,track); +pZ=part.Z(partindices,timeindices,track); +[~,Rind]=min(abs(pR-rpos),[],3); +[~,Zind]=min(abs(pZ-zpos),[],3); +locB=interp2(M.zgrid,M.rgrid,M.B',pZ,pR,'makima'); +Vpar=part.Vpar(partindices,timeindices,track,gcs); +Vperpst=part.Vperp(partindices,timeindices,track,gcs); +must=0.5*M.me*Vperpst.^2./locB; +mustm=zeros(size(must)); +for i=1:size(must,1) +mustm(i,:)=must(i,:)/mean(must(i,:)); +end + +lnstyleorder={'-','--','-.',':'}; +figure(); +semilogy(time(timeindices),mustm) +ax=gca; +ax.LineStyleOrder=lnstyleorder; +xlabel('time') +ylabel('mu*') + +Vperp=part.Vperp(partindices,timeindices,track,false); +mu=0.5*M.me*Vperp.^2./locB; +lnstyleorder={'-','--','-.',':'}; +mum=zeros(size(mu)); +for i=1:size(mu,1) +mum(i,:)=mu(i,:)/mean(mu(i,:)); +end +figure(); +semilogy(time(timeindices),mum) +ax=gca; +ax.LineStyleOrder=lnstyleorder; +xlabel('time') +ylabel('mu') + +Phi=zeros(size(Rind)); +for i=1:size(Rind,2) + [~,tfield]=min(abs(M.t2d-time(i))); + timPhi=M.pot(:,:,tfield); + %posindPhi=sub2ind(size(timPhi),Rind(:,i),Zind(:,i)); + Phi(:,i)=interp2(M.zgrid,M.rgrid,timPhi,pZ(:,i),pR(:,i));%timPhi(posindPhi); +end +Ebar=0.5*M.me*Vpar.^2+0.5*M.me*Vperpst.^2-M.qe*Phi; +ebarm=zeros(size(Ebar)); +for i=1:size(ebarm,1) +ebarm(i,:)=Ebar(i,:)/mean(Ebar(i,:)); +end +figure(); +semilogy(time(timeindices),ebarm) +ax=gca; +ax.LineStyleOrder=lnstyleorder; +xlabel('time') +ylabel('Ebar/_t') + +Phi=zeros(size(Rind)); +for i=1:size(Rind,2) + [~,tfield]=min(abs(M.t2d-time(i))); + timPhi=M.pot(:,:,tfield); + %posindPhi=sub2ind(size(timPhi),Rind(:,i),Zind(:,i)); + Phi(:,i)=interp2(M.zgrid,M.rgrid,timPhi,pZ(:,i),pR(:,i));%Phi(:,i)=timPhi(posindPhi); +end +E=0.5*M.me*Vpar.^2+0.5*M.me*Vperp.^2-M.qe*Phi; +em=zeros(size(E)); +for i=1:size(em,1) +em(i,:)=E(i,:)/mean(E(i,:)); +end +figure(); +semilogy(time(timeindices),em) +ax=gca; +ax.LineStyleOrder=lnstyleorder; +xlabel('time') +ylabel('E/_t') + +R=pR'; +Z=pZ'; +Thet=part.THET(partindices,timeindices,true)'; +figure(); +plot(Z,R) +ax=gca; +ax.LineStyleOrder=lnstyleorder; +xlabel('Z') +ylabel('R') + +figure(); +plot(R.*cos(Thet),R.*sin(Thet)) +ax=gca; +ax.LineStyleOrder=lnstyleorder; +xlabel('X') +ylabel('Y') \ No newline at end of file diff --git a/matlab/plotButtonPushed.m b/matlab/plotButtonPushed.m deleted file mode 100644 index 2230e91..0000000 --- a/matlab/plotButtonPushed.m +++ /dev/null @@ -1,10 +0,0 @@ -function plotGridButtonPushed(btn,ax,M,sld) -%UNTITLED2 Summary of this function goes here -% Detailed explanation goes here -f=figure(); -PlotEspic2dgriddata(f,M,sld.Value); -f.PaperOrientation='landscape'; -[~, name, ~] = fileparts(M.file); -print(f,sprintf('%s%d',name,sld.Value),'-dpdf','-fillpage') -end - diff --git a/matlab/plotGridButtonPushed.m b/matlab/plotGridButtonPushed.m deleted file mode 100644 index cdc139c..0000000 --- a/matlab/plotGridButtonPushed.m +++ /dev/null @@ -1,10 +0,0 @@ -function plotGridButtonPushed(btn,ax,M,sld) -%UNTITLED2 Summary of this function goes here -% Detailed explanation goes here -f=figure(); -PlotEspic2dgriddata(f,M,sld.Value); -f.PaperOrientation='landscape'; -[~, name, ~] = fileparts(M.file); -print(f,sprintf('%sGrid%d',name,sld.Value),'-dpdf','-fillpage') -end - diff --git a/matlab/plotPartButtonPushed.m b/matlab/plotPartButtonPushed.m deleted file mode 100644 index 299b9ff..0000000 --- a/matlab/plotPartButtonPushed.m +++ /dev/null @@ -1,10 +0,0 @@ -function plotPartButtonPushed(btn,ax,M,sld) -%UNTITLED2 Summary of this function goes here -% Detailed explanation goes here -f=figure(); -PlotEspic2dpartdata(f,M,sld.Value); -f.PaperOrientation='portrait'; -[~, name, ~] = fileparts(M.file); -print(f,sprintf('%sParts%d',name,sld.Value),'-dpdf','-fillpage') -end - diff --git a/matlab/savegeomtoh5.m b/matlab/savegeomtoh5.m new file mode 100644 index 0000000..58fdc93 --- /dev/null +++ b/matlab/savegeomtoh5.m @@ -0,0 +1,57 @@ +function savegeomtoh5(filename,splines,dist_extent,overwrite) +% saves the geometry to be used with Fennecs +% splines is a structure array containing each individual boundary +% Overwrite defines if the .h5 file must be overwritten +% dist_extent is the distance in m on which the geometric weight goes from 0 to 1 +% dist_extent can also be set in the job input file +% To look how to define each spline read the function +if nargin<4 + overwrite=false; +end +if nargin<4 + epsge=1e-6; + end + group='/geometry_spl/'; + try + fid=H5F.create(filename); + H5F.flush(fid,'H5F_SCOPE_LOCAL') + catch + if overwrite + delete(filename); + fid=H5F.create(filename); + H5F.flush(fid,'H5F_SCOPE_LOCAL') + else + error('cFile "%s" already exists\n',filename) ; + end + end + nbsplines=length(splines); + gid=H5G.create(fid,group,64); + h5writeatt(filename,group,'nbsplines',nbsplines); % total number of spline curves + h5writeatt(filename,group,'dist_extent',dist_extent); + if iscell(splines) + splines=cell2mat(splines); + end + for i=1:nbsplines + grp=sprintf('/geometry_spl/%2.2d',i); + gid=H5G.create(fid,grp,64); + h5writeatt(filename,grp,'Dirichlet_val',splines(i).Dval); % value of the electic potential in V at the boundary surface + h5writeatt(filename,grp,'order',splines(i).order); % order of the spline used to define the boundary + h5writeatt(filename,grp,'dim',splines(i).dim); % dimension of the spline curve + % epsge and epsce have no clear effect default values can be used + h5writeatt(filename,grp,'epsge',splines(i).epsge); % geometric presicion used by SISL for calculating distance to curve + h5writeatt(filename,grp,'epsce',splines(i).epsce); % numeric presicion used by SISL for calculating distance to curve + h5writeatt(filename,grp,'name',splines(i).name); % name of the boundary for readability of h5(not used in fennecs) + h5writeatt(filename,grp,'type',splines(i).type); % boundary condition type (0 dirichlet fixed value) + % 1 Dirichlet with value depending on s + % 2 Natural + h5writeatt(filename,grp,'periodic',splines(i).periodic); % periodicity of the curve 1 open, 0 closed, -1 closed periodic + points=splines(i).points; + if size(splines(i).points,2)>splines(i).dim + points=points'; + end + h5create(filename,[grp,'/pos'],size(points)); + h5write(filename,[grp,'/pos'],points); % control points defining the curve + end + + H5F.close(fid); +end \ No newline at end of file diff --git a/matlab/savemagtoh5.m b/matlab/savemagtoh5.m index f2a1e7b..244f27c 100644 --- a/matlab/savemagtoh5.m +++ b/matlab/savemagtoh5.m @@ -1,21 +1,31 @@ -function savemagtoh5(filename,r,z,Athet,Br,Bz) +function savemagtoh5(filename,r,z,Athet,Br,Bz,overwrite) +% saves a magnetic field to be used with Fennecs +% if Br or Bz is not set it is calulated from the magnetic vector potential +% Overwrite defines if the .h5 file must be overwritten if nargin<6 [Bz,~]=-gradient(Athet,mean(diff(z))); end if nargin<5 [~, Br]=gradient(r.*Athet,1,mean(diff(r))); Br=Br./r; end + if nargin<7 + overwrite=false; + end + + if overwrite && exist(filename,'file')==2 + delete(filename); + end h5create(filename,'/mag/r',length(r)); h5create(filename,'/mag/z',length(z)); h5create(filename,'/mag/Athet',size(Athet')); h5create(filename,'/mag/Br',size(Br')); h5create(filename,'/mag/Bz',size(Bz')); - h5write(filename,'/mag/r',r) - h5write(filename,'/mag/z',z) + h5write(filename,'/mag/r',r) % stores the radial grid position + h5write(filename,'/mag/z',z) % stores the axial grid position h5write(filename,'/mag/Athet',Athet') h5write(filename,'/mag/Br',Br') h5write(filename,'/mag/Bz',Bz') end \ No newline at end of file diff --git a/matlab/splinequantity.m b/matlab/splinequantity.m deleted file mode 100644 index 9f42f9b..0000000 --- a/matlab/splinequantity.m +++ /dev/null @@ -1,117 +0,0 @@ -classdef splinequantity < h5quantity - - properties - knotsr - knotsz - femorder - index - end - - methods - function obj=splinequantity(filename, dataset, knotsr, knotsz, femorder, scale, index) - if nargin<6 - scale=1; - end - obj=obj@h5quantity(filename, dataset, length(knotsr)-2*femorder(2), length(knotsz)-2*femorder(1), scale); - obj.knotsr=knotsr; - obj.knotsz=knotsz; - obj.femorder=double(femorder); - if nargin < 7 - obj.index=-1; - else - obj.index=index; - end - end - - function quantity=coeffs(obj,indices) - if indices{1}==':' - r=1:obj.nr+obj.femorder(2)-1; - else - r=indices{1}; - end - if indices{2}==':' - z=1:obj.nz+obj.femorder(1)-1; - else - z=indices{2}; - end - if indices{3}==':' - t=1:obj.nt; - else - t=indices{3}; - end - temp=zeros((obj.nz+obj.femorder(1)-1)*(obj.nr+obj.femorder(2)-1),length(t)); - if obj.index ~= -1 - if(sum(diff(diff(t))) == 0 && length(t)>1) - stride=t(2)-t(1); - temp = h5read(obj.filename, obj.dataset,[obj.index 1 t(1)],[1 Inf length(t)],[1 1 stride]); - else - for i=1:length(t) - temp(:,i) = h5read(obj.filename, obj.dataset,[obj.index 1 t(i)],[1 Inf 1]); - end - end - else - if(sum(diff(diff(t))) == 0 && length(t)>1) - stride=t(2)-t(1); - temp = h5read(obj.filename, obj.dataset,[obj.index 1 t(1)],[1 Inf length(t)],[1 1 stride]); - else - for i=1:length(t) - temp(:,i) = h5read(obj.filename, obj.dataset,[1 t(i)],[Inf 1]) ; - end - end - end - temp=reshape(temp,obj.nz+obj.femorder(1)-1,obj.nr+obj.femorder(2)-1,[]); - temp=permute(temp,[2,1,3]); - quantity=temp(r,z,:)*obj.scale; - end - - function quantity=val(obj,indices) - if indices{1}==':' - r=1:obj.nr; - else - r=indices{1}; - end - if indices{2}==':' - z=1:obj.nz; - else - z=indices{2}; - end - if indices{3}==':' - t=1:obj.nt; - else - t=indices{3}; - end - count=length(t); - temp=obj.coeffs({':',':',t}); - quantity=zeros(length(r),length(z),count); - for i=1:size(temp,3) - quantity(:,:,i)=fnval(spmak({obj.knotsr,obj.knotsz},temp(:,:,i)),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); - end - end - - - function quantity=der(obj,indices) - if indices{1}==':' - r=1:obj.nr; - else - r=indices{1}; - end - if indices{2}==':' - z=1:obj.nz; - else - z=indices{2}; - end - if indices{3}==':' - t=1:obj.nt; - else - t=indices{3}; - end - order=indices{4}; - count=length(t); - temp=obj.coeffs({':',':',t}); - quantity=zeros(length(r),length(z),count); - for i=1:size(temp,3) - quantity(:,:,i)=fnval(fnder(spmak({obj.knotsr,obj.knotsz},temp(:,:,i)), order),{obj.knotsr(r+obj.femorder(2)),obj.knotsz(z+obj.femorder(1))}); - end - end - end -end diff --git a/matlab/timescales.m b/matlab/timescales.m index 7e06a1f..f10a177 100644 --- a/matlab/timescales.m +++ b/matlab/timescales.m @@ -1,93 +1,98 @@ -Pn=1e-6;%mbar +%% +Pn=1e-4;%mbar kb=1.38064852e-23; T=300; estimneutraldensity=Pn*100/(kb*T); nb=estimneutraldensity; qe=1.60217662E-19; me=9.109383E-31; eps_0=8.85418781762e-12; vlight=299792458; - -Ee=[10]; %eV +%% +Ee=[500 1000]; %eV gridsize1=ceil(sqrt(length(Ee))); gridsize2=ceil(length(Ee)/gridsize1); fighandle=figure; for i=1:length(Ee) subplot(gridsize2,gridsize1,i) ve=sqrt(Ee(i)*qe/me); %m/s gamma=1/(1-ve^2/vlight^2); ne=5*logspace(13,17,3000); -B0=0.21; +B0=0.28; sigion=sigmabeb(15.58,54.91,2,1,Ee(i)); sigion=sigmabeb(41.72,71.13,2,1,Ee(i))+sigion; sigion=sigmabeb(21,63.18,2,1,Ee(i))+sigion; sigion=sigmabeb(17.07,44.30,4,1,Ee(i))+sigion; +sigion=M.sigio(Ee(i)); +weight=3.83e6; +ncreated=5e17*nb*sigion*ve*(0.030*2*pi*(0.078^2-0.076^2)) +nbsimucr=ncreated/weight Coulomblog=log(sqrt(eps_0*Ee(i)/qe*ne)/qe^2*2*pi*eps_0*me*ve^2); ne_0=1; tauion=1./(nb*sigion*ve); tauloss=tauion*log(ne/ne_0); -taucycl=2*pi*me*gamma/qe/B0; -taupe=2*pi*sqrt(eps_0*me*gamma/qe^2./ne); -tauthermal=1./(ve*ne*qe^4.*Coulomblog./(gamma^2*me^2*ve^4*2*pi*eps_0^2)); +taucycl=2*pi*me/qe/B0; +taupe=2*pi*sqrt(eps_0*me/qe^2./ne); +tauthermal=1./(ne*qe^4.*Coulomblog./(me^2*ve^3*4*pi*eps_0^2)); ldw=1.5; loglog(ne,tauion*ones(size(ne)),'displayname','\tau_{ion}','linewidth',ldw) hold on loglog(ne,tauloss,'displayname','t_{accum}','linewidth',ldw) loglog(ne,tauthermal,'displayname','\tau_{thermal}','linewidth',ldw) loglog(ne,taucycl*ones(size(ne)),'displayname','\tau_{ce}','linewidth',ldw) loglog(ne,taupe,'displayname','\tau_{pe}','linewidth',ldw) legend('location','eastoutside') %title(sprintf('E_e=%#.3g eV',Te(i))) xlabel('n_e [m^{-3}]') ylabel('\tau [s]') grid on end sgtitle(sprintf('E_e=%3.2g [eV], P_n=%3.2g [mbar]',Ee(1),Pn)) %% Saves the given figure as a pdf and a .fig fighandle.PaperUnits='centimeters'; papsize=[12 10]; fighandle.PaperSize=papsize; name=sprintf('timescaleE%2.0eP%2.0embar',Ee(1),Pn); print(fighandle,name,'-dpdf','-fillpage') savefig(fighandle,name) % Te=500; % ve=sqrt(Te*qe/me); %m/s % gamma=1/(1-ve^2/vlight^2); % time=logspace(-5,1,500); % dens=exp(time/tauion); % Coulomblog=log(sqrt(eps_0*Te/qe*dens)/qe^2*2*pi*eps_0*me*ve^2); % tauthermal=1./(ve*dens*qe^4.*Coulomblog./(gamma^2*me^2*ve^4*2*pi*eps_0^2)); % figure % yyaxis left % loglog(time,dens) % yyaxis right % loglog(time,tauthermal) % Sigma BEB see (https://physics.nist.gov/PhysRefData/Ionization/intro.html) % B binding energy % U average kinetic energy % N electron occupation number % Q dipole constant % T incident energy function sig=sigmabeb(B,U,N,Q,T) t=T/B; %incident energy u=U/B; % Average kinetic energy % B: a0=0.52918e-10; R=13.6057; S=4*pi*a0^2*N*(R/B)^2; n=1; sig=S/(t+(u+1)/n)*(Q*log(t)*0.5*(1-t^-2)+(2-Q)*(1-t^-1-log(t)/(t+1))); end diff --git a/matlab/updatefigdata.m b/matlab/updatefigdata.m deleted file mode 100644 index 7938837..0000000 --- a/matlab/updatefigdata.m +++ /dev/null @@ -1,29 +0,0 @@ -function updatefigdata(control, event, Othercontrol, fig, M) - - - if strcmp(event.EventName,'ValueChanged') - fieldstep=floor(control.Value); - control.Value=fieldstep; - else - fieldstep=floor(event.Value); - end - Othercontrol.Value=fieldstep; - - sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it1,double((fieldstep-1)*M.it1)*M.dt)) - - %% update Position histogram - ax1=fig.Children(end); - ax1.Children.CData=M.N(:,:,fieldstep); - - view(ax1,2) - %% update ES Potential - fig.Children(end-2).Children(1).ZData=M.pot(:,:,fieldstep)'; - %% update Radial electric field - fig.Children(end-4).Children(1).ZData=M.Er(:,:,fieldstep)'; - %% update Axial electric field - fig.Children(end-6).Children(1).ZData=M.Ez(:,:,fieldstep)'; - drawnow limitrate - - - -end diff --git a/matlab/updatefigpartdata.m b/matlab/updatefigpartdata.m deleted file mode 100644 index bf2c4bc..0000000 --- a/matlab/updatefigpartdata.m +++ /dev/null @@ -1,48 +0,0 @@ -function updatefigpartdata(control, event, Othercontrol, fig, M) - - - if strcmp(event.EventName,'ValueChanged') - fieldstep=floor(control.Value); - control.Value=fieldstep; - else - fieldstep=floor(event.Value); - end - Othercontrol.Value=fieldstep; - - sgtitle(fig,sprintf('step=%d t=%0.5e s',(fieldstep-1)*M.it2,double((fieldstep-1)*M.it2)*M.dt)) - %data.pos=[M.Z(:,fieldstep),M.R(:,fieldstep)]; - data.pot=M.pot(:,:,fieldstep)'; - data.Ez=M.Ez(:,:,fieldstep)'; - data.Er=M.Er(:,:,fieldstep)'; - - %% update Position plot - ax1=fig.Children(end); - ax1.Children.XData=M.Z(:,fieldstep); - ax1.Children.YData=M.R(:,fieldstep); - - - view(ax1,2) - %% update VZ VPERP - fig.Children(end-1).Children(1).XData=M.VZ(:,fieldstep); - fig.Children(end-1).Children(1).YData=M.VR(:,fieldstep); - axis(fig.Children(end-1),'equal') - - %% update Z VZ - fig.Children(end-2).Children(1).XData=M.Z(:,fieldstep); - fig.Children(end-2).Children(1).YData=M.VZ(:,fieldstep); - %% update VZ VR - fig.Children(end-3).Children(1).XData=M.VZ(:,fieldstep); - fig.Children(end-3).Children(1).YData=M.R(:,fieldstep); - - %% update R VR - fig.Children(end-4).Children(1).XData=M.Z(:,fieldstep); - fig.Children(end-4).Children(1).YData=M.VR(:,fieldstep); - - %% update Z VR - fig.Children(end-5).Children(1).XData=M.VR(:,fieldstep); - fig.Children(end-5).Children(1).YData=M.R(:,fieldstep); - drawnow limitrate - - - -end diff --git a/src/.depend b/src/.depend index 6f06fa2..edc24fc 100644 --- a/src/.depend +++ b/src/.depend @@ -1,37 +1,64 @@ -auxval.o : auxval.f90 beam_mod.o fields_mod.o basic_mod.o constants.o +auxval.o : auxval.f90 random_mod.o beam_mod.o fields_mod.o basic_mod.o constants.o auxval__genmod.o : auxval__genmod.f90 basic_mod.o : basic_mod.f90 mpihelper_mod.o constants.o -beam_mod.o : beam_mod.f90 distrib_mod.o basic_mod.o mpihelper_mod.o constants.o +beam_mod.o : beam_mod.f90 geometry_mod.o weighttypes_mod.o particletypes_mod.o distrib_mod.o basic_mod.o mpihelper_mod.o constants.o celldiag_mod.o : celldiag_mod.f90 beam_mod.o basic_mod.o mpihelper_mod.o constants.o chkrst.o : chkrst.f90 constants.o fields_mod.o beam_mod.o basic_mod.o chkrst__genmod.o : chkrst__genmod.f90 constants.o : constants.f90 cp2bk__genmod.o : cp2bk__genmod.f90 -diagnose.o : diagnose.f90 celldiag_mod.o fields_mod.o xg_mod.o beam_mod.o basic_mod.o +decimal_to_seed__genmod.o : decimal_to_seed__genmod.f90 +diagnose.o : diagnose.f90 psupply_mod.o weighttypes_mod.o splinebound_mod.o geometry_mod.o celldiag_mod.o fields_mod.o xg_mod.o beam_mod.o neutcol_mod.o maxwsrce_mod.o basic_mod.o diagnose__genmod.o : diagnose__genmod.f90 -distrib_mod.o : distrib_mod.f90 constants.o +distrib_mod.o : distrib_mod.f90 random_mod.o constants.o endrun.o : endrun.f90 fields_mod.o beam_mod.o basic_mod.o endrun__genmod.o : endrun__genmod.f90 energies.o : energies.f90 basic_mod.o -fields_mod.o : fields_mod.f90 mpihelper_mod.o beam_mod.o basic_mod.o constants.o -inital.o : inital.f90 maxwellsource_mod.o mpihelper_mod.o fields_mod.o beam_mod.o basic_mod.o +fields_mod.o : fields_mod.f90 geometry_mod.o particletypes_mod.o mpihelper_mod.o beam_mod.o basic_mod.o constants.o +geometry_mod.o : geometry_mod.f90 basic_mod.o weighttypes_mod.o splinebound_mod.o constants.o +inital.o : inital.f90 neutcol_mod.o geometry_mod.o maxwsrce_mod.o mpihelper_mod.o fields_mod.o beam_mod.o basic_mod.o inital__genmod.o : inital__genmod.f90 main.o : main.f90 basic_mod.o -maxwellsource_mod.o : maxwellsource_mod.f90 distrib_mod.o beam_mod.o basic_mod.o mpihelper_mod.o constants.o -mpihelper_mod.o : mpihelper_mod.f90 constants.o +maxwsrce_mod.o : maxwsrce_mod.f90 distrib_mod.o beam_mod.o basic_mod.o mpihelper_mod.o constants.o +mpihelper_mod.o : mpihelper_mod.f90 particletypes_mod.o constants.o mv2bk.o : mv2bk.f90 mv2bk__genmod.o : mv2bk__genmod.f90 +neutcol_mod.o : neutcol_mod.f90 distrib_mod.o random_mod.o beam_mod.o basic_mod.o constants.o newrun.o : newrun.f90 newrun__genmod.o : newrun__genmod.f90 +next_seed3__genmod.o : next_seed3__genmod.f90 +next_seed__genmod.o : next_seed__genmod.f90 +particletypes_mod.o : particletypes_mod.f90 constants.o +psupply_mod.o : psupply_mod.f90 mpihelper_mod.o weighttypes_mod.o fields_mod.o geometry_mod.o particletypes_mod.o basic_mod.o splinebound_mod.o constants.o +rand_axc__genmod.o : rand_axc__genmod.f90 +rand_batch__genmod.o : rand_batch__genmod.f90 +rand_next_seed__genmod.o : rand_next_seed__genmod.f90 +random_array__genmod.o : random_array__genmod.f90 +random_cosdist__genmod.o : random_cosdist__genmod.f90 +random_gauss__genmod.o : random_gauss__genmod.f90 +random__genmod.o : random__genmod.f90 +random_init__genmod.o : random_init__genmod.f90 +random_isodist__genmod.o : random_isodist__genmod.f90 +random_mod.o : random_mod.f90 random.h restart.o : restart.f90 restart__genmod.o : restart__genmod.f90 -resume.o : resume.f90 maxwellsource_mod.o sort_mod.o fields_mod.o basic_mod.o beam_mod.o +resume.o : resume.f90 neutcol_mod.o geometry_mod.o maxwsrce_mod.o sort_mod.o fields_mod.o basic_mod.o beam_mod.o resume__genmod.o : resume__genmod.f90 -sort_mod.o : sort_mod.f90 constants.o beam_mod.o -start.o : start.f90 basic_mod.o +seed_to_decimal__genmod.o : seed_to_decimal__genmod.f90 +set_random_seed__genmod.o : set_random_seed__genmod.f90 +sort_mod.o : sort_mod.f90 basic_mod.o constants.o beam_mod.o +splinebound_mod.o : splinebound_mod.f90 distrib_mod.o basic_mod.o constants.o +srandom_array__genmod.o : srandom_array__genmod.f90 +srandom_cosdist__genmod.o : srandom_cosdist__genmod.f90 +srandom_gauss__genmod.o : srandom_gauss__genmod.f90 +srandom__genmod.o : srandom__genmod.f90 +srandom_isodist__genmod.o : srandom_isodist__genmod.f90 +start.o : start.f90 psupply_mod.o fields_mod.o beam_mod.o neutcol_mod.o geometry_mod.o maxwsrce_mod.o basic_mod.o start__genmod.o : start__genmod.f90 -stepon.o : stepon.f90 maxwellsource_mod.o beam_mod.o fields_mod.o constants.o basic_mod.o +stepon.o : stepon.f90 sort_mod.o neutcol_mod.o celldiag_mod.o maxwsrce_mod.o beam_mod.o fields_mod.o constants.o basic_mod.o stepon__genmod.o : stepon__genmod.f90 +string_to_seed__genmod.o : string_to_seed__genmod.f90 tesend.o : tesend.f90 basic_mod.o tesend__genmod.o : tesend__genmod.f90 +weighttypes_mod.o : weighttypes_mod.f90 basic_mod.o splinebound_mod.o constants.o xg_mod.o : xg_mod.f90 fields_mod.o beam_mod.o basic_mod.o constants.o diff --git a/src/.fortls b/src/.fortls deleted file mode 100644 index ad95a23..0000000 --- a/src/.fortls +++ /dev/null @@ -1,3 +0,0 @@ -{ -"mod_dirs": ["/usr/local/crpp/intel-17.0/bsplines/include", "/usr/local/crpp/intel-17.0/futils/include", "/usr/local/mumps_par-5.0.2/include", "/usr/local/intel/17.0/impi/2017.3.196/include64"] -} \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index be18e00..3c118cc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,94 +1,130 @@ .DEFAULT_GOAL := all ifeq ($(PLATFORM),) $(error Please specify the env variable PLATFORM (mac, intel)) else $(info *** Using $(PLATFORM).mk ***) include $(PLATFORM).mk endif include .depend + + + PROG = espic2d SRCS = main.f90 basic_mod.f90 newrun.f90 restart.f90 \ auxval.f90 inital.f90 resume.f90 start.f90 diagnose.f90 \ stepon.f90 tesend.f90 endrun.f90 chkrst.f90 mv2bk.f90 \ - constants.f90 fields_mod.f90 beam_mod.f90 \ - mpihelper_mod.f90 sort_mod.f90 distrib_mod.f90 \ - maxwellsource_mod.f90 celldiag_mod.f90 + constants.f90 fields_mod.f90 beam_mod.f90 \ + mpihelper_mod.f90 sort_mod.f90 distrib_mod.f90 \ + maxwsrce_mod.f90 celldiag_mod.f90 geometry_mod.f90 \ + random_mod.f90 neutcol_mod.f90 particletypes_mod.f90 \ + splinebound_mod.f90 weighttypes_mod.f90 SRCS_C = extra.c +SRCS_F = random.f randother.f + + MKDIR_P = mkdir -p OUT_DIR = release F90FLAGS += -I$(BSPLINES)/include -I$(FUTILS)/include \ - -I$(MUMPS)/include -I../src + -I$(MUMPS)/include -I../ + +CCFLAGS += -qopenmp -O3 LDFLAGS += -L$(BSPLINES)/lib -L$(FUTILS)/lib -L${HDF5}/lib -L${HDF5}/lib \ -L$(MUMPS)/lib -L$(PARMETIS)/lib -LIBS += -lbsplines -lpppack -lfutils -lhdf5_fortran -lhdf5 -lz $(MUMPSLIBS) -lpputils2 - +LIBS += -lbsplines -lpppack -lfutils -lhdf5_fortran -lhdf5 -lz $(MUMPSLIBS) -lpputils2 ifeq ($(USE_X),) F90FLAGS+=-DUSE_X=0 else $(info *** Using Xgrafix ***) -LIBS+=-lXGF -lXGC -lX11 +LIBS+=-lXGF -lXGC -lX11 -lSWIG LDFLAGS+=-L/usr/local/xgrafix_1.2/src-double F90FLAGS+=-DUSE_X=1 SRCS+=xg_mod.f90 endif -OBJS =${SRCS:.f90=.o} ${SRCS_F90:.F90=.o} ${SRCS_C:.c=.o} +OBJS =${SRCS:.f90=.o} ${SRCS_F90:.F90=.o} ${SRCS_C:.c=.o} ${SRCS_F:.f=.o} +FPP =${SRCS:.f90=.i90} +OBJS_ =$(addprefix ./$(OUT_DIR)/,$(OBJS)) -debug: F90FLAGS = $(DEBUGFLAGS) -I$(BSPLINES)/include -I$(FUTILS)/include \ - -I$(MUMPS)/include -D_DEBUG +debug: F90FLAGS += $(DEBUGFLAGS) debug: OUT_DIR=debug debug: $(OUT_DIR) all profile: F90FLAGS+=$(PROFILEFLAGS) profile: LDFLAGS+= $(PROFILEFLAGS) profile: OUT_DIR=profile profile: $(OUT_DIR) all -.PHONY: directories +$(info *** Using $(OBJS) ***) + +.PHONY: directories clean debug profile all: directories $(PROG) $(PROG): $(OBJS) - $(F90) $(LDFLAGS) $(F90FLAGS) -o $@ $(addprefix ./$(OUT_DIR)/,$(OBJS)) $(LIBS) - mv *.mod ./$(OUT_DIR)/ + $(F90) $(LDFLAGS) $(F90FLAGS) -o $@ $(OBJS_) $(LIBS) tags: etags *.f90 clean: - rm -f $(OBJS) *.mod + rm -f $(OBJS_) *.mod $(FPP) distclean: clean rm -f $(PROG) *~ a.out *.o TAGS +extra.o: extra.c + ifeq ($(USE_X),) -diagnose.o : diagnose.f90 fields_mod.o beam_mod.o basic_mod.o +./$(OUT_DIR)/diagnose.o : diagnose.f90 fields_mod.o beam_mod.o basic_mod.o else -diagnose.o : diagnose.f90 fields_mod.o xg_mod.o beam_mod.o basic_mod.o +./$(OUT_DIR)/diagnose.o : diagnose.f90 fields_mod.o xg_mod.o beam_mod.o basic_mod.o endif directories: ${OUT_DIR} ${OUT_DIR}: ${MKDIR_P} ${OUT_DIR} -.SUFFIXES: $(SUFFIXES) .f90 .c +#tinyspline.o: tinyspline.h parson.o +# $(CC) $(CCFLAGS) -c -o ./$(OUT_DIR)/$@ $< +# +#tinyspline_wrap.o: tinyspline.h tinyspline_wrap.c ./$(OUT_DIR)/tinyspline_wrap.o +# $(CC) $(CCFLAGS) -c -o $@ $< +# +#$(OUT_DIR)/tinyspline_wrap.o: tinyspline.h tinyspline_wrap.c +# $(CC) $(CCFLAGS) -c -o $@ $< +# +#$(OUT_DIR)/tinysplinef.o: tinyspline_wrap.o +# $(CC) $(CCFLAGS) -c -o $@ $< -.f90.o: - $(F90) $(F90FLAGS) -c -o ./$(OUT_DIR)/$@ $< +.SUFFIXES: $(SUFFIXES) .f90 .c .f + +./$(OUT_DIR)/%.o: %.f90 + $(F90) $(F90FLAGS) -MD -c -o $@ $< + +./$(OUT_DIR)/%.o: %.f + $(F90) $(F90FLAGS) -MD -c -o $@ $< -.c.o: +./$(OUT_DIR)/%.o: %.c + $(CC) $(CCFLAGS) -c -o $@ $< + +%.o: %.c $(CC) $(CCFLAGS) -c -o ./$(OUT_DIR)/$@ $< +%.o: %.f90 + $(F90) $(F90FLAGS) -c -o ./$(OUT_DIR)/$@ $< + +%.o: %.f + $(F90) $(F90FLAGS) -c -o ./$(OUT_DIR)/$@ $< -depend .depend: +depend .depend .depend_rel .depend_deb : makedepf90 *.[fF]90 > .depend diff --git a/src/auxval.f90 b/src/auxval.f90 index 1f21900..523c91a 100644 --- a/src/auxval.f90 +++ b/src/auxval.f90 @@ -1,126 +1,150 @@ SUBROUTINE auxval ! USE constants USE basic USE fields USE beam + USE random + USE omp_lib ! ! Set auxiliary values ! IMPLICIT NONE ! - INTEGER:: i + INTEGER:: i, nbprocs !________________________________________________________________________________ IF(mpirank .eq. 0) WRITE(*,'(a/)') '=== Set auxiliary values ===' !________________________________________________________________________________ ! ! Total number of intervals nr=sum(nnr) ! Normalisation constants - if(nplasma.gt.0) then + + if(nplasma .gt. 0) then qsim=pi*(plasmadim(2)-plasmadim(1))*(plasmadim(4)**2-plasmadim(3)**2)*n0*elchar/nplasma else - qsim=-elchar + qsim=sign(n0,elchar) end if msim=abs(qsim)/elchar*partmass vnorm=vlight omegac=sign(elchar,qsim)/partmass*B0 omegap=sqrt(elchar**2*abs(n0)/partmass/eps_0) tnorm=min(abs(1/omegac),abs(1/omegap)) rnorm=vnorm*tnorm bnorm=B0 enorm=vlight*bnorm phinorm=enorm*rnorm ! Normalised boundary conditions potinn=potinn/phinorm potout=potout/phinorm + ! Normalised dt + dt=dt/tnorm + ! Characteristic frequencies and normalised volume IF(mpirank .eq. 0) THEN IF(abs(omegap).GT. abs(omegac)) THEN WRITE(*,'(a,3(1pe12.3))') 'omegap, omegac, omegap/omegac', omegap, omegac, omegap/omegac ELSE WRITE(*,'(a,3(1pe12.3))') 'omegap, omegac, omegac/omegap', omegap, omegac, omegac/omegap END IF END IF ! Construction of the mesh rgrid in r and zgrid in z and its normalisation CALL mesh rgrid=rgrid/rnorm zgrid=zgrid/rnorm dz=dz/rnorm dr=dr/rnorm + Where(dr.gt.0) invdr=1/dr + invdz=1/dz ! Define Zindex bounds for the MPI process in the case of sequential run ALLOCATE( Zbounds(0:mpisize), Nplocs_all(0:mpisize-1)) Zbounds=0 Zbounds(mpisize) = nz IF(mpisize.gt.1) THEN ! If we use mpi we define the left and right nodes used for communications leftproc=mpirank-1 IF(mpirank .eq. 0 .AND. partperiodic) THEN leftproc = mpisize-1 ELSE IF (mpirank .eq. 0) THEN leftproc=-1 END IF rightproc = mpirank+1 IF(mpirank .eq. mpisize-1 .AND. partperiodic) THEN rightproc = 0 ELSE IF (mpirank .eq. mpisize-1) THEN rightproc=-1 END IF ELSE - leftproc=-1 - rightproc=-1 + leftproc=-1 + rightproc=-1 END IF WRITE(*,*) "mpirank, left, right", mpirank, leftproc, rightproc ! Auxiliary vectors ALLOCATE(vec1((nz+1)*(nr+1)),vec2((nr+1)*(nz+1))) DO i=0,nr vec1(i*(nz+1)+1:(i+1)*(nz+1))=zgrid!(0:nz) vec2(i*(nz+1)+1:(i+1)*(nz+1))=rgrid(i) END DO +! Initialize random number generator +nbprocs = omp_get_max_threads() +allocate(seed(ran_s,nbprocs), ran_index(nbprocs), ran_array(ran_k,nbprocs)) + +Do i=1,nbprocs +! Generate seed from the default seed-string in random module + CALL decimal_to_seed(random_seed_str, seed(:,i)) + +! Generate a different seed for each processor from the mother seed + + CALL next_seed(mpirank*nbprocs+i,seed(:,i)) + +! Initialize the random array (first hundred numbers) + + CALL random_init(seed(:,i), ran_index(i), ran_array(:,i)) +end do !________________________________________________________________________________ CONTAINS !--------------------------------------------------------------------------- !> @author !> Patryk Kaminski EPFL/SPC ! ! DESCRIPTION: !> !> @brief Creates the mesh in r and z direction for calculating the electric and magnetic fields. !--------------------------------------------------------------------------- SUBROUTINE mesh - USE basic, ONLY: zgrid, rgrid, nr, nnr, nz, dr, dz, lz, radii - INTEGER :: j + USE basic, ONLY: zgrid, rgrid, nr, nnr, nz, dr, dz, lz, radii, nsubr + INTEGER :: j,i,k ALLOCATE(zgrid(0:nz),rgrid(0:nr)) dz=(lz(2)-lz(1))/nz + nsubr=count(nnr.gt.0) DO j=0,nz zgrid(j)=j*dz+lz(1) END DO - dr(1)=(radii(2)-radii(1))/nnr(1) - DO j=0,nnr(1) - rgrid(j)=radii(1)+j*dr(1) - END DO - dr(2)=(radii(3)-radii(2))/nnr(2) - DO j=1,nnr(2) - rgrid(nnr(1)+j)=radii(2)+j*dr(2) - END DO - dr(3)=(radii(4)-radii(3))/nnr(3) - DO j=1,nnr(3) - rgrid(nnr(1)+nnr(2)+j)=radii(3)+j*dr(3) - END DO + k=0 + rgrid(0)=radii(1) + do i=1,nsubr + dr(i)=(radii(i+1)-radii(i))/nnr(i) + if (nnr(i).gt.0) then + DO j=1,nnr(i) + rgrid(j+k)=radii(i)+j*dr(i) + END DO + end if + k=k+nnr(i) + end do END SUBROUTINE mesh !________________________________________________________________________________ END SUBROUTINE auxval diff --git a/src/basic_mod.f90 b/src/basic_mod.f90 index 4ad1631..ca2fcd1 100644 --- a/src/basic_mod.f90 +++ b/src/basic_mod.f90 @@ -1,336 +1,311 @@ MODULE basic ! USE hashtable USE constants USE bsplines USE mumps_bsplines USE futils USE mpihelper IMPLICIT NONE ! ! Basic module for time dependent problems ! CHARACTER(len=128) :: label1, label2, label3, label4 ! ! BASIC Namelist ! LOGICAL :: nlres = .FALSE. !< Restart flag LOGICAL :: nlsave = .TRUE. !< Checkpoint (save) flag LOGICAL :: newres=.FALSE. !< New result HDF5 file LOGICAL :: nlxg=.FALSE. !< Show graphical interface Xgrafix LOGICAL :: nlmaxwellsource = .FALSE. !< Activate the maxwell source INTEGER :: nrun=1 !< Number of time steps to run REAL(kind=db) :: job_time=3600.0 !< Time allocated to this job in seconds REAL(kind=db) :: tmax=100000.0 !< Maximum simulation time REAL(kind=db) :: extra_time=60.0 !< Extra time allocated REAL(kind=db) :: dt=1 !< Time step REAL(kind=db) :: time=0 !< Current simulation time (Init from restart file) ! ! Other basic global vars and arrays ! INTEGER :: jobnum !< Job number INTEGER :: step !< Calculation step of this run INTEGER :: cstep=0 !< Current step number (Init from restart file) LOGICAL :: nlend !< Signal end of run INTEGER :: ierr !< Integer used for MPI INTEGER :: it0d=1 !< Number of iterations between 0d values writes to hdf5 INTEGER :: it2d=100 !< Number of iterations between 2d values writes to hdf5 INTEGER :: itparts=1000 !< Number of iterations between particles values writes to hdf5 INTEGER :: ittext=10 !< Number of iterations between text outputs in the console INTEGER :: itrestart=10000 !< Number of iterations between save of restart.h5 file INTEGER :: ittracer=100 !< Number of iterations between save of traced particles position and velocity INTEGER :: itcelldiag=100000 !< Number of iterations between save of celldiag diagnostic INTEGER :: nbcelldiag=0 !< Number of celldiagnostics INTEGER :: itgraph !< Number of iterations between graphical interface updates INTEGER :: mpirank !< MPIrank of the current processus INTEGER :: mpisize !< Size of the MPI_COMM_WORLD communicator INTEGER :: rightproc !< Rank of next processor in the z decomposition INTEGER :: leftproc !< Rank of previous processor in the z decomposition ! ! List of logical file units INTEGER :: lu_in = 90 !< File duplicated from STDIN INTEGER :: lu_stop = 91 !< stop file, see subroutine TESEND INTEGER :: lu_partfile = 120 !< particle loading file, see beam::loadpartfile ! ! HDF5 file CHARACTER(len=256) :: resfile = "results.h5" !< Main result file CHARACTER(len=256) :: rstfile = "restart.h5" !< Restart file CHARACTER(len=256) :: magnetfile = "" !< H5 file containing the magnetic field definition CHARACTER(len=256) :: partfile(10)="" !< Particle loading file + CHARACTER(len=256) :: addedtestspecfile(10)="" !< Particle file list for added particles at restart INTEGER :: fidres !< File ID for resfile INTEGER :: fidrst !< File ID for restart file TYPE(BUFFER_TYPE) :: hbuf0 !< Hashtable for 0d var ! ! Plasma parameters LOGICAL :: nlPhis= .TRUE. !< Calculate self consistent electric field flag + LOGICAL :: nlfreezephi= .FALSE. !< Freeze the Poisson solver to the field obtained at (re-)start LOGICAL :: nlclassical= .FALSE. !< If true, solves the equation of motion according to classical !! dynamics LOGICAL :: nlperiod(2)=(/.false.,.false./)!< Set periodic splines on or off LOGICAL :: partperiodic= .TRUE. !< Sets if the particles boundary conditions are periodic or open + INTEGER :: nbaddtestspecies=0 !< On restart number of files to read to add test particles INTEGER :: nplasma !< Number of macro-particles on initialisation INTEGER :: nbspecies = 1 !< Number of particles species also counting tracing particles INTEGER :: npartsalloc = 0 !< Size of particle memory allocated at the begining of the simulation - INTEGER :: nblock !< Number of slices in Z for stable distribution initialisation - REAL(kind=db) :: potinn !< Electric potential at the inner metallic wall - REAL(kind=db) :: potout !< Electric potential at the outer metallic wall + INTEGER :: nblock !< Number of slices in Z for stable distribution initialisation + REAL(kind=db) :: potinn=0 !< Electric potential at the inner metallic wall + REAL(kind=db) :: potout=0 !< Electric potential at the outer metallic wall REAL(kind=db) :: B0 !< Max magnitude of magnetic field REAL(kind=db), allocatable :: Bz(:), Br(:) !< Magnetic field components REAL(kind=db), allocatable :: Athet(:) !< Theta component of the magnetic vector potential - TYPE(spline2d) :: splrz !< Spline at r and z - REAL(kind=db), allocatable :: Ez(:), Er(:) !< Electric field components - REAL(kind=db), allocatable :: pot(:) !< Electro static potential - REAL(kind=db) :: radii(4) !< Inner and outer radius of cylinder and radii of fine mesh region + TYPE(spline2d), SAVE :: splrz !< Spline at r and z for total electric field + TYPE(spline2d), SAVE :: splrz_ext !< Spline at r and z for external electric field + REAL(kind=db), allocatable :: Ez(:), Er(:) !< Electric field components ( ext+self ) + REAL(kind=db), allocatable :: pot(:) !< Electro static potential ( ext+self ) + REAL(kind=db), allocatable :: Ezxt(:), Erxt(:) !< external Electric field components + REAL(kind=db), allocatable :: potxt(:) !< external Electro static potential + REAL(kind=db) :: radii(11) !< Inner and outer radius of cylinder and radii of fine mesh region REAL(kind=db) :: plasmadim(4) !< Zmin Zmax Rmin Rmax values for plasma particle loading - INTEGER :: distribtype=1 !< Type of distribution function used to load the particles - !!1: gaussian, 2: Stable as defined in 4.95 of Davidson + INTEGER :: distribtype=1 !< Type of distribution function used to load the particles + !!1: gaussian, 2: Stable as defined in 4.95 of Davidson REAL(kind=db) :: H0=0 !< Initial value of Hamiltonian for distribution 2 REAL(kind=db) :: P0=0 !< Initial canonical angular momentum for distribution 2 REAL(kind=db) :: temprescale = -1.0 !< Factor used for temperature rescaling in case of a restart (<0 -> no rescaling) - INTEGER :: samplefactor =-1 !< Factor used for the up-sampling of the particles number - REAL(kind=db) :: lz(2) !< Lower and upper cylinder limits in z direction - REAL(kind=db) :: n0 !< Physical plasma density parameter - REAL(kind=db), DIMENSION(:,:), ALLOCATABLE, SAVE:: moments !< Moments of the distribution function evaluated every it2d + INTEGER :: samplefactor =-1 !< Factor used for the up-sampling of the particles number + REAL(kind=db) :: lz(2) !< Lower and upper cylinder limits in z direction + REAL(kind=db) :: n0 !< Physical plasma density parameter + !REAL(kind=db), DIMENSION(:,:), ALLOCATABLE, SAVE:: moments !< Moments of the distribution function evaluated every it2d REAL(kind=db), DIMENSION(:), ALLOCATABLE, SAVE:: rhs !< right hand side of the poisson equation solver REAL(kind=db), DIMENSION(:), ALLOCATABLE, SAVE:: volume !< Volume covered by each spline for density calculation - INTEGER :: nz !< Number of grid intervals in z - INTEGER :: nr !< Total number of grid intervals in r - INTEGER :: nnr(3) !< Number of grid intervals in r in each subdomain + INTEGER :: nz !< Number of grid intervals in z + INTEGER :: nr !< Total number of grid intervals in r + INTEGER :: nnr(10) !< Number of grid intervals in r in each subdomain + INTEGER :: nsubr=10 !< Number of sub-intervals in r REAL(kind=db) :: dz !< Cell size in z - REAL(kind=db) :: dr(3) !< Cell size in r for each region + REAL(kind=db) :: dr(10) !< Cell size in r for each region REAL(kind=db), ALLOCATABLE :: zgrid(:) !< Nodes positions in longitudinal direction REAL(kind=db), ALLOCATABLE :: rgrid(:) !< Nodes positions in radial direction - REAL(kind=db) :: bnorm,enorm,vnorm,tnorm,rnorm,phinorm !< Normalization constants + REAL(kind=db) :: bnorm,enorm,vnorm,tnorm,rnorm,phinorm,qnorm !< Normalization constants REAL(kind=db) :: qsim !< Charge of superparticles REAL(kind=db) :: msim !< Mass of superparticles REAL(kind=db) :: partmass=me !< Mass of physical particle - INTEGER :: femorder(2) !< FEM order - INTEGER :: ngauss(2) !< Number of gauss points - LOGICAL :: nlppform =.TRUE. !< Argument of set_spline - INTEGER, SAVE :: nrank(2) !< Number of splines in both directions + INTEGER :: femorder(2) !< FEM order + INTEGER :: ngauss(2) !< Number of gauss points + LOGICAL :: nlppform =.TRUE. !< Argument of set_spline + INTEGER, SAVE :: nrank(2) !< Number of splines in both directions REAL(kind=db) :: omegac !< Cyclotronic frequency REAL(kind=db) :: omegap !< Plasma frequency REAL(kind=db) :: temp !< Initial temperature of plasma REAL(kind=db) :: Rcurv !< Magnetic field curvature coefficient REAL(kind=db) :: Width !< Distance between two magnetic mirrors + REAL(kind=db) :: weights_scale=1.0 !< Scale factor for the particle weights on restart (only for newres=.true.) INTEGER, DIMENSION(:), ALLOCATABLE :: Zbounds !< Index of bounds for local processus in Z direction - - TYPE(BASICDATA) :: bdata !< Structure used for the mpi communication of the run parameters + INTEGER :: bscaling = -1 !< if >0 rescale the magnetic field read from h5 file before calculating value at grid points, if <0 rescale after interpolation, if = 0 doesn't rescale + REAL(kind=db):: invdz, invdr(10) CONTAINS ! !================================================================================ SUBROUTINE basic_data ! ! Define basic data ! use mpihelper IMPLICIT NONE ! ! Local vars and arrays CHARACTER(len=256) :: inputfilename INTEGER, EXTERNAL :: OMP_GET_MAX_THREADS ! NAMELIST /BASIC/ job_time, extra_time, nrun, tmax, dt, nlres, nlsave, newres, nlxg, & & nplasma, potinn, potout, B0, lz, n0, nz, nnr, femorder, ngauss, & & nlppform, plasmadim, radii, temp, Rcurv, width, it0d, it2d, itparts, ittext, & & resfile, rstfile, itgraph, nlPhis, distribtype, nblock, nlclassical, H0, P0, partperiodic, & & temprescale, samplefactor, nlmaxwellsource, npartsalloc, partfile, partmass, nbspecies, & - & ittracer, itcelldiag, nbcelldiag, magnetfile + & ittracer, itcelldiag, nbcelldiag, magnetfile, weights_scale, nlfreezephi, nbaddtestspecies, & + & addedtestspecfile, bscaling !________________________________________________________________________________ ! 1. Process Standard Input File ! IF(COMMAND_ARGUMENT_COUNT().NE.1)THEN WRITE(*,*)'ERROR, ONE COMMAND-LINE ARGUMENT REQUIRED, STOPPING' STOP ENDIF CALL GET_COMMAND_ARGUMENT(1,inputfilename) OPEN(UNIT=lu_in,FILE=trim(inputfilename),ACTION='READ') IF(mpirank .eq. 0) THEN !________________________________________________________________________________ ! 1. Label the run ! READ(lu_in,'(a)') label1 READ(lu_in,'(a)') label2 READ(lu_in,'(a)') label3 READ(lu_in,'(a)') label4 ! WRITE(*,'(12x,a/)') label1(1:len_trim(label1)) WRITE(*,'(12x,a/)') label2(1:len_trim(label2)) WRITE(*,'(12x,a/)') label3(1:len_trim(label3)) WRITE(*,'(12x,a/)') label4(1:len_trim(label4)) !________________________________________________________________________________ ! 2. Read in basic data specific to run ! READ(lu_in,basic) WRITE(*,basic) #if _DEBUG==1 WRITE(*,*) "Compiled in debug mode" #endif ELSE READ(lu_in,basic) END IF ! Distribute run parameters to all MPI workers - CALL init_mpitypes ! initialize all mpi types that will be needed in the simulation + CALL mpitypes_init ! initialize all mpi types that will be needed in the simulation WRITE(*,'(a,i4.2,a,i4.2,a)')"Running on ",mpisize," tasks with", omp_get_max_threads() ," openMP threads" IF(samplefactor .gt. 1 .and. .not. newres) THEN IF(mpirank.eq.0) WRITE(*,*)"To increase the number of particles, you need to create a new result file (set newres to 1)" CALL MPI_abort(MPI_COMM_WORLD,-1,ierr) END IF IF (npartsalloc .lt. nplasma) THEN npartsalloc=nplasma END IF + + + + + + + + + ! END SUBROUTINE basic_data !================================================================================ SUBROUTINE daytim(str) ! ! Print date and time ! IMPLICIT NONE ! CHARACTER(len=*), INTENT(in) :: str ! ! Local vars and arrays CHARACTER(len=16) :: d, t, dat, functime !________________________________________________________________________________ ! CALL DATE_AND_TIME(d,t) dat=d(7:8) // '/' // d(5:6) // '/' // d(1:4) functime=t(1:2) // ':' // t(3:4) // ':' // t(5:10) WRITE(*,'(a,1x,a,1x,a)') str, dat(1:10), functime(1:12) ! END SUBROUTINE daytim !================================================================================ SUBROUTINE timera(cntrl, str, eltime) ! ! Timers (cntrl=0/1 to Init/Update) ! IMPLICIT NONE INTEGER, INTENT(in) :: cntrl CHARACTER(len=*), INTENT(in) :: str REAL(kind=db), OPTIONAL, INTENT(out) :: eltime ! INTEGER, PARAMETER :: ncmax=128 INTEGER, SAVE :: icall=0, nc=0 REAL(kind=db), SAVE :: startt0=0.0 CHARACTER(len=16), SAVE :: which(ncmax) INTEGER :: lstr, found, i REAL(kind=db) :: seconds REAL(kind=db), DIMENSION(ncmax), SAVE :: startt = 0.0, endt = 0.0 !________________________________________________________________________________ IF( icall .EQ. 0 ) THEN icall = icall+1 startt0 = seconds() END IF lstr = LEN_TRIM(str) IF( lstr .GT. 0 ) found = loc(str) !________________________________________________________________________________ ! SELECT CASE (cntrl) ! CASE(-1) ! Current wall time IF( PRESENT(eltime) ) THEN eltime = seconds() - startt0 ELSE WRITE(*,'(/a,a,1pe10.3/)') "++ ", ' Wall time used so far = ', seconds() - startt0 END IF ! CASE(0) ! Init Timer IF( found .EQ. 0 ) THEN ! Called for the 1st time for 'str' nc = nc+1 which(nc) = str(1:lstr) found = nc END IF startt(found) = seconds() ! CASE(1) ! Update timer endt(found) = seconds() - startt(found) IF( PRESENT(eltime) ) THEN eltime = endt(found) ELSE WRITE(*,'(/a,a,1pe10.3/)') "++ "//str, ' wall clock time = ', endt(found) END IF ! CASE(2) ! Update and reset timer endt(found) = endt(found) + seconds() - startt(found) startt(found) = seconds() IF( PRESENT(eltime) ) THEN eltime = endt(found) END IF ! CASE(9) ! Display all timers IF( nc .GT. 0 ) THEN WRITE(*,'(a)') "Timer Summary" WRITE(*,'(a)') "=============" DO i=1,nc WRITE(*,'(a20,2x,2(1pe12.3))') TRIM(which(i))//":", endt(i) END DO END IF ! END SELECT ! CONTAINS INTEGER FUNCTION loc(funcstr) CHARACTER(len=*), INTENT(in) :: funcstr INTEGER :: j, ind loc = 0 DO j=1,nc ind = INDEX(which(j), funcstr(1:lstr)) IF( ind .GT. 0 .AND. LEN_TRIM(which(j)) .EQ. lstr ) THEN loc = j EXIT END IF END DO END FUNCTION loc END SUBROUTINE timera -!================================================================================ -SUBROUTINE readbdata -nlres = bdata%nlres -nlsave = bdata%nlsave -newres = bdata%newres -nlxg = bdata%nlxg -nlppform = bdata%nlppform -nlPhis = bdata%nlPhis -nlclassical = bdata%nlclassical -nplasma = bdata%nplasma -nz = bdata%nz -it0d = bdata%it0d -it2d = bdata%it2d -itparts = bdata%itparts -itgraph = bdata%itgraph -distribtype = bdata%distribtype -nblock = bdata%nblock -nrun = bdata%nrun -job_time = bdata%job_time -extra_time = bdata%extra_time -tmax = bdata%tmax -dt = bdata%dt -potinn = bdata%potinn -potout = bdata%potout -B0 = bdata%B0 -n0 = bdata%n0 -temp = bdata%temp -Rcurv = bdata%Rcurv -width = bdata%width -H0 = bdata%H0 -P0 = bdata%P0 -femorder = bdata%femorder -ngauss = bdata%ngauss -nnr = bdata%nnr -lz = bdata%lz -radii = bdata%radii -plasmadim = bdata%plasmadim -resfile = bdata%resfile -partperiodic = bdata%partperiodic -samplefactor = bdata%samplefactor - -END SUBROUTINE readbdata - !================================================================================ END MODULE basic diff --git a/src/beam_mod.f90 b/src/beam_mod.f90 index db6a217..4f2a33c 100644 --- a/src/beam_mod.f90 +++ b/src/beam_mod.f90 @@ -1,1964 +1,1952 @@ +MODULE beam !------------------------------------------------------------------------------ ! EPFL/Swiss Plasma Center !------------------------------------------------------------------------------ ! ! MODULE: beam ! !> @author !> Guillaume Le Bars EPFL/SPC !> Patryk Kaminski EPFL/SPC !> Trach Minh Tran EPFL/SPC ! ! DESCRIPTION: !> Module responsible for loading, advancing and computing the necessary diagnostics for the simulated particles. !------------------------------------------------------------------------------ - -MODULE beam ! USE constants - USE mpi + use mpi USE mpihelper USE basic, ONLY: mpirank, mpisize USE distrib + USE particletypes + USE weighttypes IMPLICIT NONE - !> Stores the particles properties for the run. - TYPE particles - INTEGER :: Nploc !< Local number of simulated particles - INTEGER :: Nptot !< Total number of simulated particles - REAL(kind=db) :: m !< Particle mass - REAL(kind=db) :: q !< Particle charge - REAL(kind=db) :: weight !< Number of particles represented by one macro-particle - REAL(kind=db) :: qmRatio !< Charge over mass ratio - REAL(kind=db) :: H0 - REAL(kind=db) :: P0 - REAL(kind=db) :: temperature - LOGICAL :: Davidson=.false. - LOGICAL :: is_test= .false. - INTEGER, DIMENSION(:), ALLOCATABLE :: Rindex !< Index in the electric potential grid for the R direction - INTEGER, DIMENSION(:), ALLOCATABLE :: Zindex !< Index in the electric potential grid for the Z direction - INTEGER, DIMENSION(:), ALLOCATABLE :: partindex !< Index of the particle to be able to follow it when it goes from one MPI host to the other - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: R !< radial coordinates of the particles - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: Z !< longitudinal coordinates of the particles - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: THET !< azimuthal coordinates of the particles - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: WZ !< axial radial relative distances to the left grid line - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: WR !< radial relative distances to the bottom grid line - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: pot !< Electric potential - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: Er !< Radial Electric field - REAL(kind=db), DIMENSION(:), ALLOCATABLE :: Ez !< Axial electric field - REAL(kind=db), DIMENSION(:),POINTER:: UR !< normalized radial velocity at the current time step - REAL(kind=db), DIMENSION(:),POINTER:: URold !< normalized radial velocity at the previous time step - REAL(kind=db), DIMENSION(:),POINTER:: UTHET !< normalized azimuthal velocity at the current time step - REAL(kind=db), DIMENSION(:),POINTER:: UTHETold !< normalized azimuthal velocity at the previous time step - REAL(kind=db), DIMENSION(:),POINTER:: UZ !< normalized axial velocity at the current time step - REAL(kind=db), DIMENSION(:),POINTER:: UZold !< normalized axial velocity at the previous time step - REAL(kind=db), DIMENSION(:),POINTER:: Gamma !< Lorentz factor at the current time step - REAL(kind=db), DIMENSION(:),POINTER:: Gammaold !< Lorentz factor at the previous time step - LOGICAL:: collected !< Stores if the particles data have been collected to MPI root process during this timestep - END TYPE particles - ! !TYPE(particles) :: parts !< Storage for all the particles !SAVE :: parts TYPE(particles), DIMENSION(:), ALLOCATABLE, SAVE :: partslist ! Diagnostics (scalars) REAL(kind=db) :: ekin=0 !< Total kinetic energy (J) REAL(kind=db) :: epot=0 !< Total potential energy (J) REAL(kind=db) :: etot=0 !< Current total energy (J) REAL(kind=db) :: etot0=0 !< Initial total energy (J) REAL(kind=db) :: loc_etot0=0 !< theoretical local total energy (J) REAL(kind=db) :: Energies(4) !< (1) kinetic energy, (2) potential energy, (3) total energy and (4) gained/lossed energy due to gain or loss of particles (J) - INTEGER, DIMENSION(3), SAVE :: ireducerequest=MPI_REQUEST_NULL !< MPI requests used for the IREDUCE in the diagnostic routine ! INTEGER, DIMENSION(:), ALLOCATABLE, SAVE :: Nplocs_all !< Array containing the local numbers of particles in each MPI process -! -CONTAINS - -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Allocate the memory for the particles variable storing the particles quantities. -! -!> @param[inout] p the particles variable needing to be allocated. -!> @param[in] nparts the maximum number of particles that will be stored in this variable -!--------------------------------------------------------------------------- + INTERFACE add_created_part + MODULE PROCEDURE add_linked_created_part, add_list_created_part + END INTERFACE add_created_part +! + + abstract interface + subroutine rloader(nbase,y,rminus,rplus) + USE constants + REAL(kind=db), INTENT(out) :: y(:) + INTEGER, INTENT(in) :: nbase + REAL(kind=db), INTENT(in) :: rplus, rminus + end subroutine + REAL(kind=db) FUNCTION gamma(UZ, UR, UTHET) + USE constants + REAL(kind=db), INTENT(IN):: UR,UZ,UTHET + end FUNCTION +end interface -SUBROUTINE creat_parts(p, nparts) - TYPE(particles) :: p - INTEGER, INTENT(in) :: nparts - - IF (.NOT. ALLOCATED(p%Z) ) THEN - p%Nploc = nparts - p%Nptot = nparts - ALLOCATE(p%Z(nparts)) - ALLOCATE(p%R(nparts)) - ALLOCATE(p%THET(nparts)) - ALLOCATE(p%WZ(nparts)) - ALLOCATE(p%WR(nparts)) - ALLOCATE(p%UR(nparts)) - ALLOCATE(p%UZ(nparts)) - ALLOCATE(p%UTHET(nparts)) - ALLOCATE(p%URold(nparts)) - ALLOCATE(p%UZold(nparts)) - ALLOCATE(p%UTHETold(nparts)) - ALLOCATE(p%Gamma(nparts)) - ALLOCATE(p%Rindex(nparts)) - ALLOCATE(p%Zindex(nparts)) - ALLOCATE(p%partindex(nparts)) - ALLOCATE(p%pot(nparts)) - ALLOCATE(p%Er(nparts)) - ALLOCATE(p%Ez(nparts)) - ALLOCATE(p%GAMMAold(nparts)) - p%partindex=-1 - p%URold=0 - p%UZold=0 - p%UTHETold=0 - p%rindex=0 - p%zindex=0 - p%WR=0 - p%WZ=0 - p%UR=0 - p%UZ=0 - p%UTHET=0 - p%Z=0 - p%R=0 - p%THET=0 - p%Gamma=1 - p%Er=0 - p%Ez=0 - p%pot=0 - p%gammaold=1 - p%collected=.false. - p%Davidson=.false. - p%is_test=.false. - p%m=me - p%q=-elchar - p%qmRatio=p%q/p%m - p%weight=1.0_db - p%H0=0 - p%P0=0 - p%temperature=0 - END IF -END SUBROUTINE creat_parts +CONTAINS !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Loads the particles at the beginning of the simulation and create the parts variable if necessary !--------------------------------------------------------------------------- SUBROUTINE load_parts - USE basic, ONLY: nplasma, mpirank, ierr, distribtype, mpisize, nlclassical, nbspecies, Zbounds - USE mpi + USE basic, ONLY: nplasma, mpirank, ierr, distribtype, mpisize, nlclassical, nbspecies, Zbounds, partfile + use mpi - INTEGER:: i, j + INTEGER:: i REAL(kind=db), DIMENSION(:), ALLOCATABLE :: VZ, VR, VTHET ALLOCATE(VZ(nplasma), VR(nplasma), VTHET(nplasma)) ! Select case to define the type of distribution SELECT CASE(distribtype) CASE(1) ! Gaussian distribution in V, uniform in Z and 1/R in R CALL loaduniformRZ(partslist(1), VR, VZ, VTHET) CASE(2) !Stable distribution from Davidson 4.95 p.119 CALL loadDavidson(partslist(1), VR, VZ, VTHET, lodunir) CASE(3) !Stable distribution from Davidson 4.95 p.119 but with constant distribution in R CALL loadDavidson(partslist(1), VR, VZ, VTHET, lodinvr) CASE(4) !Stable distribution from Davidson 4.95 p.119 but with gaussian distribution in R CALL loadDavidson(partslist(1), VR, VZ, VTHET, lodgausr) CASE(5) !Stable distribution from Davidson 4.95 p.119 with gaussian in V computed from v_th given by temp CALL loadDavidson(partslist(1), VR, VZ, VTHET, lodunir) CASE(6) ! Uniform distribution in R and Z and Gaussian distribution in V with Vz @brief Checks for each particle if the z position is outside of the local/global simulation space. !> Depending on the boundary conditions, the leaving particles are sent to the correct neighbouring MPI process !> or deleted. ! !> @param[in] p particles structure ! !> @author Guillaume Le Bars EPFL/SPC !--------------------------------------------------------------------------- SUBROUTINE bound(p) - USE basic, ONLY: zgrid, nz, Zbounds, mpirank, partperiodic, step, leftproc, rightproc + USE basic, ONLY: zgrid, nz, Zbounds, mpirank, step, leftproc, rightproc, partperiodic + USE IFPORT IMPLICIT NONE type(particles), INTENT(INOUT):: p INTEGER :: i, rsendnbparts, lsendnbparts, nblostparts - INTEGER :: receivednbparts, partdiff, lostindex, sentindex + INTEGER :: receivednbparts, partdiff INTEGER, DIMENSION(p%Nploc) :: sendhole INTEGER, DIMENSION(p%Nploc) :: losthole - LOGICAL:: leftcomm, rightcomm, sent + LOGICAL:: leftcomm, rightcomm INTEGER, ALLOCATABLE:: partstoremove(:) + receivednbparts=0 + nblostparts=0 rsendnbparts=0 lsendnbparts=0 - nblostparts=0 + + IF (p%Nploc .gt. 0) THEN losthole=0 sendhole=0 - receivednbparts=0 + ! We communicate with the left processus leftcomm = leftproc .ne. -1 ! We communicate with the right processus rightcomm = rightproc .ne. -1 - IF (p%Nploc .gt. 0) THEN ! Boundary condition at z direction - !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i) + !$OMP PARALLEL DO DEFAULT(SHARED) DO i=1,p%Nploc ! If the particle is to the right of the local simulation space, it is sent to the right MPI process IF (p%Z(i) .ge. zgrid(Zbounds(mpirank+1))) THEN + IF(partperiodic) THEN + DO WHILE (p%Z(i) .GT. zgrid(nz)) + p%Z(i) = p%Z(i) - zgrid(nz) + zgrid(0) + END DO + END IF !$OMP CRITICAL (nbparts) IF(rightcomm) THEN rsendnbparts=rsendnbparts+1 sendhole(lsendnbparts+rsendnbparts)=i - DO WHILE (p%Z(i) .GT. zgrid(nz)) - p%Z(i) = p%Z(i) - zgrid(nz) + zgrid(0) - END DO ELSE nblostparts=nblostparts+1 losthole(nblostparts)=i + p%nblost(2)=p%nblost(2)+1 END IF !$OMP END CRITICAL (nbparts) ! If the particle is to the left of the local simulation space, it is sent to the left MPI process ELSE IF (p%Z(i) .lt. zgrid(Zbounds(mpirank))) THEN + IF(partperiodic) THEN + DO WHILE (p%Z(i) .LT. zgrid(0)) + p%Z(i) = p%Z(i) + zgrid(nz) - zgrid(0) + END DO + END IF !$OMP CRITICAL (nbparts) IF(leftcomm) THEN + ! We send the particle to the left process lsendnbparts=lsendnbparts+1 sendhole(lsendnbparts+rsendnbparts)=-i - DO WHILE (p%Z(i) .LT. zgrid(0)) - p%Z(i) = p%Z(i) + zgrid(nz) - zgrid(0) - END DO ELSE + ! we destroy the particle nblostparts=nblostparts+1 losthole(nblostparts)=i + p%nblost(1)=p%nblost(1)+1 END IF !$OMP END CRITICAL (nbparts) END IF END DO !$OMP END PARALLEL DO END IF IF(mpisize .gt. 1) THEN ! We send the particles leaving the local simulation space to the closest neighbour - CALL particlescommunication(p, lsendnbparts, rsendnbparts, sendhole, receivednbparts) + CALL particlescommunication(p, lsendnbparts, rsendnbparts, sendhole, receivednbparts, (/leftproc,rightproc/)) END IF ! If the boundary conditions are not periodic, we delete the corresponding particles IF(nblostparts .gt. 0 .and. step .ne. 0) THEN - DO i=nblostparts,1,-1 + DO i=1,nblostparts CALL delete_part(p, losthole(i), .false. ) END DO - WRITE(*,'(i8.2,a,i4.2)') nblostparts, " particles lost in z on process: ", mpirank + !WRITE(*,'(i8.2,a,i4.2)') nblostparts, " particles lost in z on process: ", mpirank END IF ! computes if we received less particles than we sent partdiff=max(lsendnbparts+rsendnbparts-receivednbparts,0) - ALLOCATE(partstoremove(nblostparts+partdiff)) - IF(nblostparts.gt.0 .and. partdiff .gt. 0) THEN - sentindex=receivednbparts+1 - lostindex=1 - DO i=1,nblostparts+partdiff - IF(sentindex .le. lsendnbparts+rsendnbparts) THEN - IF(losthole(lostindex) .eq. 0) THEN - sent= .true. - ELSE IF(abs(sendhole(sentindex)) .lt. abs(losthole(lostindex))) THEN - sent=.true. - ELSE - sent=.false. - END IF - ELSE - sent=.false. - END IF - IF(sent) THEN - partstoremove(i)=abs(sendhole(sentindex)) - sentindex=sentindex+1 - ELSE - partstoremove(i)=abs(losthole(lostindex)) - lostindex=lostindex+1 - END IF + IF(nblostparts + partdiff .gt. 0) THEN + ALLOCATE(partstoremove(nblostparts+partdiff)) + partstoremove(1:partdiff)=abs(sendhole(receivednbparts+1:receivednbparts+partdiff)) + partstoremove(partdiff+1:partdiff+nblostparts)=abs(losthole(1:nblostparts)) + call LSDRADIXSORT(partstoremove,size(partstoremove)) + ! If we received less particles than we sent, or lost particles we fill the remaining holes with the particles from the end of the parts arrays + DO i=nblostparts+partdiff,1,-1 + CALL move_part(p, p%Nploc, partstoremove(i)) + p%partindex(p%Nploc)=-1 + p%Nploc = p%Nploc-1 END DO - ELSEIF(nblostparts .gt. 0) THEN - partstoremove=abs(losthole(1:nblostparts)) - ELSEIF(partdiff .gt. 0) THEN - partstoremove=abs(sendhole(receivednbparts+1:lsendnbparts+rsendnbparts)) - ELSE - RETURN END IF - ! If we received less particles than we sent, or lost particles we fill the remaining holes with the particles from the end of the - ! parts arrays - DO i=nblostparts+partdiff,1, -1 - CALL move_part(p, p%Nploc, partstoremove(i)) - p%Nploc = p%Nploc-1 - END DO END subroutine bound !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: - !> @brief Compute the grid cell indices for each particle as well as the distance weight Wr, Wz. + !> @brief Check if a particle is outside the simulation domain and remove it if needed !> @param[in] p particles structure !--------------------------------------------------------------------------- -SUBROUTINE localisation(p) - USE basic, ONLY: zgrid, rgrid, dz, nr, nnr, dr +SUBROUTINE boundary_loss(p) + USE basic, ONLY: rgrid, nr + Use geometry, ONLY: r_a, geom_weight, dom_weight + USE IFPORT type(particles), INTENT(INOUT):: p - INTEGER :: i, nblostparts + INTEGER :: i,j,isup, nblostparts, iend,nbunch INTEGER, DIMENSION(p%Nploc) :: losthole - REAL(kind=db):: invdz, invdr(3) - losthole=0 + INTEGER, DIMENSION(16)::idwall + INTEGER :: nblost(size(p%nblost,1)) + nblostparts=0 - invdz=1/dz - invdr=1/dr + nblost=0 + nbunch=16 - IF (p%Nploc .gt. 0) THEN - !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i) - DO i=1,p%Nploc - p%zindex(i)=(p%Z(i)-zgrid(0))*invdz - IF (p%R(i) .GT. rgrid(0) .AND. p%R(i) .LT. rgrid(nnr(1))) THEN - p%rindex(i)=(p%R(i)-rgrid(0))*invdr(1) - ELSE IF(p%R(i) .GE. rgrid(nnr(1)) .AND. p%R(i) .LT. rgrid(nnr(1)+nnr(2))) THEN - p%rindex(i)=(p%R(i)-rgrid(nnr(1)))*invdr(2)+nnr(1) - ELSE IF(p%R(i) .GE. rgrid(nnr(1)+nnr(2)) .AND. p%R(i) .LT. rgrid(nr)) THEN - p%rindex(i)=(p%R(i)-rgrid(nnr(1)+nnr(2)))*invdr(3)+nnr(1)+nnr(2) - ELSE - ! If the particle is outside of the simulation space in the r direction, it is deleted. - !$OMP CRITICAL (lostparts) - nblostparts=nblostparts+1 - losthole(nblostparts)=i - !$OMP END CRITICAL (lostparts) - END IF - END DO - !$OMP END PARALLEL DO + IF (p%Nploc .le. 0) return + losthole=0 + !$OMP PARALLEL DEFAULT(SHARED), private(i,iend,j,isup,idwall) + !$OMP DO reduction(+:nblost) + DO i=1,p%Nploc,nbunch + ! Avoid segmentation fault caused by accessing non relevant data + iend=min(i+nbunch-1,p%Nploc) + ! calculate the weight do determine if a particle is inside the simulation domain. + call dom_weight(p%Z(i:iend), p%r(i:iend), p%geomweight(i:iend,0),idwall) + do j=i,iend + if(p%geomweight(j,0).le.0 .or. p%R(j) .ge. rgrid(nr) .or. p%R(j) .le. rgrid(0)) then + ! If the particle is outside of the simulation space in the r direction, or if it is outside of the vacuum region it is deleted. + !$OMP CRITICAL (lostparts) + nblostparts=nblostparts+1 + losthole(nblostparts)=j + !$OMP END CRITICAL (lostparts) + isup=0 + if(p%R(j) .ge. rgrid(nr) .or. idwall(j-i+1) .gt.0) then + isup=1 + end if + nblost(3+isup+idwall(j-i+1))=nblost(3+isup+idwall(j-i+1))+1 + end if + end do + END DO + !$OMP END DO + !$OMP END PARALLEL IF(nblostparts.gt.0) THEN + p%nblost=nblost+p%nblost + !call qsort(losthole,p%Nploc,sizeof(losthole(1)),compare_int) + call LSDRADIXSORT(losthole(1:nblostparts),nblostparts) + !Write(*,'(a,60i)') "losthole: ", losthole(nblostparts:nblostparts+1) DO i=nblostparts,1,-1 - !WRITE(*,'(a,i8.8,a,i4.2,a,3(1pe12.3))') "Particle: ", losthole(i) , " of process: ", mpirank, " is out of bound in r: ", p%R(losthole(i))*rnorm, rgrid(0)*rnorm, rgrid(nr)*rnorm CALL delete_part(p,losthole(i)) END DO - WRITE(*,'(i6.2,a,i6.2)') nblostparts, " particles lost in r on process: ", mpirank + END IF + END SUBROUTINE boundary_loss - !$OMP PARALLEL DO SIMD DEFAULT(SHARED) - DO i=1,p%Nploc - p%WZ(i)=(p%Z(i)-zgrid(p%zindex(i)))/dz; - p%WR(i)=(p%R(i)-rgrid(p%rindex(i)))/(rgrid(p%rindex(i)+1)-rgrid(p%rindex(i))); - END DO - !$OMP END PARALLEL DO SIMD - - END IF + !--------------------------------------------------------------------------- + !> @author + !> Guillaume Le Bars EPFL/SPC + ! + ! DESCRIPTION: + !> @brief Compute the grid cell indices for each particle as well as the distance weight Wr, Wz. + !> @param[in] p particles structure + !--------------------------------------------------------------------------- +SUBROUTINE localisation(p) + USE basic, ONLY: rgrid, nr + Use geometry, ONLY: r_a, geom_weight, dom_weight + USE IFPORT + type(particles), INTENT(INOUT):: p + INTEGER :: i, j, iend,nbunch + nbunch=16 + + IF (p%Nploc .le. 0) return + !$OMP PARALLEL DEFAULT(SHARED), private(i,iend,j) + !$OMP DO + DO i=1,p%Nploc,nbunch + ! Avoid segmentation fault by accessing non relevant data + iend=min(i+nbunch-1,p%Nploc) + do j=i,iend + call p_calc_rzindex(p,j) + end do + call geom_weight(p%Z(i:iend), p%r(i:iend), p%geomweight(i:iend,:)) + END DO + !$OMP END DO + !$OMP END PARALLEL END SUBROUTINE localisation +subroutine p_calc_rzindex(p,i) + use basic, only: rgrid,zgrid,invdz,invdr, nnr, nr, nsubr + integer::i,j,k + type(particles)::p + k=0 + do j=1,nsubr + IF (p%R(i) .GT. rgrid(k) .AND. p%R(i) .LT. rgrid(k+nnr(j))) THEN + p%rindex(i)=floor((p%R(i)-rgrid(k))*invdr(j))+k + exit + end if + k=k+nnr(j) + end do + !ELSE IF(p%R(i) .GE. rgrid(nnr(1)) .AND. p%R(i) .LT. rgrid(nnr(1)+nnr(2))) THEN + ! p%rindex(i)=floor((p%R(i)-rgrid(nnr(1)))*invdr(2))+nnr(1) + !ELSE IF(p%R(i) .GE. rgrid(nnr(1)+nnr(2)) .AND. p%R(i) .LT. rgrid(nr)) THEN + ! p%rindex(i)=floor((p%R(i)-rgrid(nnr(1)+nnr(2)))*invdr(3))+nnr(1)+nnr(2) + !End if + p%zindex(i)=floor((p%Z(i)-zgrid(0))*invdz) + +end subroutine p_calc_rzindex + +SUBROUTINE comp_mag_p(p) + USE basic, ONLY: zgrid, rgrid, BZ, BR, nz, invdz + type(particles), INTENT(INOUT):: p + INTEGER :: i + Real(kind=db):: WZ,WR + INTEGER:: j1,j2,j3,j4 + + !$OMP PARALLEL DO SIMD DEFAULT(SHARED) Private(J1,J2,J3,J4,WZ,WR) + DO i=1,p%Nploc + WZ=(p%Z(i)-zgrid(p%zindex(i)))*invdz; + WR=(p%R(i)-rgrid(p%rindex(i)))/(rgrid(p%rindex(i)+1)-rgrid(p%rindex(i))); + J1=(p%rindex(i))*(nz+1) + p%zindex(i)+1 + J2=(p%rindex(i))*(nz+1) + p%zindex(i)+2 + J3=(p%rindex(i)+1)*(nz+1)+p%zindex(i)+1 + J4=(p%rindex(i)+1)*(nz+1)+p%zindex(i)+2 + + ! Interpolation for magnetic field + p%BZ(i)=(1-WZ)*(1-WR)*Bz(J4) & + & +WZ*(1-WR)*Bz(J3) & + & +(1-WZ)*WR*Bz(J2) & + & +WZ*WR*Bz(J1) + p%BR(i)=(1-WZ)*(1-WR)*Br(J4) & + & +WZ*(1-WR)*Br(J3) & + & +(1-WZ)*WR*Br(J2) & + & +WZ*WR*Br(J1) + END DO + !$OMP END PARALLEL DO SIMD +end subroutine comp_mag_p + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Routine used to compute the lorentz factor \f$\gamma\f$ in the classical simulations. +!> This routine systematically returns 1.0 to treat the system according to classical dynamic. +! +!> @param[out] gamma the lorentz factor \f$\gamma\f$ +!> @param[in] UZ \f$\gamma\beta_z=\gamma v_z/c\f$ the normalized particle longitudinal velocity +!> @param[in] UR \f$\gamma\beta_r=\gamma v_r/c\f$ the normalized particle radial velocity +!> @param[in] UTHET \f$\gamma\beta_\theta=\gamma v_\theta/c\f$ the normalized particle azimuthal velocity +!--------------------------------------------------------------------------- +ELEMENTAL REAL(kind=db) FUNCTION gamma_classical(UZ, UR, UTHET) +#if __INTEL_COMPILER > 1700 +!$OMP declare simd(gamma_classical) +#endif + REAL(kind=db), INTENT(IN):: UR,UZ,UTHET + gamma_classical=1.0 +END FUNCTION gamma_classical +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> @brief Routine used to compute the lorentz factor \f$\gamma\f$ in the relativistic simulations. +!> This routine computes the Lorentz factor \f$\gamma=\sqrt{1+\mathbf{\gamma\beta}^2}\f$ +! +!> @param[out] gamma the lorentz factor \f$\gamma\f$ +!> @param[in] UZ \f$\gamma\beta_z=\gamma v_z/c\f$ the normalized particle longitudinal velocity +!> @param[in] UR \f$\gamma\beta_r=\gamma v_r/c\f$ the normalized particle radial velocity +!> @param[in] UTHET \f$\gamma\beta_\theta=\gamma v_\theta/c\f$ the normalized particle azimuthal velocity +!--------------------------------------------------------------------------- +ELEMENTAL REAL(kind=db) FUNCTION gamma_relativistic(UZ, UR, UTHET) +#if __INTEL_COMPILER > 1700 +!$OMP declare simd(gamma_relativistic) +#endif + REAL(kind=db), INTENT(IN):: UR,UZ,UTHET + gamma_relativistic=sqrt(1+UZ**2+UR**2+UTHET**2) +END FUNCTION gamma_relativistic + !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief General routine to compute the velocities at time t+1. !> This routine allows to treat the classical and relativistic case efficiently from a numerical standpoint, !> by using a pointer to the routine computing gamma. This avoid the nlclassical flag check on each particle. ! !> @param[in] p The particles structure being updated !--------------------------------------------------------------------------- SUBROUTINE comp_velocity(p) ! ! Computes the new velocity of the particles due to Lorentz force ! USE basic, ONLY : nlclassical type(particles), INTENT(INOUT):: p ! Store old Velocities CALL swappointer(p%UZold, p%UZ) CALL swappointer(p%URold, p%UR) CALL swappointer(p%UTHETold, p%UTHET) CALL swappointer(p%Gammaold, p%Gamma) IF (nlclassical) THEN CALL comp_velocity_fun(p, gamma_classical) ELSE CALL comp_velocity_fun(p, gamma_relativistic) END IF END SUBROUTINE comp_velocity + !--------------------------------------------------------------------------- !> @author !> Patryk Kaminski EPFL/SPC !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Routine called by comp_velocity to compute the velocities at time t+1. !> This routine allows to treat the classical and relativistic case efficiently from a numerical standpoint, !> by using the routine computing gamma as an input. This avoid the nlclassical flag check on each particle. ! !> @param[in] gamma the function used to compute the value of the lorentz factor \f$\gamma\f$ !> @param[in] p The particles structure being updated !--------------------------------------------------------------------------- -SUBROUTINE comp_velocity_fun(p, gamma) +SUBROUTINE comp_velocity_fun(p, gammafun) ! ! Computes the new velocity of the particles due to Lorentz force ! - USE basic, ONLY : bnorm, dt, BZ, BR, nz - interface - REAL(kind=db) FUNCTION gamma(UZ, UR, UTHET) - USE constants - REAL(kind=db), INTENT(IN):: UR,UZ,UTHET - end FUNCTION - end interface + USE basic, ONLY : bnorm, dt, tnorm + procedure(gamma)::gammafun type(particles), INTENT(INOUT):: p REAL(kind=db) :: tau REAL(kind=db):: BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, SQR, ZBZ2, ZBR2 INTEGER:: J1, J2, J3, J4 INTEGER:: i ! Normalized time increment - !tau=omegac/2/omegap*dt/tnorm - tau=p%qmRatio*bnorm*0.5*dt + !tau=p%qmRatio*bnorm*tnorm*0.5*dt/tnorm + tau=p%qmRatio*bnorm*0.5*dt*tnorm IF (p%Nploc .NE. 0) THEN !$OMP PARALLEL DO SIMD DEFAULT(SHARED) PRIVATE(J1,J2,J3,J4,BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, SQR, ZBZ2, ZBR2) DO i=1,p%Nploc - J1=(p%rindex(i))*(nz+1) + p%zindex(i)+1 - J2=(p%rindex(i))*(nz+1) + p%zindex(i)+2 - J3=(p%rindex(i)+1)*(nz+1)+p%zindex(i)+1 - J4=(p%rindex(i)+1)*(nz+1)+p%zindex(i)+2 - ! Interpolation for magnetic field - BRZ=(1-p%WZ(i))*(1-p%WR(i))*Bz(J4) & - & +p%WZ(i)*(1-p%WR(i))*Bz(J3) & - & +(1-p%WZ(i))*p%WR(i)*Bz(J2) & - & +p%WZ(i)*p%WR(i)*Bz(J1) - BRR=(1-p%WZ(i))*(1-p%WR(i))*Br(J4) & - & +p%WZ(i)*(1-p%WR(i))*Br(J3) & - & +(1-p%WZ(i))*p%WR(i)*Br(J2) & - & +p%WZ(i)*p%WR(i)*Br(J1) ! First half of electric pulse p%UZ(i)=p%UZold(i)+p%Ez(i)*tau p%UR(i)=p%URold(i)+p%ER(i)*tau - p%Gamma(i)=gamma(p%UZ(i), p%UR(i), p%UTHETold(i)) + p%Gamma(i)=gammafun(p%UZ(i), p%UR(i), p%UTHETold(i)) ! Rotation along magnetic field - ZBZ=tau*BRZ/p%Gamma(i) - ZBR=tau*BRR/p%Gamma(i) + ZBZ=tau*p%BZ(i)/p%Gamma(i) + ZBR=tau*p%BR(i)/p%Gamma(i) ZPZ=p%UZ(i)-ZBR*p%UTHETold(i) !u'_{z} ZPR=p%UR(i)+ZBZ*p%UTHETold(i) !u'_{r} ZPTHET=p%UTHETold(i)+(ZBR*p%UZ(i)-ZBZ*p%UR(i)) !u'_{theta} SQR=1+ZBZ*ZBZ+ZBR*ZBR ZBZ2=2*ZBZ/SQR ZBR2=2*ZBR/SQR p%UZ(i)=p%UZ(i)-ZBR2*ZPTHET !u+_{z} p%UR(i)=p%UR(i)+ZBZ2*ZPTHET !u+_{r} p%UTHET(i)=p%UTHETold(i)+(ZBR2*ZPZ-ZBZ2*ZPR) !u+_{theta} ! Second half of acceleration p%UZ(i)=p%UZ(i)+p%EZ(i)*tau p%UR(i)=p%UR(i)+p%ER(i)*tau - + !p%ur(i)=0.001 ! Final computation of the Lorentz factor - p%Gamma(i)=gamma(p%UZ(i), p%UR(i), p%UTHET(i)) + p%Gamma(i)=gammafun(p%UZ(i), p%UR(i), p%UTHET(i)) END DO !$OMP END PARALLEL DO SIMD END IF p%collected=.false. END SUBROUTINE comp_velocity_fun -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Routine used to compute the lorentz factor \f$\gamma\f$ in the classical simulations. -!> This routine systematically returns 1.0 to treat the system according to classical dynamic. -! -!> @param[out] gamma the lorentz factor \f$\gamma\f$ -!> @param[in] UZ \f$\gamma\beta_z=\gamma v_z/c\f$ the normalized particle longitudinal velocity -!> @param[in] UR \f$\gamma\beta_r=\gamma v_r/c\f$ the normalized particle radial velocity -!> @param[in] UTHET \f$\gamma\beta_\theta=\gamma v_\theta/c\f$ the normalized particle azimuthal velocity -!--------------------------------------------------------------------------- -ELEMENTAL REAL(kind=db) FUNCTION gamma_classical(UZ, UR, UTHET) -#if __INTEL_COMPILER > 1700 -!$OMP declare simd(gamma_classical) -#endif - REAL(kind=db), INTENT(IN):: UR,UZ,UTHET - gamma_classical=1.0 -END FUNCTION gamma_classical -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> @brief Routine used to compute the lorentz factor \f$\gamma\f$ in the relativistic simulations. -!> This routine computes the Lorentz factor \f$\gamma=\sqrt{1+\mathbf{\gamma\beta}^2}\f$ -! -!> @param[out] gamma the lorentz factor \f$\gamma\f$ -!> @param[in] UZ \f$\gamma\beta_z=\gamma v_z/c\f$ the normalized particle longitudinal velocity -!> @param[in] UR \f$\gamma\beta_r=\gamma v_r/c\f$ the normalized particle radial velocity -!> @param[in] UTHET \f$\gamma\beta_\theta=\gamma v_\theta/c\f$ the normalized particle azimuthal velocity -!--------------------------------------------------------------------------- -ELEMENTAL REAL(kind=db) FUNCTION gamma_relativistic(UZ, UR, UTHET) -#if __INTEL_COMPILER > 1700 -!$OMP declare simd(gamma_relativistic) -#endif - REAL(kind=db), INTENT(IN):: UR,UZ,UTHET - gamma_relativistic=sqrt(1+UZ**2+UR**2+UTHET**2) -END FUNCTION gamma_relativistic !--------------------------------------------------------------------------- !> @author !> Patryk Kaminski EPFL/SPC !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Computes the particles position at time t+1 !> This routine computes the particles position at time t+1 according to the Bunemann algorithm. ! !> @param[in] p The particles structure being updated !--------------------------------------------------------------------------- SUBROUTINE push(p) Use basic, ONLY: dt, tnorm type(particles), INTENT(INOUT):: p REAL(kind=db):: XP, YP, COSA, SINA, U1, U2, ALPHA INTEGER :: i IF (p%Nploc .NE. 0) THEN !$OMP PARALLEL DO SIMD DEFAULT(SHARED) PRIVATE(XP, YP, COSA, SINA, U1, U2, ALPHA) DO i=1,p%Nploc ! Local Cartesian coordinates - XP=p%R(i)+dt/tnorm*p%UR(i)/p%Gamma(i) - YP=dt/tnorm*p%UTHET(i)/p%Gamma(i) + XP=p%R(i)+dt*p%UR(i)/p%Gamma(i) + YP=dt*p%UTHET(i)/p%Gamma(i) ! Conversion to cylindrical coordiantes - p%Z(i)=p%Z(i)+dt/tnorm*p%UZ(i)/p%Gamma(i) + p%Z(i)=p%Z(i)+dt*p%UZ(i)/p%Gamma(i) p%R(i)=sqrt(XP**2+YP**2) ! Computation of the rotation angle IF (p%R(i) .EQ. 0) THEN COSA=1 SINA=0 ALPHA=0 ELSE COSA=XP/p%R(i) SINA=YP/p%R(i) ALPHA=asin(SINA) END IF ! New azimuthal position p%THET(i)=MOD(p%THET(i)+ALPHA,2*pi) ! Velocity in rotated reference frame U1=COSA*p%UR(i)+SINA*p%UTHET(i) U2=-SINA*p%UR(i)+COSA*p%UTHET(i) p%UR(i)=U1 p%UTHET(i)=U2 END DO !$OMP END PARALLEL DO SIMD END IF p%collected=.false. END SUBROUTINE push !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Computes several diagnostic quantities !> This routine computes the total kinetic and electric potential energy. !> It keeps track of the reference energy and the number of particle per mpi node. ! !--------------------------------------------------------------------------- -SUBROUTINE diagnostics +SUBROUTINE partdiagnostics ! ! Compute energies ! USE constants, ONLY: vlight USE basic, ONLY: phinorm, cstep, nlclassical, ierr, step, nlend,& - & itparts - INTEGER :: i - INTEGER:: stat(MPI_STATUS_SIZE,3) - CALL MPI_WAITALL(3, ireducerequest, stat, ierr) + & itparts, nbspecies + + INTEGER:: i,j + ! Reset the quantities ekin=0 epot=0 etot=0 + ! Computation of the kinetic and potential energy as well as fluid velocities and density - !$OMP PARALLEL DO SIMD REDUCTION(+:epot, ekin) DEFAULT(SHARED) - DO i=1,partslist(1)%Nploc -! Potential energy - epot=epot+0.5*partslist(1)%q*partslist(1)%weight*(partslist(1)%pot(i))*phinorm - ! Kinetic energy - IF(.not. nlclassical) THEN - ekin=ekin+partslist(1)%m*partslist(1)%weight*vlight**2*(partslist(1)%Gamma(i)-1) - ELSE - ekin=ekin+0.5*partslist(1)%m*partslist(1)%weight*vlight**2*(partslist(1)%UR(i)**2& - & +partslist(1)%UZ(i)**2& - & +partslist(1)%UTHET(i)**2) - END IF + !$OMP PARALLEL DO REDUCTION(+:epot, ekin) DEFAULT(SHARED), PRIVATE(i,j) + Do j=1,nbspecies + if(.not. partslist(j)%is_field) CYCLE + DO i=1,partslist(j)%Nploc + +! Potential energy + epot=epot+(partslist(j)%pot(i)+partslist(j)%potxt(i))*partslist(j)%q*partslist(j)%weight + ! Kinetic energy + IF(.not. nlclassical) THEN + ekin=ekin+(0.5*(partslist(j)%Gammaold(i)+partslist(j)%Gamma(i))-1)*partslist(j)%m*partslist(j)%weight + ELSE + ekin=ekin+0.5*( partslist(j)%UR(i)*partslist(j)%URold(i) & + & + partslist(j)%UZ(i)*partslist(j)%UZold(i) & + & + partslist(j)%UTHET(i)*partslist(j)%UTHETold(i) )*partslist(j)%m*partslist(j)%weight + END IF + END DO END DO - !$OMP END PARALLEL DO SIMD + !$OMP END PARALLEL DO + epot=epot*phinorm*0.5 + ekin=ekin*vlight**2 ! Shift to Etot at cstep=1 (not valable yet at cstep=0!) - IF(cstep.EQ.0 ) THEN + IF(cstep.EQ. 1) THEN ! Compute the local total energy loc_etot0 = epot+ekin etot0=0 END IF !etot=loc_etot0 ! Compute the total energy etot=epot+ekin - Energies=(/ekin,epot,ekin+epot,loc_etot0/) + Energies=(/ekin,epot,etot,loc_etot0/) ! The computed energy is sent to the root process IF(mpisize .gt.1) THEN IF(mpirank .eq.0 ) THEN - !CALL MPI_REDUCE(MPI_IN_PLACE, epot, 1, db_type, db_sum_op, & - !& 0, MPI_COMM_WORLD, ierr) - !CALL MPI_REDUCE(MPI_IN_PLACE, ekin, 1, db_type, db_sum_op, & - !& 0, MPI_COMM_WORLD, ierr) - !CALL MPI_REDUCE(etot, etot0, 1, db_type, db_sum_op, & - !& 0, MPI_COMM_WORLD, ierr) CALL MPI_REDUCE(MPI_IN_PLACE, Energies, 4, db_type, db_sum_op, & & 0, MPI_COMM_WORLD, ierr) etot0=etot0+Energies(4) ekin=Energies(1) epot=Energies(2) etot=Energies(3) ELSE - !CALL MPI_REDUCE(epot, epot, 1, db_type, db_sum_op, & - !& 0, MPI_COMM_WORLD, ierr) - !CALL MPI_REDUCE(ekin, ekin, 1, db_type, db_sum_op, & - !& 0, MPI_COMM_WORLD, ierr) - !CALL MPI_REDUCE(etot, etot0, 1, db_type, db_sum_op, & - !& 0, MPI_COMM_WORLD, ierr) CALL MPI_REDUCE(Energies, Energies, 4, db_type, db_sum_op, & & 0, MPI_COMM_WORLD, ierr) END IF - loc_etot0=0 ELSE - etot0=loc_etot0 + etot0=etot0+loc_etot0 END IF + loc_etot0=0 ! Send the local number of particles on each node to the root process IF(mpisize .gt. 1) THEN Nplocs_all(mpirank)=partslist(1)%Nploc IF(mpirank .eq.0 ) THEN CALL MPI_gather(MPI_IN_PLACE, 1, MPI_INTEGER, Nplocs_all, 1, MPI_INTEGER,& & 0, MPI_COMM_WORLD, ierr) + !CALL MPI_REDUCE(MPI_IN_PLACE,partslist(1)%nudcol,3,db_type,db_sum_op,0,MPI_COMM_WORLD,ierr) partslist(1)%Nptot=sum(Nplocs_all) + !partslist(1)%nudcol=partslist(1)%nudcol/partslist(1)%Nptot ELSE CALL MPI_gather(Nplocs_all(mpirank), 1, MPI_INTEGER, Nplocs_all, 1, MPI_INTEGER,& & 0, MPI_COMM_WORLD, ierr) + !CALL MPI_REDUCE(partslist(1)%nudcol,partslist(1)%nudcol,3,db_type,db_sum_op,0,MPI_COMM_WORLD,ierr) END IF ELSE partslist(1)%Nptot=partslist(1)%Nploc END IF - - !Calls the communications to send the particles data to the root process for diagnostic file every itparts time steps - IF(mpisize .gt. 1 .and. (modulo(step,itparts) .eq. 0 .or. nlend)) THEN - CALL collectparts(partslist(1)) - END IF - end subroutine diagnostics + end subroutine partdiagnostics !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Collect the particles positions and velocities on the root process. !> If the collection has already been performed at this time step, the routine does nothing. ! !--------------------------------------------------------------------------- SUBROUTINE collectparts(p) USE basic, ONLY: mpirank, mpisize, ierr type(particles), INTENT(INOUT):: p INTEGER, DIMENSION(:), ALLOCATABLE :: displs, Nploc INTEGER:: i + INTEGER:: particles_type(mpisize-1) !< Stores the MPI data type used for particles gathering on node 0 and broadcast from node 0 + INTEGER :: part_requests(mpisize-1) + INTEGER:: stats(MPI_STATUS_SIZE,mpisize-1) + + part_requests=MPI_REQUEST_NULL + particles_type=MPI_DATATYPE_NULL IF(p%collected) RETURN ! exit subroutine if particles have already been collected during this time step ALLOCATE(Nploc(0:mpisize-1)) ALLOCATE(displs(0:mpisize-1)) displs=0 Nploc(mpirank)=p%Nploc CALL MPI_Allgather(MPI_IN_PLACE, 1, MPI_INTEGER, Nploc, 1, MPI_INTEGER,& & MPI_COMM_WORLD, ierr) p%Nptot=sum(Nploc) IF(p%Nptot .eq. 0 ) THEN p%collected=.true. RETURN END IF + Do i=1,mpisize-1 + displs(i)=displs(i-1)+Nploc(i-1) + END DO + IF(mpirank.eq.0 .and. p%Nptot .gt. size(p%R,1)) THEN + CALL change_parts_allocation(p,max(p%Nptot-size(P%R,1),floor(0.5*size(P%R,1)))) + END IF + + IF(mpirank .ne. 0) THEN - ! Send Particles informations to root process - CALL MPI_Gatherv(p%Z, Nploc(mpirank), db_type, p%Z, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%R, Nploc(mpirank), db_type, p%R, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%THET, Nploc(mpirank), db_type, p%THET, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%UR, Nploc(mpirank), db_type, p%UR, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%UZ, Nploc(mpirank), db_type, p%UZ, Nploc, displs, & - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%UTHET, Nploc(mpirank), db_type, p%UTHET, Nploc, displs, & - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%pot, Nploc(mpirank), db_type, p%pot, Nploc, displs, & - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%Rindex, Nploc(mpirank), MPI_INTEGER, p%Rindex, Nploc, displs, & - & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%Zindex, Nploc(mpirank), MPI_INTEGER, p%Zindex, Nploc, displs, & - & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(p%partindex, Nploc(mpirank), MPI_INTEGER, p%partindex, Nploc, displs, & - & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) - ELSE - Do i=1,mpisize-1 - displs(i)=displs(i-1)+Nploc(i-1) - END DO - IF(p%Nptot .gt. size(p%R,1)) THEN - CALL change_parts_allocation(p,p%Nptot-size(P%R,1)) + if(Nploc(mpirank) .gt. 0) THEN + Call init_particles_gather_mpi(p,1,Nploc(mpirank),particles_type(mpirank)) + ! Send Particles informations to root process + CALL MPI_SEND(p, 1, particles_type(mpirank), 0, partsgather_tag, MPI_COMM_WORLD, ierr) + CALL MPI_TYPE_FREE(particles_type(mpirank),ierr) END IF + ELSE ! Receive particle information from all processes - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), db_type, p%Z, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), db_type, p%R, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), db_type, p%THET, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), db_type, p%UR, Nploc, displs,& - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), db_type, p%UZ, Nploc, displs, & - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), db_type, p%UTHET, Nploc, displs, & - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), db_type, p%pot, Nploc, displs, & - & db_type, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), MPI_INTEGER, p%Rindex, Nploc, displs, & - & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), MPI_INTEGER, p%Zindex, Nploc, displs, & - & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) - CALL MPI_Gatherv(MPI_IN_PLACE, Nploc(mpirank), MPI_INTEGER, p%partindex, Nploc, displs, & - & MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) - p%partindex(sum(Nploc)+1:)=-1 + DO i=1,mpisize-1 + if(Nploc(i) .lt. 1) cycle + Call init_particles_gather_mpi(p,displs(i)+1,Nploc(i),particles_type(i)) + CALL MPI_IRECV(p,1,particles_type(i),i,partsgather_tag,MPI_COMM_WORLD, part_requests(i), ierr) + END DO + CALL MPI_WAITALL(mpisize-1,part_requests, stats, ierr) + p%partindex(sum(Nploc)+1:)=-1 + Do i=1,mpisize-1 + if(Nploc(i) .lt. 1) cycle + CALL MPI_TYPE_FREE(particles_type(i),ierr) + END DO END IF p%collected=.TRUE. END SUBROUTINE collectparts !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: -!> @brief Computes the velocities at time t-1/2 to keep the second order precision in time on the velocity. +!> @brief Computes the velocities at time t-1/2 delta t to keep the second order precision in time on the velocity. +!> This should only be used at particle initialisation time, ot in the case of a restart. ! !--------------------------------------------------------------------------- SUBROUTINE adapt_vinit(p) !! Computes the velocity at time -dt/2 from velocities computed at time 0 ! - USE basic, ONLY : bnorm, dt, BZ, BR, nlclassical, phinorm, nz, distribtype, vnorm + USE basic, ONLY : bnorm, dt, tnorm, nlclassical, phinorm, distribtype, vnorm type(particles), INTENT(INOUT):: p REAL(kind=db) :: tau, BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, & & SQR, Vperp, v2 INTEGER :: J1, J2, J3, J4, i REAL(kind=db), DIMENSION(:), ALLOCATABLE :: VZ, VR, VTHET ! In case Davidson distribution is used the longitudinal and radial velocities are adapted to take into account the ! electric potential. IF(distribtype .EQ. 2 .OR. distribtype .EQ. 3 .OR. distribtype .EQ. 4 .or. p%Davidson) THEN ALLOCATE(VR(p%Nploc),VZ(p%Nploc),VTHET(p%Nploc)) CALL loduni(7,VZ) VZ=VZ*2*pi VTHET=p%UTHET/p%Gamma*vnorm DO i=1,p%Nploc Vperp=sqrt(MAX(2*p%H0/p%m-2*p%qmRatio*p%pot(i)*phinorm-VTHET(i)**2,0.0_db)) VR(i)=Vperp*sin(VZ(i)) VZ(i)=Vperp*cos(VZ(i)) IF(nlclassical) THEN p%Gamma(i)=1 ELSE v2=VR(i)**2+VZ(i)**2+VTHET(i)**2 p%Gamma(i)=sqrt(1/(1-v2/vnorm**2)) END IF p%UR(i)=p%Gamma(i)*VR(i)/vnorm p%UZ(i)=p%Gamma(i)*VZ(i)/vnorm p%UTHET(i)=p%Gamma(i)*VTHET(i)/vnorm END DO DEALLOCATE(VR,VZ,VTHET) - RETURN END IF - ! Normalised time increment !tau=-omegac/2/omegap*dt/tnorm - tau=p%qmRatio*bnorm*0.5*dt + tau=-p%qmRatio*bnorm*0.5*dt*tnorm ! Store old Velocities CALL swappointer(p%UZold, p%UZ) CALL swappointer(p%URold, p%UR) CALL swappointer(p%UTHETold, p%UTHET) CALL swappointer(p%Gammaold, p%Gamma) IF (p%Nploc .NE. 0) THEN - !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i,J1,J2,J3,J4,BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, SQR) + !$OMP PARALLEL DO SIMD DEFAULT(SHARED) PRIVATE(J1,J2,J3,J4,BRZ, BRR, ZBR, ZBZ, ZPR, ZPZ, ZPTHET, SQR) DO i=1,p%Nploc - ! Compute the particle linear indices for the magnetic field interpolation - J1=(p%rindex(i))*(nz+1)+p%zindex(i)+1 - J2=J1+1 - J3=(p%rindex(i)+1)*(nz+1)+p%zindex(i)+1 - J4=J3+1 - - ! Interpolation for magnetic field - BRZ=(1-p%WZ(i))*(1-p%WR(i))*Bz(J4) & - & +p%WZ(i)*(1-p%WR(i))*Bz(J3) & - & +(1-p%WZ(i))*p%WR(i)*Bz(J2) & - & +p%WZ(i)*p%WR(i)*Bz(J1) - BRR=(1-p%WZ(i))*(1-p%WR(i))*Br(J4) & - & +p%WZ(i)*(1-p%WR(i))*Br(J3) & - & +(1-p%WZ(i))*p%WR(i)*Br(J2) & - & +p%WZ(i)*p%WR(i)*Br(J1) ! Half inverse Rotation along magnetic field - ZBZ=tau*BRZ/p%Gammaold(i) - ZBR=tau*BRR/p%Gammaold(i) + ZBZ=tau*p%BZ(i)/p%Gammaold(i) + ZBR=tau*p%BR(i)/p%Gammaold(i) SQR=1+ZBZ*ZBZ+ZBR*ZBR ZPZ=(p%UZold(i)-ZBR*p%UTHETold(i))/SQR !u-_{z} ZPR=(p%URold(i)+ZBZ*p%UTHETold(i))/SQR !u-_{r} ZPTHET=p%UTHETold(i)+(ZBR*p%UZold(i)-ZBZ*p%URold(i))/SQR !u-_{theta} p%UZ(i)=ZPZ p%UR(i)=ZPR p%UTHET(i)=ZPTHET ! half of decceleration p%UZ(i)=p%UZ(i)+p%Ez(i)*tau p%UR(i)=p%UR(i)+p%Er(i)*tau IF(.not. nlclassical) THEN p%Gamma(i)=sqrt(1+p%UZ(i)**2+p%UR(i)**2+p%UTHET(i)**2) END IF END DO - !$OMP END PARALLEL DO + !$OMP END PARALLEL DO SIMD END IF END SUBROUTINE adapt_vinit + !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: -!> @brief In the case of MPI parallelism, computes the indices of the particle assigned to the current process. +!> @brief Calculates the number of paticles per column of the spatial grid ( at fixed axial cell position) +!> This facilitate the computation of the axial grid limits for each MPI worker +! !--------------------------------------------------------------------------- - SUBROUTINE distribpartsonprocs(p) +SUBROUTINE calcnbperz(p,nbperz) + USE basic, only: nz + IMPLICIT NONE + type(particles):: p + INTEGER, INTENT(INOUT):: nbperz(0:) + Integer::i, zindex + + nbperz=0 + !$OMP PARALLEL DO DEFAULT(SHARED) reduction(+:nbperz), private(zindex,i) + Do i=1,p%Nploc + ! we make sure zindex is in [0, nz-1] to avoid segmentation faults + zindex=min(nz-1,max(p%zindex(i),0)) + nbperz(zindex)=nbperz(zindex)+1 + END DO + !$OMP END PARALLEL DO +END SUBROUTINE calcnbperz + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> @brief In the case of MPI parallelism, computes the axial limits of the local domain. +!--------------------------------------------------------------------------- + SUBROUTINE calc_Zbounds(p, Zbounds, norder) ! Computes the start and end indices for the Z boundaries on local processus ! Computes the particle indices from initial particle loading vector, that stay in current process - USE basic, ONLY: nz, ierr, Zbounds + USE basic, ONLY: nz, cstep, mpirank, mpisize,step + USE mpihelper TYPE(particles), INTENT(INOUT):: p + INTEGER:: Zbounds(0:) + INTEGER:: norder(2) + INTEGER:: old_Zbounds(0:size(Zbounds,1)-1) INTEGER:: k, i, nbparts REAL(kind=db):: idealnbpartsperproc - INTEGER:: totparts ! Total number of particle from zgrid(0) to zgrid(k) - INTEGER:: partindexstart ! Starting index of local particle in the parts variable used later to move the local particles at the begining of the vector - INTEGER:: partindexlast ! Ending index of local particle in the parts variable used later to move the local particles at the begining of the vector - INTEGER, DIMENSION(0:nz):: partspercol ! Vector containing the number of particles between zgrid(n) and zgrid(n+1) + INTEGER, DIMENSION(0:nz-1):: partspercol ! Vector containing the number of particles between zgrid(n) and zgrid(n+1) INTEGER:: Zmin, Zmax ! Minimum and maximum indices of particles in Z direction - INTEGER:: Zperproc, zindex - partspercol=0 + INTEGER:: Zperproc, ierr, remparts + CHARACTER(12)::fmt + ! calculatese the axial disstibution integrated along the radial direction + call calcnbperz(p,partspercol) - idealnbpartsperproc = FLOOR(REAL(p%Nploc)/REAL(mpisize)) - Zmin=MINVAL(p%Zindex(1:p%Nploc)) - Zmax=MAXVAL(p%Zindex(1:p%Nploc)) - Zperproc=(Zmax-Zmin)/mpisize + ! gather this data on all nodes + if(step .gt. 0 .and. mpisize .gt. 1) THEN + old_Zbounds=Zbounds + CALL MPI_ALLREDUCE(MPI_IN_PLACE, partspercol, nz, MPI_INTEGER, MPI_SUM, MPI_COMM_WORLD, ierr) + END IF - IF(Zmax .eq. 0) Zmax=nz + ! estimate the ideal number of particle per MPI worker + idealnbpartsperproc = p%Nptot/mpisize + Zmin=findloc(partspercol.gt.0,.true.,1)-1 + Zmax=findloc(partspercol.gt.0,.true.,1,BACK=.true.)-1 - DO k=1,p%Nploc - zindex=p%Zindex(k) - partspercol(zindex)=partspercol(zindex)+1 - END DO - IF (Zperproc .eq. 0) THEN + ! Find naive axial limits assuming uniform axial distribution + IF(Zmax .le. 0) Zmax=nz-1 + IF(Zmin .gt. nz) Zmin=0 + Zperproc=(Zmax-Zmin)/mpisize + + + IF (Zperproc .lt. 1 .or. cstep .eq. 0) THEN !! No particles are present initially Zperproc=nz/mpisize Zmin=0 - + ! Define boundaries using naive guess on start or restart (allow to start with 0 parts) DO k=1,mpisize-1 IF(k .lt. mpisize-1-MODULO(Zmax-Zmin,mpisize)) THEN Zbounds(k)=Zmin+k*Zperproc-1 ELSE Zbounds(k)=Zmin+k*Zperproc-1+k-mpisize+2+MODULO(Zmax-Zmin,mpisize) END IF END DO ELSE - i=0 + ! Define axial boundaries using the axial distribution information. + ! the subdomains are not equal + remparts=p%Nptot DO k=1,mpisize-1 nbparts=0 - DO WHILE(nbparts<0.99*idealnbpartsperproc .and. i .lt. Zmax) + DO WHILE(nbparts<0.98*idealnbpartsperproc .and. i .lt. Zmax .and. (nbparts+partspercol(i)).lt.1.25*idealnbpartsperproc) nbparts=nbparts+partspercol(i) i=i+1 END DO + remparts=remparts-nbparts Zbounds(k)=i END DO - END IF - partindexstart=1 - totparts=0 - partindexlast=p%Nploc + IF(step .gt. 0 .and. mpirank .eq. 0) THEN + Do i=1,mpisize-1 + !We check that the new limits will not exceed the old limits of the left and right process + ! This avoids particle communications with process >mpirank+2 and < mpirank-2 + ! However this should converge over time + IF(Zbounds(i) .lt. old_Zbounds(i-1)) Zbounds(i)=old_Zbounds(i-1) + if(Zbounds(i) .gt. old_Zbounds(i+1))Zbounds(i)=old_Zbounds(i+1) + ! If a process would have an axial domain shoter than axial norder, we revert to the old boundaries. + IF((Zbounds(i)-Zbounds(i-1)).lt. norder(1) .or. (Zbounds(i+1)-Zbounds(i)).lt. norder(1)) THEN + Zbounds=old_Zbounds + EXIT + END IF + END DO + END IF + ! send the new boundaries to all the workers + CALL MPI_Bcast(Zbounds,mpisize+1,MPI_INTEGER,0,MPI_COMM_WORLD, ierr) DO k=0,mpisize-1 Nplocs_all(k)=SUM(partspercol(Zbounds(k):Zbounds(k+1)-1)) END DO - WRITE(*,*) mpirank, " Zbounds: ", Zbounds(mpirank), Zbounds(mpirank+1), " nptot", Nplocs_all(mpirank) - - END SUBROUTINE distribpartsonprocs + if(mpirank .eq. 0) THEN + WRITE(fmt,'(a,i3,a)')"(a,",mpisize+1, "i5)" + WRITE(*,fmt) "Zbounds: ", Zbounds + WRITE(fmt,'(a,i3,a)')"(a,",mpisize, "i8)" + WRITE(*,fmt) "Nplocs: ", Nplocs_all + END IF + + END SUBROUTINE calc_Zbounds + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> @brief After a restart keep only the particles in the local domain of the current MPI worker +!--------------------------------------------------------------------------- SUBROUTINE keep_mpi_self_parts(p,Zbounds) TYPE(particles),INTENT(INOUT):: p INTEGER,INTENT(in)::Zbounds(0:) INTEGER :: i, partstart, old_sum,ierr partstart=1 p%Nploc=0 Do i=1,p%Nptot IF(p%Zindex(i).ge.Zbounds(mpirank).and.p%Zindex(i).lt.Zbounds(mpirank+1)) THEN p%Nploc=p%Nploc+1 CALL move_part(p,i,p%Nploc) END IF END DO old_sum=p%Nptot CALL MPI_REDUCE(p%Nploc, p%Nptot,1,MPI_INTEGER, MPI_SUM, 0, MPI_COMM_WORLD, ierr) - IF(p%Nptot .ne. old_sum) WRITE(*,*) "Error in particle distribution kept: ", p%Nptot, "/",old_sum + IF(p%Nptot .ne. old_sum) THEN + WRITE(*,*) "Error in particle distribution kept: ", p%Nptot, "/",old_sum + !call MPI_Abort(MPI_COMM_WORLD, -1, ierr) + !stop + END IF END SUBROUTINE keep_mpi_self_parts !_______________________________________________________________________________ !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Manage the particle communication between neighbours. !> This routine is responsible to receive the incoming particles from the MPI neighbours and to send its outgoing !> particles to these neighbours ! !> @param [in] lsendnbparts number of particles to send to the left neighbour (mpirank-1) !> @param [in] rsendnbparts number of particles to send to the right neighbour (mpirank+1) !> @param [in] sendholes array containing the indices of the particle leaving the local domain in ascending order. If the index is positive, the particle goes to the right neigbour, and to the left neighbour if the index is negative !--------------------------------------------------------------------------- - SUBROUTINE particlescommunication(p, lsendnbparts, rsendnbparts, sendholes, receivednbparts) + SUBROUTINE particlescommunication(p, lsendnbparts, rsendnbparts, sendholes, receivednbparts, procs) USE mpihelper, ONLY: particle_type - USE basic, ONLY: rightproc, leftproc, ierr #ifdef _DEBUG USE basic, ONLY: step #endif type(particles), INTENT(INOUT):: p INTEGER, INTENT(in) :: lsendnbparts, rsendnbparts INTEGER, INTENT(out) :: receivednbparts INTEGER, INTENT(in) :: sendholes(:) + INTEGER, INTENT(in) :: procs(2) INTEGER, ASYNCHRONOUS :: rrecvnbparts=0, lrecvnbparts=0 INTEGER, ASYNCHRONOUS :: sendrequest(2), recvrequest(2) INTEGER, ASYNCHRONOUS :: sendstatus(MPI_STATUS_SIZE,2), recvstatus(MPI_STATUS_SIZE,2) TYPE(particle), ALLOCATABLE :: rrecvpartbuff(:), lrecvpartbuff(:), rsendpartbuff(:), lsendpartbuff(:) ! buffers to send and receive particle from left and right processes INTEGER :: lsentnbparts, rsentnbparts - INTEGER :: lreceivednbparts, rreceivednbparts + INTEGER :: lreceivednbparts, rreceivednbparts, ierr lsentnbparts=lsendnbparts rsentnbparts=rsendnbparts sendrequest=MPI_REQUEST_NULL recvrequest=MPI_REQUEST_NULL lrecvnbparts=0 rrecvnbparts=0 ! Send and receive the number of particles to exchange - CALL MPI_IRECV(lrecvnbparts, 1, MPI_INTEGER, leftproc, 0, MPI_COMM_WORLD, recvrequest(1), ierr) - CALL MPI_IRECV(rrecvnbparts, 1, MPI_INTEGER, rightproc, 0, MPI_COMM_WORLD, recvrequest(2), ierr) - CALL MPI_ISEND(lsentnbparts, 1, MPI_INTEGER, leftproc, 0, MPI_COMM_WORLD, sendrequest(1), ierr) - CALL MPI_ISEND(rsentnbparts, 1, MPI_INTEGER, rightproc, 0, MPI_COMM_WORLD, sendrequest(2), ierr) + CALL MPI_IRECV(lrecvnbparts, 1, MPI_INTEGER, procs(1), nbpartsexchange_tag, MPI_COMM_WORLD, recvrequest(1), ierr) + CALL MPI_IRECV(rrecvnbparts, 1, MPI_INTEGER, procs(2), nbpartsexchange_tag, MPI_COMM_WORLD, recvrequest(2), ierr) + CALL MPI_ISEND(lsentnbparts, 1, MPI_INTEGER, procs(1), nbpartsexchange_tag, MPI_COMM_WORLD, sendrequest(1), ierr) + CALL MPI_ISEND(rsentnbparts, 1, MPI_INTEGER, procs(2), nbpartsexchange_tag, MPI_COMM_WORLD, sendrequest(2), ierr) CALL MPI_Waitall(2,recvrequest(1:2), recvstatus(:,1:2), ierr) recvrequest=MPI_REQUEST_NULL lreceivednbparts=lrecvnbparts rreceivednbparts=rrecvnbparts ! Re/allocate enough memory to store the incoming particles ALLOCATE(rrecvpartbuff(rreceivednbparts)) ALLOCATE(lrecvpartbuff(lreceivednbparts)) ! Receive particles from left and right processes to the corresponding buffers IF ( lrecvnbparts .gt. 0) THEN - CALL MPI_IRECV(lrecvpartbuff, lreceivednbparts, particle_type, leftproc, 1, MPI_COMM_WORLD, recvrequest(1), ierr) + CALL MPI_IRECV(lrecvpartbuff, lreceivednbparts, particle_type, procs(1), partsexchange_tag, MPI_COMM_WORLD, recvrequest(1), ierr) END IF IF( rrecvnbparts .gt. 0) THEN - CALL MPI_IRECV(rrecvpartbuff, rreceivednbparts, particle_type, rightproc, 1, MPI_COMM_WORLD, recvrequest(2), ierr) + CALL MPI_IRECV(rrecvpartbuff, rreceivednbparts, particle_type, procs(2), partsexchange_tag, MPI_COMM_WORLD, recvrequest(2), ierr) END IF ALLOCATE(rsendpartbuff(rsendnbparts)) ALLOCATE(lsendpartbuff(lsendnbparts)) ! Copy the leaving particles to the corresponding send buffers IF ( (lsendnbparts + rsendnbparts) .gt. 0) THEN CALL AddPartSendBuffers(p, lsendnbparts, rsendnbparts, sendholes, lsendpartbuff, rsendpartbuff) END IF CALL MPI_Waitall(2,sendrequest(1:2), sendstatus(:,1:2), ierr) ! Send the particles to the left and right neighbours IF( lsendnbparts .gt. 0) THEN - CALL MPI_ISEND(lsendpartbuff, lsendnbparts, particle_type, leftproc, 1, MPI_COMM_WORLD, sendrequest(1), ierr) + CALL MPI_ISEND(lsendpartbuff, lsendnbparts, particle_type, procs(1), partsexchange_tag, MPI_COMM_WORLD, sendrequest(1), ierr) #ifdef _DEBUG !WRITE(*,*)"snding ", lsendnbparts , " to left at step: ",step #endif END IF IF( rsendnbparts .gt. 0) THEN - CALL MPI_ISEND(rsendpartbuff, rsendnbparts, particle_type, rightproc, 1, MPI_COMM_WORLD, sendrequest(2), ierr) + CALL MPI_ISEND(rsendpartbuff, rsendnbparts, particle_type, procs(2), partsexchange_tag, MPI_COMM_WORLD, sendrequest(2), ierr) #ifdef _DEBUG !WRITE(*,*)"snding ", rsendnbparts , " to right at step: ",step #endif END IF ! Receive the incoming parts in the receive buffers IF ( lreceivednbparts .gt. 0) THEN CALL MPI_Wait(recvrequest(1), recvstatus(:,1), ierr) IF(ierr .ne. MPI_SUCCESS) THEN WRITE(*,*) "Error in particle reception on proc:", mpirank, " error code:", ierr, "status:", recvstatus(:,1) CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) END IF #ifdef _DEBUG !WRITE(*,*)"rcvd ", lreceivednbparts , " from left at step: ",step #endif END IF IF ( rreceivednbparts .gt. 0) THEN CALL MPI_Wait(recvrequest(2), recvstatus(:,2), ierr) IF(ierr .ne. MPI_SUCCESS) THEN WRITE(*,*) "Error in particle reception on proc:", mpirank, " error code:", ierr, "status:", recvstatus(:,2) CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) END IF #ifdef _DEBUG !WRITE(*,*)"rcvd ", rreceivednbparts , " from right at step: ",step #endif END IF receivednbparts=rreceivednbparts+lreceivednbparts IF(p%Nploc+receivednbparts-lsendnbparts-rsendnbparts .gt. size(p%R,1)) THEN CALL change_parts_allocation(p,receivednbparts) END IF ! Copy the incoming particles from the receive buffers to the simulation parts variable CALL Addincomingparts(p, rreceivednbparts, lreceivednbparts, lsendnbparts+rsendnbparts, & & sendholes, lrecvpartbuff, rrecvpartbuff) ! Wait for the outgoing particles to be fully received by the neighbours IF( lsendnbparts .gt. 0) THEN CALL MPI_Wait(sendrequest(1), sendstatus(:,1), ierr) #ifdef _DEBUG !WRITE(*,*)"sent ", lsentnbparts , " to left at step: ",step #endif END IF IF( rsendnbparts .gt. 0) THEN CALL MPI_Wait(sendrequest(2), sendstatus(:,2), ierr) #ifdef _DEBUG !WRITE(*,*)"sent ", rsentnbparts , " to right at step: ",step #endif END IF - ! ! END SUBROUTINE particlescommunication !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Copy the particles from the receive buffers to the local simulation variable parts. !> The incoming particles will first be stored in the holes left by the outgoing particles, then they !> will be added at the end of the parts variable ! !> @param [in] rrecvnbparts number of particles received from the right neighbour (mpirank+1) !> @param [in] lrecvnbparts number of particles received from the left neighbour (mpirank-1) !> @param [in] sendnbparts total number of particles having left the local domain !> @param [in] sendholes array containing the indices of the particle having left the local domain in ascending order. !--------------------------------------------------------------------------- SUBROUTINE Addincomingparts(p, rrecvnbparts, lrecvnbparts, sendnbparts, sendholes,lrecvpartbuff, rrecvpartbuff) ! USE mpihelper TYPE(particles), INTENT(INOUT):: p INTEGER, INTENT(in) :: rrecvnbparts, lrecvnbparts, sendnbparts INTEGER, INTENT(in) :: sendholes(:) TYPE(particle), INTENT(IN) :: rrecvpartbuff(:), lrecvpartbuff(:) INTEGER k,partpos ! First import the particles coming from the right IF(rrecvnbparts .gt. 0) THEN Do k=1,rrecvnbparts IF(k .le. sendnbparts) THEN - ! Fill the holes + ! Fill the holes left by sent parts partpos=abs(sendholes(k)) ELSE - ! Add at the end of parts + ! Add at the end of parts and keep track of number of parts p%Nploc=p%Nploc+1 partpos=p%Nploc END IF - CALL Insertincomingpart(p, rrecvpartbuff, k, partpos) + CALL Insertincomingpart(p, rrecvpartbuff(k), partpos) END DO END IF ! Then import the particles coming from the left IF(lrecvnbparts .gt. 0) THEN Do k=1,lrecvnbparts IF(k+rrecvnbparts .le. sendnbparts) THEN + ! Fill the holes left by sent parts partpos=abs(sendholes(k+rrecvnbparts)) ELSE + ! Add at the end of parts and keep track of number of parts p%Nploc=p%Nploc+1 partpos=p%Nploc END IF - CALL Insertincomingpart(p, lrecvpartbuff, k, partpos) + CALL Insertincomingpart(p, lrecvpartbuff(k), partpos) END DO END IF ! END SUBROUTINE Addincomingparts -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Copy one particle from the receive buffers to the local simulation variable parts. -! -!> @param [in] buffer receive buffer containing the particles parameters to copy from -!> @param [in] bufferindex particle index in the receive buffer -!> @param [in] partsindex destination particle index in the local parts variable -!--------------------------------------------------------------------------- - SUBROUTINE Insertincomingpart(p, buffer, bufferindex, partsindex) - USE mpihelper - TYPE(particles), INTENT(INOUT):: p - INTEGER, INTENT(in) :: bufferindex, partsindex - TYPE(particle), DIMENSION(:), INTENT(in) :: buffer - p%partindex(partsindex) = buffer(bufferindex)%partindex - p%Rindex(partsindex) = buffer(bufferindex)%Rindex - p%Zindex(partsindex) = buffer(bufferindex)%Zindex - p%R(partsindex) = buffer(bufferindex)%R - p%Z(partsindex) = buffer(bufferindex)%Z - p%THET(partsindex) = buffer(bufferindex)%THET - p%UZ(partsindex) = buffer(bufferindex)%UZ - p%UR(partsindex) = buffer(bufferindex)%UR - p%UTHET(partsindex) = buffer(bufferindex)%UTHET - p%Gamma(partsindex) = buffer(bufferindex)%Gamma -! -! - END SUBROUTINE Insertincomingpart - - !--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Copy one particle from the local parts variable to the send buffer. -! -!> @param [in] buffer send buffer to copy to -!> @param [in] bufferindex particle index in the send buffer -!> @param [in] partsindex origin particle index in the local parts variable -!--------------------------------------------------------------------------- - SUBROUTINE Insertsentpart(p, buffer, bufferindex, partsindex) - USE mpihelper - TYPE(particles), INTENT(INOUT):: p - INTEGER, INTENT(in) :: bufferindex, partsindex - TYPE(particle), DIMENSION(:), INTENT(inout) :: buffer - buffer(bufferindex)%partindex = p%partindex(partsindex) - buffer(bufferindex)%Rindex = p%Rindex(partsindex) - buffer(bufferindex)%Zindex = p%Zindex(partsindex) - buffer(bufferindex)%R = p%R(partsindex) - buffer(bufferindex)%Z = p%Z(partsindex) - buffer(bufferindex)%THET = p%THET(partsindex) - buffer(bufferindex)%UZ = p%UZ(partsindex) - buffer(bufferindex)%UR = p%UR(partsindex) - buffer(bufferindex)%UTHET = p%UTHET(partsindex) - buffer(bufferindex)%Gamma = p%Gamma(partsindex) -! - END SUBROUTINE Insertsentpart !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Copy the particles from the local parts variable to the left and right send buffers. ! !> @param [in] lsendnbparts number of particles to send to the left neighbour (mpirank-1) !> @param [in] rsendnbparts number of particles to send to the right neighbour (mpirank+1) !> @param [in] sendholes array containing the indices of the particle leaving the local domain in ascending order. If the index is positive, the particle goes to the right neigbour, and to the left neighbour if the index is negative !--------------------------------------------------------------------------- SUBROUTINE AddPartSendBuffers(p, lsendnbparts, rsendnbparts, sendholes, lsendpartbuff, rsendpartbuff) ! USE mpihelper TYPE(particles), INTENT(INOUT):: p INTEGER, INTENT(in) :: lsendnbparts, rsendnbparts INTEGER, INTENT(in) :: sendholes(:) TYPE(particle), INTENT(OUT) :: rsendpartbuff(:), lsendpartbuff(:) INTEGER:: partpos, k INTEGER:: lsendpos, rsendpos lsendpos=0 rsendpos=0 ! Loop over the outgoing particles and fill the correct send buffer Do k=lsendnbparts+rsendnbparts,1,-1 partpos=abs(sendholes(k)) IF(sendholes(k) .GT. 0) THEN rsendpos=rsendpos+1 CALL Insertsentpart(p, rsendpartbuff, rsendpos, partpos) ELSE IF(sendholes(k) .LT. 0) THEN lsendpos=lsendpos+1 CALL Insertsentpart(p, lsendpartbuff, lsendpos, partpos) END IF END DO ! ! END SUBROUTINE AddPartSendBuffers -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> @brief Exchange two particles in the parts variable. -! -!> @param [in] index1 index in parts of the first particle to exchange. -!> @param [in] index2 index in parts of the second particle to exchange. -!--------------------------------------------------------------------------- - SUBROUTINE exchange_parts(p, index1, index2) - TYPE(particles), INTENT(INOUT):: p - INTEGER, INTENT(IN) :: index1, index2 - REAL(kind=db):: R, Z, THET, UR, UZ, UTHET, Gamma - INTEGER :: Rindex, Zindex, partindex - !! Exchange particle at index1 with particle at index2 - - ! Store part at index1 in temporary value - partindex= p%partindex(index1) - Gamma = p%Gamma(index1) - R = p%R(index1) - Z = p%Z(index1) - THET = p%THET(index1) - UR = p%UR(index1) - UTHET = p%UTHET(index1) - UZ = p%UZ(index1) - Rindex = p%Rindex(index1) - Zindex = p%Zindex(index1) - - ! Move part at index2 in part at index 1 - p%partindex(index1)= p%partindex(index2) - p%Gamma(index1) = p%Gamma(index2) - p%R(index1) = p%R(index2) - p%Z(index1) = p%Z(index2) - p%THET(index1) = p%THET(index2) - p%UR(index1) = p%UR(index2) - p%UTHET(index1) = p%UTHET(index2) - p%UZ(index1) = p%UZ(index2) - p%Rindex(index1) = p%Rindex(index2) - p%Zindex(index1) = p%Zindex(index2) - - ! Move temporary values from part(index1) to part(index2) - p%partindex(index2)= partindex - p%Gamma(index2) = Gamma - p%R(index2) = R - p%Z(index2) = Z - p%THET(index2) = THET - p%UR(index2) = UR - p%UTHET(index2) = UTHET - p%UZ(index2) = UZ - p%Rindex(index2) = Rindex - p%Zindex(index2) = Zindex - - END SUBROUTINE exchange_parts - - SUBROUTINE change_parts_allocation(p, sizedifference) - implicit none - TYPE(particles), INTENT(INOUT):: p - INTEGER,INTENT(IN) :: sizedifference - CALL change_array_size_int(p%Rindex, sizedifference) - CALL change_array_size_int(p%Zindex, sizedifference) - CALL change_array_size_int(p%partindex, sizedifference) - CALL change_array_size_dp(p%ER,sizedifference) - CALL change_array_size_dp(p%EZ,sizedifference) - CALL change_array_size_dp(p%pot,sizedifference) - CALL change_array_size_dp(p%R,sizedifference) - CALL change_array_size_dp(p%Z,sizedifference) - CALL change_array_size_dp(p%THET,sizedifference) - CALL change_array_size_dp(p%WR,sizedifference) - CALL change_array_size_dp(p%WZ,sizedifference) - CALL change_array_size_dp_ptr(p%UR,sizedifference) - CALL change_array_size_dp_ptr(p%URold,sizedifference) - CALL change_array_size_dp_ptr(p%UZ,sizedifference) - CALL change_array_size_dp_ptr(p%UZold,sizedifference) - CALL change_array_size_dp_ptr(p%UTHET,sizedifference) - CALL change_array_size_dp_ptr(p%UTHETold,sizedifference) - CALL change_array_size_dp_ptr(p%Gamma,sizedifference) - CALL change_array_size_dp_ptr(p%Gammaold,sizedifference) - p%Nploc=MIN(p%Nploc,size(p%R)) - END SUBROUTINE change_parts_allocation - - SUBROUTINE change_array_size_dp(arr, sizedifference) - implicit none - REAL(kind=db), ALLOCATABLE, INTENT(INOUT):: arr(:) - INTEGER, INTENT(IN):: sizedifference - REAL(kind=db), ALLOCATABLE:: temp(:) - INTEGER:: current_size, new_size - if(allocated(arr)) THEN - current_size=size(arr) - new_size=current_size+sizedifference - ALLOCATE(temp(new_size)) - temp(1:min(current_size,new_size))=arr(1:min(current_size,new_size)) - DEALLOCATE(arr) - CALL move_alloc(temp, arr) - END IF - END SUBROUTINE change_array_size_dp - - SUBROUTINE change_array_size_dp_ptr(arr, sizedifference) - implicit none - REAL(kind=db), POINTER, INTENT(INOUT):: arr(:) - INTEGER, INTENT(IN):: sizedifference - REAL(kind=db), POINTER:: temp(:) - INTEGER:: current_size, new_size - if(associated(arr)) THEN - current_size=size(arr) - new_size=current_size+sizedifference - ALLOCATE(temp(new_size)) - temp(1:min(current_size,new_size))=arr(1:min(current_size,new_size)) - DEALLOCATE(arr) - arr=> temp - END IF - END SUBROUTINE change_array_size_dp_ptr - - SUBROUTINE change_array_size_int(arr, sizedifference) - implicit none - INTEGER, ALLOCATABLE, INTENT(INOUT):: arr(:) - INTEGER, INTENT(IN):: sizedifference - INTEGER, ALLOCATABLE:: temp(:) - INTEGER:: current_size, new_size - - if(allocated(arr)) THEN - current_size=size(arr) - new_size=current_size+sizedifference - ALLOCATE(temp(new_size)) - temp(1:min(current_size,new_size))=arr(1:min(current_size,new_size)) - DEALLOCATE(arr) - CALL move_alloc(temp,arr) - END IF - END SUBROUTINE change_array_size_int - - SUBROUTINE add_created_part(p, buffer) - USE bsplines - USE basic, ONLY: splrz, phinorm, nlclassical + SUBROUTINE add_list_created_part(p, buffer,nb_ins) IMPLICIT NONE TYPE(particles), INTENT(INOUT):: p TYPE(particle), ALLOCATABLE, INTENT(in) :: buffer(:) - INTEGER:: i, nptotinit, parts_size_increase, nb_added, newindex + INTEGER, OPTIONAL:: nb_ins + INTEGER:: i, nptotinit, parts_size_increase, nb_added + nptotinit=p%Nploc+1 - nb_added=size(buffer,1) + if(present(nb_ins)) THEN + nb_added=nb_ins + ELSE + nb_added=size(buffer,1) + end if + + IF(nb_added .le. 0) RETURN ! No particles to add + ! if there is not enough space in the parts simulation buffer, increase the parst size IF(p%Nploc + nb_added .gt. size(p%Z,1)) THEN parts_size_increase=Max(floor(0.1*size(p%Z,1)),nb_added) CALL change_parts_allocation(p, parts_size_increase) END IF + + DO i=1,nb_added + CALL add_created_particle(p,buffer(i)) + END DO + nb_added=p%Nploc-nptotinit+1 + if(p%is_field) then + IF(allocated(p%addedlist)) then + call change_array_size_int(p%addedlist,2) + else + allocate(p%addedlist(2)) + end if + p%addedlist(size(p%addedlist)-1)=nptotinit + p%addedlist(size(p%addedlist))=nb_added + end if + END SUBROUTINE add_list_created_part + + SUBROUTINE add_linked_created_part(p, linked_buffer, destroy, zerovelocity) + + IMPLICIT NONE + TYPE(particles), INTENT(INOUT):: p + TYPE(linked_part_row), INTENT(in) :: linked_buffer + LOGICAL:: destroy, zerovelocity + TYPE(linked_part), POINTER:: part + INTEGER:: i, nptotinit, parts_size_increase, nb_added + + nptotinit=p%Nploc+1 + nb_added=linked_buffer%n + + IF(nb_added .le. 0) RETURN ! No particles to add - newindex=MAXVAL(p%partindex) + ! if there is not enough space in the parts simulation buffer, increase the parst size + IF(p%Nploc + nb_added .gt. size(p%Z,1)) THEN + parts_size_increase=Max(floor(0.1*size(p%Z,1)),nb_added) + CALL change_parts_allocation(p, parts_size_increase) + END IF + + part=>linked_buffer%start DO i=1,nb_added - p%Nploc=p%Nploc+1 - newindex=newindex+1 - CALL Insertincomingpart(p, buffer,i,p%Nploc) - p%partindex(p%Nploc)=newindex + CALL add_created_particle(p,part%p) + part=>part%next END DO + nb_added=p%Nploc-nptotinit+1 + if(p%is_field) then + IF(allocated(p%addedlist)) then + call change_array_size_int(p%addedlist,2) + else + allocate(p%addedlist(2)) + end if + p%addedlist(size(p%addedlist)-1)=nptotinit + p%addedlist(size(p%addedlist))=nb_added + end if + if(zerovelocity)then + p%UR(nptotinit:p%Nploc)=0 + p%UTHET(nptotinit:p%Nploc)=0 + p%UZ(nptotinit:p%Nploc)=0 + end if + if (destroy) call destroy_linked_parts(linked_buffer%start) + if (p%is_field) then + ! we keep track of energy by removing the ionisation energy + ! with conversion from electronvolt to joules + loc_etot0=loc_etot0-sum(p%pot(nptotinit:p%Nploc)*elchar) + end if + END SUBROUTINE add_linked_created_part - ! Compute the energy added by these new particles for energy conservation diagnostic - IF(nb_added .gt. 0) THEN - CALL getgrad(splrz, p%Z(nptotinit:p%Nploc), p%R(nptotinit:p%Nploc), & - & p%pot(nptotinit:p%Nploc), p%EZ(nptotinit:p%Nploc), p%ER(nptotinit:p%Nploc)) - p%EZ(nptotinit:p%Nploc)=-p%Ez(nptotinit:p%Nploc) - p%ER(nptotinit:p%Nploc)=-p%ER(nptotinit:p%Nploc) + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Copy the particles from the local parts variable to the left and right send buffers. +! +!> @param [in] lsendnbparts number of particles to send to the left neighbour (mpirank-1) +!> @param [in] rsendnbparts number of particles to send to the right neighbour (mpirank+1) +!> @param [in] sendholes array containing the indices of the particle leaving the local domain in ascending order. If the index is positive, the particle goes to the right neigbour, and to the left neighbour if the index is negative +!--------------------------------------------------------------------------- - loc_etot0=loc_etot0+p%q*p%weight*sum(p%pot(nptotinit:p%Nploc))*phinorm + SUBROUTINE add_created_particle(p,part) + USE geometry + TYPE(particles):: p + TYPE(particle):: part + p%Nploc=p%Nploc+1 + p%newindex=p%newindex+1 + ! add the data to the p structure + CALL Insertincomingpart(p, part, p%Nploc) + p%partindex(p%Nploc)=p%newindex + ! calculate the new domain weight + CALL dom_weight(p%Z(p%Nploc),p%R(p%Nploc),p%geomweight(p%Nploc,0)) + ! delete the particle if it is outside of the computational domain + if( .not. is_inside(p,p%Nploc) ) then + p%Nploc=p%Nploc-1 + p%newindex=p%newindex-1 + RETURN + end if + ! Calculate the geometric weight for the Poisson solver and the grid indices + CALL geom_weight(p%Z(p%Nploc),p%R(p%Nploc),p%geomweight(p%Nploc,:)) + call p_calc_rzindex(p,p%Nploc) + END SUBROUTINE add_created_particle + + function is_inside(p,id) + Use basic, ONLY: rgrid,zgrid, nr, nz + IMPLICIT NONE + logical :: is_inside + type(particles) :: p + integer :: id + is_inside=.true. + if(p%geomweight(id,0).le.0)then + is_inside=.false. + return + end if + if(p%R(id).ge.rgrid(nr) .or. p%R(id) .le. rgrid(0))then + is_inside=.false. + return + end if + if(p%Z(id).ge.zgrid(nz) .or. p%Z(id) .le. zgrid(0))then + is_inside=.false. + return + end if + end function is_inside + + SUBROUTINE calc_newparts_energy(p) + USE basic, ONLY: phinorm, nlclassical + type(particles)::p + integer::i,n,nptotinit,nbadded, nptotend + if(.not. p%is_field) return + if( allocated(p%addedlist)) then + n=size(p%addedlist) + !write(*,*) n, "addedlist: ", p%addedlist + Do i=1,n,2 + nptotinit=p%addedlist(i) + nbadded=p%addedlist(i+1) + p%nbadded=p%nbadded+nbadded + nptotend=nptotinit+nbadded-1 + + ! Potential energy + loc_etot0=loc_etot0+p%q*p%weight*sum(p%pot(nptotinit:nptotend))*phinorm + + ! Kinetic energy IF(.not. nlclassical) THEN - loc_etot0=loc_etot0+sum(p%m*p%weight*vlight**2*(p%Gamma(nptotinit:p%Nploc)-1)) + loc_etot0=loc_etot0+p%m*p%weight*vlight**2*sum(0.5*(p%Gamma(nptotinit:nptotend)+p%Gammaold(nptotinit:nptotend))-1) ELSE - loc_etot0=loc_etot0+0.5*p%m*p%weight*vlight**2*sum(p%UR(nptotinit:p%Nploc)**2 & - & +p%UZ(nptotinit:p%Nploc)**2 & - & +p%UTHET(nptotinit:p%Nploc)**2) + loc_etot0=loc_etot0+0.5*p%m*p%weight*vlight**2*sum(p%UR(nptotinit:nptotend)*p%URold(nptotinit:nptotend) & + & +p%UZ(nptotinit:nptotend)*p%UZold(nptotinit:nptotend) & + & +p%UTHET(nptotinit:nptotend)*p%UTHETold(nptotinit:nptotend)) END IF - END IF - END SUBROUTINE + end do + deallocate(p%addedlist) + end if + + end subroutine calc_newparts_energy !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Delete particle at given index removing its energy from the system ! !> @param [in] index index of particle to be deleted !--------------------------------------------------------------------------- SUBROUTINE delete_part(p, index, replace) - !! This will destroy particle at index + !! This will destroy particle at the given index USE constants, ONLY: vlight USE bsplines - USE basic, ONLY: phinorm, nlclassical,splrz, step + USE geometry + USE basic, ONLY: phinorm, nlclassical TYPE(particles), INTENT(INOUT):: p INTEGER, INTENT(IN) :: index LOGICAL, OPTIONAL :: replace LOGICAL:: repl - REAL(kind=db):: pot(2),er(2),ez(2) + IF(present(replace)) THEN repl=replace ELSE repl=.true. END IF !Computes the potential at the particle position with phi_ext+phi_s IF(index .le. p%Nploc) THEN - IF(.not. p%is_test) THEN - CALL getgrad(splrz, p%Z(index:index), p%R(index:index), pot, EZ, ER) - loc_etot0=loc_etot0-p%q*p%weight*(pot(1))*phinorm + IF(p%is_field) THEN + loc_etot0=loc_etot0-p%q*p%weight*(p%pot(index))*phinorm IF(.not. nlclassical) THEN loc_etot0=loc_etot0-p%m*p%weight*vlight**2*(p%Gamma(index)-1) ELSE loc_etot0=loc_etot0-0.5*p%m*p%weight*vlight**2*(p%UR(index)**2+p%UZ(index)**2+p%UTHET(index)**2) END IF END IF IF(repl) THEN ! We fill the gap CALL move_part(p, p%Nploc, index) p%partindex(p%Nploc)=-1 ! Reduce the total number of simulated parts p%Nploc=p%Nploc-1 END IF END IF END SUBROUTINE delete_part - -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Move particle with index sourceindex to particle with index destindex. -!> !WARNING! This will overwrite particle at destindex. -! -!> @param [in] sourceindex index in parts of the particle to move. -!> @param [in] destindex index in parts of the moved particle destination. -!--------------------------------------------------------------------------- - SUBROUTINE move_part(p, sourceindex, destindex) - !! This will destroy particle at destindex - INTEGER, INTENT(IN) :: destindex, sourceindex - TYPE(particles), INTENT(INOUT)::p - - IF(sourceindex .eq. destindex) RETURN - IF(sourceindex .le. 0 .or. destindex .le. 0) RETURN - ! Move part at sourceindex in part at destindex - Call copy_part(p,sourceindex,destindex,p) - - END SUBROUTINE move_part - -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief Copy particle with index sourceindex in particles sourcep to particle with index destindex in particles destp. -!> !WARNING! This will overwrite particle at destp(destindex). -! -!> @param [inout] sourcep Structure of source particles. -!> @param [in] sourceindex index in parts of the particle to move. -!> @param [in] destindex index in parts of the moved particle destination. -!> @param [inout] destp Structure of source particles. -!--------------------------------------------------------------------------- - SUBROUTINE copy_part(sourcep, sourceindex, destindex, destp) - !! This will destroy particle at destindex - INTEGER, INTENT(IN) :: destindex, sourceindex - TYPE(particles), INTENT(IN)::sourcep - TYPE(particles), INTENT(INOUT)::destp - - IF(sourceindex .le. 0 .or. destindex .le. 0) RETURN - IF( destindex .gt. size(destp%R,1)) RETURN - ! Move part at sourceindex in part at destindex - destp%partindex(destindex)= sourcep%partindex(sourceindex) - destp%Gamma(destindex) = sourcep%Gamma(sourceindex) - destp%R(destindex) = sourcep%R(sourceindex) - destp%Z(destindex) = sourcep%Z(sourceindex) - destp%THET(destindex) = sourcep%THET(sourceindex) - destp%UR(destindex) = sourcep%UR(sourceindex) - destp%UTHET(destindex) = sourcep%UTHET(sourceindex) - destp%UZ(destindex) = sourcep%UZ(sourceindex) - destp%Rindex(destindex) = sourcep%Rindex(sourceindex) - destp%Zindex(destindex) = sourcep%Zindex(sourceindex) - - END SUBROUTINE copy_part -!________________________________________________________________________________ - - SUBROUTINE destroy_parts(p) - TYPE(particles) :: p - p%Nploc=0 - IF(ALLOCATED(p%Z)) DEALLOCATE(p%Z) - IF(ALLOCATED(p%R)) DEALLOCATE(p%R) - IF(ALLOCATED(p%THET)) DEALLOCATE(p%THET) - IF(ALLOCATED(p%WZ)) DEALLOCATE(p%WZ) - IF(ALLOCATED(p%WR)) DEALLOCATE(p%WR) - IF(ASSOCIATED(p%UR)) DEALLOCATE(p%UR) - IF(Associated(p%URold)) DEALLOCATE(p%URold) - IF(Associated(p%UZ)) DEALLOCATE(p%UZ) - IF(Associated(p%UZold)) DEALLOCATE(p%UZold) - IF(Associated(p%UTHET)) DEALLOCATE(p%UTHET) - IF(Associated(p%UTHETold)) DEALLOCATE(p%UTHETold) - IF(Associated(p%Gamma)) DEALLOCATE(p%Gamma) - IF(Associated(p%Gammaold)) DEALLOCATE(p%Gammaold) - IF(ALLOCATED(p%Rindex)) DEALLOCATE(p%Rindex) - IF(ALLOCATED(p%Zindex)) DEALLOCATE(p%Zindex) - IF(ALLOCATED(p%partindex)) DEALLOCATE(p%partindex) - END SUBROUTINE -!________________________________________________________________________________ - SUBROUTINE clean_beam -! - INTEGER:: i - Do i=1,size(partslist,1) - CALL destroy_parts(partslist(i)) - END DO -! - END SUBROUTINE clean_beam -!________________________________________________________________________________ - SUBROUTINE swappointer( pointer1, pointer2) - REAL(kind=db), DIMENSION(:), POINTER, INTENT(inout):: pointer1, pointer2 - REAL(kind=db), DIMENSION(:), POINTER:: temppointer - temppointer=>pointer1 - pointer1=>pointer2 - pointer2=>temppointer - END SUBROUTINE swappointer !_______________________________________________________________________________ SUBROUTINE loaduniformRZ(p, VR,VZ,VTHET) USE basic, ONLY: plasmadim, rnorm, temp, qsim, msim USE constants, ONLY: me, kb, elchar REAL(kind=db), INTENT(inout) ::VZ(:), VR(:), VTHET(:) TYPE(particles), INTENT(INOUT):: p CALL creat_parts(p, size(VR,1)) p%Nploc=size(VR,1) p%Nptot=size(VR,1) p%q=sign(elchar,qsim) p%weight=msim/me p%m=me p%qmRatio=qsim/msim ! Initial distribution in z with normalisation CALL loduni(1,p%Z(1:p%Nploc)) p%Z(1:p%Nploc)=(plasmadim(1)+(plasmadim(2)-plasmadim(1))*p%Z(1:p%Nploc))/rnorm ! Initial distribution in r with normalisation - CALL loduni(2,p%R(1:p%Nploc)) - p%R(1:p%Nploc)=(plasmadim(3)+p%R(1:p%Nploc)*(plasmadim(4)-plasmadim(3)))/rnorm + CALL lodlinr(2,p%R(1:p%Nploc),plasmadim(3),plasmadim(4)) + p%R(1:p%Nploc)=p%R(1:p%Nploc)/rnorm ! Initial velocities distribution CALL loadGaussianVelocities(p, VR, VZ, VTHET, temp) END SUBROUTINE loaduniformRZ !_______________________________________________________________________________ SUBROUTINE loadDavidson(p, VR,VZ,VTHET, lodr) USE constants, ONLY: me, kb, elchar USE basic, ONLY: nplasma, rnorm, plasmadim, distribtype, H0, P0, Rcurv, width, qsim, msim, & & omegac, zgrid, nz, rnorm, n0, nblock, temp - interface - subroutine lodr(nbase,y,ra,rb) - USE constants - REAL(kind=db), INTENT(out) :: y(:) - INTEGER, INTENT(in) :: nbase - REAL(kind=db), INTENT(in) :: rb, ra - end subroutine - end interface + procedure(rloader)::lodr TYPE(particles), INTENT(INOUT):: p REAL(kind=db), INTENT(INOUT)::VZ(:), VR(:), VTHET(:) REAL(kind=db), DIMENSION(:), ALLOCATABLE::ra, rb, z REAL(kind=db) :: r0, deltar2, halfLz, Mirrorratio, Le, VOL INTEGER :: j, n, blockstart, blockend, addedpart, remainparts INTEGER, DIMENSION(:), ALLOCATABLE :: blocksize CALL creat_parts(p, size(VR,1)) p%Nploc=size(VR,1) p%Nptot=p%Nploc Allocate(ra(nblock),rb(nblock), z(0:nblock)) !r0=(plasmadim(4)+plasmadim(3))/2 r0=sqrt(4*H0/(me*omegac**2)) halfLz=(zgrid(nz)+zgrid(0))/2 MirrorRatio=(Rcurv-1)/(Rcurv+1) z(0)=plasmadim(1) DO n=1,nblock ! Compute limits in radius and load radii for each part Le=(plasmadim(2)-plasmadim(1))/nblock*(n-0.5)-halfLz*rnorm+plasmadim(1) z(n)=z(0)+n*(plasmadim(2)-plasmadim(1))/nblock deltar2=1-MirrorRatio*cos(2*pi*Le/width) rb(n)=r0/deltar2*sqrt(1-P0*abs(omegac)/2/H0*deltar2+sqrt(1-P0*abs(omegac)/H0*deltar2)) ra(n)=r0/deltar2*sqrt(1-P0*abs(omegac)/2/H0*deltar2-sqrt(1-P0*abs(omegac)/H0*deltar2)) END DO VOL=SUM(2*pi*MINVAL(ra)*(rb-ra)*(plasmadim(2)-plasmadim(1))/nblock) qsim=VOL*n0*elchar/nplasma msim=abs(qsim)/elchar*me p%weight=abs(qsim)/elchar p%m=me p%q=sign(elchar,qsim) p%qmRatio=p%q/p%m blockstart=1 blockend=0 ALLOCATE(blocksize(nblock)) WRITE(*,*) "blocksize: ", size(blocksize), nblock DO n=1,nblock blocksize(n)=nplasma/VOL*2*pi*MINVAL(ra)*(rb(n)-ra(n))*(plasmadim(2)-plasmadim(1))/nblock END DO remainparts=p%Nploc-SUM(blocksize) addedpart=1 n=nblock/2 j=1 DO WHILE(remainparts .GT. 0) blocksize(n)=blocksize(n)+addedpart remainparts=remainparts-addedpart n=n+j j=-1*(j+SIGN(1,j)) END DO CALL loadPartSlices(p, lodr, ra, rb, z, blocksize) IF(distribtype .eq. 5) THEN CALL loadGaussianVelocities(p, VR, VZ, VTHET, temp) VZ=VZ/4 VR=VR*8 VTHET=VTHET*8 ELSE Call loadDavidsonVelocities(p, VR, VZ, VTHET, H0, P0) END IF END SUBROUTINE loadDavidson SUBROUTINE loadDavidsonVelocities(p, VR,VZ,VTHET, H0, P0) USE constants, ONLY: me, kb, elchar USE basic, ONLY: rnorm, Rcurv, B0, width, vnorm, zgrid, nz TYPE(particles), INTENT(INOUT):: p REAL(kind=db), INTENT(INOUT)::VZ(:), VR(:), VTHET(:) REAL(kind=db), INTENT(IN):: H0, P0 REAL(kind=db) :: athetpos, rg, zg, halfLz, Mirrorratio, Pcomp, Acomp INTEGER :: i MirrorRatio=(Rcurv-1)/(Rcurv+1) halfLz=(zgrid(nz)+zgrid(0))/2 ! Load velocities theta velocity ! Loading of r and z velocity is done in adapt_vinit to have ! access to parts%pot DO i=1,p%Nploc ! Interpolation for Magnetic potential rg=p%R(i)*rnorm zg=(p%Z(i)-halfLz)*rnorm Athetpos=0.5*B0*(rg - width/pi*MirrorRatio*bessi1(2*pi*rg/width)*COS(2*pi*zg/width)) Pcomp=P0/rg/p%m Acomp=-p%qmRatio*Athetpos VTHET(i)=SIGN(MIN(abs(Pcomp+Acomp),sqrt(2*H0/p%m)),Pcomp+Acomp) !VTHET(i)=Pcomp+Acomp END DO VTHET=VTHET/vnorm VZ=0._db VR=0._db p%Davidson=.true. p%H0=H0 p%P0=P0 END SUBROUTINE loadDavidsonvelocities SUBROUTINE loadGaussianVelocities(p, VR,VZ,VTHET, temperature) USE basic, ONLY: vnorm USE constants, ONLY: kb REAL(kind=db), INTENT(inout) ::VZ(:), VR(:), VTHET(:) TYPE(particles), INTENT(INOUT):: p REAL(kind=db), INTENT(IN):: temperature REAL(kind=db):: vth ! Initial velocities distribution vth=sqrt(2.0/3.0*kb*temperature/p%m)/vnorm !thermal velocity CALL lodgaus(3,VZ) CALL lodgaus(5,VR) CALL lodgaus(7,VTHET) VZ=VZ*vth VR=VR*vth VTHET=VTHET*vth p%temperature=temperature p%Davidson=.false. END SUBROUTINE loadGaussianVelocities + SUBROUTINE loadFlatTopVelocities(p, VR,VZ,VTHET, meanv, spanv) + USE basic, ONLY: vnorm + USE constants, ONLY: kb + REAL(kind=db), INTENT(inout) ::VZ(:), VR(:), VTHET(:) + TYPE(particles), INTENT(INOUT):: p + REAL(kind=db), INTENT(INOUT):: meanv(3), spanv(3) + + ! Initial velocities distribution + meanv=meanv/vnorm !thermal velocity + spanv=spanv/vnorm + CALL loduni(3,VZ) + CALL loduni(5,VR) + CALL loduni(7,VTHET) + VR=(VR*2-1)*spanv(1)+meanv(1) + VTHET=(VTHET*2-1)*spanv(2)+meanv(2) + VZ=(VZ*2-1)*spanv(3)+meanv(3) + p%Davidson=.false. + END SUBROUTINE loadFlatTopVelocities + SUBROUTINE loadPartslices(p, lodr, ra, rb, z, blocksize) USE basic, ONLY: rnorm - interface - subroutine lodr(nbase,y,ra,rb) - USE constants - REAL(kind=db), INTENT(out) :: y(:) - INTEGER, INTENT(in) :: nbase - REAL(kind=db), INTENT(in) :: rb, ra - end subroutine - end interface TYPE(particles), INTENT(INOUT):: p REAL(kind=db), INTENT(IN)::ra(:), rb(:), z(0:) INTEGER, DIMENSION(:), INTENT(IN) :: blocksize + procedure(rloader)::lodr INTEGER :: n, blockstart, blockend, nblock nblock=size(blocksize,1) blockstart=1 blockend=0 DO n=1,nblock blockstart=blockend+1 blockend=MIN(blockstart+blocksize(n)-1,p%Nploc) ! Initial distribution in z with normalisation between magnetic mirrors - CALL loduni(1,p%Z(blockstart:blockend)) + CALL loduni(1, p%Z(blockstart:blockend)) p%Z(blockstart:blockend)= (z(n-1)+p%Z(blockstart:blockend)*(z(n)-z(n-1)))/rnorm - CALL lodr(2,p%R(blockstart:blockend),ra(n), rb(n)) + CALL lodr(2, p%R(blockstart:blockend), ra(n), rb(n)) p%R(blockstart:blockend)=p%R(blockstart:blockend)/rnorm END DO END SUBROUTINE loadPartslices - SUBROUTINE loadPartFile(p, partfileindex, VR, VZ, VTHET) - USE basic, ONLY: partfile, nplasma, lu_partfile + + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Read a particle file format to load a simulated specie in the simulation +! +!--------------------------------------------------------------------------- + SUBROUTINE read_part_file(p, partfilename, VR, VZ, VTHET) + USE basic, ONLY: lu_partfile, rnorm, vnorm implicit None TYPE(particles), INTENT(INOUT):: p REAL(kind=db), DIMENSION(:), ALLOCATABLE, INTENT(INOUT)::VR, VZ, VTHET - INTEGER, INTENT(IN):: partfileindex + CHARACTER(len=*)::partfilename INTEGER:: nblock = 0 REAL(kind=db), Dimension(:), ALLOCATABLE:: ra, rb, z INTEGER, Dimension(:), ALLOCATABLE:: npartsslice INTEGER:: velocitytype=1 !< 1) gaussian with temp 2) Davidson with H0, P0 INTEGER:: radialtype=1 !< 1) 1/R 2) uniform 3) 1/R^2 4) gauss INTEGER:: npartsalloc !< initial size of particles arrays REAL(kind=db):: mass=me REAL(kind=db):: charge=-elchar REAL(kind=db):: weight=1.0 + REAL(kind=db):: qmratioscale + REAL(kind=db):: meanv(3) !< mean velocity in each direction for velocitytype 3 + REAL(kind=db):: spanv(3) !< pos/neg extent of velocity in each direction for velocitytype 3 CHARACTER(len=256) :: header=' ' !< header of csv file REAL(kind=db):: H0=3.2e-14 !< Total energy REAL(kind=db):: P0=8.66e-25 !< Canonical angular momentum REAL(kind=db):: temperature=10000 !< temperature in kelvins - LOGICAL :: is_test = .false. !< Defines if particle are test particles or tracers + real(kind=db):: n0 !< density factor + LOGICAL :: is_test !< Defines if particle are saved on ittracer or not + LOGICAL :: is_field !< Defines if particle contributes to Poisson solver + LOGICAL :: calc_moments !< Defines if moments matrix must be calculated each it2d + CHARACTER(len=16) :: partformat = 'slice' INTEGER:: i, ierr, openerr NAMELIST /partsload/ nblock, mass, charge, weight, npartsalloc, velocitytype, & - & radialtype, temperature, H0, P0, is_test - + & radialtype, temperature, H0, P0, is_test, n0, partformat, meanv, spanv, & + & calc_moments, qmratioscale, is_field - OPEN(UNIT=lu_partfile,FILE=trim(partfile(partfileindex)),ACTION='READ',IOSTAT=openerr) + ! Set defaults + qmratioscale=1.0 + weight=1.0 + meanv=0 + spanv=0 + mass=me + charge=-elchar + calc_moments=.false. + is_test=.false. + is_field=.true. + + ! Open the paticle file + OPEN(UNIT=lu_partfile,FILE=trim(partfilename),ACTION='READ',IOSTAT=openerr) header=' ' IF(openerr .ne. 0) THEN CLOSE(unit=lu_partfile) RETURN END IF READ(lu_partfile,partsload) IF(mpirank .eq.0) THEN - WRITE(*,'(a,i2,a,a)')"reading partfile: ", partfileindex, " ", partfile(partfileindex) + WRITE(*,'(a,a)')"reading partfile: ", trim(partfilename) WRITE(*,partsload) END IF - - - IF( nblock .ge. 1) THEN - ALLOCATE(z(0:nblock),ra(nblock),rb(nblock), npartsslice(nblock)) - DO WHILE(header(1:8) .ne. '//slices') - READ(lu_partfile,'(a)') header - END DO - DO i=1,nblock - READ(lu_partfile,*) z(i-1),ra(i),rb(i),npartsslice(i) - END DO - READ(lu_partfile,*) z(nblock) - - CALL creat_parts(p,max(npartsalloc,sum(npartsslice))) - p%Nploc=sum(npartsslice) - p%Nptot=p%Nploc - IF(partfileindex .eq. 1) nplasma=p%Nploc - IF( .not. allocated(VR)) ALLOCATE(VR(p%Nploc)) - IF( .not. allocated(VZ)) ALLOCATE(VZ(p%Nploc)) - IF( .not. allocated(VTHET)) ALLOCATE(VTHET(p%Nploc)) - - p%m=mass - p%q=charge - p%weight=weight - p%qmRatio=charge/mass - p%is_test=is_test - - SELECT CASE(radialtype) - CASE(1) ! 1/R distribution in R - CALL loadPartslices(p, lodunir, ra, rb, z, npartsslice) - CASE(2) ! flat top distribution in R - CALL loadPartslices(p, lodlinr, ra, rb, z, npartsslice) - CASE(3) ! 1/R^2 distribution in R - CALL loadPartslices(p, lodinvr, ra, rb, z, npartsslice) - CASE(4) ! gaussian distribution in R - CALL loadPartslices(p, lodgausr, ra, rb, z, npartsslice) + ! The plasma cloud is defined as a set of slices + IF(trim(partformat).eq.'slice') THEN + IF( nblock .ge. 1) THEN + ALLOCATE(z(0:nblock),ra(nblock),rb(nblock), npartsslice(nblock)) + DO WHILE(header(1:8) .ne. '//slices') + READ(lu_partfile,'(a)') header + END DO + DO i=1,nblock + READ(lu_partfile,*) z(i-1),ra(i),rb(i),npartsslice(i) + END DO + READ(lu_partfile,*) z(nblock) + + CALL creat_parts(p,max(npartsalloc,sum(npartsslice))) + p%Nploc=sum(npartsslice) + p%Nptot=p%Nploc + IF( allocated(VR) ) THEN + DEALLOCATE(VR,VZ,VTHET) + end if + if(.not. allocated(VR)) THEN + ALLOCATE(VR(p%Nploc)) + ALLOCATE(VZ(p%Nploc)) + ALLOCATE(VTHET(p%Nploc)) + END IF + + p%m=mass + p%q=charge + p%weight=weight + p%qmRatio=charge/mass*qmratioscale + p%is_test=is_test + p%is_field=is_field + p%calc_moments=calc_moments + p%Newindex=sum(npartsslice) + + SELECT CASE(radialtype) + CASE(1) ! 1/R distribution in R + CALL loadPartslices(p, lodunir, ra, rb, z, npartsslice) + CASE(2) ! flat top distribution in R + CALL loadPartslices(p, lodlinr, ra, rb, z, npartsslice) + CASE(3) ! 1/R^2 distribution in R + CALL loadPartslices(p, lodinvr, ra, rb, z, npartsslice) + CASE(4) ! gaussian distribution in R + CALL loadPartslices(p, lodgausr, ra, rb, z, npartsslice) + CASE DEFAULT + IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of radial distribution:", radialtype + CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) + END SELECT + + SELECT CASE(velocitytype) + CASE(1) ! Gaussian with temperature + CALL loadGaussianVelocities(p, VR, VZ, VTHET, temperature) + CASE(2) ! Davidson magnetic mirror high wr equilibrium + CALL loadDavidsonVelocities(p, VR, VZ, VTHET, H0, P0) + CASE(3) ! flat top velocity + CALL loadFlatTopVelocities(p, VR, VZ, VTHET, meanv, spanv) CASE DEFAULT - IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of radial distribution:", radialtype + IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of velocity distribution:", velocitytype CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) - END SELECT + END SELECT - SELECT CASE(velocitytype) - CASE(1) ! Gaussian with temperature - CALL loadGaussianVelocities(p, VR, VZ, VTHET, temperature) - CASE(2) - CALL loadDavidsonVelocities(p, VR, VZ, VTHET, H0, P0) - CASE DEFAULT - IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of velocity distribution:", velocitytype - CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) - END SELECT + END IF + + END IF + + + ! The plasma cloud is defined as a set individual particles + IF( trim(partformat) .eq. 'parts' ) THEN + IF( nblock .ge. 1) THEN + !Allocate necessary memory + CALL creat_parts(p,max(npartsalloc,nblock)) + IF( allocated(VR) ) THEN + DEALLOCATE(VR,VZ,VTHET) + end if + if(.not. allocated(VR)) THEN + ALLOCATE(VR(nblock)) + ALLOCATE(VZ(nblock)) + ALLOCATE(VTHET(nblock)) + END IF + + ! Read the particles from the file + DO WHILE(header(1:8) .ne. '//parts') + READ(lu_partfile,'(a)') header + END DO + DO i=1,nblock + READ(lu_partfile,*) p%R(i),p%THET(i),p%Z(i), VR(i), VTHET(i), VZ(i) + END DO + + p%Nploc=nblock + p%Nptot=p%Nploc + p%m=mass + p%q=charge + p%Newindex=nblock + p%weight=weight + p%qmRatio=charge/mass*qmratioscale + p%is_test=is_test + p%is_field=is_field + p%calc_moments=calc_moments + + !normalizations + p%r=p%r/rnorm + p%z=p%z/rnorm + VR=VR/vnorm + VTHET=VTHET/vnorm + VZ=VZ/vnorm + END IF END IF CLOSE(unit=lu_partfile) END SUBROUTINE - !=============================================================================== - SUBROUTINE Temp_rescale - ! USE basic, ONLY: temprescale, nlclassical, nz - ! INTEGER:: i, gridindex - ! REAL(kind=db) :: vr, vz, vthet - ! DO i=1,parts%Nptot - ! gridindex=parts%zindex(i)+1+parts%rindex(i)*nz - ! vr=parts%UR(i)/parts%Gamma(i)-fluidur(gridindex) - ! vz=parts%UZ(i)/parts%Gamma(i)-fluiduz(gridindex) - ! vthet=parts%UTHET(i)/parts%Gamma(i)-fluiduthet(gridindex) - ! parts%UR(i)=vr*temprescale+fluidur(gridindex) - ! parts%UTHET(i)=vthet*temprescale+fluiduthet(gridindex) - ! parts%UZ(i)=vz*temprescale+fluiduz(gridindex) - ! IF(nlclassical) THEN - ! parts%Gamma(i)=1.0 - ! ELSE - ! parts%Gamma(i)=1/sqrt(1-(parts%UR(i)**2+parts%UTHET(i)**2+parts%UZ(i)**2)) - ! END IF - ! parts%UR(i)=parts%UR(i)*parts%Gamma(i) - ! parts%UTHET(i)=parts%UTHET(i)*parts%Gamma(i) - ! parts%UZ(i)=parts%UZ(i)*parts%Gamma(i) - ! END DO - END SUBROUTINE Temp_rescale - !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Increase the number of macroparticles by separating each previous macroparticles into !> samplefactor new macroparticles of equally divided weight. The new sub particles are distributed !> uniformly in space to maintain the density and other moments. ! !> @param [in] samplefactor multiplicator of the number of macroparticles. !> @param [in] p particles type to increase. !--------------------------------------------------------------------------- SUBROUTINE upsample(p, samplefactor) USE basic, ONLY : nplasma, dr, dz INTEGER, INTENT(IN) ::samplefactor TYPE(particles), INTENT(INOUT):: p INTEGER:: i, j, currentindex REAL(kind=db), DIMENSION(p%Nploc) :: spreaddir ! random direction for the spread of each initial macro particle REAL(kind=db) :: dir ! Direction in which the particle is moved REAL(kind=db) :: dl ! Particle displacement used for ! Load and scale the direction angle for spreading the new particles CALL loduni(2, spreaddir) spreaddir=spreaddir*2*pi/samplefactor - dl=min(dz,minval(dr))/100 + dl=min(dz,minval(dr,1,dr.GT.0))/100 DO i=1,p%Nploc DO j=1,samplefactor-1 currentindex=p%Nploc+(i-1)*(samplefactor-1)+j CALL move_part(p,i,currentindex) p%partindex(currentindex)=currentindex dir = spreaddir(i)+2*pi*j/samplefactor p%R(currentindex)=p%R(currentindex) + dl*cos(dir) p%Z(currentindex)=p%Z(currentindex) + dl*sin(dir) END DO p%partindex(i)=i p%R(i)=p%R(i) + dl*cos(spreaddir(i)) p%Z(i)=p%Z(i) + dl*sin(spreaddir(i)) END DO nplasma=nplasma*samplefactor p%weight=p%weight/samplefactor p%Nploc=p%Nploc*samplefactor + p%Nptot=p%Nptot*samplefactor END SUBROUTINE upsample + +! Taken from https://rosettacode.org/wiki/Sorting_algorithms/Radix_sort#Fortran +! No Copyright is exerted due to considerable prior art in the Public Domain. +! This Fortran version by Peter Kelly ~ peter.kelly@acm.org +! +! Permission is hereby granted, free of charge, to any person obtaining +! a copy of this software and associated documentation files (the +! "Software"), to deal in the Software without restriction, including +! without limitation the rights to use, copy, modify, merge, publish, +! distribute, sublicense, and/or sell copies of the Software, and to +! permit persons to whom the Software is furnished to do so, subject to +! the following conditions: +! The above copyright notice and this permission notice shall be +! included in all copies or substantial portions of the Software. +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +! IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +! TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +! SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +! +! Implementation of a classic Radix Sort LSD style :) + SUBROUTINE LSDRADIXSORT(A , N) + IMPLICIT NONE +! +! Dummy arguments +! + INTEGER :: N + INTEGER , target, DIMENSION(0:N - 1) :: A ! All arrays based off zero, one day I'll fix it + INTENT (IN) N + INTENT (INOUT) A +! +! Local variables +! + INTEGER , DIMENSION(0:9) :: counts + INTEGER :: digitplace + INTEGER :: i + INTEGER :: j + INTEGER :: largestnum + INTEGER, DIMENSION(0:N - 1) :: results +! + digitplace = 1 ! Count of the keys + largestnum = MAXVAL(A) + + DO WHILE ( (largestnum/digitplace)>0 ) + counts = 0 ! Init the count array + DO i = 0 , N - 1 , 1 + J = (A(i)/digitplace) + J = MODULO(j , 10) + counts(j) = counts(j) + 1 + END DO + +! Change count(i) so that count(i) now contains actual position of this digit in result() +! Working similar to the counting sort algorithm + DO i = 1 , 9 , 1 + counts(i) = counts(i) + counts(i - 1) ! Build up the prefix sum + END DO +! + DO i = N - 1 , 0 , -1 ! Move from left to right + j = (A(i)/digitplace) + j = MODULO(j, 10) + results(counts(j) - 1) = A(i) ! Need to subtract one as we are zero based but prefix sum is 1 based + counts(j) = counts(j) - 1 + END DO +! + DO i = 0 , N - 1 , 1 ! Copy the semi-sorted data into the input + A(i) = results(i) + END DO +! + digitplace = digitplace*10 + END DO ! While loop + RETURN + END SUBROUTINE LSDRADIXSORT + END MODULE beam diff --git a/src/celldiag_mod.f90 b/src/celldiag_mod.f90 index 7b56489..144f8c6 100644 --- a/src/celldiag_mod.f90 +++ b/src/celldiag_mod.f90 @@ -1,183 +1,184 @@ !------------------------------------------------------------------------------ ! EPFL/Swiss Plasma Center !------------------------------------------------------------------------------ ! ! MODULE: celldiag ! !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> Represent a diagnostic positioned at cell indices (rindex,zindex) that saves the individual particles !> position and velocity !------------------------------------------------------------------------------ MODULE celldiag ! USE constants - USE mpi + use mpi USE mpihelper USE basic, ONLY: mpirank, mpisize, vnorm, rnorm, Zbounds, zgrid, & & nlclassical, nlmaxwellsource, phinorm, nbcelldiag USE beam USE futils IMPLICIT NONE PRIVATE INTEGER, SAVE, ALLOCATABLE :: specieid(:) !< position of the specie in partslist INTEGER, SAVE, ALLOCATABLE :: rindex(:) !< radial index for the diagnostic position INTEGER, SAVE, ALLOCATABLE :: zindex(:) !< axial index for the diagnostic position TYPE(particles), ALLOCATABLE, SAVE :: diagnosed_parts(:) !< Stores the particles properties at position (rindex,zindex) CHARACTER(len=20), SAVE, ALLOCATABLE :: groupname(:) !< Name of the group in the hdf5 file INTEGER, SAVE , ALLOCATABLE :: h5storelength(:) !< particles capacity of the hdf5 dataset NAMELIST /celldiagparams/ specieid, rindex, zindex PUBLIC:: celldiag_init, celldiag_save contains - subroutine celldiag_init(lu_in, time, diagfile_id) + subroutine celldiag_init(lu_in, diagfile_id) implicit none INTEGER, INTENT(IN) :: lu_in - REAL(kind=db), INTENT(IN):: time INTEGER, INTENT(IN):: diagfile_id - INTEGER:: cellnb, i + INTEGER:: i ALLOCATE(specieid(nbcelldiag), rindex(nbcelldiag), zindex(nbcelldiag)) ALLOCATE(diagnosed_parts(nbcelldiag), groupname(nbcelldiag), h5storelength(nbcelldiag)) + + Rewind(lu_in) if(nbcelldiag .gt. 0) then - READ(lu_in, celldiagparams) - WRITE(*, celldiagparams) - end if + READ(lu_in, celldiagparams) + if(mpirank .eq. 0) WRITE(*, celldiagparams) - Do i=1,nbcelldiag - CALL creat_parts(diagnosed_parts(i), 2000) - cellnb=specieid(i) + Do i=1,nbcelldiag + CALL creat_parts(diagnosed_parts(i), 500) - IF(mpirank .eq. 0) THEN - + IF(mpirank .eq. 0) THEN - WRITE(groupname(i),'(a,i2.2)') "/data/celldiag/",cellnb - If(.not. isgroup(diagfile_id, "/data/celldiag/")) THEN - CALL creatg(diagfile_id, "/data/celldiag") - CALL attach(diagfile_id, "/data/celldiag", "nbcelldiag", nbcelldiag) - END IF - CALL celldiag_createh5group(diagfile_id, groupname(i), rindex(i), zindex(i), i, diagnosed_parts(i), h5storelength(i)) + WRITE(groupname(i),'(a,i2.2)') "/data/celldiag/",i - END IF - END DO + If(.not. isgroup(diagfile_id, "/data/celldiag/")) THEN + CALL creatg(diagfile_id, "/data/celldiag") + CALL attach(diagfile_id, "/data/celldiag", "nbcelldiag", nbcelldiag) + END IF + CALL celldiag_createh5group(diagfile_id, groupname(i), rindex(i), zindex(i), specieid(i), diagnosed_parts(i), h5storelength(i)) + + END IF + END DO + END IF End subroutine celldiag_init subroutine celldiag_createh5group(diagfile_id, groupname, rindex, zindex, specid, diag_parts, h5strlength) INTEGER, INTENT(IN):: diagfile_id, rindex, zindex, specid CHARACTER(len=*), INTENT(IN):: groupname TYPE(particles), INTENT(IN):: diag_parts INTEGER, INTENT(INOUT):: h5strlength INTEGER:: partsrank, partsdim(2) - If(.not. isgroup(diagfile_id, groupname)) CALL creatg(diagfile_id, TRIM(groupname)) + If(.not. isgroup(diagfile_id, TRIM(groupname))) CALL creatg(diagfile_id, TRIM(groupname)) CALL attach(diagfile_id, TRIM(groupname), "rindex", rindex) CALL attach(diagfile_id, TRIM(groupname), "zindex", zindex) CALL attach(diagfile_id, trim(groupname), "q", partslist(specid)%q) CALL attach(diagfile_id, trim(groupname), "m", partslist(specid)%m) CALL attach(diagfile_id, trim(groupname), "weight", partslist(specid)%weight) If(.not. isdataset(diagfile_id, trim(groupname) // "/time")) CALL creatd(diagfile_id, 0, SHAPE(rindex), trim(groupname) // "/time", "time") If(.not. isdataset(diagfile_id, trim(groupname) // "/Nparts")) CALL creatd(diagfile_id, 0, SHAPE(rindex), trim(groupname) //"/Nparts", "number of remaining parts") If(.not. isdataset(diagfile_id, trim(groupname) // "/R")) CALL creatd(diagfile_id, 1, SHAPE(diag_parts%R), trim(groupname) // "/R", "radial pos") If(.not. isdataset(diagfile_id, trim(groupname) // "/Z")) CALL creatd(diagfile_id, 1, SHAPE(diag_parts%R), trim(groupname) // "/Z", "axial pos") If(.not. isdataset(diagfile_id, trim(groupname) // "/THET")) CALL creatd(diagfile_id, 1, SHAPE(diag_parts%R), trim(groupname) // "/THET", "azimuthal pos") If(.not. isdataset(diagfile_id, trim(groupname) // "/UZ")) CALL creatd(diagfile_id, 1, SHAPE(diag_parts%R), trim(groupname) // "/UZ", "axial beta*gamma") If(.not. isdataset(diagfile_id, trim(groupname) // "/UR")) CALL creatd(diagfile_id, 1, SHAPE(diag_parts%R), trim(groupname) // "/UR", "radial beta*gamma") If(.not. isdataset(diagfile_id, trim(groupname) // "/UTHET")) CALL creatd(diagfile_id, 1, SHAPE(diag_parts%R), trim(groupname) // "/UTHET", "azimuthal beta*gamma") If(.not. isdataset(diagfile_id, trim(groupname) // "/pot")) CALL creatd(diagfile_id, 1, SHAPE(diag_parts%R), trim(groupname) // "/pot", "electric potential") CALL getdims(diagfile_id, trim(groupname) // '/R', partsrank, partsdim) h5strlength=partsdim(1) END subroutine celldiag_createh5group subroutine celldiag_save(time, diagfile_id) implicit none REAL(kind=db), INTENT(IN) :: time INTEGER, INTENT(IN) :: diagfile_id INTEGER :: Nbtosave, i ! check if source is on IF(.not. celldiag_on(time)) THEN RETURN END IF Do i=1,nbcelldiag CALL celldiag_save_specie(partslist(specieid(i)),rindex(i),zindex(i),diagnosed_parts(i)) END DO Do i=1,nbcelldiag - if(mpisize .gt. 1) call collectparts(diagnosed_parts(i)) + if(mpisize .gt. 1) then + call collectparts(diagnosed_parts(i)) + else + diagnosed_parts(i)%Nptot=diagnosed_parts(i)%Nploc + end if Nbtosave=min(diagnosed_parts(i)%Nptot,h5storelength(i)) CALL celldiag_write_specie(diagfile_id, diagnosed_parts(i), groupname(i), Nbtosave, time) END DO end subroutine celldiag_save SUBROUTINE celldiag_save_specie(p, rindex, zindex, savedp) - USE basic, ONLY: rgrid, zgrid Type(particles), INTENT(IN) :: p Type(particles), INTENT(INOUT) :: savedp INTEGER, INTENT(IN) :: rindex, zindex INTEGER:: i, destcopyindex savedp%Nploc=0 savedp%collected=.false. IF (p%Nploc .gt. 0 .and. zindex .ge. Zbounds(mpirank) .and. zindex .lt. Zbounds(mpirank+1)) THEN ! Boundary condition at z direction - !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i) + !$OMP PARALLEL DO DEFAULT(SHARED) DO i=1,p%Nploc ! If the particle is in the correct cell, it is saved - IF (p%Z(i) .ge. zgrid(zindex) .and. p%Z(i) .lt. zgrid(zindex+1) .and. & - & p%R(i) .ge. rgrid(rindex) .and. p%R(i) .lt. rgrid(rindex+1) ) THEN + IF (p%Zindex(i) .eq. zindex.and. p%Rindex(i) .eq. rindex ) THEN !$OMP CRITICAL (diagparts) savedp%Nploc=savedp%Nploc+1 destcopyindex= savedp%Nploc !$OMP END CRITICAL (diagparts) CALL copy_part(p,i,destcopyindex,savedp) END IF END DO !$OMP END PARALLEL DO END IF END subroutine celldiag_save_specie SUBROUTINE celldiag_write_specie(diagfile_id, savedp, groupname, Nbtosave, time) Type(particles), INTENT(IN) :: savedp INTEGER, INTENT(IN) :: diagfile_id CHARACTER(LEN=*), INTENT(IN) :: groupname INTEGER, INTENT(IN) :: Nbtosave REAL(kind=db), INTENT(IN) :: time - IF(mpirank .eq. 0) THEN CALL append(diagfile_id, trim(groupname) // "/time", time) CALL append(diagfile_id, trim(groupname) // "/Nparts", REAL(savedp%Nptot,kind=db)) CALL append(diagfile_id, trim(groupname) // "/R", savedp%R(1:Nbtosave)*rnorm) CALL append(diagfile_id, trim(groupname) // "/Z", savedp%Z(1:Nbtosave)*rnorm) CALL append(diagfile_id, trim(groupname) // "/THET", savedp%THET(1:Nbtosave)) CALL append(diagfile_id, trim(groupname) // "/UZ", savedp%UZ(1:Nbtosave)/savedp%gamma(1:Nbtosave)) CALL append(diagfile_id, trim(groupname) // "/UR", savedp%UR(1:Nbtosave)/savedp%gamma(1:Nbtosave)) CALL append(diagfile_id, trim(groupname) // "/UTHET", savedp%UTHET(1:Nbtosave)/savedp%gamma(1:Nbtosave)) CALL append(diagfile_id, trim(groupname) // "/pot", savedp%pot(1:Nbtosave)*phinorm) END IF END subroutine celldiag_write_specie logical function celldiag_on(time) REAL(kind=db), intent(in):: time celldiag_on=.true. end function End Module celldiag diff --git a/src/chkrst.f90 b/src/chkrst.f90 index 167633f..dff068b 100644 --- a/src/chkrst.f90 +++ b/src/chkrst.f90 @@ -1,245 +1,352 @@ SUBROUTINE chkrst(flag) ! ! Process checkpoint/restart file ! USE basic USE futils USE beam USE fields USE constants, ONLY: elchar, me + Use psupply IMPLICIT NONE INTEGER, INTENT(in) :: flag INTEGER :: remainingparts REAL(kind=db):: old_msim, old_qsim, old_n0 - INTEGER:: partsrank, partsdim(1), i + INTEGER:: partsrank, partsdim(1), i, err REAL(kind=db), ALLOCATABLE:: charges(:), weights(:), masses(:) CHARACTER(len=64):: group CHARACTER(len=2):: specieindex + real(kind=db):: old_rnorm, old_tnorm, qmratioscale + real(kind=db):: prev_bias + INTEGER:: logical_val ! Only process 0 should save on file ! ! Local vars and arrays !________________________________________________________________________________ ! SELECT CASE(flag) !________________________________________________________________________________ ! 1. Open and read restart file ! CASE(0) CALL openf(rstfile, fidrst,'r',real_prec='d') CALL getatt(fidrst, '/Basic', 'cstep', cstep) CALL getatt(fidrst, '/Basic', 'time', time) CALL getatt(fidrst, '/Basic', 'n0', old_n0) + + IF(isgroup(fidrst,'/Basic/norm')) THEN + CALL getatt(fidrst, '/Basic/norm', 'rnorm', old_rnorm) + CALL getatt(fidrst, '/Basic/norm', 'tnorm', old_tnorm) + else + old_rnorm=rnorm + old_tnorm=tnorm + end if IF(isdataset(fidrst,'/Parts/charges')) THEN ! If we have multiple saved species we need to load all of them CALL getatt(fidrst,'/Parts','nbspecies',nbspecies) + nbspecies=nbspecies ALLOCATE(charges(nbspecies),masses(nbspecies),weights(nbspecies)) - ALLOCATE(partslist(nbspecies)) + ALLOCATE(partslist(nbspecies+nbaddtestspecies)) CALL getarr(fidrst, '/Parts/charges', charges) CALL getarr(fidrst, '/Parts/masses', masses) CALL getarr(fidrst, '/Parts/weights', weights) weights(1)=weights(1)/old_n0*n0 ELSE ! If we have an old restart file, we load only the electrons CALL getatt(fidrst, '/Basic', 'msim', old_msim) CALL getatt(fidrst, '/Basic', 'qsim', old_qsim) qsim=old_qsim/old_n0*n0 msim=old_msim/old_n0*n0 nbspecies=1 ALLOCATE(charges(nbspecies),masses(nbspecies),weights(nbspecies)) ALLOCATE(partslist(nbspecies)) charges(1)=sign(elchar,qsim) weights(1)=msim/me masses(1)=me END IF - + + if(newres) then + weights=weights*weights_scale + end if CALL getatt(fidrst, '/var0d', 'etot0', loc_etot0) - etot0=loc_etot0/old_n0*n0 + etot0=loc_etot0 + if(n0.ne.old_n0) cstep=0 loc_etot0=0 CALL getatt(fidrst, '/var0d', 'epot', epot) CALL getatt(fidrst, '/var0d', 'ekin', ekin) CALL getatt(fidrst, '/var0d', 'etot', etot) CALL getatt(fidrst, '/Parts', 'nplasma', nplasma) CALL getatt(fidrst, '/Parts', 'remainingparts', remainingparts) + if(remainingparts .gt. 0) Then + CALL getdims(fidrst, '/Parts/Z', partsrank, partsdim) + else + partsdim=0 + end if IF (samplefactor .gt. 1 ) THEN ! We increase the number of macro particles - CALL creat_parts(partslist(1),remainingparts*samplefactor) + CALL creat_parts(partslist(1),max(remainingparts*samplefactor,partsdim(1))) ELSE - CALL getdims(fidrst, '/Parts/Z', partsrank, partsdim) CALL creat_parts(partslist(1),partsdim(1)) END IF partslist(1)%q=charges(1) partslist(1)%m=masses(1) partslist(1)%weight=weights(1) partslist(1)%qmRatio=charges(1)/masses(1) - CALL getarr(fidrst, '/Parts/Z', partslist(1)%Z) - CALL getarr(fidrst, '/Parts/R', partslist(1)%R) - ! Renormalize R and Z - partslist(1)%R=partslist(1)%R*sqrt(n0/old_n0) - partslist(1)%Z=partslist(1)%Z*sqrt(n0/old_n0) - CALL getarr(fidrst, '/Parts/THET', partslist(1)%THET) - CALL getarr(fidrst, '/Parts/UZ', partslist(1)%UZ) - CALL getarr(fidrst, '/Parts/UR', partslist(1)%UR) - CALL getarr(fidrst, '/Parts/UTHET', partslist(1)%UTHET) - CALL getarr(fidrst, '/Parts/Zindex', partslist(1)%Zindex) - CALL getarr(fidrst, '/Parts/Rindex', partslist(1)%Rindex) - CALL getarr(fidrst, '/Parts/partindex', partslist(1)%partindex) - CALL getatt(fidrst, '/Parts', 'remainingparts', partslist(1)%Nploc) - partslist(1)%Nptot=partslist(1)%Nploc - WRITE(*,*) "Read ", partslist(1)%Nploc, " particles out of ", nplasma - IF(isdataset(fidrst,'/Parts/fluidur')) THEN - CALL getarr(fidrst, '/Parts/GAMMA', partslist(1)%Gamma) - IF(temprescale .gt. 0) THEN - CALL Temp_rescale - END IF - END IF + err=0 + CALL getatt(fidrst, 'Parts', 'qmratioscale', qmratioscale, err) + if(err .ge.0) partslist(1)%qmRatio=partslist(1)%qmRatio*qmratioscale + if(remainingparts .gt. 0) then + CALL getarr(fidrst, '/Parts/Z', partslist(1)%Z) + CALL getarr(fidrst, '/Parts/R', partslist(1)%R) + ! Renormalize R and Z + IF(isgroup(fidrst,'/Basic/norm')) THEN + partslist(1)%R=partslist(1)%R*old_rnorm/rnorm + partslist(1)%Z=partslist(1)%Z*old_rnorm/rnorm + ELSE + partslist(1)%R=partslist(1)%R*sqrt(n0/old_n0) + partslist(1)%Z=partslist(1)%Z*sqrt(n0/old_n0) + END IF + CALL getarr(fidrst, '/Parts/THET', partslist(1)%THET) + CALL getarr(fidrst, '/Parts/UZ', partslist(1)%UZ) + CALL getarr(fidrst, '/Parts/UR', partslist(1)%UR) + CALL getarr(fidrst, '/Parts/UTHET', partslist(1)%UTHET) + CALL getarr(fidrst, '/Parts/UZ', partslist(1)%UZold) + CALL getarr(fidrst, '/Parts/UR', partslist(1)%URold) + CALL getarr(fidrst, '/Parts/UTHET', partslist(1)%UTHETold) + CALL getarr(fidrst, '/Parts/Zindex', partslist(1)%Zindex) + CALL getarr(fidrst, '/Parts/Rindex', partslist(1)%Rindex) + CALL getarr(fidrst, '/Parts/partindex', partslist(1)%partindex) + IF(isdataset(fidrst,'/Parts/fluidur')) THEN + CALL getarr(fidrst, '/Parts/GAMMA', partslist(1)%Gamma) + END IF + end if + partslist(1)%Nploc=remainingparts + partslist(1)%Nptot=partslist(1)%Nploc + partslist(1)%Newindex=maxval(partslist(1)%partindex) + WRITE(*,*) "Read ", partslist(1)%Nploc, " particles out of ", remainingparts + IF(nbspecies .gt. 1) THEN DO i=2,nbspecies WRITE(group,'(a,i2)')'/Parts/',i WRITE(specieindex,'(i2)') i partsdim=0 CALL getatt(fidrst, trim(group), 'remainingparts', remainingparts) - CALL getdims(fidrst, trim(group) // '/Z', partsrank, partsdim) + if(remainingparts .gt. 0) Then + CALL getdims(fidrst, trim(group) // '/Z', partsrank, partsdim) + else + partsdim=0 + end if IF(partsdim(1).gt.remainingparts) THEN CALL creat_parts(partslist(i),partsdim(1)) partslist(i)%Nploc=remainingparts ELSE - CALL creat_parts(partslist(i),remainingparts) + CALL creat_parts(partslist(i),max(10,remainingparts)) ENDIF partslist(i)%q=charges(i) partslist(i)%m=masses(i) partslist(i)%weight=weights(i) partslist(i)%qmRatio=charges(i)/masses(i) + err=0 + CALL getatt(fidrst, trim(group), 'qmratioscale', qmratioscale, err) + if(err .ge.0) partslist(i)%qmRatio=partslist(i)%qmRatio*qmratioscale partslist(i)%Nptot=remainingparts - CALL getatt(fidrst, trim(group), 'is_test', partslist(i)%is_test) + partslist(i)%Nploc=remainingparts + partslist(i)%is_test =.false. + partslist(i)%is_field =.false. + partslist(i)%calc_moments =.false. + err=0 + CALL getatt(fidrst, trim(group), 'is_test', logical_val,err) + if(err .ge.0)then + if(logical_val.gt.0) partslist(i)%is_test =.true. + end if + err=0 + CALL getatt(fidrst, trim(group), 'is_field', logical_val,err) + if(err .ge.0)then + if(logical_val.gt.0) partslist(i)%is_field =.true. + end if + err=0 + CALL getatt(fidrst, trim(group), 'calc_moments', logical_val,err) + if(err .ge.0)then + if(logical_val.gt.0) partslist(i)%calc_moments =.true. + end if IF(partslist(i)%Nptot .gt. 0) THEN CALL getarr(fidrst, trim(group) // '/Z', partslist(i)%Z) CALL getarr(fidrst, trim(group) // '/R', partslist(i)%R) CALL getarr(fidrst, trim(group) // '/THET', partslist(i)%THET) CALL getarr(fidrst, trim(group) // '/UZ', partslist(i)%UZ) CALL getarr(fidrst, trim(group) // '/UR', partslist(i)%UR) CALL getarr(fidrst, trim(group) // '/UTHET', partslist(i)%UTHET) + CALL getarr(fidrst, trim(group) // '/UZ', partslist(i)%UZold) + CALL getarr(fidrst, trim(group) // '/UR', partslist(i)%URold) + CALL getarr(fidrst, trim(group) // '/UTHET', partslist(i)%UTHETold) CALL getarr(fidrst, trim(group) // '/GAMMA', partslist(i)%Gamma) CALL getarr(fidrst, trim(group) // '/Zindex', partslist(i)%Zindex) CALL getarr(fidrst, trim(group) // '/Rindex', partslist(i)%Rindex) CALL getarr(fidrst, trim(group) // '/partindex', partslist(i)%partindex) - partslist(i)%R=partslist(i)%R*sqrt(n0/old_n0) - partslist(i)%Z=partslist(i)%Z*sqrt(n0/old_n0) + IF(isgroup(fidrst,'/Basic/norm')) THEN + partslist(i)%R=partslist(i)%R*old_rnorm/rnorm + partslist(i)%Z=partslist(i)%Z*old_rnorm/rnorm + ELSE + partslist(i)%R=partslist(i)%R*sqrt(n0/old_n0) + partslist(i)%Z=partslist(i)%Z*sqrt(n0/old_n0) + END IF + partslist(i)%Newindex=maxval(partslist(i)%partindex) END IF END DO END IF + IF(isgroup(fidrst,'/psupply')) THEN + call getatt(fidrst,'/psupply', 'active', logical_val) + if(logical_val .gt. 0) then + call getatt(fidrst,'/psupply', 'bias', prev_bias) + the_ps%active=.true. + the_ps%bias=prev_bias/phinorm + else + the_ps%active=.false. + end if + end if + CALL closef(fidrst) IF(samplefactor .gt. 1) THEN ! We increase the number of macro particles CALL upsample(partslist(1), samplefactor) END IF WRITE(*,'(3x,a)') "Reading from restart file "//TRIM(rstfile)//" completed!" !________________________________________________________________________________ ! 2. Create and write to restart file (DP reals) ! CASE(1) - - IF(mpisize .gt. 1) THEN - DO i=1,nbspecies - call bound(partslist(i)) - call collectparts(partslist(i)) - END DO - ELSE - DO i=1,nbspecies - call bound(partslist(i)) - partslist(i)%Nptot=partslist(i)%Nploc - END DO - END IF - IF(mpirank .ne. 0) RETURN IF( .NOT. nlsave ) RETURN CALL mv2bk(rstfile) CALL creatf(rstfile, fidrst, real_prec='d', desc='Restart file') CALL creatg(fidrst, '/Basic', 'Basic data') CALL attach(fidrst, '/Basic', 'cstep', cstep) CALL attach(fidrst, '/Basic', 'time', time) CALL attach(fidrst, '/Basic', 'jobnum', jobnum) CALL attach(fidrst, '/Basic', 'qsim', partslist(1)%q*partslist(1)%weight) CALL attach(fidrst, '/Basic', 'msim', partslist(1)%m*partslist(1)%weight) CALL attach(fidrst, '/Basic', 'n0', n0) + CALL creatg(fidrst, '/Basic/norm', 'Normalisation quantities') + CALL attach(fidrst, '/Basic/norm', 'rnorm', 1.0) + CALL attach(fidrst, '/Basic/norm', 'bnorm', bnorm) + CALL attach(fidrst, '/Basic/norm', 'enorm', enorm) + CALL attach(fidrst, '/Basic/norm', 'tnorm', tnorm) + CALL attach(fidrst, '/Basic/norm', 'phinorm', phinorm) ! ! 0D variables ! CALL creatg(fidrst, '/var0d', '0D variables') CALL attach(fidrst, '/var0d','etot0', etot0) CALL attach(fidrst, '/var0d','epot', epot) CALL attach(fidrst, '/var0d','ekin', ekin) CALL attach(fidrst, '/var0d','etot', etot) ! ! Parts ! CALL creatg(fidrst, '/Parts', 'Particles data') CALL attach(fidrst, '/Parts', 'nplasma', nplasma) nbspecies=size(partslist,1) CALL attach(fidrst, '/Parts', 'nbspecies', nbspecies) ALLOCATE(charges(nbspecies),masses(nbspecies),weights(nbspecies)) DO i=1,nbspecies charges(i) = partslist(i)%q masses(i) = partslist(i)%m weights(i) = partslist(i)%weight END DO CALL putarr(fidrst, '/Parts/charges', charges) CALL putarr(fidrst, '/Parts/masses', masses ) CALL putarr(fidrst, '/Parts/weights', weights) IF(mpisize .gt. 1) THEN remainingparts=sum(Nplocs_all) ELSE remainingparts=partslist(1)%Nploc END IF CALL attach(fidrst, '/Parts', 'remainingparts', remainingparts) - CALL putarr(fidrst, '/Parts/Z', partslist(1)%Z) - CALL putarr(fidrst, '/Parts/R', partslist(1)%R) + CALL attach(fidrst, '/Parts', 'qmratioscale',partslist(1)%qmRatio/(partslist(1)%q/partslist(1)%m)) + CALL putarr(fidrst, '/Parts/Z', partslist(1)%Z*rnorm) + CALL putarr(fidrst, '/Parts/R', partslist(1)%R*rnorm) CALL putarr(fidrst, '/Parts/THET', partslist(1)%THET) CALL putarr(fidrst, '/Parts/UZ', partslist(1)%UZ) CALL putarr(fidrst, '/Parts/UR', partslist(1)%UR) CALL putarr(fidrst, '/Parts/UTHET', partslist(1)%UTHET) CALL putarr(fidrst, '/Parts/GAMMA', partslist(1)%Gamma) CALL putarr(fidrst, '/Parts/Zindex', partslist(1)%Zindex) CALL putarr(fidrst, '/Parts/Rindex', partslist(1)%Rindex) CALL putarr(fidrst, '/Parts/partindex', partslist(1)%partindex) - CALL putarr(fidrst, '/Parts/fluidur', moments(2,:)) - CALL putarr(fidrst, '/Parts/fluiduthet', moments(3,:)) - CALL putarr(fidrst, '/Parts/fluiduz', moments(4,:)) + CALL putarr(fidrst, '/Parts/fluidur', partslist(1)%moments(2,:)) + CALL putarr(fidrst, '/Parts/fluiduthet', partslist(1)%moments(3,:)) + CALL putarr(fidrst, '/Parts/fluiduz', partslist(1)%moments(4,:)) + partslist(1)%is_field=.true. + partslist(1)%is_test=.false. + partslist(1)%calc_moments=.true. + CALL attach(fidrst, '/Parts', 'is_field', 1) + CALL attach(fidrst, '/Parts', 'calc_moments', 1) + CALL attach(fidrst, '/Parts', 'is_test', 0) + + IF(nbspecies .gt. 1) THEN DO i=2,nbspecies WRITE(group,'(a,i2)')'/Parts/',i WRITE(specieindex,'(i2)') i CALL creatg(fidrst, trim(group), 'Particles ' // specieindex// ' data') + CALL attach(fidrst, trim(group), 'qmratioscale', partslist(i)%qmRatio/(partslist(i)%q/partslist(i)%m)) CALL attach(fidrst, trim(group), 'remainingparts', partslist(i)%Nptot) - CALL attach(fidrst, trim(group), 'is_test', partslist(i)%is_test) + if(partslist(i)%is_test)then + CALL attach(fidrst, trim(group), 'is_test', 1) + else + CALL attach(fidrst, trim(group), 'is_test', 0) + end if + if(partslist(i)%is_field)then + CALL attach(fidrst, trim(group), 'is_field', 1) + else + CALL attach(fidrst, trim(group), 'is_field', 0) + end if + if(partslist(i)%calc_moments)then + CALL attach(fidrst, trim(group), 'calc_moments', 1) + else + CALL attach(fidrst, trim(group), 'calc_moments', 0) + end if IF(partslist(i)%Nptot .gt. 0) THEN - CALL putarr(fidrst, trim(group) // '/Z', partslist(i)%Z(1:partslist(i)%Nptot)) - CALL putarr(fidrst, trim(group) // '/R', partslist(i)%R(1:partslist(i)%Nptot)) + CALL putarr(fidrst, trim(group) // '/Z', partslist(i)%Z(1:partslist(i)%Nptot)*rnorm) + CALL putarr(fidrst, trim(group) // '/R', partslist(i)%R(1:partslist(i)%Nptot)*rnorm) CALL putarr(fidrst, trim(group) // '/THET', partslist(i)%THET(1:partslist(i)%Nptot)) CALL putarr(fidrst, trim(group) // '/UZ', partslist(i)%UZ(1:partslist(i)%Nptot)) CALL putarr(fidrst, trim(group) // '/UR', partslist(i)%UR(1:partslist(i)%Nptot)) CALL putarr(fidrst, trim(group) // '/UTHET', partslist(i)%UTHET(1:partslist(i)%Nptot)) CALL putarr(fidrst, trim(group) // '/GAMMA', partslist(i)%Gamma(1:partslist(i)%Nptot)) CALL putarr(fidrst, trim(group) // '/Zindex', partslist(i)%Zindex(1:partslist(i)%Nptot)) CALL putarr(fidrst, trim(group) // '/Rindex', partslist(i)%Rindex(1:partslist(i)%Nptot)) CALL putarr(fidrst, trim(group) // '/partindex', partslist(i)%partindex(1:partslist(i)%Nptot)) END IF END DO END IF ! ! Fields ! + ! + ! Power supply status + ! + CALL creatg(fidrst, '/psupply', 'Power supply status and values') + if(the_ps%active) then + CALL attach(fidrst, '/psupply','active', 1) + else + CALL attach(fidrst, '/psupply','active', 0) + end if + CALL attach(fidrst, '/psupply','bias', the_ps%bias*phinorm) + + CALL closef(fidrst) WRITE(*,'(3x,a)') "Writing to restart file "//TRIM(rstfile)//" completed!" ! END SELECT ! END SUBROUTINE chkrst diff --git a/src/constants.f90 b/src/constants.f90 index a1f77fa..6221e01 100644 --- a/src/constants.f90 +++ b/src/constants.f90 @@ -1,16 +1,16 @@ MODULE constants ! ! Define some constants ! INTEGER, PARAMETER :: dprequestedprec = 15 INTEGER, PARAMETER :: db = SELECTED_REAL_KIND(dprequestedprec) ! REAL(kind=db), PARAMETER :: vlight = 299792458.0_db ! c REAL(kind=db), PARAMETER :: vacimp = 376.73031346177066_db ! \mu_0*c REAL(kind=db), PARAMETER :: eev = 510998.89613320108_db ! m_e*c^2/e REAL(kind=db), PARAMETER :: me = 9.109383E-31 ! electron mass [kg] REAL(kind=db), PARAMETER :: pi = 3.1415926535897931_db REAL(kind=db), PARAMETER :: elchar = 1.60217662E-19_db ! electron charge [C] REAL(kind=db), PARAMETER :: eps_0 = 8.85418781762E-12_db ! electric constant [F/m] REAL(kind=db), PARAMETER :: kb = 1.38064852E-23_db ! Boltzman constant [J/K] -END MODULE constants +END MODULE constants \ No newline at end of file diff --git a/src/diagnose.f90 b/src/diagnose.f90 index 6f76c61..698eb58 100644 --- a/src/diagnose.f90 +++ b/src/diagnose.f90 @@ -1,325 +1,432 @@ SUBROUTINE diagnose(kstep) ! ! Diagnostics ! USE basic USE futils USE hashtable + Use maxwsrce + Use neutcol USE beam, ONLY : partslist, epot, ekin, etot, etot0, Nplocs_all, collectparts #if USE_X == 1 USE xg, ONLY : initw, updt_xg_var #endif - USE fields, ONLY: phi_spline + USE fields, ONLY: phi_spline, nbmoments USE celldiag - USE mpi + use mpi + Use geometry + Use splinebound + Use weighttypes + use psupply + IMPLICIT NONE ! INTEGER, INTENT(in) :: kstep ! ! Local vars and arrays INTEGER, PARAMETER :: BUFSIZE = 20 CHARACTER(len=128) :: str, fname, grpname + CHARACTER(len=12):: charspec INTEGER:: Ntotal ! Total number of simulated particles remaining in the simulation INTEGER:: partsrank, partsdim(2) INTEGER:: Nplocs_all_save(64) - INTEGER:: i + INTEGER:: i, nbbounds + INTEGER, allocatable, save:: partnbBuffer(:,:) !________________________________________________________________________________ ! 1. Initial diagnostics IF( kstep .EQ. 0 .and. mpirank .eq. 0) THEN ! Only process 0 should save on file ! WRITE(*,'(a)') ' Initial diagnostics' ! ! 1.1 Initial run or when NEWRES set to .TRUE. ! IF( .NOT. nlres .OR. newres) THEN CALL creatf(resfile, fidres, 'Espic2d result file', real_prec='d') WRITE(*,'(3x,a,a)') TRIM(resfile), ' created' ! ! Label the run IF( LEN_TRIM(label1).GT.0 ) CALL attach(fidres, "/", "label1", TRIM(label1)) IF( LEN_TRIM(label2).GT.0 ) CALL attach(fidres, "/", "label2", TRIM(label2)) IF( LEN_TRIM(label3).GT.0 ) CALL attach(fidres, "/", "label3", TRIM(label3)) IF( LEN_TRIM(label4).GT.0 ) CALL attach(fidres, "/", "label4", TRIM(label4)) ! ! Job number jobnum = 0 ! ! Data group CALL creatg(fidres, "/data", "data") CALL creatg(fidres, "/data/var0d", "0d history arrays") CALL creatg(fidres, "/data/var1d","1d history arrays") CALL creatg(fidres, "/data/part", "part phase space") CALL creatg(fidres, "/data/fields", "Electro static potential and Er Ez fields") ! ! File group CALL creatg(fidres, "/files", "files") CALL attach(fidres, "/files", "jobnum", jobnum) CALL putarr(fidres, "/data/var1d/rgrid", rgrid) CALL putarr(fidres, "/data/var1d/zgrid", zgrid) CALL creatd(fidres, 1, (/ 64 /), "/data/var0d/Nplocs_all", "Nplocs_all") + CALL creatd(fidres, 1, (/3/), "/data/var0d/nudcol", "nudcol") ! Part and fields vectors ! Initialize time-dependent particle variables - CALL creatd(fidres, 1, SHAPE(partslist(1)%R), "/data/part/R", "R") - CALL creatd(fidres, 1, SHAPE(partslist(1)%Z), "/data/part/Z", "Z") - CALL creatd(fidres, 1, SHAPE(partslist(1)%Z), "/data/part/THET", "THET") - CALL creatd(fidres, 1, SHAPE(partslist(1)%UZ), "/data/part/UR", "UZ") - CALL creatd(fidres, 1, SHAPE(partslist(1)%UR), "/data/part/UZ", "UR") - CALL creatd(fidres, 1, SHAPE(partslist(1)%UTHET), "/data/part/UTHET", "UTHET") - CALL creatd(fidres, 1, SHAPE(partslist(1)%Rindex), "/data/part/Rindex", "Rindex") - CALL creatd(fidres, 1, SHAPE(partslist(1)%Zindex), "/data/part/Zindex", "Zindex") - CALL creatd(fidres, 1, SHAPE(partslist(1)%partindex), "/data/part/partindex", "partindex") - CALL creatd(fidres, 1, SHAPE(partslist(1)%pot), "/data/part/pot", "pot") + CALL creatd(fidres, 1, SHAPE(partslist(1)%R), "/data/part/R", "R",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%Z), "/data/part/Z", "Z",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%Z), "/data/part/THET", "THET",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%UZ), "/data/part/UR", "UZ",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%UR), "/data/part/UZ", "UR",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%UTHET), "/data/part/UTHET", "UTHET",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%Rindex), "/data/part/Rindex", "Rindex",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%Zindex), "/data/part/Zindex", "Zindex",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%partindex), "/data/part/partindex", "partindex",compress=.true.,chunking=SHAPE(partslist(1)%R)) + CALL creatd(fidres, 1, SHAPE(partslist(1)%pot), "/data/part/pot", "pot",compress=.true.,chunking=SHAPE(partslist(1)%R)) CALL creatd(fidres, 0, SHAPE(time), "/data/part/time", "time" ) CALL creatd(fidres, 0, SHAPE(Ntotal), "/data/part/Nparts", "number of remaining parts in the simulation space") + CALL creatd(fidres, 1, (/7/), "/data/part/nbchange", "number of added parts, lost parts zm,zp,rm,rp, and collisions per type io, ela") CALL attach(fidres,'/data/part', "q", partslist(1)%q) CALL attach(fidres,'/data/part', "m", partslist(1)%m) CALL attach(fidres,'/data/part', "weight", partslist(1)%weight) - DO i=2,nbspecies - IF ( .not. partslist(i)%is_test) CONTINUE - WRITE(grpname,'(a,i2)')'/data/part/',i - CALL creatg(fidres, grpname, "specific specie phase space") - CALL creatd(fidres, 0, SHAPE(time), trim(grpname) // "/time", "time") - CALL creatd(fidres, 0, SHAPE(time), trim(grpname) //"/Nparts", "number of remaining parts") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/R", "radial pos") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/Z", "axial pos") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/THET", "azimuthal pos") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/UZ", "axial beta*gamma") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/UR", "radial beta*gamma") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/UTHET", "azimuthal beta*gamma") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/pot", "electric potential") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/Rindex", "radial grid index") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/Zindex", "axial grid index") - CALL creatd(fidres, 1, SHAPE(partslist(i)%R), trim(grpname) // "/partindex", "particle index") - CALL attach(fidres,trim(grpname), "q", partslist(i)%q) - CALL attach(fidres,trim(grpname), "m", partslist(i)%m) - CALL attach(fidres,trim(grpname), "weight", partslist(i)%weight) - END DO - - CALL creatd(fidres, 1, SHAPE(pot), "/data/fields/pot", "pot") CALL creatd(fidres, 1, SHAPE(Er), "/data/fields/Er", "Er") CALL creatd(fidres, 1, SHAPE(Ez), "/data/fields/Ez", "Ez") CALL creatd(fidres, 1, SHAPE(phi_spline), "/data/fields/phi", "spline form of Phi") - CALL creatd(fidres, 2, SHAPE(moments), "/data/fields/moments", "moments",compress=.true.,chunking=(/1,nrank(1)*nrank(2)/)) + CALL creatd(fidres, 2, (/nbmoments,nrank(1)*nrank(2)/), "/data/fields/moments", "moments",compress=.true.,chunking=(/1,nrank(1)*nrank(2)/)) !CALL creatd(fidres, 2, SHAPE(moments), "/data/fields/moments", "moments") CALL creatd(fidres, 0, SHAPE(time), "/data/fields/time", "time" ) CALL putarr(fidres, "/data/fields/Br", Br) CALL putarr(fidres, "/data/fields/Bz", Bz) CALL putarr(fidres, "/data/fields/Athet", Athet) CALL putarr(fidres, "/data/fields/volume", Volume) ! ! 1.2 Restart run ! ELSE CALL cp2bk(resfile) ! backup previous result file CALL openf(resfile, fidres, real_prec='d') WRITE(*,'(3x,a,a)') TRIM(resfile), ' open' CALL getatt(fidres, "/files", "jobnum", jobnum) jobnum = jobnum+1 WRITE(*,'(3x,a,i3)') "Current Job Number =", jobnum CALL attach(fidres, "/files", "jobnum", jobnum) + !allocate(partnbBuffer(nbspecies,4+size(partslist(1)%nblost,1))) + !partnbBuffer=0 END IF ! ! Add input namelist variables as attributes of /data/input WRITE(str,'(a,i2.2)') "/data/input.",jobnum CALL creatg(fidres, TRIM(str)) CALL attach(fidres, TRIM(str), "job_time", job_time) CALL attach(fidres, TRIM(str), "extra_time", extra_time) - CALL attach(fidres, TRIM(str), "dt", dt) + CALL attach(fidres, TRIM(str), "dt", dt*tnorm) CALL attach(fidres, TRIM(str), "tmax", tmax) CALL attach(fidres, TRIM(str), "nrun", nrun) CALL attach(fidres, TRIM(str), "nlres", nlres) CALL attach(fidres, TRIM(str), "nlsave", nlsave) CALL attach(fidres, TRIM(str), "newres", newres) CALL attach(fidres, TRIM(str), "nz", nz) CALL putarr(fidres, TRIM(str)//"/lz", lz) CALL attach(fidres, TRIM(str), "nplasma", nplasma) CALL attach(fidres, TRIM(str), "potinn", potinn) CALL attach(fidres, TRIM(str), "potout", potout) CALL attach(fidres, TRIM(str), "B0", B0) CALL attach(fidres, TRIM(str), "Rcurv", Rcurv) CALL attach(fidres, TRIM(str), "width", width) CALL attach(fidres, TRIM(str), "n0", n0) CALL attach(fidres, TRIM(str), "temp", partslist(1)%temperature) CALL attach(fidres, TRIM(str), "it0d", it0d) CALL attach(fidres, TRIM(str), "it2d", it2d) CALL attach(fidres, TRIM(str), "itparts", itparts) CALL attach(fidres, TRIM(str), "nlclassical", nlclassical) CALL attach(fidres, TRIM(str), "nlPhis", nlPhis) CALL attach(fidres, TRIM(str), "qsim", partslist(1)%q*partslist(1)%weight) CALL attach(fidres, TRIM(str), "msim", partslist(1)%m*partslist(1)%weight) CALL attach(fidres, TRIM(str), "startstep", cstep) CALL attach(fidres, TRIM(str), "H0", partslist(1)%H0) CALL attach(fidres, TRIM(str), "P0", partslist(1)%P0) CALL putarr(fidres, TRIM(str)//"/femorder", femorder) CALL putarr(fidres, TRIM(str)//"/ngauss", ngauss) CALL putarr(fidres, TRIM(str)//"/nnr", nnr) CALL putarr(fidres, TRIM(str)//"/radii", radii) CALL putarr(fidres, TRIM(str)//"/plasmadim", plasmadim) CALL attach(fidres, TRIM(str), "rawparts", .true.) CALL attach(fidres, TRIM(str), "nbspecies", nbspecies) + CALL putarr(fidres, TRIM(str)//"/potxt", potxt) + CALL putarr(fidres, TRIM(str)//"/Erxt", Erxt) + CALL putarr(fidres, TRIM(str)//"/Ezxt", Ezxt) + +! Save geometry parameters for non conforming boundary conditions + Call geom_diag(fidres,str,rnorm) +! Save geometry parameters for non conforming boundary conditions using b-spline curves + call splinebound_diag(fidres, str, the_domain) + +! Save Maxwellsource parameters for the ad-hoc source + Call maxwsrce_diag(fidres,str,vnorm) + +! Save neutcol parameters for the electron collisions with neutrals + Call neutcol_diag(fidres,str,vnorm) + + if(.not. isdataset(fidres,'/data/var0d/nudcol'))then + CALL creatd(fidres, 1, (/3/), "/data/var0d/nudcol", "nudcol") + end if + +! Save psupply parameters for the simulation of realistic power supplies + Call psupply_diag(fidres,str) + + if(.not. isdataset(fidres,'/data/var0d/biases'))then + nbbounds=2 + if(the_domain%nbsplines .gt. 0) nbbounds=the_domain%nbsplines + CALL creatd(fidres, 1, (/nbbounds/), "/data/var0d/biases", "biases") + end if ! Save STDIN of this run WRITE(str,'(a,i2.2)') "/files/STDIN.",jobnum INQUIRE(unit=lu_in, name=fname) CALL putfile(fidres, TRIM(str), TRIM(fname)) +! Prepare hdf5 file for storing test particles + DO i=2,nbspecies + WRITE(grpname,'(a,i2)')'/data/part/',i + call create_parts_group(partslist(i),trim(grpname),time) + END DO + CALL attach(fidres, "/data/part", "nbspecies", nbspecies) + ! ! Initialize buffers for 0d history arrays CALL htable_init(hbuf0, BUFSIZE) CALL set_htable_fileid(hbuf0, fidres, "/data/var0d") ! - -! ! Initialize Xgrafix #if USE_X == 1 IF(nlxg) THEN CALL initw END IF #endif END IF IF(kstep .EQ. 0) THEN ! Initialize particle cell diagnostic - CALL celldiag_init(lu_in,time, fidres) + CALL celldiag_init(lu_in, fidres) CLOSE(lu_in) + + allocate(partnbBuffer(nbspecies,4+size(partslist(1)%nblost,1))) + partnbBuffer=0 END IF + !________________________________________________________________________________ ! 2. Periodic diagnostics IF( kstep .NE. -1) THEN IF(modulo(step,ittracer) .eq. 0 .or. nlend) THEN ! We gather the traced particles on the mpi host DO i=1,nbspecies IF(partslist(i)%is_test) CALL collectparts(partslist(i)) END DO END IF - ! Cell diag quantities - IF(modulo(step,itcelldiag).eq. 0 .or. nlend) THEN - ! - CALL celldiag_save(time, fidres) + IF(modulo(step,itrestart) .eq. 0 .or. modulo(step,itparts) .eq. 0 .or. nlend) THEN + ! We gather the traced particles on the mpi host + DO i=1,nbspecies + CALL collectparts(partslist(i)) + END DO END IF + + do i=1,nbspecies + partnbBuffer(i,1)=partnbBuffer(i,1)+partslist(i)%nbadded + partnbBuffer(i,2:3)=partnbBuffer(i,2:3)+partslist(i)%nbcolls + partnbBuffer(i,4)=partslist(i)%Nploc + partnbBuffer(i,5:)=partnbBuffer(i,5:)+partslist(i)%nblost + partslist(i)%nbadded=0 + partslist(i)%nblost=0 + partslist(i)%nbcolls=0 + end do + + IF(modulo(step,ittext) .eq. 0 .or. nlend) THEN + ! We gather the number of gained and lost particles on the mpi host + IF(mpirank .eq.0 ) THEN + CALL MPI_REDUCE(MPI_IN_PLACE, partnbBuffer, nbspecies*(4+size(partslist(1)%nblost,1)), MPI_INTEGER, MPI_SUM, & + & 0, MPI_COMM_WORLD, ierr) + ELSE + CALL MPI_REDUCE(partnbBuffer, partnbBuffer, nbspecies*(4+size(partslist(1)%nblost,1)), MPI_INTEGER, MPI_SUM, & + & 0, MPI_COMM_WORLD, ierr) + END IF + + end if ! ! Only process 0 should save on file IF(mpirank .ne. 0) RETURN ! IF (mpisize .gt. 1) THEN partslist(1)%Nptot=sum(Nplocs_all) END IF ! IF(modulo(step,ittext).eq. 0 .or. nlend) THEN WRITE(*,'(a,1x,i8.8,a1,i8.8,20x,a,1pe10.3)') '*** Timestep (this run/total) =', & & step, '/', cstep, 'Time =', time - WRITE(*,'(a,4(1pe12.4),1x,i8.8,a1,i8.8)') 'Epot, Ekin, Etot, Eerr, Nbparts/Ntotal', epot, ekin, etot, etot-etot0, partslist(1)%Nptot,'/',nplasma - IF(mpisize .gt. 1 ) WRITE(*,'(a,64i10.7)') 'Nbparts per proc', Nplocs_all + if( abs(etot).gt. 0) then + WRITE(*,'(a,6(1pe12.4),1x,i8.8,a1,i8.8)') 'Epot, Ekin, Etot, Etot0, Eerr, Eerr rel, Nbparts/Ntotal', epot, ekin, etot, etot0, etot-etot0,(etot-etot0)/etot, partslist(1)%Nptot,'/',nplasma + else + WRITE(*,'(a,4(1pe12.4),1x,i8.8,a1,i8.8)') 'Epot, Ekin, Etot, Eerr, Nbparts/Ntotal', epot, ekin, etot, etot-etot0, partslist(1)%Nptot,'/',nplasma + end if + IF(mpisize .gt. 1 ) then + WRITE(*,'(a,64i10.7)') 'Nbparts per proc', Nplocs_all + end if + Write(*,'(a)')"speci, added, iocoll, elacoll, tot var, tot, Losses (zmin zmax rmin rmax boundaries(i))" + write(charspec,'(a,i02,a)') '(i04,',size(partnbBuffer,2)+1,'i9.7)' + do i=1,nbspecies + WRITE(*,charspec) i, partnbBuffer(i,1),partnbBuffer(i,2:3), partnbBuffer(i,1)-sum(partnbBuffer(i,5:)), partnbBuffer(i,4),-partnbBuffer(i,5:) + partslist(i)%nptot= partnbBuffer(i,4) + end do + partnbBuffer=0 END IF !________________________________________________________________________________ ! ! 2.1 0d history arrays -! +! + ! if we do a restart, we don't want to save the same data twice + IF( kstep .eq. 0 .and. nlres .and. (.not. newres)) return + IF(modulo(step,it0d).eq. 0 .or. nlend) THEN CALL add_record(hbuf0, "time", "simulation time", time) CALL add_record(hbuf0, "epot", "potential energy", epot) CALL add_record(hbuf0, "ekin", "kinetic energy", ekin) CALL add_record(hbuf0, "etot", "total energy", etot) CALL add_record(hbuf0, "etot0", "theoretical total energy", etot0) CALL add_record(hbuf0, "nbparts", "number of remaining parts in the simulation space", REAL(partslist(1)%Nptot,kind=db)) + CALL add_record(hbuf0,"current", "unscaled current flowing between the electrodes of the power supplies [A]", the_ps%current(1)*qnorm/tnorm) CALL htable_endstep(hbuf0) Nplocs_all_save=0 Nplocs_all_save(1:mpisize)=Nplocs_all(0:mpisize-1) CALL append(fidres, "/data/var0d/Nplocs_all", REAL(Nplocs_all_save,kind=db)) + CALL append(fidres, "/data/var0d/nudcol", partslist(1)%nudcol/(dt*tnorm)) + CALL append(fidres, "/data/var0d/biases", the_ps%biases*phinorm) + END IF ! ! 2.2 2d profiles IF(modulo(step,it2d).eq. 0 .or. nlend) THEN CALL append(fidres, "/data/fields/time", time) CALL append(fidres, "/data/fields/pot", pot*phinorm) CALL append(fidres, "/data/fields/Er", Er*enorm) CALL append(fidres, "/data/fields/Ez", Ez*enorm) CALL append(fidres, "/data/fields/phi", phi_spline*phinorm) - CALL append(fidres, "/data/fields/moments", moments) + CALL append(fidres, "/data/fields/moments", partslist(1)%moments) + DO i=2,nbspecies + IF ( .not. partslist(i)%calc_moments) CYCLE + WRITE(grpname,'(a,i2,a)')'/data/part/',i,'/' + CALL append(fidres, trim(grpname) // "moments", partslist(i)%moments) + end DO END IF ! ! 2.3 main specie quantities IF(modulo(step,itparts).eq. 0 .or. nlend) THEN !PRINT*, 'write particles to file_____________________' CALL append(fidres, "/data/part/time", time) CALL append(fidres, "/data/part/Nparts", REAL(partslist(1)%Nptot,kind=db)) + !CALL append(fidres, "/data/part/nbchange", REAL((/partslist(1)%nbadded,partslist(1)%nblost,partslist(1)%nbcolls/),kind=db)) + IF ( isdataset(fidres,'/data/part/R') ) THEN CALL getdims(fidres, '/data/part/R', partsrank, partsdim) partsdim(1)=min(size(partslist(1)%R,1), partsdim(1)) CALL append(fidres, "/data/part/R", partslist(1)%R(1:partsdim(1))*rnorm) CALL append(fidres, "/data/part/Z", partslist(1)%Z(1:partsdim(1))*rnorm) CALL append(fidres, "/data/part/THET", partslist(1)%THET(1:partsdim(1))) - CALL append(fidres, "/data/part/UZ", partslist(1)%UZ(1:partsdim(1))/partslist(1)%gamma(1:partsdim(1))) - CALL append(fidres, "/data/part/UR", partslist(1)%UR(1:partsdim(1))/partslist(1)%gamma(1:partsdim(1))) - CALL append(fidres, "/data/part/UTHET", partslist(1)%UTHET(1:partsdim(1))/partslist(1)%gamma(1:partsdim(1))) + CALL append(fidres, "/data/part/UZ", 0.5*(partslist(1)%UZ(1:partsdim(1))/partslist(1)%gamma(1:partsdim(1))+partslist(1)%UZold(1:partsdim(1))/partslist(1)%gammaold(1:partsdim(1)))) + CALL append(fidres, "/data/part/UR", 0.5*(partslist(1)%UR(1:partsdim(1))/partslist(1)%gamma(1:partsdim(1))+partslist(1)%URold(1:partsdim(1))/partslist(1)%gammaold(1:partsdim(1)))) + CALL append(fidres, "/data/part/UTHET", 0.5*(partslist(1)%UTHET(1:partsdim(1))/partslist(1)%gamma(1:partsdim(1))+partslist(1)%UTHETold(1:partsdim(1))/partslist(1)%gammaold(1:partsdim(1)))) CALL append(fidres, "/data/part/pot", partslist(1)%pot(1:partsdim(1))*phinorm) CALL append(fidres, "/data/part/Rindex", REAL(partslist(1)%Rindex(1:partsdim(1)),kind=db)) CALL append(fidres, "/data/part/Zindex", REAL(partslist(1)%Zindex(1:partsdim(1)),kind=db)) CALL append(fidres, "/data/part/partindex", REAL(partslist(1)%partindex(1:partsdim(1)),kind=db)) END IF END IF ! ! 2.4 Tracer quantities IF(modulo(step,ittracer).eq. 0 .or. nlend) THEN !PRINT*, 'write particles to file_____________________' DO i=2,nbspecies IF ( .not. partslist(i)%is_test) CYCLE WRITE(grpname,'(a,i2,a)')'/data/part/',i,'/' CALL append(fidres, trim(grpname) // "time", time) CALL append(fidres, trim(grpname) //"Nparts", REAL(partslist(i)%Nptot,kind=db)) + !CALL append(fidres, trim(grpname) //"nbchange", REAL((/partslist(i)%nbadded,partslist(i)%nblost,partslist(i)%nbcolls/),kind=db)) IF ( isdataset(fidres,trim(grpname)//'R') ) THEN CALL getdims(fidres, trim(grpname) // 'R', partsrank, partsdim) partsdim(1)=min(size(partslist(i)%R,1), partsdim(1)) CALL append(fidres, trim(grpname) // "R", partslist(i)%R(1:partsdim(1))*rnorm) CALL append(fidres, trim(grpname) // "Z", partslist(i)%Z(1:partsdim(1))*rnorm) CALL append(fidres, trim(grpname) // "THET", partslist(i)%THET(1:partsdim(1))) - CALL append(fidres, trim(grpname) // "UZ", partslist(i)%UZ(1:partsdim(1))/partslist(i)%gamma(1:partsdim(1))) - CALL append(fidres, trim(grpname) // "UR", partslist(i)%UR(1:partsdim(1))/partslist(i)%gamma(1:partsdim(1))) - CALL append(fidres, trim(grpname) // "UTHET", partslist(i)%UTHET(1:partsdim(1))/partslist(i)%gamma(1:partsdim(1))) + CALL append(fidres, trim(grpname) // "UZ", 0.5*(partslist(i)%UZ(1:partsdim(1))/partslist(i)%gamma(1:partsdim(1)) + partslist(i)%UZold(1:partsdim(1))/partslist(i)%gammaold(1:partsdim(1)))) + CALL append(fidres, trim(grpname) // "UR", 0.5*(partslist(i)%UR(1:partsdim(1))/partslist(i)%gamma(1:partsdim(1)) + partslist(i)%URold(1:partsdim(1))/partslist(i)%gammaold(1:partsdim(1)))) + CALL append(fidres, trim(grpname) // "UTHET", 0.5*(partslist(i)%UTHET(1:partsdim(1))/partslist(i)%gamma(1:partsdim(1)) + partslist(i)%UTHETold(1:partsdim(1))/partslist(i)%gammaold(1:partsdim(1)))) CALL append(fidres, trim(grpname) // "pot", partslist(i)%pot(1:partsdim(1))*phinorm) CALL append(fidres, trim(grpname) // "Rindex", REAL(partslist(i)%Rindex(1:partsdim(1)),kind=db)) CALL append(fidres, trim(grpname) // "Zindex", REAL(partslist(i)%Zindex(1:partsdim(1)),kind=db)) CALL append(fidres, trim(grpname) // "partindex", REAL(partslist(i)%partindex(1:partsdim(1)),kind=db)) END IF END DO ! END IF ! 2.5 3d profiles ! ! ! 2.6 Xgrafix ! #if USE_X == 1 IF(nlxg .AND. modulo(kstep,itgraph) .eq. 0) THEN call xgevent CALL updt_xg_var CALL xgupdate END IF #endif !________________________________________________________________________________ ! 3. Final diagnostics ELSE ! Only process 0 should save on file IF(mpirank .ne. 0) RETURN ! ! Flush 0d history array buffers CALL htable_hdf5_flush(hbuf0) ! ! Close all diagnostic files CALL closef(fidres) !________________________________________________________________________________ END IF ! + +CONTAINS + + SUBROUTINE create_parts_group(p,grpname, time) + USE beam,ONLY: particles + type(particles):: p + real(kind=db):: time + character(len=*):: grpname + If(isgroup(fidres, trim(grpname))) return + CALL creatg(fidres, grpname, "specific specie phase space") + CALL creatd(fidres, 0, SHAPE(time), trim(grpname) // "/time", "time") + CALL creatd(fidres, 0, SHAPE(time), trim(grpname) //"/Nparts", "number of remaining parts") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/R", "radial pos") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/Z", "axial pos") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/THET", "azimuthal pos") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/UZ", "axial beta*gamma") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/UR", "radial beta*gamma") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/UTHET", "azimuthal beta*gamma") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/pot", "electric potential") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/Rindex", "radial grid index") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/Zindex", "axial grid index") + CALL creatd(fidres, 1, SHAPE(p%R), trim(grpname) // "/partindex", "particle index") + CALL creatd(fidres, 1, (/7/), trim(grpname) // "nbchange", "number of added parts, lost parts zm,zp,rm,rp, and collisions per type io, ela") + CALL attach(fidres,trim(grpname), "q", p%q) + CALL attach(fidres,trim(grpname), "m", p%m) + CALL attach(fidres,trim(grpname), "weight", p%weight) + CALL creatd(fidres, 2, (/nbmoments,nrank(1)*nrank(2)/), trim(grpname) // "/moments", "moments",compress=.true.,chunking=(/1,nrank(1)*nrank(2)/)) + END SUBROUTINE create_parts_group + END SUBROUTINE diagnose diff --git a/src/distrib_mod.f90 b/src/distrib_mod.f90 index 66dd37f..2993ddf 100644 --- a/src/distrib_mod.f90 +++ b/src/distrib_mod.f90 @@ -1,268 +1,315 @@ !------------------------------------------------------------------------------ ! EPFL/Swiss Plasma Center !------------------------------------------------------------------------------ ! ! MODULE: distrib ! !> @author !> Guillaume Le Bars EPFL/SPC !> Trach Minh Tran EPFL/SPC ! ! DESCRIPTION: !> Contains the routines used to load several type of distribution functions !------------------------------------------------------------------------------ MODULE distrib USE constants + USE omp_lib + USE random ! IMPLICIT NONE contains !--------------------------------------------------------------------------- !> @author !> Trach Minh Tran EPFL/SPC ! ! DESCRIPTION: !> !> @brief Load a uniform distribution using the Hammersley's sequence (0<=y<=1). !> (nbase = 0 => Random sampling) ! !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) !> @param [out] y array containing the random variables. !--------------------------------------------------------------------------- SUBROUTINE loduni(nbase, y) ! ! Load a uniform distribution using the Hammersley's sequence. ! (nbase = 0 => Random sampling) ! INTEGER, INTENT(in) :: nbase REAL(kind=db), INTENT(inout) :: y(:) - INTEGER :: n, i + INTEGER :: n, i, ompthread ! n = SIZE(y) ! SELECT CASE (nbase) CASE(0) - CALL RANDOM_NUMBER(y) + ompthread=omp_get_thread_num()+1 + CALL random_array(y,n,ran_index(ompthread),ran_array(:,ompthread)) + !CALL RANDOM_NUMBER(y) CASE(1) DO i=1,n y(i) = (i-0.5_db)/n END DO CASE(2:) DO i=1,n y(i) = rev(nbase,i) END DO CASE default WRITE(*,'(a,i5)') 'Invalid value of NBASE =', nbase END SELECT ! END SUBROUTINE loduni !--------------------------------------------------------------------------- !> @author !> Trach Minh Tran EPFL/SPC ! ! DESCRIPTION: !> !> @brief Load a gaussian distribution using a uniform distribution according to the Hammersley's sequence. !> (nbase = 0 => Random sampling) ! !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) !> @param [out] y array containing the random variables. !--------------------------------------------------------------------------- SUBROUTINE lodgaus(nbase, y) ! ! Sample y from the Gauss distributrion ! REAL(kind=db), INTENT(inout) :: y(:) INTEGER, INTENT(in) :: nbase ! INTEGER :: n, i, iflag REAL(kind=db) :: r(SIZE(y)) REAL(kind=db) :: t ! REAL(kind=db) :: c0, c1, c2 REAL(kind=db) :: d1, d2, d3 DATA c0, c1, c2/2.515517_db, 0.802853_db, 0.010328_db/ DATA d1, d2, d3/1.432788_db, 0.189269_db, 0.001308_db/ ! n = SIZE(y) CALL loduni(nbase, r) r = 1.0E-5 + 0.99998*r ! DO i=1,n iflag = 1 IF (r(i) .GT. 0.5) THEN r(i) = 1.0 - r(i) iflag = -1 END IF t = SQRT(LOG(1.0/(r(i)*r(i)))) y(i) = t - (c0+c1*t+c2*t**2) / (1.0+d1*t+d2*t**2+d3*t**3) y(i) = y(i) * iflag END DO y = y - SUM(y)/REAL(n) END SUBROUTINE lodgaus !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Load the array y according to a probability distribution function proportional to 1/r !> between ra and rb ! !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) !> @param [in] ra Lower limit for the values in y !> @param [in] rb Upper limit for the values in y !> @param [out] y array containing the random variables. !--------------------------------------------------------------------------- SUBROUTINE lodinvr(nbase, y, ra, rb) ! ! REAL(kind=db), INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase REAL(kind=db), INTENT(in) :: rb, ra ! REAL(kind=db) :: r(SIZE(y)) ! CALL loduni(nbase, r) r=r*log(rb/ra) y=ra*exp(r) END SUBROUTINE lodinvr !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Load the array y according to a probability distribution function proportional to r !> between ra and rb ! !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) !> @param [in] ra Lower limit for the values in y !> @param [in] rb Upper limit for the values in y !> @param [out] y array containing the random variables. !--------------------------------------------------------------------------- SUBROUTINE lodlinr(nbase, y, ra, rb) REAL(kind=db), INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase REAL(kind=db), INTENT(in) :: rb, ra ! REAL(kind=db) :: r(SIZE(y)) ! CALL loduni(nbase, r) y=sqrt(r*(rb**2-ra**2)+ra**2) END SUBROUTINE lodlinr !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Load the array y according to a uniform probability distribution function !> between ra and rb ! !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) !> @param [in] ra Lower limit for the values in y !> @param [in] rb Upper limit for the values in y !> @param [out] y array containing the random variables. !--------------------------------------------------------------------------- SUBROUTINE lodunir(nbase, y, ra, rb) REAL(kind=db), INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase REAL(kind=db), INTENT(in) :: rb, ra ! REAL(kind=db) :: r(SIZE(y)) ! CALL loduni(nbase, r) y=ra+(rb-ra)*r END SUBROUTINE lodunir !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief Load the array y according to a gaussian probability distribution function !> around the mean of rb and ra and a sigma of (rb-ra)/10 ! !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) !> @param [in] ra Lower limit for the values in y !> @param [in] rb Upper limit for the values in y !> @param [out] y array containing the random variables. !--------------------------------------------------------------------------- SUBROUTINE lodgausr(nbase, y, ra, rb) REAL(kind=db), INTENT(out) :: y(:) INTEGER, INTENT(in) :: nbase REAL(kind=db), INTENT(in) :: rb, ra ! REAL(kind=db) :: r(SIZE(y)) ! CALL lodgaus(nbase, r) y=0.5*(rb+ra)+ 0.1*(rb-ra)*r END SUBROUTINE lodgausr !--------------------------------------------------------------------------- !> @author !> Trach Minh Tran EPFL/SPC ! ! DESCRIPTION: !> !> @brief Return an element of the Hammersley's sequence ! !> @param [in] nbase Base of the Hammersley's sequence. (0 => Random) !> @param [in] i Index of the sequence. !> @param [out] rev Returned element of the Hammersley's sequence !--------------------------------------------------------------------------- REAL(kind=db) FUNCTION rev(nbase,i) ! INTEGER, INTENT(IN) :: nbase ! Base of the Hammersley's sequence (IN) INTEGER, INTENT(IN) :: i ! Index of the sequence (IN) ! ! Local vars INTEGER :: j1, j2 REAL(kind=db) :: xs, xsi !----------------------------------------------------------------------- xs = 0.0 xsi = 1.0 j2 = i DO xsi = xsi/nbase j1 = j2/nbase xs = xs + (j2-nbase*j1)*xsi j2 = j1 IF( j2.LE.0 ) EXIT END DO rev = xs END FUNCTION rev !--------------------------------------------------------------------------- !> @author !> Trach Minh Tran EPFL/SPC ! ! DESCRIPTION: !> !> @brief Modified Bessel functions of the first kind of the first order 1 ! !--------------------------------------------------------------------------- FUNCTION bessi1(x) REAL(kind=db) :: bessi1,x REAL(kind=db) :: ax REAL(kind=db) p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9,y SAVE p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9 DATA p1,p2,p3,p4,p5,p6,p7/0.5d0,0.87890594d0,0.51498869d0,0.15084934d0,0.2658733d-1,0.301532d-2,0.32411d-3/ DATA q1,q2,q3,q4,q5,q6,q7,q8,q9 /0.39894228d0, -0.3988024d-1, -0.362018d-2, 0.163801d-2,-0.1031555d-1, & & 0.2282967d-1, -0.2895312d-1, 0.1787654d-1,-0.420059d-2/ if (abs(x).lt.3.75) then y=(x/3.75)**2 bessi1=x*(p1+y*(p2+y*(p3+y*(p4+y*(p5+y*(p6+y*p7)))))) else ax=abs(x) y=3.75/ax bessi1=(exp(ax)/sqrt(ax))*(q1+y*(q2+y*(q3+y*(q4+y*(q5+y*(q6+y*(q7+y*(q8+y*q9)))))))) if(x.lt.0.)bessi1=-bessi1 endif return END FUNCTION bessi1 + + ! find the index for the value in array closest but inferior to val + function closest(array, val, siz) + real(kind=db):: array(1:), val + integer:: closest + integer:: siz + integer:: i, j, mid + + ! Corner cases + if (val.lt.array(1)) then + closest=-1 + RETURN + end if + if (val .ge. array(siz)) then + closest=siz + return + end if + + !Start binary search + i=1 + j=siz + mid=1 + do while (i .lt. j) + mid = (i + j) / 2 + + if (val .ge. array(mid) .and. val .lt. array(mid+1)) then + closest = mid + return + end if + + ! if val > array(mid) search in right + if (val .gt. array(mid)) then + ! update i + i = mid+1 + else ! if val < array(mid) search in left + ! update j + j = mid + end if + end do + + closest=-1 ! not found + end function closest + END MODULE distrib \ No newline at end of file diff --git a/src/endrun.f90 b/src/endrun.f90 index 9d8bfa5..9f81966 100644 --- a/src/endrun.f90 +++ b/src/endrun.f90 @@ -1,38 +1,53 @@ SUBROUTINE endrun ! ! Terminate the run ! USE basic USE beam USE fields IMPLICIT NONE + INTEGER:: i ! ! Local vars and arrays ! !________________________________________________________________________________ ! IF( nlend ) THEN ! !---------------------------------------------------------------------- ! 1. Normal end of run ! WRITE(*,'(/a)') ' Terminate the run' +! Prepare the particles for writing the restart file +! in case of mpi parallelism, gather the particles on the host + DO i=1,nbspecies + call bound(partslist(i)) + call boundary_loss(partslist(i)) + partslist(i)%collected=.false. + IF(mpisize .gt. 1) THEN + call collectparts(partslist(i)) + else + partslist(i)%Nptot=partslist(i)%Nploc + end if + END DO ! !---------------------------------------------------------------------- ! 2. Abnormal exit ! ELSE WRITE(*,'(/a)') ' Abnormal exit' END IF ! !---------------------------------------------------------------------- ! 9. Epilogue ! ! Create restart file - CALL chkrst(1) - CALL clean_beam + IF(mpirank .eq. 0) then + CALL chkrst(1) + end if + CALL clean_beam(partslist) CALL clean_fields ! ! Closing all files CLOSE(lu_in) END SUBROUTINE endrun diff --git a/src/fields_mod.f90 b/src/fields_mod.f90 index c2a9b4e..6c11614 100644 --- a/src/fields_mod.f90 +++ b/src/fields_mod.f90 @@ -1,820 +1,1317 @@ -!------------------------------------------------------------------------------ -! EPFL/Swiss Plasma Center -!------------------------------------------------------------------------------ -! -! MODULE: beam -! -!> @author -!> Patryk Kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> Module responsible for initializing the magnetic field and solve the Poisson equation -!------------------------------------------------------------------------------ -MODULE fields -USE constants -USE basic, ONLY: nr, nz, zgrid, rgrid, Br, Bz, Er, Ez, femorder, ngauss, nlppform, pot, Athet, & -& splrz, nlperiod, phinorm, nlPhis, nrank, mpirank, mpisize, step, it2d -USE beam, ONLY: partslist -USE bsplines -USE mumps_bsplines -USE mpi -Use omp_lib -Use mpihelper, ONLY: db_type -IMPLICIT NONE - -REAL(kind=db), allocatable, SAVE :: matcoef(:,:), phi_spline(:), vec1(:), vec2(:) -REAL(kind=db), allocatable, SAVE :: loc_moments(:,:) -INTEGER:: nbmoments = 10 -INTEGER, SAVE:: loc_zspan -TYPE(mumps_mat), SAVE :: femat !< Finite Element Method matrix -REAL(kind=db), SAVE:: potextA, potextB !< Variables used for fast calculation of the external potential - - -CONTAINS - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Set-up the necessary variables for solving Poisson and computes the magnetic field on the grid -! -!--------------------------------------------------------------------------- -SUBROUTINE fields_init - USE basic, ONLY: pot, nlperiod, nrank, rhs, moments, Zbounds, volume, potinn, potout, rgrid - USE bsplines - USE mumps_bsplines - USE mpihelper - INTEGER :: nrz(2) - - ! Set up 2d spline splrz used in the FEM - CALL set_spline(femorder, ngauss, zgrid, rgrid, splrz, nlppform=nlppform, period=nlperiod) - - ! Calculate dimension of splines - nrz(1)=nz - nrz(2)=nr - CALL get_dim(splrz, nrank, nrz, femorder) - - ALLOCATE(matcoef(nrank(1),nrank(2))) - ALLOCATE(pot((nr+1)*(nz+1))) - ALLOCATE(rhs(nrank(1)*nrank(2))) - IF (mpirank .eq. 0) THEN - ALLOCATE(moments(nbmoments,nrank(1)*nrank(2))) - ELSE - ALLOCATE(moments(0,0)) - END IF - ALLOCATE(phi_spline(nrank(1)*nrank(2))) - ALLOCATE(volume(nrank(1)*nrank(2))) - - ALLOCATE(Br((nr+1)*(nz+1)),Bz((nr+1)*(nz+1))) - ALLOCATE(Athet((nr+1)*(nz+1))) - ALLOCATE(Er((nr+1)*(nz+1)),Ez((nr+1)*(nz+1))) - ! Calculate magnetic field mirror components in grid points (Davidson analytical formula employed) - CALL magnet - - ! Calculate and factorise FEM matrix (depends only on mesh) - CALL fematrix - CALL factor(femat) - CALL comp_volume - - IF(rgrid(0) .ne. 0) THEN - potextA= (potout-potinn)/log(rgrid(nr)/rgrid(0)) - potextB= (potinn*log(rgrid(nr))-potout*log(rgrid(0)))/log(rgrid(nr)/rgrid(0)) - ELSE - potextA=0 - potextB=potout - END IF - END SUBROUTINE fields_init - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Set-up the necessary variables for the communication of moments grid -! -!--------------------------------------------------------------------------- -SUBROUTINE fields_comm_init - USE basic, ONLY: pot, nlperiod, nrank, rhs, moments, Zbounds, volume, potinn, potout, rgrid - USE bsplines - USE mumps_bsplines - USE mpihelper - loc_zspan=Zbounds(mpirank+1)-Zbounds(mpirank)+femorder(1) - ALLOCATE(loc_moments(10,loc_zspan*nrank(2))) - IF(mpisize .gt. 1) THEN - CALL init_overlaps(nrank, femorder, Zbounds(mpirank), Zbounds(mpirank+1), nbmoments) - END IF - - -END SUBROUTINE fields_comm_init - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Construct the right hand side vector used in the FEM Poisson solver -! -!> @param[in] p the particles type storing the desired specie parameters -! -!--------------------------------------------------------------------------- -SUBROUTINE rhscon(p) - USE bsplines - USE mumps_bsplines - USE mpi - USE basic, ONLY: rnorm, potinn, potout, nrank, rhs, moments, nlend, step, it2d - USE beam, ONLY: particles - USE mpihelper - type(particles), INTENT(INOUT):: p - REAL(kind=db) :: chargecoeff - - IF(p%is_test) RETURN - - loc_moments=0 ! Reset the moments matrix - rhs=0 ! reset rhs - moments=0 ! reset moments - chargecoeff=p%q/(2*pi*eps_0*phinorm*rnorm) ! Normalized charge density simulated by each macro particle - - ! TODO: Separate charge deposition for each specie and avoid destruction of data with multiple non test species - -! Assemble rhs - IF (p%Nploc .ne. 0) THEN - IF(modulo(step,it2d).eq. 0 .or. nlend) THEN - CALL deposit_moments(p, loc_moments) - ELSE - CALL deposit_charge(p, loc_moments) - END IF - END IF - - ! If we are using MPI parallelism, reduce the rhs on the root process - IF(mpisize .gt. 1) THEN - CALL fields_comm(moments) - ELSE - moments=loc_moments - END IF - - IF(mpirank.eq.0 )THEN - IF(nlphis) THEN - ! We consider self-consistent interactions - rhs=moments(1,:)*chargecoeff ! Normalized charge density simulated by each macro particle - ELSE - rhs=0 - END IF - END IF - - ! Apply the Dirichlet boundary conditions on the coaxial insert (if present) - IF(rgrid(0).ne.0.0_db) rhs(1:nrank(1))=potinn - ! Apply the Dirichlet boundary conditions on the cylinder walls - rhs((nrank(2)-1)*nrank(1)+1:nrank(2)*nrank(1))=potout - - END SUBROUTINE rhscon - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Deposit the particles moments (q,v,v^2) from p on the grid -! -!> @param[in] p the particles type storing the desired specie parameters -!> @param[in] p_loc_moments local tensor used to store the moments of the given specie -!--------------------------------------------------------------------------- - - SUBROUTINE deposit_moments(p, p_loc_moments) - USE bsplines - USE mumps_bsplines - USE mpi - USE basic, ONLY: Zbounds - USE beam, ONLY: particles - USE mpihelper - TYPE(particles), INTENT(IN):: p - REAL(kind=db), DIMENSION(:,:), INTENT(INOUT):: p_loc_moments - INTEGER ::irow, jcol, it, jw, mu, i, k, iend, nbunch - INTEGER,DIMENSION(:),ALLOCATABLE::zleft, rleft - REAL(kind=db) :: vr, vthet, vz, coeff - REAL(kind=db), ALLOCATABLE :: fun(:,:,:), fun2(:,:,:) - INTEGER:: num_threads - - num_threads=omp_get_max_threads() - nbunch=p%Nploc/num_threads ! Particle bunch size used when calling basfun - nbunch=max(nbunch,1) ! Particle bunch size used when calling basfun - nbunch=min(nbunch,64) ! Particle bunch size used when calling basfun - - ALLOCATE(zleft(nbunch),rleft(nbunch)) - ALLOCATE(fun(1:femorder(1)+1,0:0,nbunch),fun2(1:femorder(2)+1,0:0,nbunch)) ! Arrays keeping values of b-splines at gauss node - -! Assemble rhs - IF (p%Nploc .ne. 0) THEN -!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(zleft, rleft, jw, it, iend, irow, jcol, mu, k, vr, vz, vthet, coeff, fun, fun2) - DO i=1,p%Nploc,nbunch - ! Avoid segmentation fault by accessing non relevant data - iend=min(i+nbunch-1,p%Nploc) - k=iend-i+1 - ! Localize the particle - !CALL locintv(splrz%sp2, p%R(i:iend), rleft(1:k)) - !CALL locintv(splrz%sp1, p%Z(i:iend), zleft(1:k)) - rleft(1:k)=p%rindex(i:iend) - zleft(1:k)=p%zindex(i:iend) - ! Compute the value of the splines at the particles positions - CALL basfun(p%Z(i:iend), splrz%sp1, fun(:,:,1:k), zleft(1:k)+1) - CALL basfun(p%R(i:iend), splrz%sp2, fun2(:,:,1:k), rleft(1:k)+1) - DO k=1,(iend-i+1) - DO jw=1,(femorder(2)+1) - DO it=1,(femorder(1)+1) - irow=zleft(k)+it-Zbounds(mpirank) - jcol=rleft(k)+jw - mu=irow+(jcol-1)*(loc_zspan) - coeff=p%weight*fun(it,0,k)*fun2(jw,0,k) - ! Add contribution of particle nbunch to rhs grid point mu - vr=p%UR(i+k-1)/p%Gamma(i+k-1) - vz=p%UZ(i+k-1)/p%Gamma(i+k-1) - vthet=p%UTHET(i+k-1)/p%Gamma(i+k-1) - !$OMP ATOMIC UPDATE - p_loc_moments(1, mu) = p_loc_moments(1, mu) + coeff - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(2, mu) = p_loc_moments(2, mu) + coeff*vr - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(3, mu) = p_loc_moments(3, mu) + coeff*vthet - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(4, mu) = p_loc_moments(4, mu) + coeff*vz - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(5, mu) = p_loc_moments(5, mu) + coeff*vr*vr - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(6, mu) = p_loc_moments(6, mu) + coeff*vr*vthet - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(7, mu) = p_loc_moments(7, mu) + coeff*vr*vz - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(8, mu) = p_loc_moments(8, mu) + coeff*vthet*vthet - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(9, mu) = p_loc_moments(9, mu) + coeff*vthet*vz - !$OMP END ATOMIC - !$OMP ATOMIC UPDATE - p_loc_moments(10,mu) = p_loc_moments(10,mu) + coeff*vz*vz - !$OMP END ATOMIC - END DO - END DO - END DO - END DO -!$OMP END PARALLEL DO - END IF - DEALLOCATE(fun,fun2, zleft, rleft) - - END subroutine deposit_moments - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Deposit the particles charges (q) from p on the grid -! -!> @param[in] p the particles type storing the desired specie parameters -!> @param[in] p_loc_moments local tensor used to store the moments of the given specie -!--------------------------------------------------------------------------- - - - SUBROUTINE deposit_charge(p, p_loc_moments) - USE bsplines - USE mumps_bsplines - USE mpi - USE basic, ONLY: Zbounds - USE beam, ONLY: particles - USE mpihelper - TYPE(particles), INTENT(IN):: p - REAL(kind=db), DIMENSION(:,:), INTENT(INOUT):: p_loc_moments - INTEGER ::irow, jcol, it, jw, mu, i, k, iend, nbunch - INTEGER,DIMENSION(:),ALLOCATABLE::zleft, rleft - REAL(kind=db), ALLOCATABLE :: fun(:,:,:), fun2(:,:,:) - INTEGER:: num_threads - - num_threads=omp_get_max_threads() - nbunch=p%Nploc/num_threads ! Particle bunch size used when calling basfun - nbunch=max(nbunch,1) ! Particle bunch size used when calling basfun - nbunch=min(nbunch,64) ! Particle bunch size used when calling basfun - ALLOCATE(zleft(nbunch),rleft(nbunch)) - ALLOCATE(fun(1:femorder(1)+1,0:0,nbunch),fun2(1:femorder(2)+1,0:0,nbunch)) ! Arrays keeping values of b-splines at gauss node - -! Assemble rhs - IF (p%Nploc .ne. 0) THEN -!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(zleft, rleft, jw, it, iend, irow, jcol, mu, k, fun, fun2) - DO i=1,p%Nploc,nbunch - ! Avoid segmentation fault by accessing non relevant data - iend=min(i+nbunch-1,p%Nploc) - k=iend-i+1 - ! Localize the particle - !CALL locintv(splrz%sp2, p%R(i:iend), rleft(1:k)) - !CALL locintv(splrz%sp1, p%Z(i:iend), zleft(1:k)) - rleft(1:k)=p%rindex(i:iend) - zleft(1:k)=p%zindex(i:iend) - - ! Compute the value of the splines at the particles positions - CALL basfun(p%Z(i:iend), splrz%sp1, fun, zleft(1:k)+1) - CALL basfun(p%R(i:iend), splrz%sp2, fun2, rleft(1:k)+1) - DO k=1,(iend-i+1) - DO jw=1,(femorder(2)+1) - DO it=1,(femorder(1)+1) - irow=zleft(k)+it-Zbounds(mpirank) - jcol=rleft(k)+jw - mu=irow+(jcol-1)*(loc_zspan) - ! Add contribution of particle nbunch to rhs grid point mu - !$OMP ATOMIC update - p_loc_moments(1, mu) = p_loc_moments(1, mu) + p%weight*fun(it,0,k)*fun2(jw,0,k) - !$OMP END ATOMIC - END DO - END DO - END DO - END DO -!$OMP END PARALLEL DO - END IF - - DEALLOCATE(fun,fun2, zleft, rleft) - - END subroutine deposit_charge - -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Do the communication of the local moment matrices between mpi workers and reduce the result on the host -! -!--------------------------------------------------------------------------- - -SUBROUTINE fields_comm(moments) -USE mpihelper -USE Basic, ONLY: Zbounds, mpirank, nlend, it2d, step, leftproc, rightproc -REAL(kind=db), DIMENSION(:,:), INTENT(INOUT):: moments -INTEGER:: ierr, i, j -INTEGER:: displs(mpisize), counts(mpisize) -INTEGER:: overlap_type -INTEGER:: rcvoverlap_type - - - displs=Zbounds(0:mpisize-1) - counts=Zbounds(1:mpisize)-Zbounds(0:mpisize-1) - counts(mpisize)=counts(mpisize)+femorder(1) - - IF(modulo(step,it2d).eq. 0 .or. nlend) THEN - CALL start_momentscomm(mpirank, leftproc, rightproc, loc_moments, nrank, femorder, loc_zspan-femorder(1)) - IF(mpirank .gt. 0) THEN - !$OMP PARALLEL DO SIMD DEFAULT(SHARED) - DO j=1,femorder(1) - DO i=1,nrank(2) - loc_moments(1:10,(i-1)*loc_zspan+j) = loc_moments(1:10,(i-1)*loc_zspan+j)& - & + momentsoverlap_buffer(10*(nrank(2)*(j-1)+i-1)+1:10*(nrank(2)*(j-1)+i)) - END DO - END DO - !$OMP END PARALLEL DO SIMD - END IF - ! Set communication vector type - overlap_type=momentsoverlap_type - rcvoverlap_type=rcvmomentsoverlap_type - ELSE - CALL start_rhscomm(mpirank, leftproc, rightproc, loc_moments, nrank, femorder, loc_zspan-femorder(1)) - IF(mpirank .gt. 0) THEN - !$OMP PARALLEL DO SIMD DEFAULT(SHARED) - DO j=1,femorder(1) - DO i=1,nrank(2) - loc_moments(1, (i-1)*loc_zspan+j) = loc_moments(1,(i-1)*loc_zspan+j)& - & + rhsoverlap_buffer(nrank(2)*(j-1)+i) - END DO - END DO - !$OMP END PARALLEL DO SIMD - END IF - ! Set communication vector type - overlap_type=rhsoverlap_type - rcvoverlap_type=rcvrhsoverlap_type - END IF - IF(mpirank.eq.0) THEN - moments=0 - END IF -#ifdef _DEBUG - WRITE(*,*)"started gatherv at step: ",step - !WRITE(*,*)"counts:", counts - !WRITE(*,*)"displs:", displs -#endif - CALL MPI_GATHERV(loc_moments(1,1), counts(mpirank+1), overlap_type, & - & moments(1,1), counts, displs, rcvoverlap_type, 0, MPI_COMM_WORLD, ierr) -#ifdef _DEBUG - !WRITE(*,*)"ended gatherv at step: ",step -#endif -END SUBROUTINE fields_comm -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Solves Poisson equation using FEM. Distributes the result on all MPI workers and interpolate the electric forces -!> for each particle. -! -!--------------------------------------------------------------------------- - SUBROUTINE poisson - USE basic, ONLY: rhs, nrank, pot - USE bsplines - USE mumps_bsplines - USE futils - INTEGER:: ierr, jder(2) - jder(1)=0 - jder(2)=0 - - ! Only the root process solves Poisson - IF(mpirank.eq.0) CALL bsolve(femat,rhs,phi_spline) - - ! Broadcast the solution to all MPI workers - CALL MPI_Bcast(phi_spline, nrank(1)*nrank(2), db_type, 0, MPI_COMM_WORLD, ierr) - matcoef= reshape(phi_spline, (/nrank(1),nrank(2)/)) - - ! Evaluate the electric potential on the grid points - CALL gridval(splrz, vec1, vec2, pot, jder, matcoef) - - IF( mpirank .eq. 0 .and. modulo(step,it2d) .eq. 0) THEN - ! On the root process, compute the electric field for diagnostic purposes - CALL gridval(splrz, vec1, vec2, Ez, (/1,0/)) - CALL gridval(splrz, vec1, vec2, Er, (/0,1/)) - Ez=-Ez - Er=-Er - END IF - - END SUBROUTINE poisson - -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Computes the electric fields and potential at the particles position -! -!> @param[in] p the particles type storing the desired specie parameters -!--------------------------------------------------------------------------- - - SUBROUTINE EForcescomp(p) - Use beam, ONLY: particles - TYPE(particles), INTENT(INOUT):: p - INTEGER:: i, iend - INTEGER:: nbunch=64 - INTEGER:: num_threads - - num_threads=omp_get_max_threads() - nbunch=p%Nploc/num_threads ! Particle bunch size used when calling basfun - nbunch=max(nbunch,1) ! Particle bunch size used when calling basfun - nbunch=min(nbunch,64) ! Particle bunch size used when calling basfun - - ! Evaluate the electric potential and field at the particles position - !$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(iend) - DO i=1,p%Nploc,nbunch - ! Avoid segmentation fault by accessing non relevant data - iend=min(i+nbunch-1,p%Nploc) - CALL getgrad(splrz, p%Z(i:iend), p%R(i:iend), p%pot(i:iend), p%EZ(i:iend), p%ER(i:iend)) - p%EZ(i:iend)=-p%Ez(i:iend) - p%ER(i:iend)=-p%ER(i:iend) - END DO - !$OMP END PARALLEL DO - IF(.not. p%is_test) THEN - !$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(iend) - DO i=1,p%Nploc,nbunch - ! Avoid segmentation fault by accessing non relevant data - iend=min(i+nbunch-1,p%Nploc) - p%pot(i:iend)=p%pot(i:iend)+ext_potential(p%R(i:iend)) - END DO - !$OMP END PARALLEL DO - END IF - - END SUBROUTINE EForcescomp - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Constucts the FEM matrix using bsplines initialized in fields_init -!--------------------------------------------------------------------------- -SUBROUTINE fematrix - - USE bsplines - USE mumps_bsplines - - REAL(kind=db), ALLOCATABLE :: xgauss(:,:), wgauss(:), zg(:), rg(:), wzg(:), wrg(:) - REAL(kind=db), ALLOCATABLE :: f(:,:), aux(:) - REAL(kind=db), ALLOCATABLE :: coefs(:) - REAL(kind=db), ALLOCATABLE :: fun(:,:), fun2(:,:) - REAL(kind=db) :: contrib - INTEGER, ALLOCATABLE :: idert(:), iderw(:) - INTEGER :: i, j, k, jt, iw, irow, jcol, mu, igauss, iterm, irow2, jcol2, mu2, kterms - kterms=2 !Number of terms in weak form - - ALLOCATE(fun(1:femorder(1)+1,0:1),fun2(1:femorder(2)+1,0:1))!Arrays keeping values of b-splines at gauss node - ALLOCATE(xgauss(ngauss(1)*ngauss(2),2), wgauss(ngauss(1)*ngauss(2)),zg(ngauss(1)),rg(ngauss(2)), wzg(ngauss(1)), wrg(ngauss(2))) !Gaussian nodes and weights arrays - ALLOCATE(f((femorder(1)+1)*(femorder(2)+1),2),aux(femorder(1)+1)) !Auxiliary arrays ordering bsplines - ALLOCATE(idert(kterms), iderw(kterms), coefs(kterms)) !Pointers on the order of derivatives - -! Constuction of auxiliary array ordering bsplines in given interval - DO i=1,(femorder(1)+1) - aux(i)=i - END DO - DO i=1,(femorder(2)+1) - f((i-1)*(femorder(1)+1)+1:i*(femorder(1)+1),1)=aux - f((i-1)*(femorder(1)+1)+1:i*(femorder(1)+1),2)=i - END DO - -! Initialisation of FEM matrix - CALL init(nrank(1)*nrank(2),2,femat) - -! Assemble FEM matrix - DO j=1,nr ! Loop on r position - DO i=1,nz ! Loop on z position - ! Computation of gauss weight and position in r and z direction for gaussian integration - CALL get_gauss(splrz%sp1, ngauss(1), i, zg, wzg) - CALL get_gauss(splrz%sp2, ngauss(2), j, rg, wrg) - ! Construction of matrix xgauss and wgauss storing the weight and position for 2d gaussian integration - DO k=1,ngauss(2) - xgauss((k-1)*ngauss(1)+1:k*ngauss(1),1)=zg - xgauss((k-1)*ngauss(1)+1:k*ngauss(1),2)=rg(k) - wgauss((k-1)*ngauss(1)+1:k*ngauss(1))=wrg(k)*wzg - END DO - DO igauss=1,ngauss(1)*ngauss(2) ! Loop on gaussian weights and positions - CALL basfun(xgauss(igauss,1), splrz%sp1, fun, i) - CALL basfun(xgauss(igauss,2), splrz%sp2, fun2, j) - CALL coefeq(xgauss(igauss,:), idert, iderw, coefs) - DO iterm=1,kterms ! Loop on the two integration dimensions - DO jt=1,(1+femorder(1))*(femorder(2)+1) - DO iw=1,(1+femorder(1))*(femorder(2)+1) - irow=i+f(jt,1)-1; jcol=j+f(jt,2)-1 - irow2=i+f(iw,1)-1; jcol2=j+f(iw,2)-1 - mu=irow+(jcol-1)*nrank(1) - mu2=irow2+(jcol2-1)*nrank(1) - contrib=fun(f(jt,1),idert(iterm))* fun(f(iw,1),idert(iterm))* & - & fun2(f(jt,2),iderw(iterm))*fun2(f(iw,2),iderw(iterm))* & - & wgauss(igauss)*xgauss(igauss,2) - CALL updtmat(femat, mu, mu2, contrib) - END DO - END DO - END DO - END DO - END DO - END DO - - -! Impose Dirichlet boundary conditions on FEM matrix - CALL fe_dirichlet - - DEALLOCATE(xgauss, wgauss,zg,rg, wzg, wrg) - DEALLOCATE(f, aux) - DEALLOCATE(idert, iderw, coefs, fun,fun2) - END SUBROUTINE fematrix - -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Computes the volume of the splines cells needed to display the density in post-processing -!--------------------------------------------------------------------------- - - SUBROUTINE comp_volume - - USE bsplines - USE mumps_bsplines - USE basic, ONLY: Volume - - REAL(kind=db), ALLOCATABLE :: xgauss(:,:), wgauss(:), zg(:), rg(:), wzg(:), wrg(:) - REAL(kind=db), ALLOCATABLE :: f(:,:), aux(:) - REAL(kind=db), ALLOCATABLE :: fun(:,:), fun2(:,:) - INTEGER :: i, j, k, jt, irow, jcol, mu, igauss - - ALLOCATE(fun(1:femorder(1)+1,0:0),fun2(1:femorder(2)+1,0:0))!Arrays keeping values of b-splines at gauss node - ALLOCATE(xgauss(ngauss(1)*ngauss(2),2), wgauss(ngauss(1)*ngauss(2)),zg(ngauss(1)),rg(ngauss(2)), wzg(ngauss(1)), wrg(ngauss(2))) !Gaussian nodes and weights arrays - ALLOCATE(f((femorder(1)+1)*(femorder(2)+1),2),aux(femorder(1)+1)) !Auxiliary arrays ordering bsplines - -! Constuction of auxiliary array ordering bsplines in given interval - DO i=1,(femorder(1)+1) - aux(i)=i - END DO - DO i=1,(femorder(2)+1) - f((i-1)*(femorder(1)+1)+1:i*(femorder(1)+1),1)=aux - f((i-1)*(femorder(1)+1)+1:i*(femorder(1)+1),2)=i - END DO - -! Assemble Volume matrix - DO j=1,nr ! Loop on r position - DO i=1,nz ! Loop on z position - ! Computation of gauss weight and position in r and z direction for gaussian integration - CALL get_gauss(splrz%sp1, ngauss(1), i, zg, wzg) - CALL get_gauss(splrz%sp2, ngauss(2), j, rg, wrg) - ! Construction of matrix xgauss and wgauss storing the weight and position for 2d gaussian integration - DO k=1,ngauss(2) - xgauss((k-1)*ngauss(1)+1:k*ngauss(1),1)=zg - xgauss((k-1)*ngauss(1)+1:k*ngauss(1),2)=rg(k) - wgauss((k-1)*ngauss(1)+1:k*ngauss(1))=wrg(k)*wzg - END DO - DO igauss=1,ngauss(1)*ngauss(2) ! Loop on gaussian weights and positions - CALL basfun(xgauss(igauss,1), splrz%sp1, fun, i) - CALL basfun(xgauss(igauss,2), splrz%sp2, fun2, j) - DO jt=1,(1+femorder(1))*(femorder(2)+1) - irow=i+f(jt,1)-1; jcol=j+f(jt,2)-1 - mu=irow+(jcol-1)*nrank(1) - volume(mu)=volume(mu)+2*pi*fun(f(jt,1),0) * fun2(f(jt,2),0)* wgauss(igauss)*xgauss(igauss,2) - END DO - END DO - END DO - END DO - - - DEALLOCATE(xgauss, wgauss,zg,rg, wzg, wrg) - DEALLOCATE(f, aux) - DEALLOCATE(fun,fun2) - - END SUBROUTINE comp_volume - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Imposes the dirichlet boundary conditions on the FEM matrix. -!--------------------------------------------------------------------------- - SUBROUTINE fe_dirichlet - REAL(kind=db), ALLOCATABLE :: arr(:) - INTEGER :: i - ALLOCATE(arr(nrank(1)*nrank(2))) - DO i=1,nrank(1)*nrank(2) - CALL getrow(femat,i,arr) - END DO - DO i=1,nrank(1) - IF(rgrid(0).ne.0.0_db) THEN - arr=0; arr(i)=1; - CALL putrow(femat,i,arr) - END IF - arr=0; arr(nrank(1)*nrank(2)+1-i)=1 ; - CALL putrow(femat,nrank(1)*nrank(2)+1-i,arr) - END DO - DEALLOCATE(arr) - END SUBROUTINE fe_dirichlet -!________________________________________________________________________________ - SUBROUTINE coefeq(x, idt, idw, c) - REAL(kind=db), INTENT(in) :: x(2) - INTEGER, INTENT(out) :: idt(:), idw(:) - REAL(kind=db), INTENT(out) :: c(SIZE(idt)) - - c(1) = x(2) - idt(1) = 0 - idw(1) = 1 - c(2) = x(2) - idt(2) = 1 - idw(2) = 0 - END SUBROUTINE coefeq - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Computes the magnetic field on the grid according to a magnetic mirror. -!--------------------------------------------------------------------------- -SUBROUTINE magnet - USE basic, ONLY: B0, Rcurv, rgrid, zgrid, width, rnorm, nr, nz, bnorm - USE constants, ONLY: Pi - REAL(kind=db) :: rg, zg,halfLz, MirrorRatio - INTEGER :: i, rindex - halfLz=(zgrid(nz)+zgrid(0))/2 - MirrorRatio=(Rcurv-1)/(Rcurv+1) - DO i=1,(nr+1)*(nz+1) - rindex=(i-1)/(nz+1) - rg=rgrid(rindex) - zg=zgrid(i-rindex*(nz+1)-1)-halfLz - Br(i)=-B0*MirrorRatio*SIN(2*pi*zg/width*rnorm)*bessi1(2*pi*rg/width*rnorm)/bnorm - Bz(i)=B0*(1-MirrorRatio*COS(2*pi*zg/width*rnorm)*bessi0(2*pi*rg/width*rnorm))/bnorm - Athet(i)=0.5*B0*(rg*rnorm - width/pi*MirrorRatio*bessi1(2*pi*rg/width*rnorm)*COS(2*pi*zg/width*rnorm)) - END DO - END SUBROUTINE magnet -!________________________________________________________________________________ -!Modified Bessel functions of the first kind of the zero order -FUNCTION bessi0(x) - REAL(kind=db) :: bessi0,x - REAL(kind=db) :: ax - REAL(kind=db) p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9,y - SAVE p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9 - DATA p1,p2,p3,p4,p5,p6,p7/1.0d0,3.5156229d0,3.0899424d0,1.2067492d0,0.2659732d0,0.360768d-1,0.45813d-2/ - DATA q1,q2,q3,q4,q5,q6,q7,q8,q9 /0.39894228d0, 0.1328592d-1, 0.225319d-2, -0.157565d-2 ,0.916281d-2, & - & -0.2057706d-1, 0.2635537d-1,-0.1647633d-1, 0.392377d-2 / - if (abs(x).lt.3.75) then - y=(x/3.75)**2 - bessi0=p1+y*(p2+y*(p3+y*(p4+y*(p5+y*(p6+y*p7))))) - else - ax=abs(x) - y=3.75/ax - bessi0=(exp(ax)/sqrt(ax))*(q1+y*(q2+y*(q3+y*(q4+y*(q5+y*(q6+y*(q7+y*(q8+y*q9)))))))) - endif - return - END FUNCTION bessi0 -!________________________________________________________________________________ -!Modified Bessel functions of the first kind of the first order - FUNCTION bessi1(x) - REAL(kind=db) :: bessi1,x - REAL(kind=db) :: ax - REAL(kind=db) p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9,y - SAVE p1,p2,p3,p4,p5,p6,p7,q1,q2,q3,q4,q5,q6,q7,q8,q9 - DATA p1,p2,p3,p4,p5,p6,p7/0.5d0,0.87890594d0,0.51498869d0,0.15084934d0,0.2658733d-1,0.301532d-2,0.32411d-3/ - DATA q1,q2,q3,q4,q5,q6,q7,q8,q9 /0.39894228d0, -0.3988024d-1, -0.362018d-2, 0.163801d-2,-0.1031555d-1, & - & 0.2282967d-1, -0.2895312d-1, 0.1787654d-1,-0.420059d-2/ - if (abs(x).lt.3.75) then - y=(x/3.75)**2 - bessi1=x*(p1+y*(p2+y*(p3+y*(p4+y*(p5+y*(p6+y*p7)))))) - else - ax=abs(x) - y=3.75/ax - bessi1=(exp(ax)/sqrt(ax))*(q1+y*(q2+y*(q3+y*(q4+y*(q5+y*(q6+y*(q7+y*(q8+y*q9)))))))) - if(x.lt.0.)bessi1=-bessi1 - endif - return - END FUNCTION bessi1 - -!--------------------------------------------------------------------------- -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Computes the value of the external electric field at position R,Z -! -!> @param[in] R radial positon -!> @param[in] Z axial position -!> @param[out] external potential at position R,Z -!--------------------------------------------------------------------------- - - ELEMENTAL FUNCTION ext_potential(R) - REAL(kind=db), INTENT(IN):: R - REAL(kind=db):: ext_potential - - ext_potential=potextA*log(R)+potextB - - END FUNCTION ext_potential - -!--------------------------------------------------------------------------- -!> @author -!> Patryk kaminski EPFL/SPC -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> -!> @brief -!> Free the memory used by the fields module -!--------------------------------------------------------------------------- - SUBROUTINE clean_fields - Use bsplines - USE basic, ONLY: rhs, moments - DEALLOCATE(matcoef) - DEALLOCATE(pot) - DEALLOCATE(rhs) - DEALLOCATE(phi_spline) - DEALLOCATE(moments) - DEALLOCATE(Br,Bz) - DEALLOCATE(Er,Ez) - DEALLOCATE(vec1,vec2) - Call DESTROY_SP(splrz) - - END SUBROUTINE clean_fields - -END MODULE fields +!------------------------------------------------------------------------------ +! EPFL/Swiss Plasma Center +!------------------------------------------------------------------------------ +! +! MODULE: beam +! +!> @author +!> Patryk Kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> Module responsible for initializing the magnetic field, solving the Poisson equation and computing the moments of the particles distribution function +!------------------------------------------------------------------------------ +MODULE fields + USE constants + USE basic, ONLY: nr, nz, zgrid, rgrid, Br, Bz, Er, Ez, femorder, ngauss, nlppform, pot, Athet, & + & splrz, splrz_ext, nlperiod, phinorm, nlPhis, nrank, mpirank, mpisize, step, it2d, timera, potxt, erxt, ezxt + USE beam, ONLY: partslist + USE bsplines + USE mumps_bsplines + use mpi + Use omp_lib + Use mpihelper, ONLY: db_type + USE particletypes + IMPLICIT NONE + + REAL(kind=db), allocatable, SAVE :: matcoef(:, :), phi_spline(:), vec1(:), vec2(:) + REAL(kind=db), allocatable, SAVE :: loc_moments(:, :), loc_rhs(:), gradgtilde(:), fverif(:) + INTEGER, SAVE:: loc_zspan + TYPE(mumps_mat), SAVE :: femat !< Finite Element Method matrix + TYPE(mumps_mat), SAVE :: reduccedmat !< Finite Element Method matrix + REAL(kind=db), SAVE:: potextA, potextB !< Variables used for fast calculation of the external potential + INTEGER :: nbmoments = 10 !< number of moments to be calculated and stored + INTEGER(kind=omp_lock_kind), Allocatable:: mu_lock(:) !< Stores the lock for fields parallelism + +CONTAINS + + SUBROUTINE mag_init + USE basic, ONLY: magnetfile, nr, nz + USE bsplines + USE mumps_bsplines + USE mpihelper + USE geometry + + ALLOCATE (Br((nr + 1)*(nz + 1)), Bz((nr + 1)*(nz + 1))) + ALLOCATE (Athet((nr + 1)*(nz + 1))) + + ! Calculate magnetic field mirror components in grid points (Davidson analytical formula employed) + ! or load it from magnetfile if present + CALL magnet(magnetfile) + + end subroutine mag_init +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Set-up the necessary variables for solving Poisson and computes the magnetic field on the grid +! +!--------------------------------------------------------------------------- + SUBROUTINE fields_init + USE basic, ONLY: pot, nlperiod, nrank, rhs, volume, rgrid + USE bsplines + USE geometry + USE mumps_bsplines + USE mpihelper + INTEGER :: nrz(2), i + + ! Set up 2d spline splrz used in the FEM + CALL set_spline(femorder, ngauss, zgrid, rgrid, splrz, nlppform=nlppform, period=nlperiod) + ! Set up 2d spline splrz_ext used in the FEM to calculate the external electric field and potential + CALL set_spline(femorder, ngauss, zgrid, rgrid, splrz_ext, nlppform=nlppform, period=nlperiod) + + ! Calculate dimension of splines + nrz(1) = nz + nrz(2) = nr + CALL get_dim(splrz, nrank, nrz, femorder) + + ! Allocate necessary variables + ALLOCATE (matcoef(nrank(1), nrank(2))) + ALLOCATE (pot((nr + 1)*(nz + 1))) + ALLOCATE (potxt((nr + 1)*(nz + 1))) + ALLOCATE (Erxt((nr + 1)*(nz + 1))) + ALLOCATE (Ezxt((nr + 1)*(nz + 1))) + ALLOCATE (rhs(nrank(1)*nrank(2))) + ALLOCATE (gradgtilde(nrank(1)*nrank(2))) + gradgtilde = 0 + ALLOCATE (phi_spline(nrank(1)*nrank(2))) + ALLOCATE (volume(nrank(1)*nrank(2))) + volume = 0 + + ALLOCATE (Er((nr + 1)*(nz + 1)), Ez((nr + 1)*(nz + 1))) + ALLOCATE (mu_lock(nrank(1)*nrank(2))) + do i = 1, nrank(1)*nrank(2) + call omp_init_lock(mu_lock(i)) + end do + + end SUBROUTINE fields_init + + SUBROUTINE fields_start + USE geometry + USE basic, ONLY: pot, nlperiod, nrank, rhs, volume, rgrid + implicit none + ! set up the geometry module for setting up non-conforming boundary conditions + call timera(0, "geom_init") + call geom_init(splrz, vec1, vec2) + call timera(1, "geom_init") + + ! Calculate and factorise FEM matrix (depends only on mesh) + CALL fematrix + + + If (walltype .lt. 0) then + allocate (fverif(nrank(1)*nrank(2))) + fverif = 0 + end if + + ! Compute the volume of the splines and gtilde for solving E using web-splines + CALL comp_volume + Call comp_gradgtilde + + if (nlweb) then + ! Calculate reduced matrix for use of web splines + call timera(0, "reduce femat") + call Reducematrix(femat, reduccedmat) + call factor(reduccedmat) + call timera(1, "reduce femat") + else + CALL factor(femat) + end if + + ! Computes the externally imposed electric field + rhs = -gradgtilde + if (walltype .lt. 0) rhs = rhs + fverif + call poisson(splrz_ext) + rhs = 0 + potxt = pot + erxt = Er + Ezxt = Ez + + END SUBROUTINE fields_start + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Set-up the necessary variables for the communication of moments and rhs grid +! +!--------------------------------------------------------------------------- + SUBROUTINE fields_comm_init(Zbounds) + USE basic, ONLY: nrank + USE mpihelper + INTEGER:: Zbounds(0:) + loc_zspan = Zbounds(mpirank + 1) - Zbounds(mpirank) + femorder(1) + if (allocated(loc_moments)) deallocate (loc_moments) + ALLOCATE (loc_moments(nbmoments, loc_zspan*nrank(2))) + if (allocated(loc_rhs)) deallocate (loc_rhs) + ALLOCATE (loc_rhs(loc_zspan*nrank(2))) + IF (mpisize .gt. 1) THEN + CALL init_overlaps(nrank, femorder, Zbounds(mpirank), Zbounds(mpirank + 1), nbmoments) + END IF + + END SUBROUTINE fields_comm_init + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Construct the right hand side vector used in the FEM Poisson solver +! +!> @param[in] plist list of the particles type storing the desired specie parameters +! +!--------------------------------------------------------------------------- + SUBROUTINE rhscon(plist) + USE bsplines + use mpi + USE basic, ONLY: rnorm, rhs, nlend, step, it2d + USE beam, ONLY: particles + USE mpihelper + Use geometry + type(particles), INTENT(INOUT):: plist(:) + INTEGER:: i + + IF (nlphis) then ! We calculate the self-consistent field + loc_rhs = 0 ! Reset the moments matrix + +! Assemble rhs for each specie + Do i = 1, size(plist, 1) + if (plist(i)%is_field) CALL deposit_charge(plist(i), loc_rhs) + END Do + + ! If we are using MPI parallelism, reduce the rhs on the root process + IF (mpisize .gt. 1) THEN + CALL rhs_gather(rhs) + ELSE + rhs = loc_rhs + END IF + ELSE ! We only consider the externally imposed field + rhs = 0 + END IF + + IF (mpirank .eq. 0) THEN + rhs = rhs - gradgtilde + if (walltype .lt. 0) rhs = rhs + fverif + END IF + + END SUBROUTINE rhscon + + !--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Calculate the 0th 1st and 2nd order moments of the particle p and stores it in moment +! +!> @param[in] p the particles type storing the desired specie parameters +!> @param[out] moment the 2d array storing the calculated moments +! +!--------------------------------------------------------------------------- + SUBROUTINE momentsdiag(p) + USE bsplines + use mpi + USE basic, ONLY: rnorm, nlend, step, it2d + USE beam, ONLY: particles + USE mpihelper + Use geometry + type(particles), INTENT(INOUT):: p + !REAL(kind=db), INTENT(INOUT):: moment(:, :) + + loc_moments = 0 ! Reset the moments matrix +! Assemble rhs + IF (p%Nploc .ne. 0) THEN + CALL deposit_moments(p, loc_moments) + END IF + + if(.not. allocated(p%moments))THEN + if(mpirank.eq.0)THEN + Allocate(p%moments(nbmoments,nrank(1)*nrank(2))) + else + Allocate(p%moments(0,0)) + end if + end if +! If we are using MPI parallelism, reduce the rhs on the root process + IF (mpisize .gt. 1) THEN + CALL moments_gather(p%moments) + ELSE + p%moments = loc_moments + END IF + + END SUBROUTINE momentsdiag + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Deposit the particles moments (n,v,v^2) from p on the grid +! +!> @param[in] p the particles type storing the desired specie parameters +!> @param[in] p_loc_moments local tensor used to store the moments of the given specie +!--------------------------------------------------------------------------- + + SUBROUTINE deposit_moments(p, p_loc_moments) + USE bsplines + use mpi + USE basic, ONLY: Zbounds + USE beam, ONLY: particles + USE mpihelper + USE geometry + USE omp_lib + + TYPE(particles), INTENT(IN):: p + REAL(kind=db), DIMENSION(:, :), INTENT(INOUT):: p_loc_moments + INTEGER ::irow, jcol, it, jw, mu, i, k, iend, nbunch + INTEGER, DIMENSION(:), ALLOCATABLE::zleft, rleft + REAL(kind=db) :: vr, vthet, vz, coeff + REAL(kind=db), ALLOCATABLE :: fun(:, :, :), fun2(:, :, :), wgeom(:, :) + INTEGER:: num_threads + + num_threads = omp_get_max_threads() + nbunch = p%Nploc/num_threads ! Particle bunch size used when calling basfun + nbunch = max(nbunch, 1) ! Particle bunch size used when calling basfun + nbunch = min(nbunch, 64) ! Particle bunch size used when calling basfun + + ALLOCATE (zleft(nbunch), rleft(nbunch)) + ALLOCATE (fun(1:femorder(1) + 1, 0:0, nbunch), fun2(1:femorder(2) + 1, 0:0, nbunch)) ! Arrays keeping values of b-splines at gauss node + ALLOCATE (wgeom(nbunch, 0:0)) + +! Assemble rhs + IF (p%Nploc .ne. 0) THEN +!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(zleft, rleft, jw, it, iend, irow, jcol, mu, k, vr, vz, vthet, coeff, fun, fun2) + DO i = 1, p%Nploc, nbunch + ! Avoid segmentation fault by accessing non relevant data + iend = min(i + nbunch - 1, p%Nploc) + k = iend - i + 1 + ! Localize the particle + !CALL locintv(splrz%sp2, p%R(i:iend), rleft(1:k)) + !CALL locintv(splrz%sp1, p%Z(i:iend), zleft(1:k)) + rleft(1:k) = p%rindex(i:iend) + zleft(1:k) = p%zindex(i:iend) + ! Compute the value of the splines at the particles positions + CALL basfun(p%Z(i:iend), splrz%sp1, fun(:, :, 1:k), zleft(1:k) + 1) + CALL basfun(p%R(i:iend), splrz%sp2, fun2(:, :, 1:k), rleft(1:k) + 1) + !CALL geom_weight(p%Z(i:iend),p%R(i:iend),wgeom) + DO k = 1, (iend - i + 1) + DO jw = 1, (femorder(2) + 1) + DO it = 1, (femorder(1) + 1) + irow = zleft(k) + it - Zbounds(mpirank) + jcol = rleft(k) + jw + mu = irow + (jcol - 1)*(loc_zspan) + coeff = p%weight*fun(it, 0, k)*fun2(jw, 0, k) + ! Add contribution of particle nbunch to rhs grid point mu + vr = 0.5*(p%UR(i + k - 1)/p%Gamma(i + k - 1) + p%URold(i + k - 1)/p%Gammaold(i + k - 1)) + vz = 0.5*(p%UZ(i + k - 1)/p%Gamma(i + k - 1) + p%UZold(i + k - 1)/p%Gammaold(i + k - 1)) + vthet = 0.5*(p%UTHET(i + k - 1)/p%Gamma(i + k - 1) + p%UTHETold(i + k - 1)/p%Gammaold(i + k - 1)) + call omp_set_lock(mu_lock(mu)) + !!$OMP ATOMIC UPDATE + p_loc_moments(1, mu) = p_loc_moments(1, mu) + coeff + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(2, mu) = p_loc_moments(2, mu) + coeff*vr + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(3, mu) = p_loc_moments(3, mu) + coeff*vthet + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(4, mu) = p_loc_moments(4, mu) + coeff*vz + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(5, mu) = p_loc_moments(5, mu) + coeff*vr*vr + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(6, mu) = p_loc_moments(6, mu) + coeff*vr*vthet + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(7, mu) = p_loc_moments(7, mu) + coeff*vr*vz + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(8, mu) = p_loc_moments(8, mu) + coeff*vthet*vthet + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(9, mu) = p_loc_moments(9, mu) + coeff*vthet*vz + !!$OMP END ATOMIC + !!$OMP ATOMIC UPDATE + p_loc_moments(10, mu) = p_loc_moments(10, mu) + coeff*vz*vz + !!$OMP END ATOMIC + call omp_unset_lock(mu_lock(mu)) + END DO + END DO + END DO + END DO +!$OMP END PARALLEL DO + END IF + DEALLOCATE (fun, fun2, zleft, rleft) + + END subroutine deposit_moments + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Deposit the particles charges (q) from p on the grid +! +!> @param[in] p the particles type storing the desired specie parameters +!> @param[in] p_loc_moments local tensor used to store the moments of the given specie +!--------------------------------------------------------------------------- + + SUBROUTINE deposit_charge(p, p_loc_moments) + USE bsplines + use mpi + USE constants + USE basic, ONLY: Zbounds, rnorm, phinorm + USE beam, ONLY: particles + USE mpihelper + USE geometry + USE omp_lib + + TYPE(particles), INTENT(IN):: p + REAL(kind=db), DIMENSION(:), INTENT(INOUT):: p_loc_moments + INTEGER ::irow, jcol, it, jw, mu, i, k, iend, nbunch + INTEGER, DIMENSION(:), ALLOCATABLE::zleft, rleft + REAL(kind=db), ALLOCATABLE :: fun(:, :, :), fun2(:, :, :) + INTEGER:: num_threads + real(kind=db):: contrib, chargecoeff + + num_threads = omp_get_max_threads() + nbunch = p%Nploc/num_threads ! Particle bunch size used when calling basfun + nbunch = max(nbunch, 1) ! Particle bunch size used when calling basfun + nbunch = min(nbunch, 64) ! Particle bunch size used when calling basfun + chargecoeff = p%weight*p%q/(2*pi*eps_0*phinorm*rnorm) ! Normalized charge density simulated by each macro particle + +! Assemble rhs + IF (p%Nploc .ne. 0) THEN + !$OMP PARALLEL DEFAULT(SHARED), PRIVATE(zleft, rleft, jw, it, iend, irow, jcol, mu, k, fun, fun2, contrib) + ALLOCATE (zleft(nbunch), rleft(nbunch)) + ALLOCATE (fun(1:femorder(1) + 1, 0:0, nbunch), fun2(1:femorder(2) + 1, 0:0, nbunch)) ! Arrays keeping values of b-splines at gauss node + !$OMP DO + DO i = 1, p%Nploc, nbunch + ! Avoid segmentation fault by accessing non relevant data + iend = min(i + nbunch - 1, p%Nploc) + k = iend - i + 1 + ! Localize the particle + rleft(1:k) = p%rindex(i:iend) + zleft(1:k) = p%zindex(i:iend) + + ! Compute the value of the splines at the particles positions + CALL basfun(p%Z(i:iend), splrz%sp1, fun, zleft(1:k) + 1) + CALL basfun(p%R(i:iend), splrz%sp2, fun2, rleft(1:k) + 1) + !CALL geom_weight(p%Z(i:iend),p%R(i:iend),wgeom) + DO k = 1, (iend - i + 1) + DO jw = 1, (femorder(2) + 1) + DO it = 1, (femorder(1) + 1) + irow = zleft(k) + it - Zbounds(mpirank) + jcol = rleft(k) + jw + mu = irow + (jcol - 1)*(loc_zspan) + ! Add contribution of particle k to rhs grid point mu + contrib = fun(it, 0, k)*fun2(jw, 0, k)*p%geomweight(i + k - 1, 0)*chargecoeff + !$OMP ATOMIC UPDATE + p_loc_moments(mu) = p_loc_moments(mu) + contrib + !$OMP END ATOMIC + END DO + END DO + END DO + END DO +!$OMP END DO + DEALLOCATE (fun, fun2, zleft, rleft) +!$OMP END PARALLEL + END IF + + END subroutine deposit_charge + + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Do the communication of the local moment matrices between mpi workers for the overlap grid points +!> and reduce the result on the host +! +!--------------------------------------------------------------------------- + + SUBROUTINE rhs_gather(rhs) + USE mpihelper + USE Basic, ONLY: Zbounds, mpirank, nlend, it2d, step, leftproc, rightproc + REAL(kind=db), DIMENSION(:), INTENT(INOUT):: rhs + INTEGER:: ierr, i, j + INTEGER:: displs(mpisize), counts(mpisize) + INTEGER:: overlap_type + INTEGER:: rcvoverlap_type + + displs = Zbounds(0:mpisize - 1) + counts = Zbounds(1:mpisize) - Zbounds(0:mpisize - 1) + counts(mpisize) = counts(mpisize) + femorder(1) + CALL rhsoverlapcomm(mpirank, leftproc, rightproc, loc_rhs, nrank, femorder, loc_zspan - femorder(1)) + IF (mpirank .gt. 0) THEN + !$OMP PARALLEL DO SIMD DEFAULT(SHARED) private(i) + DO j = 1, femorder(1) + DO i = 1, nrank(2) + loc_rhs((i - 1)*loc_zspan + j) = loc_rhs((i - 1)*loc_zspan + j)& + & + rhsoverlap_buffer(nrank(2)*(j - 1) + i) + END DO + END DO + !$OMP END PARALLEL DO SIMD + END IF + ! Set communication vector type + overlap_type = rhsoverlap_type + rcvoverlap_type = rcvrhsoverlap_type + IF (mpirank .eq. 0) THEN + rhs = 0 + END IF + + CALL MPI_GATHERV(loc_rhs, counts(mpirank + 1), overlap_type, & + & rhs, counts, displs, rcvoverlap_type, 0, MPI_COMM_WORLD, ierr) + END SUBROUTINE rhs_gather + + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Do the communication of the local moment matrices between mpi workers for the overlap grid points +!> and reduce the result on the host +! +!--------------------------------------------------------------------------- + + SUBROUTINE moments_gather(moment) + USE mpihelper + USE Basic, ONLY: Zbounds, mpirank, nlend, it2d, step, leftproc, rightproc + REAL(kind=db), DIMENSION(:, :), INTENT(INOUT):: moment + INTEGER:: ierr, i, j + INTEGER:: displs(mpisize), counts(mpisize) + + displs = Zbounds(0:mpisize - 1) + counts = Zbounds(1:mpisize) - Zbounds(0:mpisize - 1) + counts(mpisize) = counts(mpisize) + femorder(1) + CALL momentsoverlapcomm(mpirank, leftproc, rightproc, loc_moments, nrank, femorder, loc_zspan - femorder(1)) + IF (mpirank .gt. 0) THEN + !$OMP PARALLEL DO SIMD DEFAULT(SHARED) private(i) + DO j = 1, femorder(1) + DO i = 1, nrank(2) + loc_moments(1:nbmoments, (i - 1)*loc_zspan + j) = loc_moments(1:nbmoments, (i - 1)*loc_zspan + j)& + & + momentsoverlap_buffer(nbmoments*(nrank(2)*(j - 1) + i - 1) + 1:nbmoments*(nrank(2)*(j - 1) + i)) + END DO + END DO + !$OMP END PARALLEL DO SIMD + END IF + ! Set communication vector type + IF (mpirank .eq. 0) THEN + moment = 0 + END IF + CALL MPI_GATHERV(loc_moments, counts(mpirank + 1), momentsoverlap_type, & + & moment, counts, displs, rcvmomentsoverlap_type, 0, MPI_COMM_WORLD, ierr) + END SUBROUTINE moments_gather + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Solves Poisson equation using FEM. Distributes the result on all MPI workers and interpolate the electric forces +!> for each particle. +! +!--------------------------------------------------------------------------- + SUBROUTINE poisson(splinevar) + USE basic, ONLY: rhs, nrank, pot, nlend + USE bsplines, ONLY: spline2d, gridval + USE mumps_bsplines, ONLY: bsolve, vmx + USE futils + Use geometry + type(spline2d):: splinevar + INTEGER:: ierr, jder(2) + real(kind=db), allocatable::reducedrhs(:) + real(kind=db), allocatable:: reducedsol(:), tempcol(:) + jder(1) = 0 + jder(2) = 0 + + ! Only the root process solves Poisson + IF (mpirank .eq. 0) then + if (nlweb) then ! we use the web-spline reduction for stability + allocate (reducedrhs(nrank(1)*nrank(2))) + allocate (reducedsol(nbreducedspline), tempcol(nrank(1)*nrank(2))) + reducedrhs = vmx(etilde, rhs) + Call bsolve(reduccedmat, reducedrhs(1:nbreducedspline), reducedsol) + tempcol = 0 + tempcol(1:nbreducedspline) = reducedsol + !phi_spline = 0 + phi_spline = vmx(etildet, tempcol) + else + CALL bsolve(femat, rhs, phi_spline) + end if + end if + + ! Broadcast the solution to all MPI workers + CALL MPI_Bcast(phi_spline, nrank(1)*nrank(2), db_type, 0, MPI_COMM_WORLD, ierr) + matcoef = reshape(phi_spline, (/nrank(1), nrank(2)/)) + + ! Evaluate the electric potential on the grid points + CALL gridval(splinevar, vec1(1:2), vec2(1:2), pot(1:4), jder, matcoef) + + IF (mpirank .eq. 0 .and. (modulo(step, it2d) .eq. 0 .or. nlend)) THEN + ! On the root process, compute the electric field for diagnostic purposes + CALL gridval(splinevar, vec1, vec2, pot, (/0, 0/)) + CALL gridval(splinevar, vec1, vec2, Ez, (/1, 0/)) + CALL gridval(splinevar, vec1, vec2, Er, (/0, 1/)) + Ez = -pot*gridwgeom(:, 1) - Ez*gridwgeom(:, 0) - gtilde(:, 1) + Er = -pot*gridwgeom(:, 2) - Er*gridwgeom(:, 0) - gtilde(:, 2) + pot = pot*gridwgeom(:, 0) + gtilde(:, 0) + END IF + + END SUBROUTINE poisson + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Computes the electric fields and potential at the particles position for particles +!> between positions nstart and nend in the list +! +!> @param[in] p the particles type storing the desired specie parameters +!> @param[in] nstart starting index for the particle list +!> @param[in] nend ending index for the particle list +!--------------------------------------------------------------------------- + + SUBROUTINE EFieldscompatparts(p, nstart, nend) + Use beam, ONLY: particles + Use geometry + TYPE(particles), INTENT(INOUT):: p + INTEGER, OPTIONAL::nstart, nend + INTEGER:: i, iend, nst, nnd + INTEGER:: nbunch + INTEGER:: num_threads + Real(kind=db), ALLOCATABLE:: erext(:), ezext(:), gtildeloc(:, :) + + if (.not. present(nstart)) nst = 1 + if (.not. present(nend)) nnd = p%Nploc + num_threads = omp_get_max_threads() + nbunch = (nnd - nst + 1)/num_threads ! Particle bunch size used when calling basfun + nbunch = max(nbunch, 1) ! Particle bunch size used when calling basfun + nbunch = min(nbunch, 64) ! Particle bunch size used when calling basfun + Allocate (erext(nbunch), ezext(nbunch), gtildeloc(0:nbunch - 1, 0:2)) + + ! Evaluate the electric potential and field at the particles position + !$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(iend) firstprivate(erext,ezext,gtildeloc) + DO i = nst, nnd, nbunch + ! Avoid segmentation fault by accessing non relevant data + iend = min(i + nbunch - 1, nnd) + CALL getgrad(splrz, p%Z(i:iend), p%R(i:iend), p%pot(i:iend), p%EZ(i:iend), p%ER(i:iend)) + CALL getgrad(splrz_ext, p%Z(i:iend), p%R(i:iend), p%potxt(i:iend), EZext(:), erext(:)) + + !Call geom_weight(p%Z(i:iend), p%R(i:iend), wgeom(0:iend-i,:)) + Call total_gtilde(p%Z(i:iend), p%R(i:iend), gtildeloc(0:iend - i, :),p%geomweight(i:iend,:)) + p%EZ(i:iend) = -p%Ez(i:iend)*p%geomweight(i:iend, 0) - p%pot(i:iend)*p%geomweight(i:iend, 1) - gtildeloc(0:iend - i, 1) + p%ER(i:iend) = -p%ER(i:iend)*p%geomweight(i:iend, 0) - p%pot(i:iend)*p%geomweight(i:iend, 2) - gtildeloc(0:iend - i, 2) + p%pot(i:iend) = p%geomweight(i:iend, 0)*p%pot(i:iend) + gtildeloc(0:iend - i, 0) + p%potxt(i:iend) = p%geomweight(i:iend, 0)*p%potxt(i:iend) + gtildeloc(0:iend - i, 0) + END DO + !$OMP END PARALLEL DO + + END SUBROUTINE EFieldscompatparts + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Constucts the FEM matrix using bsplines initialized in fields_init +!--------------------------------------------------------------------------- + SUBROUTINE fematrix + USE bsplines + USE geometry + USE omp_lib + USE sparse + + REAL(kind=db), ALLOCATABLE :: xgauss(:, :), wgauss(:), wgeom(:, :) + REAL(kind=db), ALLOCATABLE :: f(:, :), aux(:) + REAL(kind=db), ALLOCATABLE :: coefs(:) + REAL(kind=db), ALLOCATABLE :: fun(:, :), fun2(:, :) + REAL(kind=db) :: contrib + INTEGER, ALLOCATABLE :: idert(:, :), iderw(:, :), iderg(:, :) + INTEGER :: i, j, jt, iw, irow, jcol, mu, igauss, iterm, irow2, jcol2, mu2, kterms, gausssize + kterms=8 + ALLOCATE (fun(1:femorder(1) + 1, 0:1), fun2(1:femorder(2) + 1, 0:1))!Arrays keeping values of b-splines at gauss node + !ALLOCATE(xgauss(ngauss(1)*ngauss(2),2), wgauss(ngauss(1)*ngauss(2)),zg(ngauss(1)),rg(ngauss(2)), wzg(ngauss(1)), wrg(ngauss(2))) !Gaussian nodes and weights arrays + ALLOCATE (f((femorder(1) + 1)*(femorder(2) + 1), 2), aux(femorder(1) + 1)) !Auxiliary arrays ordering bsplines + ALLOCATE (idert(kterms, 2), iderw(kterms, 2), coefs(kterms), iderg(kterms, 2)) + !Pointers on the order of derivatives + call timera(0, "fematrix") + +! Constuction of auxiliary array ordering bsplines in given interval + DO i = 1, (femorder(1) + 1) + aux(i) = i + END DO + DO i = 1, (femorder(2) + 1) + f((i - 1)*(femorder(1) + 1) + 1:i*(femorder(1) + 1), 1) = aux + f((i - 1)*(femorder(1) + 1) + 1:i*(femorder(1) + 1), 2) = i + END DO + +! Initialisation of FEM matrix + CALL init(nrank(1)*nrank(2), 2, femat) + +! Assemble FEM matrix +!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(j,i,xgauss,wgauss,gausssize,wgeom, igauss, iterm,jt,irow,jcol, mu, iw, irow2,jcol2, mu2, contrib, iderw, idert, iderg, coefs, fun, fun2), firstprivate(ngauss) + DO j = 1, nr ! Loop on r position + DO i = 1, nz ! Loop on z position + !! Computation of gauss weight and position in r and z direction for gaussian integration + Call calc_gauss(splrz, ngauss, i, j, xgauss, wgauss, gausssize) + if (gausssize .gt. 0) then + If (allocated(wgeom)) deallocate (wgeom) + ALLOCATE (wgeom(gausssize, 0:2)) + CALL geom_weight(xgauss(:, 1), xgauss(:, 2), wgeom) + End if + DO igauss = 1, gausssize ! Loop on gaussian weights and positions + CALL basfun(xgauss(igauss, 1), splrz%sp1, fun, i) + CALL basfun(xgauss(igauss, 2), splrz%sp2, fun2, j) + CALL coefeq(xgauss(igauss, :), idert, iderw, iderg, coefs, kterms) + DO iterm = 1, kterms ! Loop on the two integration dimensions + DO jt = 1, (1 + femorder(1))*(femorder(2) + 1) + irow = i + f(jt, 1) - 1; jcol = j + f(jt, 2) - 1 + mu = irow + (jcol - 1)*nrank(1) + DO iw = 1, (1 + femorder(1))*(femorder(2) + 1) + irow2 = i + f(iw, 1) - 1; jcol2 = j + f(iw, 2) - 1 + mu2 = irow2 + (jcol2 - 1)*nrank(1) + contrib = wgeom(igauss, iderg(iterm, 1))*wgeom(igauss, iderg(iterm, 2))* & + & fun(f(jt, 1), idert(iterm, 1))*fun(f(iw, 1), idert(iterm, 2))* & + & fun2(f(jt, 2), iderw(iterm, 1))*fun2(f(iw, 2), iderw(iterm, 2))* & + & wgauss(igauss)*coefs(iterm) + call omp_set_lock(mu_lock(mu)) + CALL updt_sploc(femat%mat%row(mu), mu2, contrib) + call omp_unset_lock(mu_lock(mu)) + END DO + END DO + END DO + END DO + END DO + END DO + !$OMP End parallel do + + DEALLOCATE (f, aux) + DEALLOCATE (idert, iderw, coefs, fun, fun2) + + call timera(1, "fematrix") + END SUBROUTINE fematrix + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Computes the volume of the splines cells needed to display the density in post-processing +!--------------------------------------------------------------------------- + + SUBROUTINE comp_volume + + USE bsplines + USE geometry + USE basic, ONLY: Volume + + REAL(kind=db), ALLOCATABLE :: xgauss(:, :), wgauss(:), wgeom(:, :) + REAL(kind=db), ALLOCATABLE :: f(:, :), aux(:), coefs(:) + REAL(kind=db), ALLOCATABLE :: fun(:, :), fun2(:, :), gtildeintegr(:, :), ftestpt(:, :) + Integer, ALLOCATABLE, Dimension(:) :: idg, idt, idp, idw + INTEGER :: i, j, jt, irow, jcol, mu, igauss, gausssize, iterm, nterms + Real(kind=db)::newcontrib + + call timera(0, "comp_volume") + + ALLOCATE (fun(1:femorder(1) + 1, 0:1), fun2(1:femorder(2) + 1, 0:1))!Arrays keeping values of b-splines at gauss node + !ALLOCATE(xgauss(ngauss(1)*ngauss(2),2), wgauss(ngauss(1)*ngauss(2)),zg(ngauss(1)),rg(ngauss(2)), wzg(ngauss(1)), wrg(ngauss(2))) !Gaussian nodes and weights arrays + ALLOCATE (f((femorder(1) + 1)*(femorder(2) + 1), 2), aux(femorder(1) + 1)) !Auxiliary arrays ordering bsplines + nterms = 4 + Allocate (idg(nterms), idt(nterms), idw(nterms), idp(nterms), coefs(nterms)) +! Constuction of auxiliary array ordering bsplines in given interval + DO i = 1, (femorder(1) + 1) + aux(i) = i + END DO + DO i = 1, (femorder(2) + 1) + f((i - 1)*(femorder(1) + 1) + 1:i*(femorder(1) + 1), 1) = aux + f((i - 1)*(femorder(1) + 1) + 1:i*(femorder(1) + 1), 2) = i + END DO + + volume = 0 + if (walltype .lt. 0) fverif = 0 + +! Assemble Volume matrix + !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(j,i,xgauss,wgauss,gausssize,wgeom, igauss, gtildeintegr, ftestpt, iterm,jt,irow,jcol, mu, idw, idt, idg, idp, coefs, fun, fun2, newcontrib), firstprivate(ngauss) + DO j = 1, nr ! Loop on r position + DO i = 1, nz ! Loop on z position + ! Computation of gauss weight and position in r and z direction for gaussian integration + Call calc_gauss(splrz, ngauss, i, j, xgauss, wgauss, gausssize) + If (allocated(wgeom)) deallocate (wgeom) + if (gausssize .gt. 0) then + ALLOCATE (wgeom(size(xgauss, 1), 0:2)) + CALL geom_weight(xgauss(:, 1), xgauss(:, 2), wgeom) + End if + If (allocated(gtildeintegr)) deallocate (gtildeintegr) + ALLOCATE (gtildeintegr(size(xgauss, 1), 0:2)) + Call total_gtilde(xgauss(:, 1), xgauss(:, 2), gtildeintegr,wgeom) + if (walltype .lt. 0) then + If (allocated(ftestpt)) deallocate (ftestpt) + ALLOCATE (ftestpt(size(xgauss, 1), 0:0)) + CALL ftest(xgauss(:, 1), xgauss(:, 2), ftestpt) + end if + + DO igauss = 1, gausssize ! Loop on gaussian weights and positions + CALL basfun(xgauss(igauss, 1), splrz%sp1, fun, i) + CALL basfun(xgauss(igauss, 2), splrz%sp2, fun2, j) + CALL coefeqext(xgauss(igauss, :), idt, idw, idg, idp, coefs) + + DO jt = 1, (1 + femorder(1))*(femorder(2) + 1) + irow = i + f(jt, 1) - 1; jcol = j + f(jt, 2) - 1 + mu = irow + (jcol - 1)*nrank(1) + newcontrib = 2*pi*fun(f(jt, 1), 0)*fun2(f(jt, 2), 0)*wgauss(igauss)*xgauss(igauss, 2)!*wgeom(igauss,0) + !$OMP ATOMIC UPDATE + volume(mu) = volume(mu) + newcontrib + !$OMP END ATOMIC + if (walltype .lt. 0) THEN + newcontrib = ftestpt(igauss, 0)*fun(f(jt, 1), 0)*fun2(f(jt, 2), 0)& + &*wgeom(igauss, 0)*wgauss(igauss)*xgauss(igauss, 2) + !$OMP ATOMIC UPDATE + fverif(mu) = fverif(mu) + newcontrib + !$OMP END ATOMIC + end if + END DO + END DO + END DO + END DO + !$OMP END PARALLEL DO + + !DEALLOCATE(xgauss, wgauss,zg,rg, wzg, wrg) + DEALLOCATE (f, aux) + DEALLOCATE (fun, fun2) + + call timera(1, "comp_volume") + + END SUBROUTINE comp_volume + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Computes the gradient of the gtilde function for the web-spline method needed to correctly apply the dirichlet boundary conditions +!--------------------------------------------------------------------------- + + SUBROUTINE comp_gradgtilde + + USE bsplines + USE geometry + + REAL(kind=db), ALLOCATABLE :: xgauss(:, :), wgauss(:), wgeom(:, :) + REAL(kind=db), ALLOCATABLE :: f(:, :), aux(:), coefs(:) + REAL(kind=db), ALLOCATABLE :: fun(:, :), fun2(:, :), gtildeintegr(:, :), ftestpt(:, :) + Integer, ALLOCATABLE, Dimension(:) :: idg, idt, idp, idw + INTEGER :: i, j, jt, irow, jcol, mu, igauss, gausssize, iterm, nterms + Real(kind=db)::newcontrib + + call timera(0, "comp_gradgtilde") + + ALLOCATE (fun(1:femorder(1) + 1, 0:1), fun2(1:femorder(2) + 1, 0:1))!Arrays keeping values of b-splines at gauss node + !ALLOCATE(xgauss(ngauss(1)*ngauss(2),2), wgauss(ngauss(1)*ngauss(2)),zg(ngauss(1)),rg(ngauss(2)), wzg(ngauss(1)), wrg(ngauss(2))) !Gaussian nodes and weights arrays + ALLOCATE (f((femorder(1) + 1)*(femorder(2) + 1), 2), aux(femorder(1) + 1)) !Auxiliary arrays ordering bsplines + nterms = 4 + Allocate (idg(nterms), idt(nterms), idw(nterms), idp(nterms), coefs(nterms)) +! Constuction of auxiliary array ordering bsplines in given interval + DO i = 1, (femorder(1) + 1) + aux(i) = i + END DO + DO i = 1, (femorder(2) + 1) + f((i - 1)*(femorder(1) + 1) + 1:i*(femorder(1) + 1), 1) = aux + f((i - 1)*(femorder(1) + 1) + 1:i*(femorder(1) + 1), 2) = i + END DO + + gradgtilde = 0 + if (walltype .lt. 0) fverif = 0 + +! Assemble Volume matrix + !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(j,i,xgauss,wgauss,gausssize,wgeom, igauss, gtildeintegr, ftestpt, iterm,jt,irow,jcol, mu, idw, idt, idg, idp, coefs, fun, fun2, newcontrib), firstprivate(ngauss) + DO j = 1, nr ! Loop on r position + DO i = 1, nz ! Loop on z position + ! Computation of gauss weight and position in r and z direction for gaussian integration + Call calc_gauss(splrz, ngauss, i, j, xgauss, wgauss, gausssize) + If (allocated(wgeom)) deallocate (wgeom) + if (gausssize .gt. 0) then + ALLOCATE (wgeom(size(xgauss, 1), 0:2)) + CALL geom_weight(xgauss(:, 1), xgauss(:, 2), wgeom) + End if + If (allocated(gtildeintegr)) deallocate (gtildeintegr) + ALLOCATE (gtildeintegr(size(xgauss, 1), 0:2)) + Call total_gtilde(xgauss(:, 1), xgauss(:, 2), gtildeintegr,wgeom) + if (walltype .lt. 0) then + If (allocated(ftestpt)) deallocate (ftestpt) + ALLOCATE (ftestpt(size(xgauss, 1), 0:0)) + CALL ftest(xgauss(:, 1), xgauss(:, 2), ftestpt) + end if + + DO igauss = 1, gausssize ! Loop on gaussian weights and positions + CALL basfun(xgauss(igauss, 1), splrz%sp1, fun, i) + CALL basfun(xgauss(igauss, 2), splrz%sp2, fun2, j) + CALL coefeqext(xgauss(igauss, :), idt, idw, idg, idp, coefs) + + DO jt = 1, (1 + femorder(1))*(femorder(2) + 1) + irow = i + f(jt, 1) - 1; jcol = j + f(jt, 2) - 1 + mu = irow + (jcol - 1)*nrank(1) + newcontrib = 0 + Do iterm = 1, nterms + newcontrib = newcontrib + wgeom(igauss, idg(iterm))*gtildeintegr(igauss, idp(iterm))* & + & fun(f(jt, 1), idt(iterm))*fun2(f(jt, 2), idw(iterm))* & + & wgauss(igauss)*coefs(iterm) + End do + !$OMP ATOMIC UPDATE + gradgtilde(mu) = gradgtilde(mu) + newcontrib + !$OMP END ATOMIC + END DO + END DO + END DO + END DO + !$OMP END PARALLEL DO + + !DEALLOCATE(xgauss, wgauss,zg,rg, wzg, wrg) + DEALLOCATE (f, aux) + DEALLOCATE (fun, fun2) + + call timera(1, "comp_gradgtilde") + + END SUBROUTINE comp_gradgtilde + + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Imposes the dirichlet boundary conditions on the FEM matrix for the case where we use regular splines ( not web-splines). +!--------------------------------------------------------------------------- + SUBROUTINE fe_dirichlet + REAL(kind=db), ALLOCATABLE :: arr(:) + INTEGER :: i + ALLOCATE (arr(nrank(1)*nrank(2))) + DO i = 1, nrank(1) + IF (rgrid(0) .ne. 0.0_db) THEN + arr = 0; arr(i) = 1; + CALL putrow(femat, i, arr) + END IF + arr = 0; arr(nrank(1)*nrank(2) + 1 - i) = 1; + CALL putrow(femat, nrank(1)*nrank(2) + 1 - i, arr) + END DO + DEALLOCATE (arr) + END SUBROUTINE fe_dirichlet +!________________________________________________________________________________ + SUBROUTINE coefeq(x, idt, idw, idg, c, kterms) + REAL(kind=db), INTENT(in) :: x(2) + INTEGER, INTENT(out) :: idt(:, :), idw(:, :), idg(:, :),kterms + REAL(kind=db), INTENT(out) :: c(:) + kterms=8 + + + c = x(2) + idt(1, 1) = 0 + idt(1, 2) = 0 + idw(1, 1) = 0 + idw(1, 2) = 0 + idg(1, 1) = 1 + idg(1, 2) = 1 + idt(2, 1) = 0 + idt(2, 2) = 1 + idw(2, 1) = 0 + idw(2, 2) = 0 + idg(2, 1) = 1 + idg(2, 2) = 0 + idt(3, 1) = 1 + idt(3, 2) = 0 + idw(3, 1) = 0 + idw(3, 2) = 0 + idg(3, 1) = 0 + idg(3, 2) = 1 + idt(4, 1) = 1 + idt(4, 2) = 1 + idw(4, 1) = 0 + idw(4, 2) = 0 + idg(4, 1) = 0 + idg(4, 2) = 0 + idt(5, 1) = 0 + idt(5, 2) = 0 + idw(5, 1) = 0 + idw(5, 2) = 0 + idg(5, 1) = 2 + idg(5, 2) = 2 + idt(6, 1) = 0 + idt(6, 2) = 0 + idw(6, 1) = 0 + idw(6, 2) = 1 + idg(6, 1) = 2 + idg(6, 2) = 0 + idt(7, 1) = 0 + idt(7, 2) = 0 + idw(7, 1) = 1 + idw(7, 2) = 0 + idg(7, 1) = 0 + idg(7, 2) = 2 + idt(8, 1) = 0 + idt(8, 2) = 0 + idw(8, 1) = 1 + idw(8, 2) = 1 + idg(8, 1) = 0 + idg(8, 2) = 0 + END SUBROUTINE coefeq + + SUBROUTINE coefeqext(x, idt, idw, idg, idp, c) + REAL(kind=db), INTENT(in) :: x(2) + INTEGER, INTENT(out) :: idp(:), idt(:), idw(:), idg(:) + REAL(kind=db), INTENT(out) :: c(:) + + c(1) = x(2) + idp(1) = 1 + idg(1) = 1 + idt(1) = 0 + idw(1) = 0 + c(2) = x(2) + idp(2) = 1 + idg(2) = 0 + idt(2) = 1 + idw(2) = 0 + c(3) = x(2) + idp(3) = 2 + idg(3) = 2 + idt(3) = 0 + idw(3) = 0 + c(4) = x(2) + idp(4) = 2 + idg(4) = 0 + idt(4) = 0 + idw(4) = 1 + END SUBROUTINE coefeqext + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Computes the magnetic field on the grid according to a magnetic mirror, +!> or according to the linear interpolation of the values on the +!> grid saved in h5 file stored at magfile. +!> @param[in] magfile filname of .h5 file containing the definitions of A and B +!--------------------------------------------------------------------------- + SUBROUTINE magnet(magfile) + USE basic, ONLY: B0, Rcurv, rgrid, zgrid, width, rnorm, nr, nz, bnorm + USE constants, ONLY: Pi + CHARACTER(LEN=*), INTENT(IN), OPTIONAL:: magfile + REAL(kind=db) :: rg, zg, halfLz, MirrorRatio + INTEGER :: i, rindex + IF (len_trim(magfile) .lt. 1) THEN + halfLz = (zgrid(nz) + zgrid(0))/2 + MirrorRatio = (Rcurv - 1)/(Rcurv + 1) + DO i = 1, (nr + 1)*(nz + 1) + rindex = (i - 1)/(nz + 1) + rg = rgrid(rindex) + zg = zgrid(i - rindex*(nz + 1) - 1) - halfLz + Br(i) = -B0*MirrorRatio*SIN(2*pi*zg/width*rnorm)*bessi1(2*pi*rg/width*rnorm)/bnorm + Bz(i) = B0*(1 - MirrorRatio*COS(2*pi*zg/width*rnorm)*bessi0(2*pi*rg/width*rnorm))/bnorm + Athet(i) = 0.5*B0*(rg*rnorm - width/pi*MirrorRatio*bessi1(2*pi*rg/width*rnorm)*COS(2*pi*zg/width*rnorm)) + END DO + ELSE + CALL load_mag_from_h5(magfile) + END IF + END SUBROUTINE magnet + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Loads the magnetic field defined in the .h5 file at location magfile +!> @param[in] magfile filname of .h5 file containing the definitions of A and B +!--------------------------------------------------------------------------- + SUBROUTINE load_mag_from_h5(magfile) + USE basic, ONLY: B0, rgrid, zgrid, rnorm, nr, nz, bnorm, bscaling + USE constants, ONLY: Pi + USE futils + CHARACTER(LEN=*), INTENT(IN):: magfile + REAL(kind=db), ALLOCATABLE :: magr(:), magz(:) + REAL(kind=db), ALLOCATABLE :: tempBr(:, :), tempBz(:, :), tempAthet(:, :) + REAL(kind=db) :: zi, rj, rcoeff, zcoeff, maxB + INTEGER :: i, j, l, m, magfid + LOGICAL:: B_is_saved + INTEGER :: magn(2), magrank + + CALL openf(trim(magfile), magfid, 'r', real_prec='d') + + CALL getdims(magfid, '/mag/Athet', magrank, magn) + + ALLOCATE (magr(magn(2)), magz(magn(1))) + ALLOCATE (tempAthet(magn(1), magn(2)), tempBr(magn(1), magn(2)), tempBz(magn(1), magn(2))) + + ! Read r and z coordinates for the definition of A_\thet, and B + CALL getarr(magfid, '/mag/r', magr) + CALL getarr(magfid, '/mag/z', magz) + CALL getarr(magfid, '/mag/Athet', tempAthet) + + IF (isdataset(magfid, '/mag/Br') .and. isdataset(magfid, '/mag/Bz')) THEN + CALL getarr(magfid, '/mag/Br', tempBr) + CALL getarr(magfid, '/mag/Bz', tempBz) + IF(bscaling .gt. 0) then + maxB=sqrt(maxval(tempBr**2+tempBz**2)) + tempBr=tempBr/maxB*B0 + tempBz=tempBz/maxB*B0 + end if + B_is_saved = .true. + ELSE + B_is_saved = .false. + END IF + + + + + ! Interpolate the magnetic field and potential at the grid points + m = 1 + Do j = 0, nr + rj = rgrid(j)*rnorm + Do while (magr(m) .lt. rj) + m = m + 1 + END DO + rcoeff = (rj - magr(m - 1))/(magr(m) - magr(m - 1)) + l = 1 + Do i = 0, nz + zi = zgrid(i)*rnorm + Do while (magz(l) .lt. zi) + l = l + 1 + END DO + zcoeff = (zi - magz(l - 1))/(magz(l) - magz(l - 1)) + Athet(i + j*(nz + 1) + 1) = interp2(tempAthet, l, m, zcoeff, rcoeff) + IF (B_is_saved) THEN + Br(i + j*(nz + 1) + 1) = interp2(tempBr, l, m, zcoeff, rcoeff) + Bz(i + j*(nz + 1) + 1) = interp2(tempBz, l, m, zcoeff, rcoeff) + ELSE + Br(i + j*(nz + 1) + 1) = -(tempAthet(l + 1, m + 1) + tempAthet(l + 1, m) - tempAthet(l, m + 1) - tempAthet(l, m))/2 & + & /(magz(l) - magz(l - 1)) + Bz(i + j*(nz + 1) + 1) = ((tempAthet(l + 1, m + 1) + tempAthet(l, m + 1))*magr(m + 1) & + & - (tempAthet(l + 1, m) - tempAthet(l, m))*magr(m)) & + & /(magr(m) - magr(m - 1))/(magr(m) + magr(m + 1)) + END IF + END DO + END DO + + if( bscaling .lt. 0 ) then + maxB = maxval(sqrt(Bz**2 + Br**2)) + + Bz = Bz/maxB*B0 + Br = Br/maxB*B0 + end if + ! We normalize + Br = Br/bnorm + Bz = Bz/bnorm + + CALL closef(magfid) + END SUBROUTINE load_mag_from_h5 + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Computes the weighted average of the 4 elements of matrix defined by +!> xi, xi+1, yj, yj+1 +! +!> @param[in] matrix The matrix containing the initial values to be interpolated +!> @param[in] xi left reference along the first dimension for the average +!> @param[in] yj left reference along the second dimension for the average +!> @param[in] xcoeff left weight along the first dimension for the average +!> @param[in] ycoeff left weight along the second dimension for the average +!> @param[in] interp2 weighted average value +!--------------------------------------------------------------------------- + + FUNCTION interp2(mat, xi, yj, xcoeff, ycoeff) + REAL(kind=db):: interp2 + Real(kind=db), INTENT(IN):: mat(:, :), xcoeff, ycoeff + INTEGER, INTENT(IN):: xi, yj + + interp2 = mat(xi, yj)*xcoeff*ycoeff + mat(xi + 1, yj)*(1 - xcoeff)*ycoeff & + & + mat(xi, yj + 1)*xcoeff*(1 - ycoeff) + mat(xi + 1, yj + 1)*(1 - xcoeff)*(1 - ycoeff) + + END function +!________________________________________________________________________________ +!Modified Bessel functions of the first kind of the zero order + FUNCTION bessi0(x) + REAL(kind=db) :: bessi0, x + REAL(kind=db) :: ax + REAL(kind=db) p1, p2, p3, p4, p5, p6, p7, q1, q2, q3, q4, q5, q6, q7, q8, q9, y + SAVE p1, p2, p3, p4, p5, p6, p7, q1, q2, q3, q4, q5, q6, q7, q8, q9 + DATA p1, p2, p3, p4, p5, p6, p7/1.0d0, 3.5156229d0, 3.0899424d0, 1.2067492d0, 0.2659732d0, 0.360768d-1, 0.45813d-2/ + DATA q1, q2, q3, q4, q5, q6, q7, q8, q9/0.39894228d0, 0.1328592d-1, 0.225319d-2, -0.157565d-2, 0.916281d-2, & + & -0.2057706d-1, 0.2635537d-1, -0.1647633d-1, 0.392377d-2/ + if (abs(x) .lt. 3.75) then + y = (x/3.75)**2 + bessi0 = p1 + y*(p2 + y*(p3 + y*(p4 + y*(p5 + y*(p6 + y*p7))))) + else + ax = abs(x) + y = 3.75/ax + bessi0 = (exp(ax)/sqrt(ax))*(q1 + y*(q2 + y*(q3 + y*(q4 + y*(q5 + y*(q6 + y*(q7 + y*(q8 + y*q9)))))))) + end if + return + END FUNCTION bessi0 +!________________________________________________________________________________ +!Modified Bessel functions of the first kind of the first order + FUNCTION bessi1(x) + REAL(kind=db) :: bessi1, x + REAL(kind=db) :: ax + REAL(kind=db) p1, p2, p3, p4, p5, p6, p7, q1, q2, q3, q4, q5, q6, q7, q8, q9, y + SAVE p1, p2, p3, p4, p5, p6, p7, q1, q2, q3, q4, q5, q6, q7, q8, q9 + DATA p1, p2, p3, p4, p5, p6, p7/0.5d0, 0.87890594d0, 0.51498869d0, 0.15084934d0, 0.2658733d-1, 0.301532d-2, 0.32411d-3/ + DATA q1, q2, q3, q4, q5, q6, q7, q8, q9/0.39894228d0, -0.3988024d-1, -0.362018d-2, 0.163801d-2, -0.1031555d-1, & + & 0.2282967d-1, -0.2895312d-1, 0.1787654d-1, -0.420059d-2/ + if (abs(x) .lt. 3.75D0) then + y = (x/3.75D0)**2 + bessi1 = x*(p1 + y*(p2 + y*(p3 + y*(p4 + y*(p5 + y*(p6 + y*p7)))))) + else + ax = abs(x) + y = 3.75D0/ax + bessi1 = (exp(ax)/sqrt(ax))*(q1 + y*(q2 + y*(q3 + y*(q4 + y*(q5 + y*(q6 + y*(q7 + y*(q8 + y*q9)))))))) + if (x .lt. 0.) bessi1 = -bessi1 + end if + return + END FUNCTION bessi1 + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Computes the value of the external electric field at position R,Z for the case of a coaxial configuration of constant radius +! +!> @param[in] R radial positon +!> @param[in] Z axial position +!> @param[out] external potential at position R,Z +!--------------------------------------------------------------------------- + + ELEMENTAL FUNCTION ext_potential(R) + REAL(kind=db), INTENT(IN):: R + REAL(kind=db):: ext_potential + + ext_potential = potextA*log(R) + potextB + + END FUNCTION ext_potential + +!--------------------------------------------------------------------------- +!> @author +!> Patryk kaminski EPFL/SPC +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Free the memory used by the fields module +!--------------------------------------------------------------------------- + SUBROUTINE clean_fields + Use bsplines + USE basic, ONLY: rhs + INTEGER:: i + do i = 1, nrank(1)*nrank(2) + call omp_destroy_lock(mu_lock(i)) + end do + DEALLOCATE (mu_lock) + DEALLOCATE (matcoef) + DEALLOCATE (pot) + DEALLOCATE (rhs) + DEALLOCATE (loc_rhs) + DEALLOCATE (loc_moments) + DEALLOCATE (phi_spline) + DEALLOCATE (Br, Bz) + DEALLOCATE (Er, Ez) + DEALLOCATE (vec1, vec2) + Call DESTROY_SP(splrz) + Call DESTROY_SP(splrz_ext) + + END SUBROUTINE clean_fields + + SUBROUTINE updt_sploc(arow, j, val) + ! + ! Update element j of row arow or insert it in an increasing "index" + ! + USE sparse + TYPE(sprow), TARGET :: arow + INTEGER, INTENT(in) :: j + DOUBLE PRECISION, INTENT(in) :: val + ! + TYPE(elt), TARGET :: pre_root + TYPE(elt), POINTER :: t, p + ! + pre_root%next => arow%row0 ! pre_root is linked to the head of the list. + t => pre_root + DO WHILE (ASSOCIATED(t%next)) + p => t%next + IF (p%index .EQ. j) THEN + p%val = p%val + val + RETURN + END IF + IF (p%index .GT. j) EXIT + t => t%next + END DO + ALLOCATE (p) + p = elt(j, val, t%next) + t%next => p + ! + arow%nnz = arow%nnz + 1 + arow%row0 => pre_root%next ! In case the head is altered + END SUBROUTINE updt_sploc + !+ +END MODULE fields diff --git a/src/geometry_mod.f90 b/src/geometry_mod.f90 new file mode 100644 index 0000000..9eddb42 --- /dev/null +++ b/src/geometry_mod.f90 @@ -0,0 +1,1192 @@ +!------------------------------------------------------------------------------ +! EPFL/Swiss Plasma Center +!------------------------------------------------------------------------------ +! +! MODULE: geometry +! +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> Module responsible for handling geometries with non constant radius using b-splines interpolation +!> This module defines ways to comupte the weight function needed for weighted b-splines and +!> can load the definition of the geometry from input files +!------------------------------------------------------------------------------ + +MODULE geometry + USE constants + USE bsplines + USE mumps_bsplines + USE splinebound + use weighttypes + IMPLICIT NONE + + type innerspline + Integer, Allocatable:: k(:) ! Index in reduced set + real(kind=db), Allocatable:: weight(:) ! geomtric weight at relevant cell + end type innerspline + type test_params + real(kind=db):: z0 + real(kind=db):: r0 + real(kind=db):: Lz + real(kind=db):: Lr + end type + + Integer, save:: testkr=1, testkz=1 + Logical, save:: nlweb=.false. + Integer, save :: walltype = 0 + Integer, save, Allocatable:: bsplinetype(:) ! Array containing the inner/outer type for each bspline + Integer, save, Allocatable:: gridcelltype(:,:) ! Array containing the inner/outer type for each gridcell + Integer, save, Allocatable:: linkedspline(:,:) ! Array containing the lowerleft linked spline in case of boundary spline + Real(kind=db), save, allocatable:: gridwgeom(:,:) ! Stores the geometric weight at the grid points + Real(kind=db), save, allocatable:: gridwdom(:,:) ! Stores the domain weight at the grid points + Real(kind=db), save, allocatable:: gtilde(:,:) ! Stores the extension to the domain of the boundary conditions + type(mumps_mat):: etilde ! Matrix of extendend web splines + type(mumps_mat):: etildet ! Transpose of Matrix of extendend web splines + integer,save :: nbreducedspline ! Number of splines in the reduced set + type(test_params), save:: test_pars + + PROCEDURE(geom_eval), POINTER:: dirichlet_weight => NULL() + PROCEDURE(geomtot_eval), POINTER:: domain_weight => NULL() + PROCEDURE(gtilde_eval), POINTER:: total_gtilde => NULL() + + + PUBLIC:: geom_weight, dom_weight + + ABSTRACT INTERFACE + SUBROUTINE gtilde_eval(z,r,g,w) + USE constants + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: g(:,0:) + Real(kind=db), INTENT(IN),OPTIONAL::w(:,0:) + + END SUBROUTINE + + SUBROUTINE geom_eval(z,r,w,wupper) + USE constants + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), INTENT(OUT), OPTIONAL:: wupper(:,0:) + END SUBROUTINE + SUBROUTINE geomtot_eval(z,r,w,idwall) + USE constants + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + INTEGER, optional, INTENT(OUT):: idwall(:) + END SUBROUTINE + END INTERFACE + + + INTERFACE geom_weight + MODULE PROCEDURE geom_weight0, geom_weight1, geom_weight2 + END INTERFACE geom_weight + + INTERFACE dom_weight + MODULE PROCEDURE dom_weight0, dom_weight1, dom_weight2, dom_weight3 + END INTERFACE dom_weight + + NAMELIST /geomparams/ z_0, r_0, z_r, r_r, r_a, r_b, z_a, z_b ,walltype, L_r, L_z, nlweb, Interior, above1, above2, alpha, r_bLeft, r_bRight, testkr, testkz + + + +contains + + SUBROUTINE read_geom(Fileid, rnorm, splrz, Potinn, Potout) + use mpi + Use bsplines + use basic, ONLY: phinorm + use weighttypes + type(spline2d):: splrz + + Real(kind=db):: rnorm, Potinn, Potout + Integer:: Fileid, mpirank, ierr, istat + character(len=1000) :: line + !type(spline_boundary):: border0 + !Real(kind=db):: cpoints(2,3),t(1), distance, pos + !Real(kind=db), ALLOCATABLE::result(:,:,:) + + + CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) + + Rewind(Fileid) + READ(Fileid, geomparams, iostat=istat) + + if (istat.gt.0) then + if(mpirank .eq. 0) then + backspace(Fileid) + read(Fileid,fmt='(A)') line + write(*,'(A)') & + 'Invalid line in geomparams: '//trim(line) + end if + call MPI_Abort(MPI_COMM_WORLD, -1, ierr) + stop + end if + + if(mpirank .eq. 0) WRITE(*, geomparams) + + + !! Normalizations and initialization of geometric variables + r_a=r_a/rnorm + r_b=r_b/rnorm + z_a=z_a/rnorm + z_b=z_b/rnorm + r_bLeft=r_bLeft/rnorm + r_bRight=r_bRight/rnorm + if(r_a .eq. 0 .and. r_b .eq.0) then + !! in case no geom_params have been defined + r_a=splrz%sp2%knots(0) + r_b=splrz%sp2%knots(splrz%sp2%nints) + end if + z_0=z_0/rnorm + r_0=r_0/rnorm + z_r=z_r/rnorm + r_r=r_r/rnorm + invr_r=1/r_r + invr_z=1/z_r + + Phidown=Potinn + Phiup=Potout + L_r=L_r/rnorm + L_z=L_z/rnorm + SELECT CASE (abs(walltype)) + CASE (2) + ! coaxial cylinder and top ellipse with cylinder extensions + total_gtilde=>gUpDown + Dirichlet_weight=>geom_w2 + domain_weight=>geom_rvaschtot + CASE (3) + ! Two ellipses with "parallel" tangents with cylinders + total_gtilde=>gUpDown + Dirichlet_weight=>geom_w3 + domain_weight=>geom_rvaschtot + CASE (4) + ! Two ellipses with same radii with cylinders + total_gtilde=>gUpDown + Dirichlet_weight=>geom_w4 + domain_weight=>geom_rvaschtot + CASE (5) + ! Two ellipses with same radii with cylinders and + total_gtilde=>gUpDown + Dirichlet_weight=>geom_w5 + domain_weight=>geom_rvaschtot + CASE (6) + ! circular coaxial tilted ellipse right Dirichlet + total_gtilde=>gUpDown + Dirichlet_weight=>geom_w6 + domain_weight=>geom_rvaschtot + CASE (7) + ! circular coaxial tilted ellipse right and left Dirichlet + total_gtilde=>gUpDown + Dirichlet_weight=>geom_w7 + domain_weight=>geom_rvaschtot + CASE (8) + ! circular coaxial tilted ellipse right and left Dirichlet + total_gtilde=>gUpDown + Dirichlet_weight=>geom_w8 + domain_weight=>geom_rvaschtot + CASE (9) + ! Geometry defined as a spline curve + total_gtilde=>gspline + Dirichlet_weight=>geom_spline + domain_weight=>geom_splinetot + call read_splinebound(Fileid,the_domain, splrz, rnorm, phinorm) + CASE (10) + ! square section disc + total_gtilde=>gUpDown + domain_weight=>geom_rvaschtot + Dirichlet_weight=>geom_w10 + CASE DEFAULT + ! Ellipse as in gt170 standard weight and straight coaxial configs + total_gtilde=>gstd + Dirichlet_weight=>geom_weightstd + domain_weight=>geom_rvaschtot + END SELECT + + ! If we are lauching a test case, we load the test_pars variable + if(walltype.lt.0) then + test_pars%Lr=(splrz%sp2%knots(splrz%sp2%nints)-splrz%sp2%knots(0))/testkr + test_pars%Lz=(splrz%sp1%knots(splrz%sp1%nints)-splrz%sp1%knots(0))/testkz + test_pars%r0=0.5*(splrz%sp2%knots(splrz%sp2%nints)+splrz%sp2%knots(0)) + test_pars%z0=0.5*(splrz%sp1%knots(splrz%sp1%nints)+splrz%sp1%knots(0)) + + total_gtilde=>gtest + end if + + end subroutine read_geom + + SUBROUTINE geom_init(spl2, vec1, vec2) + type(spline2d):: spl2 + real(kind=db):: vec1(:),vec2(:) + Real(kind=db), Allocatable:: zgrid(:),rgrid(:) + Integer:: nrank(2), nrz(2) + + Call get_dim(spl2, nrank, nrz) + + ! Obtain grid data drom the spline structure + Allocate(zgrid(1:nrz(1)+1)) + Allocate(rgrid(1:nrz(2)+1)) + zgrid=spl2%sp1%knots(0:nrz(1)) + rgrid=spl2%sp2%knots(0:nrz(2)) + + ! create a table of the calssification for each cell + Allocate(gridcelltype(nrz(1),nrz(2))) + Call classifycell(zgrid, rgrid, gridcelltype) + + if (nlweb) then + Allocate(bsplinetype(nrank(1)*nrank(2))) + Call classifyspline(spl2, gridcelltype, bsplinetype) + Call buildetilde(spl2,bsplinetype,gridcelltype) + end if + + ALLOCATE(gridwgeom((nrz(1)+1)*(nrz(2)+1),0:2)) + gridwgeom=0 + ALLOCATE(gridwdom((nrz(1)+1)*(nrz(2)+1),0:2)) + gridwdom=0 + + ALLOCATE(gtilde((nrz(1)+1)*(nrz(2)+1),0:2)) + gtilde=0 + + + Call geom_weight (vec1, vec2, gridwgeom) + CALL total_gtilde(vec1, vec2, gtilde, gridwgeom) + Call dom_weight (vec1, vec2, gridwdom) + + end Subroutine geom_init + + Subroutine geom_diag(File_handle, str, rnorm) + use mpi + Use futils + use weighttypes + + Integer:: File_handle + Real(kind=db):: rnorm + Character(len=*):: str + CHARACTER(len=128):: grpname + Integer:: ierr, mpirank + + CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) + + IF(mpirank .eq. 0) THEN + + Write(grpname,'(a,a)') trim(str),"/geometry" + If(.not. isgroup(File_handle, trim(grpname))) THEN + CALL creatg(File_handle, trim(grpname)) + END IF + Call attach(File_handle, trim(grpname), "r_a", r_a*RNORM) + Call attach(File_handle, trim(grpname), "r_b", r_b*RNORM) + Call attach(File_handle, trim(grpname), "z_a", z_a*RNORM) + Call attach(File_handle, trim(grpname), "z_b", z_b*RNORM) + Call attach(File_handle, trim(grpname), "z_0", z_0*RNORM) + Call attach(File_handle, trim(grpname), "r_0", r_0*RNORM) + Call attach(File_handle, trim(grpname), "r_r", r_r*RNORM) + Call attach(File_handle, trim(grpname), "z_r", z_r*RNORM) + Call attach(File_handle, trim(grpname), "L_r", test_pars%Lr*RNORM) + Call attach(File_handle, trim(grpname), "L_z", test_pars%Lz*RNORM) + Call attach(File_handle, trim(grpname), "interior", interior) + Call attach(File_handle, trim(grpname), "above1", above1) + Call attach(File_handle, trim(grpname), "above2", above2) + Call attach(File_handle, trim(grpname), "walltype", walltype) + Call putarr(File_handle, trim(grpname)//'/geomweight',gridwdom) + Call putarr(File_handle, trim(grpname)//'/dirichletweight',gridwgeom) + Call putarr(File_handle, trim(grpname)//'/gtilde',gtilde) + Call putarr(File_handle, trim(grpname)//'/ctype',gridcelltype) + Call putarr(File_handle, trim(grpname)//'/linked_s',linkedspline) + Call putarr(File_handle, trim(grpname)//'/bsplinetype',bsplinetype) + + END IF + + End subroutine geom_diag + + Subroutine buildetilde(spl2, bsplinetype, celltype) + USE mumps_bsplines + Use basic, ONLY: rnorm, mpirank + type(spline2d):: spl2 + Integer:: bsplinetype(:) + Integer:: celltype(1:,1:) + Logical, Allocatable:: Ibsplinetype(:) + Integer:: i,j,k, icellz, icellr, jcellz, jcellr, nrank(2), nrz(2), norder(2) + real(kind=db), allocatable:: zgrid(:), rgrid(:) + real(kind=db):: wgeomi, indexdistance, eij + integer:: l,m, n, linkedi + type(innerspline):: innersplinelist + real(kind=db), allocatable:: rgridmesh(:),zgridmesh(:), d(:), hz(:), cz(:), hr(:), cr(:), hmesh(:) + integer, allocatable:: igridmesh(:), jgridmesh(:) + + !if(allocated(etilde)) deallocate(etilde) + nbreducedspline=count(bsplinetype .eq. 1) + + Call get_dim(spl2,nrank,nrz,norder) + + ! Obtain grid data drom the spline structure + Allocate(zgrid(1:nrz(1)+1)) + Allocate(rgrid(1:nrz(2)+1)) + zgrid=spl2%sp1%knots(0:nrz(1)) + rgrid=spl2%sp2%knots(0:nrz(2)) + + allocate(linkedspline(nrank(1),nrank(2))) + + allocate(innersplinelist%k(nrank(1)*nrank(2)),innersplinelist%weight(nrank(1)*nrank(2))) + allocate(Ibsplinetype(nrank(1)*nrank(2))) + allocate(rgridmesh(nrank(1)*nrank(2)), zgridmesh(nrank(1)*nrank(2))) + allocate(igridmesh(nrank(1)*nrank(2)), jgridmesh(nrank(1)*nrank(2))) + allocate(hmesh(nrank(1)*nrank(2))) + allocate(d(size(bsplinetype))) + Ibsplinetype=.False. + + call calcsplinecenters(spl2%sp1,cz,hz) + call calcsplinecenters(spl2%sp2,cr,hr) + Do i=0,nrank(2)-1 + zgridmesh(i*nrank(1)+1:(i+1)*nrank(1))=cz + rgridmesh(i*nrank(1)+1:(i+1)*nrank(1))=cr(i+1) + hmesh(i*nrank(1)+1:(i+1)*nrank(1))=hr(i+1)*hz + igridmesh(i*nrank(1)+1:(i+1)*nrank(1))=(/ (j,j=0,nrank(1)-1)/) + jgridmesh(i*nrank(1)+1:(i+1)*nrank(1))=i + End do + + ! allocate etilde and its transpose + call init(nrank(1)*nrank(2),nbreducedspline,etilde) + call init(nrank(1)*nrank(2),nbreducedspline,etildet) + + k=1 + + Do i=1,nrank(1)*nrank(2) + if(bsplinetype(i) .ne. 1) cycle ! span of this spline is completely outside D + ! one cell of bspline i is completely in D + icellz=mod(i-1,nrank(1))+1 + icellr=(i-1)/(nrank(1))+1 + outer: do l=max(1,icellz-norder(1)),min(nrz(1),icellz) + do m=max(1,icellr-norder(2)),min(nrz(2),icellr) + if (celltype(l,m).eq.1) then + call geom_weight((zgrid(l)+zgrid(l+1))/2,(rgrid(m)+rgrid(m+1))/2,wgeomi) + EXIT outer + end if + end do + end do outer + call putele(etilde,k,i,1/wgeomi) + call putele(etildet,i,k,1/wgeomi) + innersplinelist%k(i)=k + innersplinelist%weight(i)=1/wgeomi + k=k+1 + linkedspline(icellz,icellr)=i + Ibsplinetype(i)=icellz+norder(1) .le. nrank(1) .and. icellr+norder(2) .le. nrank(2) + if(.not. Ibsplinetype(i)) cycle + do m=0,norder(2) + do l=0,norder(1) + ! Check if all positive splines in this spline domain are inner splines + Ibsplinetype(i)=Ibsplinetype(i) .and. (bsplinetype(i+l+m*nrank(1)) .eq. 1) + end do + end do + end do + !!$OMP PARALLEL DO DEFAULT(SHARED), PRIVATE(d,k,icellz,icellr,jcellr,jcellz,indexdistance,n,i,m,l,eij,linkedi) + Do j=1,nrank(1)*nrank(2) + ! find the closest b-spline fully in D for the web method + if(bsplinetype(j) .ne. 0) cycle + d=0 + where(Ibsplinetype)! calculate distance between center of Interior splines and spline j + d=(zgridmesh(j)-zgridmesh)**2+(rgridmesh(j)-rgridmesh)**2 + end where + k=minloc(d,1,MASK=Ibsplinetype) + icellz=mod(k-1,nrank(1)) + icellr=(k-1)/(nrank(1)) + jcellz=mod(j-1,nrank(1)) + jcellr=(j-1)/(nrank(1)) + indexdistance=real((jcellz-icellz)**2 + (jcellr-icellr)**2,kind=db) + if(d(k) .gt. ((norder(1)+2)**2+(norder(2)+2)**2)*2 .and. mpirank .eq. 0)then + Write(*,'(a)') 'Warning on system conditioning, the number of radial or axial points could be too low!' + Write(*,'(a,1f6.2,a,2(1pe12.4))') 'Distance found: ', sqrt(indexdistance), ' at (z,r): ',zgridmesh(j)*rnorm, rgridmesh(j)*rnorm + !stop + end if + + linkedspline(jcellz+1,jcellr+1)=k + do n=0,norder(2) + do i=0,norder(1) + eij=1 + !eij=1 + do m=0,norder(2) + if(n.eq.m) cycle + eij=eij*(rgridmesh(j)-rgridmesh(k+m*nrank(1)))/(rgridmesh(k+n*nrank(1))-rgridmesh(k+m*nrank(1))) + !eij=eij*(jcellr-icellr-m)/(n-m) + end do + do l=0,norder(1) + if( i .eq. l ) cycle + eij=eij*(zgridmesh(j)-zgridmesh(k+l))/(zgridmesh(k+i)-zgridmesh(k+l)) + !eij=eij*(jcellz-icellz-l)/(i-l) + end do + linkedi=innersplinelist%k(k+i+n*nrank(1)) ! equivalent to findloc, necessary for ifort 17 + eij=eij*innersplinelist%weight(k+i+n*nrank(1)) + call putele(etilde,linkedi,j,eij) + call putele(etildet,j,linkedi,eij) + end do + end do + end do + !!$OMP End parallel do + + call to_mat(etilde) + call to_mat(etildet) + end subroutine + + Subroutine calcsplinecenters(spl,ctrs,heights) + use bsplines + type(spline1d):: spl + real(kind=db), allocatable:: ctrs(:), heights(:) + integer:: nrank, nx, order, i, left1, j, left2, left3 + real(kind=db):: x1, x2, x3 + real(kind=db), allocatable:: fun1(:,:), fun2(:,:), fun3(:,:) + real(kind=db),allocatable:: init_heights(:) + + call get_dim(spl, nrank, nx, order) + + if (allocated(ctrs)) deallocate(ctrs) + if (allocated(heights)) deallocate(heights) + allocate(heights(nrank)) + allocate(ctrs(nrank)) + allocate(fun1(1:order+1,0:1), fun2(1:order+1,0:1), fun3(1:order+1,0:1)) + allocate(init_heights(nrank)) + + init_heights=1.0 + ctrs(:)=spl%knots(0:nrank-1) + call gridval(spl, ctrs, heights, 0, init_heights) + if (order .gt.1) then + do i=2,nrank-1 + x1=spl%knots(i-order)+1e5*Epsilon(x1) + x2=spl%knots(i-1)-1e5*Epsilon(x2) + left1=max(0,i-order) + left2=min(nx-2,i-2) + call basfun(x1,spl,fun1, left1+1) + !Write(*,*) 'i,xpt,xptm,w,wold,',i,xpt,xptm,w,wold + call basfun(x2,spl,fun2, left2+1) + fun3=1 + j=0 + Do while( j .le. 300) + !Write(*,*) 'i,xpt,xptm,w,wold,',i,xpt,xptm,w,wold + x3=(x1+x2)/2 + call locintv(spl, x3, left3) + call basfun(x3,spl,fun3, left3+1) + if( (x2-x1).lt.1e-13) exit + if(abs(fun3(i-left3,1)).lt.1e-15) exit + + if(fun3(i-left3,1)*fun1(i-left1,1).le.0) then + fun2=fun3 + x2=x3 + left2=left3 + else + fun1=fun3 + x1=x3 + left1=left3 + end if + j=j+1 + End do + ctrs(i)=x3 + heights(i)=fun3(i-left3,0) + end do + end if + end subroutine +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + Subroutine classifycell(zgrid,rgrid,gridctype) + Real(kind=db):: zgrid(:),rgrid(:) + Integer:: gridctype(:,:) + Integer::i,j + gridctype=-1 + !$OMP parallel do private(i) + Do j=1,size(rgrid,1)-1 + Do i=1,size(zgrid,1)-1 + ! Determines the type inner/boundary/outer for each cell + Call classification((/zgrid(i),zgrid(i+1)/),(/rgrid(j),rgrid(j+1)/),& + & gridctype(i,j)) + End Do + End do + !$OMP end parallel do + End subroutine +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + subroutine classifyspline(spl2,gridctype,bsptype) + type(spline2d):: spl2 + Integer:: gridctype(:,:) + Integer:: bsptype(:) + Integer:: nrank(2), nrz(2), ndegree(2) + Integer:: i, j, mu, imin, imax, jmin, jmax + Integer, Allocatable:: splinespan(:,:) + + call get_dim(spl2, nrank, nrz, ndegree) + ! by default, all splines have part of their support outside the domain D + bsptype=0 + + Allocate(splinespan(ndegree(1)+1,ndegree(2)+1)) + Do mu=1,nrank(1)*nrank(2) + ! scan the spline space + ! obtain the axial spline index + i=mod(mu-1,nrank(1))+1 + ! obtain the radial spline index + j=(mu-1)/nrank(1)+1 + + ! by default all cells are outside for correct behavior in boundaries + splinespan=-1 + + ! find the axial span of this spline in cell indices + imin=max(1,i-ndegree(1)) + imax=min(nrz(1),i) + ! find the radial span of this spline in cell indices + jmin=max(1,j-ndegree(2)) + jmax=min(nrz(2),j) + + ! obtain the cell type on which the spline is defined + splinespan(1:(imax-imin+1),1:(jmax-jmin+1))=gridctype(imin:imax,jmin:jmax) + if ( ANY( splinespan==1 ) ) then + ! if at least one cell is fully in the domain the spline is an inside spline + bsptype(mu)=1 + else if (.not. ANY( splinespan==0 )) then + ! if all the cells are outside, this is an outside spline + bsptype(mu)=-1 + end if + end do + + End subroutine +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> general placeholder function used in fields_mod to compute the weight for weighted_bsplines +!> This functions combine an ellipse function and an horizontal wall +! +!--------------------------------------------------------------------------- + SUBROUTINE geom_weight0(z,r,w) + Real(kind=db), INTENT(IN):: r,z + Real(kind=db), INTENT(OUT):: w + Real(kind=db):: rtmp(1:1), ztmp(1:1), wtmp(1:1,1:1) + + ztmp=z + rtmp=r + + call Dirichlet_weight(ztmp,rtmp,wtmp) + w=wtmp(1,1) + + End SUBROUTINE geom_weight0 + + SUBROUTINE geom_weight1(z,r,w) + Real(kind=db), INTENT(IN):: r,z + Real(kind=db), INTENT(OUT):: w(0:) + Real(kind=db):: rtmp(1:1), ztmp(1:1) + Real(kind=db):: wtmp(1:1,0:size(w,1)-1) + + ztmp=z + rtmp=r + + call Dirichlet_weight(ztmp,rtmp,wtmp) + w=wtmp(1,:) + + End SUBROUTINE geom_weight1 + + SUBROUTINE geom_weight2(z,r,w) + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + + call Dirichlet_weight(z,r,w) +! + End SUBROUTINE geom_weight2 + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> general placeholder function used in fields_mod to compute the domain weight for weighted_bsplines +!--------------------------------------------------------------------------- + SUBROUTINE dom_weight0(z,r,w,idwall) + Real(kind=db), INTENT(IN):: r,z + Real(kind=db), INTENT(OUT):: w + Real(kind=db):: rtmp(1:1), ztmp(1:1), wtmp(1:1,1:1) + INTEGER:: idwalltmp(1:1) + INTEGER, optional, INTENT(OUT):: idwall + ztmp=z + rtmp=r + + call domain_weight(ztmp,rtmp,wtmp,idwall=idwalltmp) + w=wtmp(1,1) + if (present(idwall)) idwall=idwalltmp(1) + + End SUBROUTINE dom_weight0 + + SUBROUTINE dom_weight1(z,r,w,idwall) + Real(kind=db), INTENT(IN):: r,z + Real(kind=db), INTENT(OUT):: w(0:) + Real(kind=db):: rtmp(1:1), ztmp(1:1) + Real(kind=db):: wtmp(1:1,0:size(w,1)-1) + INTEGER:: idwalltmp(1:1) + INTEGER, optional, INTENT(OUT):: idwall + + ztmp=z + rtmp=r + + + call domain_weight(ztmp,rtmp,wtmp,idwall=idwalltmp) + w=wtmp(1,:) + if (present(idwall)) idwall=idwalltmp(1) + + End SUBROUTINE dom_weight1 + + SUBROUTINE dom_weight2(z,r,w,idwall) + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + INTEGER, optional, INTENT(OUT):: idwall(:) + + call domain_weight(z,r,w,idwall=idwall) +! + End SUBROUTINE dom_weight2 + + SUBROUTINE dom_weight3(z,r,w,idwall) + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:) + Real(kind=db):: wtmp(size(w,1),1:1) + INTEGER, optional, INTENT(OUT):: idwall(:) + + call domain_weight(z,r,wtmp,idwall=idwall) + w=wtmp(:,1) +! + End SUBROUTINE dom_weight3 + + + SUBROUTINE geom_weightstd(z,r,w,wupper) + ! return the geometric weight for a coaxial configuration + ! or central conductor + ellipse + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: walltmp(size(w,1),0:size(w,2)-1), elliptmp(size(w,1),0:size(w,2)-1) + Real(kind=db):: squareroot(size(w,1)), denom(size(w,1)) + + call cyllweight(z,r,walltmp, r_a, above1) + SELECT CASE (walltype) + CASE (0) + call cyllweight(z,r,elliptmp, r_b, above2) + CASE DEFAULT + call ellipseweight(z,r,elliptmp,r_0, z_0, invr_z, invr_r, Interior) + END SELECT + + if(interior.eq.0 .and. above2.eq.0) then + w=walltmp(:,:) + else if (above1 .eq. 0) then + w=elliptmp(:,:) + else + + denom=walltmp(:,0)**2+elliptmp(:,0)**2 + squareroot=sqrt(denom) + + w(:,0)=walltmp(:,0)+elliptmp(:,0)-squareroot ! weight at position r,z + + If(size(w,2) .gt. 1) then ! first derivative + w(:,1)=walltmp(:,1)+elliptmp(:,1)-(elliptmp(:,1)*elliptmp(:,0)+walltmp(:,1)*walltmp(:,0))/& + & squareroot ! z derivative of w + w(:,2)=walltmp(:,2)+elliptmp(:,2)-(elliptmp(:,2)*elliptmp(:,0)+walltmp(:,2)*walltmp(:,0))/& + & squareroot ! r derivative of w + End If + End if + + End SUBROUTINE geom_weightstd + + SUBROUTINE classification(z, r, ctype) + ! classify if cell is fully inside, outside or on the boundary of the domain + real(kind=db), INTENT(IN):: r(2), z(2) + INTEGER, INTENT(OUT):: ctype + Real(kind=db)::weights(5,1:1) + Real(kind=db):: zeval(5),reval(5) + + zeval=(/ z(1),z(2),z(1),z(2), (z(2)+z(1))/2 /) + reval=(/ r(1),r(1),r(2),r(2), (r(2)+r(1))/2 /) + + CAll dom_weight(zeval,reval,weights) + + ctype=int(sign(1.0_db,weights(5,1))) + + If(weights(1,1)*weights(2,1) .le. 0 ) then + ctype=0 + return + End If + If(weights(1,1)*weights(3,1) .le. 0 ) then + ctype=0 + return + End If + If(weights(2,1)*weights(4,1) .le. 0 ) then + ctype=0 + return + End If + If(weights(3,1)*weights(4,1) .le. 0 ) then + ctype=0 + return + End If + end subroutine +! ################################################################## + Subroutine calc_gauss(spl2, ngauss, i, j, xgauss, wgauss, gausssize, celltype) + ! calculates the gauss integration points for the FEM method for any cell type + ! takes care of boundary cells as well + type(spline2d), INTENT(IN):: spl2 + Integer, Intent(out):: gausssize + INTEGER, Intent(out), Optional :: celltype + Real(kind=db), Allocatable::rgrid(:),zgrid(:) + Real(kind=db), ALLOCATABLE::xgauss(:,:), wgauss(:) + Integer:: i,j, ngauss(2) + Real(kind=db),Allocatable:: pts(:,:), zpoints(:) + Real(kind=db):: zg(ngauss(1)),rg(ngauss(2)), wzg(ngauss(1)), wrg(ngauss(2)) + Integer:: k, l, directiondown, directionup, nbzpoints, direction, ctype + Logical:: hasmaxpoint + Real(kind=db):: xptup, xptdown, w,wlu,wld, rmin, rmax + type(spline1d):: splz, splr + + splz=spl2%sp1 + splr=spl2%sp2 + Allocate(zgrid(1:splz%nints+1)) + Allocate(rgrid(1:splr%nints+1)) + zgrid=splz%knots(0:splz%nints) + rgrid=splr%knots(0:splr%nints) + + hasmaxpoint=.false. + If(allocated(xgauss)) deallocate(xgauss) + if(allocated(wgauss)) deallocate(wgauss) + !Call classification((/zgrid(i),zgrid(i+1)/),(/rgrid(j),rgrid(j+1)/),ctype) + ctype=gridcelltype(i,j) + If (ctype .ge. 1) then ! we have a normal internal cell + Allocate(xgauss(ngauss(1)*ngauss(2),2)) + Allocate(wgauss(ngauss(1)*ngauss(2))) + gausssize=ngauss(1)*ngauss(2) + !Computation of gauss weight and position in r and z direction for gaussian integration + CALL get_gauss(spl2%sp1, ngauss(1), i, zg, wzg) + CALL get_gauss(spl2%sp2, ngauss(2), j, rg, wrg) + ! Construction of matrix xgauss and wgauss storing the weight and position for 2d gaussian integration + DO k=1,ngauss(2) + xgauss((k-1)*ngauss(1)+1:k*ngauss(1),1)=zg + xgauss((k-1)*ngauss(1)+1:k*ngauss(1),2)=rg(k) + wgauss((k-1)*ngauss(1)+1:k*ngauss(1))=wrg(k)*wzg + END DO + Else If(ctype.eq.0) then ! we have a boundary cell + !Call Boundary_Limits(pts) + !Do k=1,size(pts,1) + ! hasmaxpoint=hasmaxpoint .or. (pts(k,1) .ge. zgrid(i) .and. pts(k,1) .le. zgrid(i+1) & + ! & .and. pts(k,2) .ge. rgrid(j) .and. pts(k,2) .le. rgrid(j+1)) + !End do + If(.not. hasmaxpoint) Then + directiondown=1 + directionup=1 + ! We check if the boundary goes through the cell upper and lower limit + Call Find_crosspointdico((/zgrid(i),zgrid(i+1)/),rgrid(j),xptdown,directiondown) + Call Find_crosspointdico((/zgrid(i),zgrid(i+1)/),rgrid(j+1),xptup,directionup) + call dom_weight(zgrid(i),rgrid(j),wld) + call dom_weight(zgrid(i),rgrid(j+1),wlu) + select case ( directionup+directiondown) + Case (0) ! The intersections are only on the left and right cell boundaries + ! or the upper and lower limits are full boundaries + nbzpoints=2 + Allocate(zpoints(nbzpoints)) + zpoints=(/zgrid(i),zgrid(i+1)/) + Case(1) + if(directiondown.eq.1)then + if( wlu .gt. 0) then + ! the lower left corner is inside + nbzpoints=3 + Allocate(zpoints(nbzpoints)) + zpoints=(/zgrid(i), xptdown,zgrid(i+1)/) + else + nbzpoints=2 + Allocate(zpoints(nbzpoints)) + if(wld.gt.0)then + ! the upper left corner is inside + zpoints=(/zgrid(i),xptdown/) + else + zpoints=(/xptdown,zgrid(i+1)/) + end if + end if + else + if(wld .gt. 0) then + ! the lower left corner is inside + nbzpoints=3 + Allocate(zpoints(nbzpoints)) + zpoints=(/zgrid(i), xptup,zgrid(i+1)/) + else + nbzpoints=2 + Allocate(zpoints(nbzpoints)) + if(wlu.gt.0)then + ! the upper left corner is inside + zpoints=(/zgrid(i),xptup/) + else + zpoints=(/xptup,zgrid(i+1)/) + end if + end if + end if + Case(2) + nbzpoints=4 + Allocate(zpoints(nbzpoints)) + zpoints=(/zgrid(i),min(xptdown,xptup),max(xptdown,xptup), zgrid(i+1) /) + !If(wld.lt.0) zpoints=(/min(xptdown,xptup),max(xptdown,xptup), zgrid(i+1) /) + !else + ! nbzpoints=2 + ! Allocate(zpoints(nbzpoints)) + ! If(wld.ge.0) zpoints=(/zgrid(i),min(xptdown,xptup) /) + ! If(wld.lt.0) zpoints=(/max(xptdown,xptup), zgrid(i+1) /) + !end if + + End select + Allocate(xgauss(ngauss(1)*ngauss(2)*(nbzpoints-1),2)) + Allocate(wgauss(ngauss(1)*ngauss(2)*(nbzpoints-1))) + gausssize=ngauss(1)*ngauss(2)*(nbzpoints-1) + ! Compute gauss points + Do l=1,nbzpoints-1 + !CALL get_gauss(spl2%sp1, ngauss(1), i, zg, wzg) + Call gauleg(zpoints(l),zpoints(l+1),zg,wzg,ngauss(1)) + + ! We test if the lower or upper side is in the domain + call dom_weight(zg(1),rgrid(j),wld) + rmin=rgrid(j) + if (wld .le. 0) rmin = rgrid(j+1) + + Do k=1,ngauss(1) + direction=2 + Call Find_crosspointdico((/rgrid(j),rgrid(j+1)/),zg(k),rmax,direction) ! We compute the radial limits at each z position + Call gauleg(min(rmin,rmax),max(rmin,rmax),rg,wrg,ngauss(2)) ! We obtain the gauss w and pos for these boundaries + + xgauss(k+(l-1)*ngauss(1)*ngauss(2) : l*ngauss(2)*ngauss(1) : ngauss(1),1) = zg(k) + xgauss(k+(l-1)*ngauss(1)*ngauss(2) : l*ngauss(2)*ngauss(1) : ngauss(1),2) = rg + wgauss(k+(l-1)*ngauss(1)*ngauss(2) : l*ngauss(2)*ngauss(1) : ngauss(1)) = wrg*wzg(k) + End do + End Do + End If + Else + gausssize=1 + Allocate(xgauss(1:1,2)) + Allocate(wgauss(1:1)) + !Computation of gauss weight and position in r and z direction for gaussian integration + CALL get_gauss(spl2%sp1, ngauss(1), i, zg, wzg) + CALL get_gauss(spl2%sp2, ngauss(2), j, rg, wrg) + ! Construction of matrix xgauss and wgauss storing the weight and position for 2d gaussian integration + xgauss(1,1)=(zg(1)+zg(ngauss(1)))*0.5 + xgauss(1,2)=(rg(1)+rg(ngauss(2)))*0.5 + wgauss(1)=0 + End If + If(PRESENT(celltype)) celltype=ctype + + End Subroutine calc_gauss + + Subroutine Find_crosspoint(x,y,xpt, direction) + ! calculates the boundary limit between x(1) and x(2) using Newton's method + Real(kind=db):: x(2), y + Real(kind=db):: xptm, xpt, temp + Real(kind=db):: w, wold + Real(kind=db):: w1, w2, w3 + Real(kind=db):: x1, x2, x3 + + + Integer, Intent(INOUT):: direction + Integer:: i + ! calculates the position of the boundary ( where the weight changes sign ) + ! between x(1) and x(2) at 2nd coordinate y + xptm=x(1) + xpt=x(2) + i=0 + ! direction=1 finds cross-point along z + if(direction .eq.1) Call dom_weight(xptm,y,wold) + ! direction=2 finds cross-point along r + if(direction .eq.2) Call dom_weight(y,xptm,wold) + if(direction .eq.1) Call dom_weight(xpt,y,w) + if(direction .eq.2) Call dom_weight(y,xpt,w) + ! if the weight doesn't change sign there is no cross point + if(w*wold.gt.0) then + direction=0 + return + End If + ! Find the cross-point + Do while( i .le.100 .and. abs(w).gt.1e-9) + !Write(*,*) 'i,xpt,xptm,w,wold,',i,xpt,xptm,w,wold + xptm=xpt-w*(xpt-xptm)/(w-wold) + wold=w + temp=xptm + xptm=xpt + xpt=temp + i=i+1 + if(direction .eq.1) Call dom_weight(xpt,y,w) + if(direction .eq.2) Call dom_weight(y,xpt,w) + End do + if(xpt .ge. x(2) .or. xpt .le. x(1) ) direction=0 + + End Subroutine + + + Subroutine Find_crosspointdico(x,y,xpt, direction) + ! calculates the boundary limit between x(1) and x(2) using dichotomy method + Real(kind=db):: x(2), y + Real(kind=db):: xpt + Real(kind=db):: w1, w2, w3 + Real(kind=db):: x1, x2, x3 + + + Integer, Intent(INOUT):: direction + Integer:: i + ! calculates the position of the boundary ( where the weight changes sign ) + ! between x(1) and x(2) at 2nd coordinate y + x1=x(1) + x2=x(2) + + i=0 + select case(direction) + case(1) + ! direction=1 finds cross-point along z + Call dom_weight(x1,y,w1) + Call dom_weight(x2,y,w2) + case(2) + ! direction=2 finds cross-point along r + Call dom_weight(y,x1,w1) + Call dom_weight(y,x2,w2) + end select + ! if the weight doesn't change sign there is no cross point + if(w1*w2.gt.0) then + direction=0 + xpt=x2 + return + End If + ! Find the cross-point + Do while( i .le. 500 .and. abs(x2-x1).gt.1e-13) + x3=0.5*(x1+x2) + i=i+1 + select case(direction) + case(1) + ! direction=1 finds cross-point along z + Call dom_weight(x3,y,w3) + case(2) + ! direction=2 finds cross-point along r + Call dom_weight(y,x3,w3) + end select + if(w1*w3.gt.0)then! we are in a region were there is no change of sign + x1=x3 + w1=w3 + else + x2=x3 + w2=w3 + end if + !if(abs(w3).lt.1e-14.and. w3 .ge.0)Exit + End do + xpt=x3 + if(xpt .ge. x(2) .or. xpt .le. x(1) ) direction=0 + + End Subroutine + + + SUBROUTINE geom_rvaschtot(z,r,w,idwall) + ! returns the total weight which is the same as the Dirichlet weight for a boundary + ! defined with Rvaschev functions + Use splinebound, ONLY: spline_w + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), allocatable:: w2(:,:) + Real(kind=db), allocatable:: w3(:,:) + INTEGER, optional, INTENT(OUT):: idwall(:) + INTEGER:: sw2 + sw2=size(w,2) + + if(present(idwall)) then + idwall=0 + allocate(w2(size(w,1),1:size(w,2))) + allocate(w3(size(w,1),1:size(w,2))) + call Dirichlet_weight(z,r,w2,w3) + ! gives total weight + where(w2(:,1).le.0) idwall=1 + where(w3(:,1).le.0) idwall=2 + call Combine(w2, w3, w, -1) + else + call Dirichlet_weight(z,r,w) + end if + + End SUBROUTINE geom_rvaschtot + + Subroutine gstd(z,r,gtilde,w) + ! g tilde function added to rhs of poisson solver for the standard coaxial configuration + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: gtilde(:,0:) + Real(kind=db), INTENT(IN),OPTIONAL::w(:,0:) + Real(kind=db):: belowtmp(size(r,1),0:size(gtilde,2)-1), abovetmp(size(r,1),0:size(gtilde,2)-1) + Real(kind=db):: denom(size(r,1)) + + if (above1.eq.0) then + gtilde=0 + gtilde(:,0)=Phiup + RETURN + end if + + ! Weight functions necessary for calculation of g + ! coaxial insert + call cyllweight(z,r,belowtmp, r_a, above1) + SELECT CASE (walltype) + CASE (0) + ! top cylinder + call cyllweight(z,r,abovetmp, r_b, above2) + CASE DEFAULT + ! Ellipse as in gt170 + call ellipseweight(z,r,abovetmp,r_0,z_0,invr_z,invr_r,Interior) + END SELECT + + ! Extension to the whole domain of the boundary conditions + ! constructed by weight multiplied with boundary value + gtilde(:,0)=(Phidown*abovetmp(:,0) + Phiup*belowtmp(:,0) ) / & + & (abovetmp(:,0)+belowtmp(:,0)) + + If(size(gtilde,2) .gt. 2) then ! first derivative + denom=(abovetmp(:,0)+belowtmp(:,0))**2 + gtilde(:,1)=(Phiup-Phidown)*(belowtmp(:,1)*abovetmp(:,0)-belowtmp(:,0)*abovetmp(:,1)) / & + & denom ! Axial derivative + gtilde(:,2)=(Phiup-Phidown)*(belowtmp(:,2)*abovetmp(:,0)-belowtmp(:,0)*abovetmp(:,2)) / & + & denom ! Radial derivative + End If + + End subroutine + + SUBROUTINE gUpDown(z,r,gtilde,w) + ! g tilde function added to rhs of poisson solver by combining two boundaries set at different potentials + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: gtilde(:,0:) + Real(kind=db), INTENT(IN),OPTIONAL::w(:,0:) + Real(kind=db):: belowtmp(size(r,1),0:size(gtilde,2)-1), abovetmp(size(r,1),0:size(gtilde,2)-1) + Real(kind=db):: denom(size(r,1)) + + ! Weight functions necessary for calculation of g + call Dirichlet_weight(z,r,belowtmp,abovetmp) + + ! Extension to the whole domain of the boundary conditions + ! constructed by weight multiplied with boundary value + gtilde(:,0)=(Phidown*abovetmp(:,0) + Phiup*belowtmp(:,0) ) / & + & (abovetmp(:,0)+belowtmp(:,0)) + + If(size(gtilde,2) .gt. 2) then ! first derivative + denom=(abovetmp(:,0)+belowtmp(:,0))**2 + gtilde(:,1)=(Phiup-Phidown)*(belowtmp(:,1)*abovetmp(:,0)-belowtmp(:,0)*abovetmp(:,1)) / & + & denom ! Axial derivative + gtilde(:,2)=(Phiup-Phidown)*(belowtmp(:,2)*abovetmp(:,0)-belowtmp(:,0)*abovetmp(:,2)) / & + & denom ! Radial derivative + End If + + END SUBROUTINE gUpDown + + Subroutine gtest(z,r,gtilde,w) + ! calculates the Poisson gtilde term for testing the solver on a new geometry + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: gtilde(:,0:) + Real(kind=db), INTENT(IN),OPTIONAL::w(:,0:) + Real(kind=db):: wtmp(size(gtilde,1),0:size(gtilde,2)-1) + + ! + if(present(w)) then + wtmp=w + else + call Dirichlet_weight(z,r,wtmp) + end if + + gtilde(:,0)=(sin(pi*(z-test_pars%z0)/(test_pars%Lz))*sin(pi*(r-test_pars%r0)/(test_pars%Lr))+2) + If(size(gtilde,2) .gt. 1) then ! first derivative + gtilde(:,1)=pi/(test_pars%Lz)*cos(pi*(z-test_pars%z0)/(test_pars%Lz))*sin(pi*(r-test_pars%r0)/(test_pars%Lr))*(1-wtmp(:,0))-wtmp(:,1)*gtilde(:,0) + gtilde(:,2)=pi/(test_pars%Lr)*sin(pi*(z-test_pars%z0)/(test_pars%Lz))*cos(pi*(r-test_pars%r0)/(test_pars%Lr))*(1-wtmp(:,0))-wtmp(:,2)*gtilde(:,0) + End If + gtilde(:,0)=gtilde(:,0)*(1-wtmp(:,0)) + + End subroutine + + Subroutine ftest(z,r,f) + ! calculates the Poisson source term for testing the solver on a new geometry + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT)::f(:,0:) + + ! + f(:,0)=(pi/test_pars%Lz)**2*sin(pi*(z-test_pars%z0)/(test_pars%Lz))*sin(pi*(r-test_pars%r0)/(test_pars%Lr))& + & + (pi/test_pars%Lr)*sin(pi*(z-test_pars%z0)/(test_pars%Lz))& + & *( -1/r*(cos(pi*(r-test_pars%r0)/(test_pars%Lr))) + (pi/test_pars%Lr)*sin(pi*(r-test_pars%r0)/(test_pars%Lr))) + + End Subroutine ftest + + subroutine Reducematrix(full,reduced) + ! Reduce the Finite element matrix from full spline space to reduced web-spline space + use mumps_bsplines + type(mumps_mat):: full, reduced, tempmat1, tempmat2 + Integer:: fullrank, reducedrank + Integer:: i,j, k + + call to_mat(full) + + fullrank=full%rank + reducedrank=nbreducedspline + + call init(reducedrank,2,reduced) + call init(fullrank,2,tempmat1) + call init(reducedrank,2,tempmat2) + + tempmat1=mmx_mumps_mat_loc(full,etildet,(/fullrank,fullrank/),(/fullrank,reducedrank/),.false.) + + tempmat2=mmx_mumps_mat_loc(etildet,tempmat1,(/fullrank,reducedrank/),(/fullrank,reducedrank/),.true.) + do i=1,reducedrank + do k=tempmat2%irow(i),tempmat2%irow(i+1)-1 + j=tempmat2%cols(k) + call putele(reduced, i, j, tempmat2%val(k)) + end do + end do + + end subroutine + + !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + FUNCTION mmx_mumps_mat_loc(mata, matb, ranka, rankb, transpose) RESULT(matc) + ! + ! Return product matc=mata*matb + ! All matrices are represented in csr sparse representation + ! + Use mumps_bsplines + TYPE(mumps_mat) :: mata,matb,matc + INTEGER:: ranka(2), rankb(2) ! nb of (rows,columns) for each matrix + INTEGER :: n, info, m, request, sort + LOGICAL:: transpose + Character(1):: T + ! + m=ranka(1) + n=ranka(2) + T='N' + if (transpose)then + T='T' + m=ranka(2) + n=ranka(1) + end if + !#ifdef MKL + ! + if(associated(matc%val)) deallocate(matc%val) + if(associated(matc%cols)) deallocate(matc%cols) + if(associated(matc%irow)) deallocate(matc%irow) + allocate(matc%irow(m+1)) + allocate(matc%cols(1)) + allocate(matc%val(1)) + + request=1 + sort=7 + + CALL mkl_dcsrmultcsr(T, request, sort, ranka(1), ranka(2), rankb(2), & + & mata%val, mata%cols, mata%irow(1), & + & matb%val, matb%cols, matb%irow(1), & + & matc%val, matc%cols, matc%irow(1), & + & 1, info) + if(info .gt. 0) WRITE(*,'(a,i)') " Error in mmx_mumps_mat_loc: ", info + + if(associated(matc%val)) deallocate(matc%val) + if(associated(matc%cols)) deallocate(matc%cols) + allocate(matc%val(matc%irow(m+1)-1)) + allocate(matc%cols(matc%irow(m+1)-1)) + + request=2 + + CALL mkl_dcsrmultcsr(T, request, sort, ranka(1), ranka(2), rankb(2), & + & mata%val, mata%cols, mata%irow(1), & + & matb%val, matb%cols, matb%irow(1), & + & matc%val, matc%cols, matc%irow(1), & + & 1, info) + + if(info .ne. 0) WRITE(*,'(a,i)') " Error in mmx_mumps_mat_loc: ", info + if (transpose)then + matc%rank=ranka(2) + else + matc%rank=ranka(1) + end if + ! + END FUNCTION mmx_mumps_mat_loc + +END MODULE geometry \ No newline at end of file diff --git a/src/inital.f90 b/src/inital.f90 index db08faa..829ed5a 100644 --- a/src/inital.f90 +++ b/src/inital.f90 @@ -1,35 +1,56 @@ SUBROUTINE inital ! USE basic USE beam USE fields USE mpihelper -USE maxwellsource +USE maxwsrce +Use geometry +Use neutcol ! ! Set initial conditions ! IMPLICIT NONE + INTEGER:: i ! !________________________________________________________________________________ IF(mpirank .eq. 0) WRITE(*,'(a/)') '=== Set initial conditions ===' !________________________________________________________________________________ ! ! Init Electric and Magnetic Fields ALLOCATE(partslist(nbspecies)) - CALL load_parts ! will call the localisation CALL fields_init - CALL fields_comm_init + call timera(0, "read_geom") + call read_geom(lu_in, rnorm, splrz, Potinn, Potout) + call timera(1, "read_geom") + call mag_init + CALL load_parts ! will call the localisation + + qnorm=abs(partslist(1)%weight*partslist(1)%q) + + IF(mpisize .gt. 1) THEN + CALL calc_Zbounds(partslist(1), Zbounds, femorder) + END IF + + Do i=1,size(partslist) + CALL keep_mpi_self_parts(partslist(i), Zbounds) + END DO + + call fields_start + CALL fields_comm_init(Zbounds) WRITE(*,*) "Fields initialized" - IF (nlmaxwellsource) CALL maxwellsource_init(lu_in, time) - CALL rhscon(partslist(1)) + CALL rhscon(partslist) WRITE(*,*) "Initial rhs computed" - CALL poisson + CALL poisson(splrz) WRITE(*,*) "Initial field solved" - CALL EForcescomp(partslist(1)) + CALL EFieldscompatparts(partslist(1)) WRITE(*,*) "Initial forces computed" - !CALL adapt_vinit(partslist(1)) - CALL diagnostics - WRITE(*,*) "Initial beam%diagnostics calculated" + CALL adapt_vinit(partslist(1)) + WRITE(*,*)"init velocity adapted" + + + + !________________________________________________________________________________ END SUBROUTINE inital diff --git a/src/intel.mk b/src/intel.mk index c72a836..725af6a 100644 --- a/src/intel.mk +++ b/src/intel.mk @@ -1,24 +1,22 @@ CC = icc CFLAGS = FC = mpiifort # -#BSPLINES=$(HOME)/lib/intel/bsplines -#FUTILS=$(HOME)/lib/intel/futils -#PARMETIS=$(PARMETIS_ROOT) -#MUMPSLIBS= -ldmumps -lzmumps -lmumps_common -lpord -#XGRAFIX=$(HOME)/lib/intel/xgrafix -#MUMPS=$(MUMPS_ROOT) -#HDF5=$(HDF5_ROOT) +PARMETIS=$(PARMETIS_ROOT) +MUMPSLIBS= -ldmumps -lzmumps -lmumps_common -lpord +XGRAFIX=$(HOME)/lib/intel/xgrafix +MUMPS=$(MUMPS_ROOT) +HDF5=$(HDF5_ROOT) # -RELEASEFLAGS = -fpp -mkl=cluster -qopenmp -qopenmp-simd -O2 -xHost -warn all -diag-enable=all -FFLAGS = -fpp -g -traceback -check bounds -mkl=cluster +RELEASEFLAGS = -fpp -mkl=cluster -qopenmp -qopenmp-simd -O3 -xHost -warn all +FFLAGS = -fpp -g -traceback -check bounds -mkl=cluster -O3 DEBUGFLAGS = -fpp -g -O0 -xHost -qopenmp -qopenmp-simd -traceback -mkl=cluster\ -check all -check bounds -check noarg_temp_created \ -warn all -ftrapuv -fpe0 -debug extended \ -check uninit -debug all -diag-enable=all PROFILEFLAGS= -g F90 = $(FC) F90FLAGS= $(RELEASEFLAGS) LDFLAGS = LIBS = diff --git a/src/main.f90 b/src/main.f90 index cc7a906..6d270eb 100644 --- a/src/main.f90 +++ b/src/main.f90 @@ -1,90 +1,89 @@ PROGRAM main ! ! Skeleton for a time dependent program ! Note: Even in this sequential version, MPI is required ! because of FUTILS (more specifcally because ! of the HASTABLE module)! ! USE basic - USE mpi + use mpi USE bsplines USE mumps_bsplines USE futils IMPLICIT NONE INTEGER:: required, provided ! ! required=MPI_THREAD_FUNNELED CALL mpi_init_thread(required,provided,ierr) IF(provided .lt. required) CALL MPI_abort(MPI_COMM_WORLD,-1,ierr) IF(ierr .ne. 0) CALL MPI_abort(MPI_COMM_WORLD,-1,ierr) CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) IF(ierr .ne. 0) CALL MPI_abort(MPI_COMM_WORLD,-1,ierr) CALL MPI_COMM_SIZE(MPI_COMM_WORLD, mpisize, ierr) IF(ierr .ne. 0) CALL MPI_abort(MPI_COMM_WORLD,-1,ierr) !-------------------------------------------------------------------------------- ! 1. Prologue CALL timera(0, 'Prologue') CALL daytim('Start at') ! ! Define data specific to run ! CALL basic_data !Definition of global variables and input paramaters loading step=0 ! IF( .NOT. nlres ) THEN CALL newrun !not implemented yet ELSE CALL restart !not implemented yet END IF ! ! Compute auxilliary values ! CALL auxval !time independent values ! ! Initial conditions ! IF( .NOT. nlres ) THEN CALL inital !plasma initialisation ELSE CALL resume !loads restart.h5 file END IF ! ! Start or restart the run ! CALL start !not implemented yet ! ! Initial diagnostocs ! CALL diagnose(0) CALL timera(1, 'Prologue') !-------------------------------------------------------------------------------- ! 2. Time stepping CALL timera(0, 'Main loop') ! DO step = step+1 cstep = cstep+1 - time = time+dt + time = time+dt*tnorm CALL tesend CALL stepon - CALL diagnose(step) - IF(modulo(step,itrestart) .eq. 0) CALL chkrst(1) + IF(modulo(step,itrestart) .eq. 0 .and. mpirank .eq. 0) CALL chkrst(1) IF( nlend ) EXIT END DO CALL timera(1, 'Main loop') !-------------------------------------------------------------------------------- ! 9. Epilogue CALL timera(0, 'Epilogue') ! CALL diagnose(-1) CALL endrun IF(mpirank .eq. 0) THEN CALL timera(1, 'Epilogue') CALL timera(9, '') CALL timera(-1, '') CALL daytim('Done at ') END IF CALL mpi_finalize(ierr) END PROGRAM main diff --git a/src/maxwellsource_mod.f90 b/src/maxwellsource_mod.f90 deleted file mode 100644 index f09575b..0000000 --- a/src/maxwellsource_mod.f90 +++ /dev/null @@ -1,166 +0,0 @@ -!------------------------------------------------------------------------------ -! EPFL/Swiss Plasma Center -!------------------------------------------------------------------------------ -! -! MODULE: maxwellsource -! -!> @author -!> Guillaume Le Bars EPFL/SPC -! -! DESCRIPTION: -!> Adds particle in the simulation according to a maxwellian distribution in velocity -!> and a uniform distribution in space -!------------------------------------------------------------------------------ - -MODULE maxwellsource - ! - USE constants - USE mpi - USE mpihelper - USE basic, ONLY: mpirank, mpisize, vnorm, rnorm, Zbounds, zgrid, & - & nlclassical, nlmaxwellsource - USE beam - USE distrib - - IMPLICIT NONE - - PRIVATE - - REAL(kind=db), SAVE :: frequency !< Number of macro particles added per second over the spawning region - REAL(kind=db), SAVE :: temperature !< temperature used for the Maxwellian velocity distribution - REAL(kind=db), SAVE :: rlimits(2) !< radial limits in which particles will be spawned - REAL(kind=db), SAVE :: zlimits(2) !< axial limits in which particles will be spawned - REAL(kind=db), SAVE :: last_t=0 !< last time when a macro-particle was added to the simulation - REAL(kind=db), SAVE :: vth !< Normalized thermal velocity computed from temperature - REAL(kind=db), SAVE :: loc_zlimits(2)!< Local axial limits in which particles will be spawned used for current mpi process - REAL(kind=db), SAVE :: loc_rlimits(2)!< Local radial limits in which particles will be spawned used for current mpi process - REAL(kind=db), SAVE :: time_start=-1.0 !< time at which the source is turned on - REAL(kind=db), SAVE :: time_end=-1.0 !< time at which the source is turned off - INTEGER, SAVE :: radialtype=2 !< type of radial distribution used for creating particles - - - NAMELIST /maxwellsourceparams/ frequency, temperature, rlimits, zlimits, time_start, time_end, radialtype - - PUBLIC:: maxwellsource_init, maxwellsource_inject - -contains - - subroutine maxwellsource_init(lu_in, time) - implicit none - INTEGER, INTENT(IN)::lu_in - REAL(kind=db), INTENT(IN):: time - REAL(kind=db):: surface - REAL(kind=db):: remsurface - INTEGER:: ierr - - READ(lu_in, maxwellsourceparams) - - IF(mpirank .eq. 0) THEN - WRITE(*, maxwellsourceparams) - END IF - vth=sqrt(kb*temperature/partslist(1)%m)/vnorm !thermal velocity - last_t=time - IF(time_start .gt. last_t) last_t=time_start - surface=(rlimits(2)-rlimits(1))*(zlimits(2)-zlimits(1)) - - loc_rlimits=rlimits/rnorm - - IF (zlimits(1) .lt. zgrid(Zbounds(mpirank))*rnorm) THEN - remsurface=(rlimits(2)-rlimits(1))*(zgrid(Zbounds(mpirank))*rnorm-zlimits(1)) - frequency=frequency*(1-remsurface/surface) - loc_zlimits(1)=zgrid(Zbounds(mpirank)) - ELSE - loc_zlimits(1)=zlimits(1)/rnorm - END IF - - IF (zlimits(2) .gt. zgrid(Zbounds(mpirank+1))*rnorm) THEN - remsurface=(rlimits(2)-rlimits(1))*(zlimits(2)-zgrid(Zbounds(mpirank+1))*rnorm) - frequency=frequency*(1-remsurface/surface) - loc_zlimits(2)=zgrid(Zbounds(mpirank+1)) - ELSE - loc_zlimits(2)=zlimits(2)/rnorm - END IF - WRITE(*,*) "init frequency : ", frequency - IF(radialtype .gt.4 .or. radialtype .lt. 1) THEN - IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of radial distribution:", radialtype - CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) - END IF - - End subroutine maxwellsource_init - - subroutine maxwellsource_inject(time) - implicit none - REAL(kind=db), INTENT(IN) :: time - Type(particle), ALLOCATABLE, Dimension(:):: newparts - INTEGER:: npartsadd - INTEGER:: i - REAL(kind=db), ALLOCATABLE:: VR(:),VZ(:),VTHET(:),R(:),Z(:) - - ! check if source is on - IF(.not. maxwellsource_on(time)) THEN - RETURN - END IF - - ! Number of particles to add at this time step - npartsadd=floor((time-last_t)*frequency) - - IF (npartsadd.gt. 0) THEN - ALLOCATE(newparts(npartsadd)) - ALLOCATE(VR(npartsadd),VZ(npartsadd),VTHET(npartsadd),R(npartsadd),Z(npartsadd)) - ! Initial velocities distribution - CALL lodgaus(0,VZ) - CALL lodgaus(0,VR) - CALL lodgaus(0,VTHET) - SELECT CASE(radialtype) - CASE(1) ! 1/R distribution in R - CALL lodunir(0,R,loc_rlimits(1),loc_rlimits(2)) - CASE(2) ! flat top distribution in R - CALL lodlinr(0,R,loc_rlimits(1),loc_rlimits(2)) - CASE(3) ! 1/R^2 distribution in R - CALL lodinvr(0,R,loc_rlimits(1),loc_rlimits(2)) - CASE(4) ! gaussian distribution in R - CALL lodgausr(0,R,loc_rlimits(1),loc_rlimits(2)) - CASE DEFAULT - IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of radial distribution:", radialtype - END SELECT - - - CALL loduni (0,Z) - - DO i=1,npartsadd - newparts(i)%UR=VR(i)*vth - newparts(i)%UZ=VZ(i)*vth - newparts(i)%UTHET=VTHET(i)*vth - newparts(i)%R=R(i) - newparts(i)%Z=Z(i)*(loc_zlimits(2)-loc_zlimits(1)) + loc_zlimits(1) - newparts(i)%THET=0 - IF (nlclassical) THEN - newparts(i)%gamma=1.0 - ELSE - newparts(i)%gamma=sqrt(1/(1-newparts(i)%UZ**2-newparts(i)%UR**2-newparts(i)%UTHET**2)) - newparts(i)%UR=VR(i)*newparts(i)%gamma - newparts(i)%UZ=VZ(i)*newparts(i)%gamma - newparts(i)%UTHET=VTHET(i)*newparts(i)%gamma - END IF - END DO - CALL add_created_part(partslist(1), newparts) - last_t=last_t+npartsadd/frequency - END IF - - - end subroutine maxwellsource_inject - - logical function maxwellsource_on(time) - REAL(kind=db), intent(in):: time - - maxwellsource_on=.false. - IF (time_start .lt. 0 .and. time_end .lt. 0) THEN - maxwellsource_on = .true. - ELSE IF (time .gt. time_start .and. (time .lt. time_end .or. time_end .lt. 0) ) THEN - maxwellsource_on = .true. - END IF - - maxwellsource_on=nlmaxwellsource .and. maxwellsource_on - end function - -End Module maxwellsource diff --git a/src/maxwsrce_mod.f90 b/src/maxwsrce_mod.f90 new file mode 100644 index 0000000..1de6c3d --- /dev/null +++ b/src/maxwsrce_mod.f90 @@ -0,0 +1,226 @@ +!------------------------------------------------------------------------------ +! EPFL/Swiss Plasma Center +!------------------------------------------------------------------------------ +! +! MODULE: maxwellsource +! +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> Adds particle in the simulation according to a maxwellian distribution in velocity +!> and a uniform distribution in space +!------------------------------------------------------------------------------ + +MODULE maxwsrce + ! + USE constants + use mpi + USE mpihelper + USE basic, ONLY: mpirank, mpisize, vnorm, rnorm, zgrid, & + & nlclassical, nlmaxwellsource + USE beam + USE distrib + + IMPLICIT NONE + + PRIVATE + + REAL(kind=db), SAVE :: frequency = 0 !< Number of macro particles added per second over the spawning region + REAL(kind=db), SAVE :: loc_frequency = 0 !< local number of macro particles added per second over the spawning region + REAL(kind=db), SAVE :: temperature=11000 !< temperature used for the Maxwellian velocity distribution in Kelvin + REAL(kind=db), SAVE :: rlimits(2) !< radial limits in which particles will be spawned + REAL(kind=db), SAVE :: zlimits(2) !< axial limits in which particles will be spawned + REAL(kind=db), SAVE :: last_t=0 !< last time when a macro-particle was added to the simulation + REAL(kind=db), SAVE :: vth = 0 !< Normalized thermal velocity computed from temperature + REAL(kind=db), SAVE :: loc_zlimits(2)!< Local axial limits in which particles will be spawned used for current mpi process + REAL(kind=db), SAVE :: loc_rlimits(2)!< Local radial limits in which particles will be spawned used for current mpi process + REAL(kind=db), SAVE :: time_start=-1.0 !< time at which the source is turned on + REAL(kind=db), SAVE :: time_end=-1.0 !< time at which the source is turned off + INTEGER, SAVE :: radialtype=2 !< type of radial distribution used for creating particles + + + NAMELIST /maxwellsourceparams/ frequency, temperature, rlimits, zlimits, time_start, time_end, radialtype + + PUBLIC:: maxwsrce_init, maxwsrce_inject, maxwsrce_diag, maxwsrce_calcfreq + +contains + + subroutine maxwsrce_init(lu_in, time, Zbounds) + implicit none + INTEGER, INTENT(IN)::lu_in + INTEGER, INTENT(IN):: Zbounds(0:) + REAL(kind=db), INTENT(IN):: time + + INTEGER:: ierr + + Rewind(lu_in) + READ(lu_in, maxwellsourceparams) + + IF(mpirank .eq. 0) THEN + WRITE(*, maxwellsourceparams) + END IF + ! compute normalized thermal velocity and source reference time + vth=sqrt(kb*temperature/partslist(1)%m)/vnorm + last_t=time + IF(time_start .gt. last_t) last_t=time_start + time_start=last_t + + + CALL maxwsrce_calcfreq(Zbounds) + + if (loc_frequency .lt. 0) loc_frequency=0 + WRITE(*,*) "init local frequency : ", loc_frequency + + IF(radialtype .gt.4 .or. radialtype .lt. 1) THEN + IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of radial distribution:", radialtype + CALL MPI_Abort(MPI_COMM_WORLD, -1, ierr) + END IF + + End subroutine maxwsrce_init + + SUBROUTINE maxwsrce_calcfreq(Zbounds) + IMPLICIT NONE + INTEGER:: Zbounds(0:) + REAL(kind=db):: surface, frequencyrem + REAL(kind=db):: remsurface + + frequencyrem=0 + + ! compute source surface + surface=(zlimits(2)-zlimits(1)) + + loc_rlimits=rlimits/rnorm + ! if the source is in the mpi process compute region + if(zlimits(2) .gt. zgrid(Zbounds(mpirank))*rnorm .and. zlimits(1) .lt. zgrid(Zbounds(mpirank+1))*rnorm) then + ! reduce the frequency and source boundaries to match the volume covered by the mpi process + IF (zlimits(1) .lt. zgrid(Zbounds(mpirank))*rnorm) THEN + remsurface=(zgrid(Zbounds(mpirank))*rnorm-zlimits(1)) + frequencyrem=frequency*remsurface/surface + loc_zlimits(1)=zgrid(Zbounds(mpirank)) + ELSE + loc_zlimits(1)=zlimits(1)/rnorm + END IF + + IF (zlimits(2) .gt. zgrid(Zbounds(mpirank+1))*rnorm) THEN + remsurface=(zlimits(2)-zgrid(Zbounds(mpirank+1))*rnorm) + frequencyrem=frequencyrem+frequency*remsurface/surface + loc_zlimits(2)=zgrid(Zbounds(mpirank+1)) + ELSE + loc_zlimits(2)=zlimits(2)/rnorm + END IF + loc_frequency=frequency-frequencyrem + else + ! otherwise turn off the source for this mpi process + loc_frequency=0 + loc_zlimits=(/zgrid(Zbounds(mpirank)), zgrid(Zbounds(mpirank+1))/) + end if + + END SUBROUTINE maxwsrce_calcfreq + + Subroutine maxwsrce_diag(File_handle, str, vnorm) + use mpi + Use futils + Integer:: File_handle + Real(kind=db):: vnorm + Character(len=*):: str + CHARACTER(len=256):: grpname + Integer:: ierr, mpirank + + CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) + + IF(mpirank .eq. 0 .and. nlmaxwellsource) THEN + + Write(grpname,'(a,a)') trim(str),"/maxwellsource" + If(.not. isgroup(File_handle, trim(grpname))) THEN + CALL creatg(File_handle, trim(grpname)) + END IF + Call attach(File_handle, trim(grpname), "frequency", frequency) + Call attach(File_handle, trim(grpname), "temperature", temperature) + Call putarr(File_handle, trim(grpname)//"/zlimits", zlimits) + Call putarr(File_handle, trim(grpname)//"/rlimits", rlimits) + Call attach(File_handle, trim(grpname), "vth", vth*vnorm) + Call attach(File_handle, trim(grpname), "time_start", time_start) + Call attach(File_handle, trim(grpname), "time_end",time_end) + Call attach(File_handle, trim(grpname), "radialtype", radialtype) + END IF + + End subroutine maxwsrce_diag + + subroutine maxwsrce_inject(time) + implicit none + REAL(kind=db), INTENT(IN) :: time + Type(particle), ALLOCATABLE, Dimension(:):: newparts + INTEGER:: npartsadd + INTEGER:: i + REAL(kind=db), ALLOCATABLE:: VR(:),VZ(:),VTHET(:),R(:),Z(:) + + ! check if source is on + IF(.not. maxwsrce_on(time)) THEN + RETURN + END IF + + ! Number of particles to add at this time step + npartsadd=floor((time-last_t)*loc_frequency) + + IF (npartsadd.gt. 0) THEN + ALLOCATE(newparts(npartsadd)) + ALLOCATE(VR(npartsadd),VZ(npartsadd),VTHET(npartsadd),R(npartsadd),Z(npartsadd)) + ! Initial velocities distribution + CALL lodgaus(0,VZ) + CALL lodgaus(0,VR) + CALL lodgaus(0,VTHET) + SELECT CASE(radialtype) + CASE(1) ! 1/R distribution in R + CALL lodunir(0,R,loc_rlimits(1),loc_rlimits(2)) + CASE(2) ! flat top distribution in R + CALL lodlinr(0,R,loc_rlimits(1),loc_rlimits(2)) + CASE(3) ! 1/R^2 distribution in R + CALL lodinvr(0,R,loc_rlimits(1),loc_rlimits(2)) + CASE(4) ! gaussian distribution in R + CALL lodgausr(0,R,loc_rlimits(1),loc_rlimits(2)) + CASE DEFAULT + IF (mpirank .eq. 0) WRITE(*,*) "Unknown type of radial distribution:", radialtype + END SELECT + + + CALL loduni (0,Z) + ! fill the added particles buffer + DO i=1,npartsadd + newparts(i)%UR=VR(i)*vth + newparts(i)%UZ=VZ(i)*vth + newparts(i)%UTHET=VTHET(i)*vth + newparts(i)%R=R(i) + newparts(i)%Z=Z(i)*(loc_zlimits(2)-loc_zlimits(1)) + loc_zlimits(1) + newparts(i)%THET=0 + IF (nlclassical) THEN + newparts(i)%gamma=1.0 + ELSE + newparts(i)%gamma=sqrt(1/(1-newparts(i)%UZ**2-newparts(i)%UR**2-newparts(i)%UTHET**2)) + newparts(i)%UR=newparts(i)%UR*newparts(i)%gamma + newparts(i)%UZ=newparts(i)%UZ*newparts(i)%gamma + newparts(i)%UTHET=newparts(i)%UTHET*newparts(i)%gamma + END IF + END DO + ! Move the buffer of created particles to the simulation buffer + CALL add_created_part(partslist(1), newparts) + last_t=last_t+npartsadd/loc_frequency + END IF + + + end subroutine maxwsrce_inject + + logical function maxwsrce_on(time) + REAL(kind=db), intent(in):: time + + maxwsrce_on=.false. + IF (time_start .lt. 0 .and. time_end .lt. 0) THEN + maxwsrce_on = .true. + ELSE IF (time .gt. time_start .and. (time .lt. time_end .or. time_end .lt. 0) ) THEN + maxwsrce_on = .true. + END IF + + maxwsrce_on=nlmaxwellsource .and. maxwsrce_on + end function + +END MODULE maxwsrce diff --git a/src/mpihelper_mod.f90 b/src/mpihelper_mod.f90 index 1398a8a..0a13e36 100644 --- a/src/mpihelper_mod.f90 +++ b/src/mpihelper_mod.f90 @@ -1,316 +1,365 @@ !------------------------------------------------------------------------------ ! EPFL/Swiss Plasma Center !------------------------------------------------------------------------------ ! ! MODULE: mpihelper ! !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> Module responsible for setting up the MPI variables used in the communications. !------------------------------------------------------------------------------ MODULE mpihelper USE constants -USE mpi +use mpi +USE particletypes + IMPLICIT NONE -!> Structure containing the simulation parameters read from the input file -TYPE BASICDATA - LOGICAL :: nlres, nlsave, newres, nlxg, nlppform, nlPhis, nlclassical - INTEGER :: nplasma, nz, it0d, it2d, itparts, & - & itgraph, distribtype, nblock, nrun - REAL(kind=db) :: job_time, extra_time, tmax, dt, & - & potinn, potout, B0, n0, temp, & - & Rcurv, width, H0, P0 - INTEGER, DIMENSION(2) :: femorder, ngauss - INTEGER, DIMENSION(3) :: nnr - REAL(kind=db), DIMENSION(2):: lz - REAL(kind=db), DIMENSION(4):: radii, plasmadim - CHARACTER(len=64) :: resfile= "results.h5" - LOGICAL :: partperiodic - INTEGER :: samplefactor - END TYPE BASICDATA - -!> Structure containing a single particle position and velocity used in MPI communications. - TYPE particle - INTEGER :: Rindex, Zindex, partindex - REAL(kind=db) :: R, Z, THET,& - & UZ, UR, UTHET, & - & Gamma - END TYPE particle - - INTEGER, SAVE :: basicdata_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for communicating basicdata - INTEGER, SAVE :: particle_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for particles communication - INTEGER, SAVE :: rhsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the communication of a rhs column - INTEGER, SAVE :: db_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the communication of a REAL(kind=db) - INTEGER, SAVE :: momentsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the communication of a column of a grid variable - INTEGER, SAVE :: rcvrhsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the receive communication of a rhs column - INTEGER, SAVE :: rcvmomentsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the receive communication of a column of a grid variable - INTEGER, SAVE:: db_sum_op !< Store the MPI sum operation for db_type +INTEGER, SAVE :: basicdata_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for communicating basicdata +INTEGER, SAVE :: particle_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for particles communication between nodes + !INTEGER, SAVE :: particles_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for particles gathering on node 0 and broadcast from node 0 +INTEGER, SAVE :: rhsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the communication of a rhs column +INTEGER, SAVE :: db_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the communication of a REAL(kind=db) +INTEGER, SAVE :: momentsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the communication of a column of a grid variable +INTEGER, SAVE :: rcvrhsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the receive communication of a rhs column +INTEGER, SAVE :: rcvmomentsoverlap_type=MPI_DATATYPE_NULL !< Stores the MPI data type used for the receive communication of a column of a grid variable +INTEGER, SAVE:: db_sum_op !< Store the MPI sum operation for db_type REAL(kind=db), ALLOCATABLE, SAVE:: rhsoverlap_buffer(:) !< buffer used for storing the rhs ghost cells !< received from the left or right MPI process REAL(kind=db), ALLOCATABLE, SAVE:: momentsoverlap_buffer(:) !< buffer used for storing the moments ghost cells !< received from the left or right MPI process !INTEGER, SAVE:: momentsoverlap_requests(2) = MPI_REQUEST_NULL !INTEGER, SAVE:: rhsoverlap_requests(2) = MPI_REQUEST_NULL - INTEGER:: rhsoverlap_tag= 20 - INTEGER:: momentsoverlap_tag= 30 + INTEGER:: rhsoverlap_tag= 200 + INTEGER:: momentsoverlap_tag= 300 + INTEGER:: partsgather_tag= 500 + INTEGER:: partsexchange_tag=600 + INTEGER:: nbpartsexchange_tag=700 CONTAINS !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief !> Initialize the MPI types used for inter process communications ! !--------------------------------------------------------------------------- - SUBROUTINE init_mpitypes + SUBROUTINE mpitypes_init IMPLICIT NONE INTEGER:: ierr ! Initialize db_type to use real(kind=db) in MPI and the sum operator for reduce CALL MPI_TYPE_CREATE_F90_REAL(dprequestedprec,MPI_UNDEFINED,db_type,ierr) CALL MPI_Type_commit(db_type,ierr) CALL MPI_Op_Create(DB_sum, .true., db_sum_op, ierr) CALL init_particlempi - END SUBROUTINE init_mpitypes + END SUBROUTINE mpitypes_init !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief !> Computes the sum in MPI_Reduce operations involving Real(kinc=db) ! !--------------------------------------------------------------------------- - SUBROUTINE DB_sum(INVEC, INOUTVEC, LEN, TYPE) - REAL(kind=db):: INVEC(0:LEN-1), INOUTVEC(0:LEN-1) - INTEGER:: LEN, TYPE + SUBROUTINE DB_sum(INVEC, INOUTVEC, LEN, TYPE)bind(c) + !REAL(kind=db):: INVEC(0:LEN-1), INOUTVEC(0:LEN-1) + use, intrinsic:: iso_c_binding, ONLY: c_ptr,c_f_pointer + use mpi + implicit none + TYPE(C_PTR), VALUE:: invec, inoutvec + real(kind=db),pointer:: ivec(:), iovec(:) + INTEGER:: LEN + INTEGER:: TYPE INTEGER:: i - Do i=0,LEN-1 - INOUTVEC(i)=INVEC(i)+INOUTVEC(i) + call c_f_pointer(INVEC,ivec, (/len/)) + call c_f_pointer(inoutvec,iovec, (/len/)) + Do i=1,LEN + IOVEC(i)=IVEC(i)+IOVEC(i) END DO + + END SUBROUTINE DB_sum !-------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief !> Initialize the MPI communicators used for allreduce between neighbors ! !> @param[in] nrank ranks of the FEM array in (1) z direction and (2) r direction !> @param[in] femorder finite element method order in z and r direction !> @param[in] zlimleft z index delimiting the mpi local left boundary !> @param[in] zlimright z index delimiting the mpi local right boundary !> @param[in] nbmoments number of moments calculated and stored. ! !--------------------------------------------------------------------------- SUBROUTINE init_overlaps(nrank, femorder, zlimleft, zlimright, nbmoments) INTEGER, INTENT(IN):: nrank(:), femorder(:), zlimright, zlimleft, nbmoments IF(ALLOCATED(rhsoverlap_buffer)) DEALLOCATE(rhsoverlap_buffer) IF(ALLOCATED(momentsoverlap_buffer)) DEALLOCATE(momentsoverlap_buffer) ALLOCATE(rhsoverlap_buffer(nrank(2)*femorder(1))) ALLOCATE(momentsoverlap_buffer(nbmoments*nrank(2)*femorder(1))) - WRITE(*,*)"nrank, femorder", nrank, femorder ! Initialize the MPI column overlap type for rhs - CALL init_coltypempi(nrank(2), zlimright-zlimleft+femorder(1), 1, nbmoments, db_type, rhsoverlap_type) + CALL init_coltypempi(nrank(2), zlimright-zlimleft+femorder(1), 1, 1, db_type, rhsoverlap_type) ! Initialize the MPI grid col type CALL init_coltypempi(nrank(2), zlimright-zlimleft+femorder(1), nbmoments, 1, db_type, momentsoverlap_type) ! Initialize the MPI receive column overlap type for rhs - CALL init_coltypempi(nrank(2), nrank(1), 1, nbmoments, db_type, rcvrhsoverlap_type) + CALL init_coltypempi(nrank(2), nrank(1), 1, 1, db_type, rcvrhsoverlap_type) ! Initialize the MPI receive grid col type CALL init_coltypempi(nrank(2), nrank(1), nbmoments, 1, db_type, rcvmomentsoverlap_type) END SUBROUTINE init_overlaps SUBROUTINE start_persistentcomm(requests, mpirank, leftproc, rightproc) INTEGER, ASYNCHRONOUS, INTENT(INOUT):: requests(:) INTEGER, INTENT(IN):: mpirank, leftproc, rightproc INTEGER:: ierr INTEGER:: stats(MPI_STATUS_SIZE,2) LOGICAL:: completed=.false. IF(leftproc .lt. mpirank) THEN ! Start to receive CALL MPI_START(requests(2),ierr) IF(IERR .ne. MPI_SUCCESS) WRITE(*,*) "error in recv_init" END IF IF(rightproc .gt. mpirank) THEN ! Start to send CALL MPI_START(requests(1),ierr) IF(IERR .ne. MPI_SUCCESS) WRITE(*,*) "error in send_init" END IF IF(leftproc .lt. mpirank) THEN ! Start to receive completed=.FALSE. DO WHILE(.not. completed) CALL MPI_TEST(requests(2), completed,stats(:,2),ierr) END DO WRITE(*,*)"status 2", completed, stats(:,2) !CALL MPI_WAIT(requests(2),stats(:,2),ierr) !WRITE(*,*)"status 2", stats(:,2) IF(IERR .ne. MPI_SUCCESS) WRITE(*,*) "error in recv_init" END IF IF(rightproc .gt. mpirank) THEN ! Start to send completed=.FALSE. DO WHILE(.not. completed) CALL MPI_TEST(requests(1), completed,stats(:,1),ierr) END DO !CALL MPI_WAIT(requests(1),stats(:,1),ierr) IF(IERR .ne. MPI_SUCCESS) WRITE(*,*) "error in send_init" END IF END SUBROUTINE start_persistentcomm -SUBROUTINE start_rhscomm(mpirank, leftproc, rightproc, moments, nrank, femorder, zlimright) +SUBROUTINE rhsoverlapcomm(mpirank, leftproc, rightproc, moments, nrank, femorder, zlimright) INTEGER, INTENT(IN):: mpirank, leftproc, rightproc -REAL(kind=db), DIMENSION(:,:), INTENT(INOUT):: moments +REAL(kind=db), DIMENSION(:), INTENT(INOUT):: moments INTEGER, INTENT(IN):: nrank(2), femorder(2), zlimright INTEGER, SAVE:: rhsoverlap_requests(2) = MPI_REQUEST_NULL INTEGER:: ierr INTEGER:: stats(MPI_STATUS_SIZE,2) rhsoverlap_requests=MPI_REQUEST_NULL rhsoverlap_buffer=0 IF(rightproc .gt. mpirank) THEN - CALL MPI_ISEND(moments(1,zlimright+1), femorder(1), rhsoverlap_type, rightproc, rhsoverlap_tag, & + CALL MPI_ISEND(moments(zlimright+1), femorder(1), rhsoverlap_type, rightproc, rhsoverlap_tag, & & MPI_COMM_WORLD, rhsoverlap_requests(1), ierr ) END IF ! If the processor on the left has actually lower z positions IF(leftproc .lt. mpirank) THEN CALL MPI_IRECV(rhsoverlap_buffer, nrank(2)*(femorder(1)), db_type, leftproc, rhsoverlap_tag, & & MPI_COMM_WORLD, rhsoverlap_requests(2), ierr ) END IF CALL MPI_WAITALL(2,rhsoverlap_requests,stats, ierr) -END SUBROUTINE start_rhscomm +END SUBROUTINE rhsoverlapcomm -SUBROUTINE start_momentscomm(mpirank, leftproc, rightproc, moments, nrank, femorder, zlimright) +SUBROUTINE momentsoverlapcomm(mpirank, leftproc, rightproc, moments, nrank, femorder, zlimright) INTEGER, INTENT(IN):: mpirank, leftproc, rightproc REAL(kind=db), DIMENSION(:,:), INTENT(INOUT):: moments INTEGER, INTENT(IN):: nrank(2), femorder(2), zlimright INTEGER, SAVE:: momentsoverlap_requests(2) = MPI_REQUEST_NULL INTEGER:: ierr INTEGER:: stats(MPI_STATUS_SIZE,2) momentsoverlap_requests=MPI_REQUEST_NULL momentsoverlap_buffer=0 IF(rightproc .gt. mpirank) THEN CALL MPI_ISEND(moments(1,zlimright+1), femorder(1), momentsoverlap_type, rightproc, momentsoverlap_tag, & & MPI_COMM_WORLD, momentsoverlap_requests(1), ierr ) END IF ! If the processor on the left has actually lower z positions IF(leftproc .lt. mpirank) THEN CALL MPI_IRECV(momentsoverlap_buffer, 10*nrank(2)*(femorder(1)), db_type, leftproc, momentsoverlap_tag, & & MPI_COMM_WORLD, momentsoverlap_requests(2), ierr ) END IF CALL MPI_WAITALL(2,momentsoverlap_requests,stats, ierr) -END SUBROUTINE start_momentscomm +END SUBROUTINE momentsoverlapcomm !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> !> @brief !> Initialize the particle MPI type used for inter process communications and publish it to !> the process in the communicator ! !--------------------------------------------------------------------------- SUBROUTINE init_particlempi() - INTEGER :: nblock = 10 - INTEGER:: blocklength(10) - INTEGER(kind=MPI_ADDRESS_KIND):: displs(10), displ0 - INTEGER:: types(10) + INTEGER :: nblock = 9 + INTEGER:: blocklength(9) + INTEGER(kind=MPI_ADDRESS_KIND):: displs(9), displ0 + INTEGER:: types(9) TYPE(particle) :: part INTEGER:: ierr - CALL mpi_get_address(part%Rindex, displs(1), ierr) - CALL mpi_get_address(part%Zindex, displs(2), ierr) - CALL mpi_get_address(part%partindex, displs(3), ierr) - types(1:3)=MPI_INTEGER - CALL mpi_get_address(part%R, displs(4), ierr) - CALL mpi_get_address(part%Z, displs(5), ierr) - CALL mpi_get_address(part%THET, displs(6), ierr) - CALL mpi_get_address(part%UZ, displs(7), ierr) - CALL mpi_get_address(part%UR, displs(8), ierr) - CALL mpi_get_address(part%UTHET, displs(9), ierr) - CALL mpi_get_address(part%GAMMA, displs(10), ierr) - types(4:10)=db_type - blocklength(1:10) = 1 + CALL mpi_get_address(part%partindex, displs(1), ierr) + types(1)=MPI_INTEGER + CALL mpi_get_address(part%R, displs(2), ierr) + CALL mpi_get_address(part%Z, displs(3), ierr) + CALL mpi_get_address(part%THET, displs(4), ierr) + CALL mpi_get_address(part%UZ, displs(5), ierr) + CALL mpi_get_address(part%UR, displs(6), ierr) + CALL mpi_get_address(part%UTHET, displs(7), ierr) + CALL mpi_get_address(part%GAMMA, displs(8), ierr) + CALL mpi_get_address(part%pot, displs(9), ierr) + types(2:9)=db_type + blocklength(1:9) = 1 CALL mpi_get_address(part, displ0, ierr) displs=displs-displ0 CALL MPI_Type_create_struct(nblock, blocklength, displs, types, particle_type, ierr) CALL MPI_Type_commit(particle_type,ierr) END SUBROUTINE init_particlempi + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Initialize the particles MPI type used for gathering particles to the root and broadcast them and publish it to +!> the process in the communicator +! +!--------------------------------------------------------------------------- + SUBROUTINE init_particles_gather_mpi(p,idstart,nsend,mpi_particles_type) + INTEGER:: mpi_particles_type + INTEGER:: nsend + INTEGER:: idstart + INTEGER :: nblock = 15 + INTEGER:: blocklength(15) + INTEGER(kind=MPI_ADDRESS_KIND):: displs(15), displ0 + INTEGER:: types(15) + TYPE(particles), INTENT(INOUT):: p + INTEGER:: ierr + INTEGER:: temptype + + IF(nsend .lt. 1) RETURN + + temptype=MPI_DATATYPE_NULL + IF( mpi_particles_type .ne. MPI_DATATYPE_NULL) CALL MPI_TYPE_FREE(mpi_particles_type,ierr) + + CALL mpi_get_address(p%Rindex(idstart), displs(1), ierr) + CALL mpi_get_address(p%Zindex(idstart), displs(2), ierr) + CALL mpi_get_address(p%partindex(idstart), displs(3), ierr) + types(1:3)=MPI_INTEGER + CALL mpi_get_address(p%R(idstart), displs(4), ierr) + CALL mpi_get_address(p%Z(idstart), displs(5), ierr) + CALL mpi_get_address(p%THET(idstart), displs(6), ierr) + CALL mpi_get_address(p%pot(idstart), displs(7), ierr) + CALL mpi_get_address(p%UR(idstart), displs(8), ierr) + CALL mpi_get_address(p%URold(idstart), displs(9), ierr) + CALL mpi_get_address(p%UTHET(idstart), displs(10), ierr) + CALL mpi_get_address(p%UTHETold(idstart), displs(11), ierr) + CALL mpi_get_address(p%UZ(idstart), displs(12), ierr) + CALL mpi_get_address(p%UZold(idstart), displs(13), ierr) + + CALL mpi_get_address(p%GAMMA(idstart), displs(14), ierr) + CALL mpi_get_address(p%GAMMAold(idstart), displs(15), ierr) + types(4:15)=db_type + blocklength = nsend + CALL mpi_get_address(p, displ0, ierr) + displs=displs-displ0 + + CALL MPI_Type_create_struct(nblock, blocklength, displs, types, mpi_particles_type, ierr) + CALL MPI_TYPE_COMMIT(mpi_particles_type, ierr) + +END SUBROUTINE init_particles_gather_mpi + + !--------------------------------------------------------------------------- !> @author Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> @brief Initialize the column MPI type used for inter process communications and publish it to !> the processes in the communicator (can be rhs or grid quantities) ! !> @param[in] nr number of elements in the r direction !> @param[in] nz number of elements in the z direction !> @param[in] init_type MPI type of the initial data !> @param[inout] mpi_coltype final type usable in communications !--------------------------------------------------------------------------- SUBROUTINE init_coltypempi(nr, nz, block_size, stride, init_type, mpi_coltype) INTEGER, INTENT(IN) :: nr INTEGER, INTENT(IN) :: nz INTEGER, INTENT(IN) :: block_size INTEGER, INTENT(IN) :: stride - INTEGER, INTENT(IN) :: init_type + INTEGER, INTENT(IN) :: init_type INTEGER, INTENT(OUT) :: mpi_coltype - INTEGER :: temp_mpi_coltype, ierr + INTEGER :: temp_mpi_coltype + INTEGER:: ierr INTEGER(KIND=MPI_ADDRESS_KIND):: init_type_lb, init_type_extent !(nrank(2), nrank(1), 1, 10, db_type, rhsoverlap_type) + ! if mpi_coltype was used, we free it first + IF( mpi_coltype .ne. MPI_DATATYPE_NULL) CALL MPI_TYPE_FREE(mpi_coltype,ierr) ! Create vector type of length nx CALL MPI_TYPE_VECTOR(nr, block_size, stride*block_size*nz, init_type, temp_mpi_coltype, ierr) CALL MPI_TYPE_COMMIT(temp_mpi_coltype, ierr) ! Get the size in bytes of the initial type CALL MPI_TYPE_GET_EXTENT(init_type, init_type_lb, init_type_extent, ierr) if(mpi_coltype .ne. MPI_DATATYPE_NULL) CALL MPI_TYPE_FREE(mpi_coltype,ierr) ! Resize temp_mpi_coltype such that the next item to read is at j+1 CALL MPI_TYPE_CREATE_RESIZED(temp_mpi_coltype, init_type_lb, stride*block_size*init_type_extent ,& & mpi_coltype, ierr) CALL MPI_TYPE_COMMIT(mpi_coltype, ierr) CALL MPI_TYPE_FREE(temp_mpi_coltype,ierr) END SUBROUTINE init_coltypempi END MODULE mpihelper diff --git a/src/neutcol_mod.f90 b/src/neutcol_mod.f90 new file mode 100644 index 0000000..a686c86 --- /dev/null +++ b/src/neutcol_mod.f90 @@ -0,0 +1,454 @@ +!------------------------------------------------------------------------------ +! EPFL/Swiss Plasma Center +!------------------------------------------------------------------------------ +! +! MODULE: neutcol +! +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> Module responsible for handling the electron-neutral collisions and creating electrons +!> by ionisation Based on the paper by Birdsall 1991 and Sengupta et al. +!------------------------------------------------------------------------------ + +module neutcol + USE constants + + IMPLICIT NONE + private + LOGICAL :: nlcol=.false. !< Flag to activate or not electron neutral collisions + LOGICAL :: nlmaxwellio=.false. !< Flag to define how ionised electrons are created (physically or according to maxwellian) + INTEGER :: itcol = 1 !< number of dt between each evaluation of neutcol_step + Real(kind=db) :: neutdens=2.4e16 !< Neutral particle density in m-3 + Real(kind=db) :: neuttemp=300 !< Neutral particle temperature in K + Real(kind=db) :: neutpressure=1e-6 !< Neutral particle pressure in mbar + Real(kind=db) :: scatter_fac = 24.2 !< Energy scattering factor for the considered gas (here for Ne) + real(kind=db) :: Eion = 21.56 !< Ionisation energy (eV) (here for Ne) + Real(kind=db) :: E0 = 27.21 !< Atomic unit of energy used for calculation of deviation angles + real(kind=db) :: collfactor !< collision factor (n_n \sigma \delta t) + INTEGER :: nb_io_cross=0 + Real(kind=db), ALLOCATABLE :: io_cross_sec(:,:) !< Ionisation cross-section table + Real(kind=db), ALLOCATABLE :: io_growth_cross_sec(:) !< Ionisation exponential fitting factor + INTEGER :: nb_ela_cross=0 + Real(kind=db), ALLOCATABLE :: ela_cross_sec(:,:) !< Elastic collision cross section table + Real(kind=db), ALLOCATABLE :: ela_growth_cross_sec(:) !< Elastic collision exponential fitting factor + Real(kind=db) :: Escale !< Energy normalisation factor used to reduce computation costs + CHARACTER(len=128) :: io_cross_sec_file='' + CHARACTER(len=128) :: ela_cross_sec_file='' + Real(kind=db) :: etemp=22000 !< In case of nlmaxwelio, defines the temperature of created electrons + Real(kind=db) :: vth !< In case of nlmaxwelio, defines the thermal velocity of created electrons + LOGICAL :: nldragio=.true. !< + INTEGER :: species(2) + + + + NAMELIST /neutcolparams/ neutdens, neuttemp, neutpressure, Eion, & + & scatter_fac, nlcol, io_cross_sec_file, ela_cross_sec_file, nlmaxwellio, etemp, & + & nldragio, itcol, species + + PUBLIC:: neutcol_init, neutcol_step, neutcol_diag, itcol, neutdens + +CONTAINS + subroutine neutcol_init(lu_in, p) + use mpi + Use basic, only: mpirank, dt, nlclassical,rnorm,tnorm, vnorm + Use beam, only: particles + Use constants + implicit none + INTEGER, INTENT(IN) :: lu_in + TYPE(particles) :: p + INTEGER:: ierr, istat + character(len=1000) :: line + + species(1)=1 + species(2)=-1 + Rewind(lu_in) + + READ(lu_in, neutcolparams, iostat=istat) + + if (istat.gt.0) then + backspace(lu_in) + read(lu_in,fmt='(A)') line + write(*,'(A)') & + 'Invalid line in neutcolparams: '//trim(line) + call MPI_Abort(MPI_COMM_WORLD, -1, ierr) + stop + end if + + IF(mpirank .eq. 0) THEN + WRITE(*, neutcolparams) + END IF + + if(.not. nlcol) return + + if(nlclassical)THEN + Escale=0.5*p%m/elchar*vlight**2 + else + Escale=p%m*vlight**2/elchar + end if + + if (nlmaxwellio) vth=sqrt(kb*etemp/p%m)/vnorm + + if(io_cross_sec_file .ne.'') then + call read_cross_sec(io_cross_sec_file,io_cross_sec, nb_io_cross) + if(nb_io_cross .gt. 0) then + allocate(io_growth_cross_sec(nb_io_cross-1)) + + ! Normalisations + io_cross_sec(:,2)=io_cross_sec(:,2)/rnorm**2 + ! Precomputing of exponential fitting factor for faster execution + io_growth_cross_sec=log(io_cross_sec(2:nb_io_cross,2)/io_cross_sec(1:nb_io_cross-1,2))/ & + & log(io_cross_sec(2:nb_io_cross,1)/io_cross_sec(1:nb_io_cross-1,1)) + end if + end if + if(ela_cross_sec_file .ne.'') then + call read_cross_sec(ela_cross_sec_file,ela_cross_sec, nb_ela_cross) + if(nb_ela_cross .gt. 0) then + allocate(ela_growth_cross_sec(nb_ela_cross-1)) + + ! Normalisations + ela_cross_sec(:,2)=ela_cross_sec(:,2)/rnorm**2 + + ! Precomputing of exponential fitting factor for faster execution + ela_growth_cross_sec=log(ela_cross_sec(2:nb_ela_cross,2)/ela_cross_sec(1:nb_ela_cross-1,2))/ & + & log(ela_cross_sec(2:nb_ela_cross,1)/ela_cross_sec(1:nb_ela_cross-1,1)) + end if + END IF + nlcol=nlcol .and. (allocated(io_cross_sec) .or. allocated(ela_cross_sec)) + + ! Collision factor depending on neutral gas parameters + collfactor=neutdens*dt*rnorm**3*itcol + neutpressure=neutdens*kb*300/100 + + end subroutine neutcol_init + + + Subroutine neutcol_diag(File_handle, str, vnorm) + use mpi + Use futils + Integer:: File_handle + Real(kind=db):: vnorm + Character(len=*):: str + CHARACTER(len=256):: grpname + Integer:: ierr, mpirank + + CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) + + IF(mpirank .eq. 0 .and. nlcol) THEN + + Write(grpname,'(a,a)') trim(str),"/neutcol" + If(.not. isgroup(File_handle, trim(grpname))) THEN + CALL creatg(File_handle, trim(grpname)) + END IF + Call attach(File_handle, trim(grpname), "neutdens", neutdens) + Call attach(File_handle, trim(grpname), "neuttemp", neuttemp) + Call attach(File_handle, trim(grpname), "neutpressure", neutpressure) + Call attach(File_handle, trim(grpname), "scatter_fac", scatter_fac) + Call attach(File_handle, trim(grpname), "Eion", Eion) + Call attach(File_handle, trim(grpname), "E0", E0) + Call attach(File_handle, trim(grpname), "Escale", Escale) + Call putarr(File_handle,trim(grpname)//"species", species) + if (allocated(io_cross_sec)) Call putarr(File_handle, trim(grpname)//"/io_cross_sec", io_cross_sec) + if (allocated(ela_cross_sec)) Call putarr(File_handle, trim(grpname)//"/ela_cross_sec", ela_cross_sec) + END IF + + End subroutine neutcol_diag + +!------------------------------------------------------------- + + SUBROUTINE neutcol_step(plist) + ! + USE random + USE beam + USE omp_lib + USE basic, ONLY: nlclassical + USE distrib, ONLY: lodgaus + type(particles), TARGET::plist(:) + type(particles),pointer::p + INTEGER:: i, omp_thread, num_threads, j, nbcolls_ela, nbcolls_io + real(kind=db):: Rand(5) + real(kind=db):: v2, v, ek, Everif, es, cosChi, thet, sig_io, sig_ela, vfact, xsi + type(linked_part_row), ALLOCATABLE:: ins_p(:) + type(linked_part), POINTER:: created, created2 + real(kind=db):: collisionfact,nucol(3),vinit(3),vend(3) + + p=>plist(species(1)) + + if(.not. nlcol .or. p%nploc .le. 0) return + + num_threads=omp_get_max_threads() + Allocate(ins_p(num_threads)) + + nbcolls_ela=0 + nbcolls_io=0 + nucol=0 + !$OMP PARALLEL DEFAULT(SHARED), private(collisionfact,i,omp_thread,Rand,v2,ek,sig_io,sig_ela,es,coschi,thet,vfact, created, v, everif,xsi,vinit,vend), reduction(+:nbcolls_ela,nbcolls_io, nucol) + omp_thread=omp_get_thread_num()+1 + allocate(ins_p(omp_thread)%start) + ins_p(omp_thread)%n=0 + created=>ins_p(omp_thread)%start + + !$OMP DO + DO i=1,p%Nploc + CALL random_array(Rand,1,ran_index(omp_thread),ran_array(:,omp_thread)) + v2=(p%UR(i)**2+p%UTHET(i)**2+p%UZ(i)**2) + if(nlclassical) THEN + ek=v2*escale + v=sqrt(v2) + vinit=(/p%UR(i),p%UTHET(i),p%UZ(i)/) + ELSE + ek=(p%gamma(i)-1)*escale + v=sqrt(v2)/p%gamma(i) + vinit=(/p%UR(i),p%UTHET(i),p%UZ(i)/)/p%gamma(i) + end if + sig_io=0 + sig_ela=0 + ! The ionisation event can only occur if the incoming electron energy is above the binding energy + if (ek .gt. Eion .and. nb_io_cross .gt. 1) then + sig_io=sig_fit(io_cross_sec,io_growth_cross_sec,ek, nb_io_cross) + end if + if (nb_ela_cross .gt. 1) then + sig_ela=sig_fit(ela_cross_sec,ela_growth_cross_sec,ek, nb_ela_cross) + xsi=Ek/(0.25*E0+Ek) + sig_ela=sig_ela*(2*xsi**2)/((1-xsi)*((1+xsi)*log((1+xsi)/(1-xsi))-2*xsi)) + end if + collisionfact=1-exp(-collfactor*(sig_io+sig_ela)*v) + ! If we have a collision + if (Rand(1) .lt.collisionfact) THEN + CALL random_array(Rand,1,ran_index(omp_thread),ran_array(:,omp_thread)) + IF(Rand(1).gt. sig_ela/(sig_io+sig_ela)) THEN ! An ionisation collision happened and we create the necessary electron + ins_p(omp_thread)%n=ins_p(omp_thread)%n+1 + allocate(created%next) + created%next%prev=>created + + ! Fill created particle physical quantities + created%p%R=p%R(i) + created%p%THET=p%THET(i) + created%p%Z=p%Z(i) + + IF( nlmaxwellio ) THEN ! the new electron is created according to a Maxwellian + CALL lodgaus(0, Rand(1:3)) + ! get random velocity + created%p%UR=vth*(Rand(1)) + created%p%UTHET=vth*(Rand(2)) + created%p%UZ=vth*(Rand(3)) + ELSE + CALL random_array(Rand,3,ran_index(omp_thread),ran_array(:,omp_thread)) + ! Compute created electron energy + Es=scatter_fac*tan(Rand(1)*atan((Ek-Eion)/(2*scatter_fac))) + ! Compute scattering angles for created electron + cosChi=1-2*Rand(2)/(1+8*Es/E0*(1-Rand(2))) + thet=Rand(3)*2*pi + if(nlclassical)THEN + ! new velocity factor for created particle + vfact=sqrt(Es/Ek) + ELSE + ! new velocity factor for created particle + vfact=sqrt(Es*(Es+2*Escale)/(Ek*(Ek+2*Escale))) + END IF + ! Fill created particle physical quantities + created%p%UR=vfact*(p%UR(i)) + created%p%UTHET=vfact*(p%UTHET(i)) + created%p%UZ=vfact*(p%UZ(i)) + + call rotate(created%p%UR,created%p%UTHET, created%p%UZ, coschi, thet) + END IF + vend=(/created%p%UR,created%p%UTHET,created%p%UZ/) + if(nlclassical)THEN + ! Lorentz factor for created particle + created%p%gamma=1.0 + ELSE + ! Lorentz factor for created particle + created%p%gamma=sqrt(1+created%p%UZ**2+created%p%UR**2+created%p%UTHET**2) + vend=vend/created%p%gamma + END IF + ! We prepare the next created particle + ins_p(omp_thread)%end=>created + created=>created%next + ! We keep track of what changed + nbcolls_io=nbcolls_io+1 + nucol=nucol-vend/vinit + + ! If we want the incoming electron to be scattered, we need to compute the + if (nldragio) THEN + ! We store the lossed energy in pot for keeping track of energy conservation + created%prev%p%pot=Eion+Es + CALL random_array(Rand,2,ran_index(omp_thread),ran_array(:,omp_thread)) + Es=Ek-Eion-Es + if(nlclassical)THEN + ! new velocity factor for scattered particle + vfact=sqrt(Es/Ek) + ELSE + ! new velocity factor for scattered particle + vfact=sqrt(Es*(Es+2*Escale)/(Ek*(Ek+2*Escale))) + END IF + ELSE + CYCLE + END IF + ELSE ! An elastic collision event happens + CALL random_array(Rand,2,ran_index(omp_thread),ran_array(:,omp_thread)) + Es=Ek + vfact=1.0 + nbcolls_ela=nbcolls_ela+1 + END IF + ! We calculate the scattered velocity angle for the scattered electron + cosChi=1-2*Rand(1)/(1+8*Es/E0*(1-Rand(1))) + thet=Rand(2)*2*pi + + ! Change the incident electron velocity direction and amplitude if necessary + p%UR(i)=p%UR(i)*vfact + p%UTHET(i)=p%UTHET(i)*vfact + p%UZ(i)=p%UZ(i)*vfact + call rotate(p%UR(i),p%UTHET(i), p%UZ(i), coschi, thet) + if(nlclassical) THEN + vend=(/p%UR(i),p%UTHET(i),p%UZ(i)/) + ELSE + p%gamma(i)=sqrt(1+p%UZ(i)**2+p%UR(i)**2+p%UTHET(i)**2) + vend=(/p%UR(i),p%UTHET(i),p%UZ(i)/)/p%gamma(i) + END IF + nucol=nucol+1-vend/vinit + END IF + END DO + !$OMP END DO + if(associated(created%prev)) then + created=>created%prev + ins_p(omp_thread)%end=>created + deallocate(created%next) + else + deallocate(ins_p(omp_thread)%start) + end if + !$OMP END PARALLEL + ! We collect all created particules into one linked list + Do i=1,num_threads + if(associated(ins_p(i)%start)) then + created=>ins_p(i)%end + Do j=i+1,num_threads + created%next=>ins_p(j)%start + ins_p(i)%n=ins_p(i)%n+ins_p(j)%n + IF(ASSOCIATED(created%next)) then + created=>ins_p(j)%end + END IF + End Do + if(species(2).gt.0) then + CALL add_created_part(plist(species(2)), ins_p(i),.false.,.true.) + end if + CALL add_created_part(p,ins_p(i),.true.,.false.) + exit + end if + end do + DEALLOCATE(ins_p) + p%nbcolls=p%nbcolls+(/nbcolls_io, nbcolls_ela/) + p%nudcol=nucol + !Write(*,*)"mpirank: ", mpirank, " Nb colls ela, io: ",nbcolls_ela, nbcolls_io + ! + END SUBROUTINE neutcol_step + + FUNCTION sig_fit(sig_vec,growth_vec,ek,nb_cross) + use distrib, ONLY: closest + real(kind=db)::sig_fit, ek + real(kind=db):: sig_vec(:,:), growth_vec(:) + Integer:: k, nb_cross + sig_fit=0 + k=closest(sig_vec(:,1),ek, nb_cross-1) + if(k.lt.1) return + + !sig_fit=(sig_vec(k,1)-sig_vec(k-1,1))/(sig_vec(k,2)-sig_vec(k-1,2))*(sig_vec(k,2)-ek)+sig_vec(k-1,1) + ! Exponential fitting relevant at high energies + sig_fit=sig_vec(k,2)*(ek/sig_vec(k,1))**growth_vec(k) + END FUNCTION sig_fit + + SUBROUTINE rotate(Ur, Uthet, Uz, coschi, thet) + real(kind=db), INTENT(INOUT):: Ur, uthet, uz, coschi, thet + real(kind=db):: norm, perp(3), U(3), U0(3), normf + real(kind=db):: sinchi, sinthet, costhet + Integer :: iperp1,iperp2 + + U0=(/Ur,Uthet,Uz/) + norm=sqrt(sum(U0**2)) + U=U0/norm + ! Find a vector perpendicular to U for chi rotation + ! find the direction with maximum amplitude + perp=(/1,1,1/) + iperp1=maxloc(abs(U),1) + ! find second direction with next max amplitude + perp(iperp1)=0 + iperp2=maxloc(abs(perp*U),1) + perp=0 + perp(iperp2)=U(iperp1) + perp(iperp1)=-U(iperp2) + ! Normalise the rotation vector + perp=perp/sqrt(sum(perp**2)) + + ! Compute sinus and cosinus for rotation + sinchi=sqrt(1-coschi**2) + costhet=cos(thet) + sinthet=sin(thet) + + ! Rotation of angle chi around perp + Ur = (coschi+perp(1)**2*(1-coschi))*U0(1) + (perp(1)*perp(2)*(1-coschi)-perp(3)*sinchi)*U0(2) + (perp(1)*perp(3)*(1-coschi) + perp(2)*sinchi)*U0(3) + Uthet = (perp(1)*perp(2)*(1-coschi)+perp(3)*sinchi)*U0(1) + (coschi + perp(2)**2*(1-coschi))*U0(2) +(perp(2)*perp(3)*(1-coschi)-perp(1)*sinchi)*U0(3) + Uz = (perp(1)*perp(3)*(1-coschi)-perp(2)*sinchi)*U0(1) +(perp(3)*perp(2)*(1-coschi)+perp(1)*sinchi)*U0(2) +( coschi+perp(3)**2*(1-coschi))*U0(3) + + U0 =(/Ur,Uthet,Uz/) + + ! second rotation according to uniform distribution + ! Rotation of angle theta around U + Ur = (costhet+U(1)**2*(1-costhet))*U0(1) + (U(1)*U(2)*(1-costhet) - U(3)*sinthet)*U0(2) + (U(1)*U(3)*(1-costhet)+U(2)*sinthet)*U0(3) + Uthet = (U(2)*U(1)*(1-costhet)+U(3)*sinthet)*U0(1) + (costhet + U(2)**2*(1-costhet))*U0(2) + (U(2)*U(3)*(1-costhet)-U(1)*sinthet)*U0(3) + Uz = (U(3)*U(1)*(1-costhet) - U(2)*sinthet)*U0(1) + (U(3)*U(2)*(1-costhet)+U(1)*sinthet)*U0(2) + (costhet +U(3)**2*(1-costhet))*U0(3) + + !normf=sqrt(Ur**2+Uthet**2+Uz**2) + !if(abs(norm-normf)/norm .gt. 1e-14) WRITE(*,*) "Error in rotate the norm of v changed" + END SUBROUTINE rotate + + + SUBROUTINE read_cross_sec(filename,cross_sec, nb_cross) + CHARACTER(len=*) ::filename + Real(kind=db), ALLOCATABLE :: cross_sec(:,:) + INTEGER:: nb_cross + INTEGER :: lu_cross_sec=9999 + INTEGER:: i, openerr, reason + CHARACTER(len=256) :: header + real(kind=db):: t1,t2 + + nb_cross=0 + + OPEN(UNIT=lu_cross_sec,FILE=trim(filename),ACTION='READ',IOSTAT=openerr) + header=' ' + IF(openerr .ne. 0) THEN + CLOSE(unit=lu_cross_sec) + RETURN + END IF + + ! The cross section table is defined as a two column energy and cross_section + DO WHILE(.true.) + READ(lu_cross_sec,'(a)',IOSTAT=reason) header + header=adjustl(header) + if(reason .lt. 0 ) exit ! We reached end of file + if( header(1:1) .ne. '!') then + READ(header,*) t1, t2 + if(t1 .ne. 0 .and. t2.ne. 0) nb_cross=nb_cross+1 + end if + END DO + if (allocated(cross_sec)) deallocate(cross_sec) + allocate(cross_sec(nb_cross,2)) + REWIND(lu_cross_sec) + + ! The cross section table is defined as a two column energy and cross_section + i=1 + DO WHILE(i .le. nb_cross) + READ(lu_cross_sec,'(a)',IOSTAT=reason) header + header=adjustl(header) + if(reason .lt. 0 ) exit ! We reached end of file + if( header(1:1) .ne. '!') then + READ(header,*) cross_sec(i,1), cross_sec(i,2) + if(cross_sec(i,1) .ne. 0 .and. cross_sec(i,2).ne. 0) i=i+1 + end if + END DO + CLOSE(unit=lu_cross_sec) + END subroutine read_cross_sec +end module neutcol + + + diff --git a/src/particletypes_mod.f90 b/src/particletypes_mod.f90 new file mode 100644 index 0000000..7610473 --- /dev/null +++ b/src/particletypes_mod.f90 @@ -0,0 +1,513 @@ +!------------------------------------------------------------------------------ +! EPFL/Swiss Plasma Center +!------------------------------------------------------------------------------ +! +! MODULE: particletypes +! +!> @author +!> Guillaume Le Bars EPFL/SPC +!> Patryk Kaminski EPFL/SPC +!> Trach Minh Tran EPFL/SPC +! +! DESCRIPTION: +!> Module responsible for defining the particle types and defining some subroutines to change their size, +!> initialize them or delete them +!------------------------------------------------------------------------------ + +MODULE particletypes + USE constants + ! + IMPLICIT NONE + + !> Stores the particles properties for the run. + TYPE particles + INTEGER :: Nploc !< Local number of simulated particles + INTEGER :: Nptot !< Total number of simulated particles + INTEGER :: Newindex !< Stores the higher partindex for the creation of new particles + REAL(kind=db) :: m !< Particle mass + REAL(kind=db) :: q !< Particle charge + REAL(kind=db) :: weight !< Number of particles represented by one macro-particle + REAL(kind=db) :: qmRatio !< Charge over mass ratio + REAL(kind=db) :: nudcol(3) !< Effective momentum drag frequency + REAL(kind=db) :: H0 + REAL(kind=db) :: P0 + REAL(kind=db) :: temperature + LOGICAL :: Davidson=.false. + LOGICAL :: is_test= .false. !< detremines if particle is saved on ittracer + LOGICAL :: is_field= .true. !< detremines if particle contributes to Poisson solver + LOGICAL :: calc_moments=.false. + INTEGER, allocatable :: nblost(:) !< number of particles lost in domain boundaries at current timestep + INTEGER :: nbadded !< number of particles added by source since last gather + INTEGER, DIMENSION(2) :: nbcolls !< number of particles collisions with neutrals ionisation, elastic) + INTEGER, DIMENSION(:), ALLOCATABLE :: Rindex !< Index in the electric potential grid for the R direction + INTEGER, DIMENSION(:), ALLOCATABLE :: Zindex !< Index in the electric potential grid for the Z direction + INTEGER, DIMENSION(:), ALLOCATABLE :: partindex !< Index of the particle to be able to follow it when it goes from one MPI host to the other + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: R !< radial coordinates of the particles + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: Z !< longitudinal coordinates of the particles + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: THET !< azimuthal coordinates of the particles + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: BZ !< axial radial relative distances to the left grid line + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: BR !< radial relative distances to the bottom grid line + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: pot !< Electric potential + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: potxt !< External electric potential + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: Er !< Radial Electric field + REAL(kind=db), DIMENSION(:), ALLOCATABLE :: Ez !< Axial electric field + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: UR !< normalized radial velocity at the current time step + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: URold !< normalized radial velocity at the previous time step + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: UTHET !< normalized azimuthal velocity at the current time step + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: UTHETold !< normalized azimuthal velocity at the previous time step + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: UZ !< normalized axial velocity at the current time step + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: UZold !< normalized axial velocity at the previous time step + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: Gamma !< Lorentz factor at the current time step + REAL(kind=db), DIMENSION(:), CONTIGUOUS, POINTER:: Gammaold !< Lorentz factor at the previous time step + Real(kind=db), Dimension(:,:),ALLOCATABLE:: geomweight !< geometric weight at the particle position + Real(kind=db), Dimension(:,:),ALLOCATABLE:: moments !< stores the moment matrix + LOGICAL:: collected !< Stores if the particles data have been collected to MPI root process during this timestep + INTEGER, DIMENSION(:), ALLOCATABLE:: addedlist + END TYPE particles + + !> Structure containing a single particle position and velocity used in MPI communications. + TYPE particle + INTEGER :: partindex =0 + REAL(kind=db) :: R =0 + REAL(kind=db) :: Z =0 + REAL(kind=db) :: THET =0 + REAL(kind=db) :: UZ =0 + REAL(kind=db) :: UR =0 + REAL(kind=db) :: UTHET =0 + REAL(kind=db) :: Gamma =0 + REAL(kind=db) :: pot =0 + END TYPE particle + + TYPE linked_part + type(particle) p + type(linked_part), POINTER:: next=> NULL() + type(linked_part), POINTER:: prev=> NULL() + END TYPE linked_part + + TYPE linked_part_row + INTEGER :: n = 0 + type(linked_part), POINTER:: start=>NULL() + type(linked_part), POINTER:: end=>NULL() + END TYPE linked_part_row + + CONTAINS + + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Allocate the memory for the particles variable storing the particles quantities. +! +!> @param[inout] p the particles variable needing to be allocated. +!> @param[in] nparts the maximum number of particles that will be stored in this variable +!--------------------------------------------------------------------------- + +SUBROUTINE creat_parts(p, nparts) + TYPE(particles) :: p + INTEGER, INTENT(in) :: nparts + + IF (.NOT. ALLOCATED(p%Z) ) THEN + p%Nploc = nparts + p%Nptot = nparts + ALLOCATE(p%Z(nparts)) + ALLOCATE(p%R(nparts)) + ALLOCATE(p%THET(nparts)) + ALLOCATE(p%BZ(nparts)) + ALLOCATE(p%BR(nparts)) + ALLOCATE(p%UR(nparts)) + ALLOCATE(p%UZ(nparts)) + ALLOCATE(p%UTHET(nparts)) + ALLOCATE(p%URold(nparts)) + ALLOCATE(p%UZold(nparts)) + ALLOCATE(p%UTHETold(nparts)) + ALLOCATE(p%Gamma(nparts)) + ALLOCATE(p%Rindex(nparts)) + ALLOCATE(p%Zindex(nparts)) + ALLOCATE(p%partindex(nparts)) + ALLOCATE(p%pot(nparts)) + ALLOCATE(p%potxt(nparts)) + ALLOCATE(p%Er(nparts)) + ALLOCATE(p%Ez(nparts)) + ALLOCATE(p%GAMMAold(nparts)) + Allocate(p%geomweight(nparts,0:2)) + allocate(p%nblost(4)) + p%newindex=0 + p%nblost=0 + p%nbadded=0 + p%partindex=-1 + p%URold=0 + p%UZold=0 + p%UTHETold=0 + p%rindex=0 + p%zindex=0 + p%BR=0 + p%BZ=0 + p%UR=0 + p%UZ=0 + p%UTHET=0 + p%Z=0 + p%R=0 + p%THET=0 + p%Gamma=1 + p%Er=0 + p%Ez=0 + p%pot=0 + p%potxt=0 + p%gammaold=1 + p%collected=.false. + p%Davidson=.false. + p%is_test=.false. + p%is_field=.true. + p%calc_moments=.true. + p%m=me + p%q=-elchar + p%qmRatio=p%q/p%m + p%weight=1.0_db + p%H0=0 + p%P0=0 + p%temperature=0 + p%geomweight=0 + END IF +END SUBROUTINE creat_parts + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Copy one particle from the receive buffers to the local simulation variable parts. +! +!> @param [in] part particle parameters to copy from +!> @param [in] partsindex destination particle index in the local parts variable +!--------------------------------------------------------------------------- +SUBROUTINE Insertincomingpart(p, part, partsindex) + TYPE(particles), INTENT(INOUT):: p + INTEGER, INTENT(in) :: partsindex + TYPE(particle), INTENT(in) :: part + p%partindex(partsindex) = part%partindex + p%R(partsindex) = part%R + p%Z(partsindex) = part%Z + p%THET(partsindex) = part%THET + p%UZ(partsindex) = part%UZ + p%UR(partsindex) = part%UR + p%UTHET(partsindex) = part%UTHET + p%Gamma(partsindex) = part%Gamma + p%pot(partsindex) = part%pot +! +END SUBROUTINE Insertincomingpart + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Copy one particle from the local parts variable to the send buffer. +! +!> @param [in] buffer send buffer to copy to +!> @param [in] bufferindex particle index in the send buffer +!> @param [in] partsindex origin particle index in the local parts variable +!--------------------------------------------------------------------------- +SUBROUTINE Insertsentpart(p, buffer, bufferindex, partsindex) + TYPE(particles), INTENT(INOUT):: p + INTEGER, INTENT(in) :: bufferindex, partsindex + TYPE(particle), DIMENSION(:), INTENT(inout) :: buffer + buffer(bufferindex)%partindex = p%partindex(partsindex) + buffer(bufferindex)%R = p%R(partsindex) + buffer(bufferindex)%Z = p%Z(partsindex) + buffer(bufferindex)%THET = p%THET(partsindex) + buffer(bufferindex)%UZ = p%UZ(partsindex) + buffer(bufferindex)%UR = p%UR(partsindex) + buffer(bufferindex)%UTHET = p%UTHET(partsindex) + buffer(bufferindex)%Gamma = p%Gamma(partsindex) + buffer(bufferindex)%pot = p%pot(partsindex) +! +END SUBROUTINE Insertsentpart + + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> @brief Exchange two particles in the parts variable. +! +!> @param [in] index1 index in parts of the first particle to exchange. +!> @param [in] index2 index in parts of the second particle to exchange. +!--------------------------------------------------------------------------- +SUBROUTINE exchange_parts(p, index1, index2) + TYPE(particles), INTENT(INOUT):: p + INTEGER, INTENT(IN) :: index1, index2 + REAL(kind=db):: R, Z, THET, UR, UZ, UTHET, Gamma, geomweight(0:2),pot + INTEGER :: Rindex, Zindex, partindex + !! Exchange particle at index1 with particle at index2 + + ! Store part at index1 in temporary value + partindex = p%partindex(index1) + Gamma = p%Gamma(index1) + pot = p%pot(index1) + R = p%R(index1) + Z = p%Z(index1) + THET = p%THET(index1) + UR = p%UR(index1) + UTHET = p%UTHET(index1) + UZ = p%UZ(index1) + Rindex = p%Rindex(index1) + Zindex = p%Zindex(index1) + geomweight = p%geomweight(index1,:) + + ! Move part at index2 in part at index 1 + p%partindex(index1) = p%partindex(index2) + p%Gamma(index1) = p%Gamma(index2) + p%pot(index1) = p%pot(index2) + p%R(index1) = p%R(index2) + p%Z(index1) = p%Z(index2) + p%THET(index1) = p%THET(index2) + p%UR(index1) = p%UR(index2) + p%UTHET(index1) = p%UTHET(index2) + p%UZ(index1) = p%UZ(index2) + p%Rindex(index1) = p%Rindex(index2) + p%Zindex(index1) = p%Zindex(index2) + p%geomweight(index1,:) = p%geomweight(index2,:) + + ! Move temporary values from part(index1) to part(index2) + p%partindex(index2) = partindex + p%Gamma(index2) = Gamma + p%pot(index2) = pot + p%R(index2) = R + p%Z(index2) = Z + p%THET(index2) = THET + p%UR(index2) = UR + p%UTHET(index2) = UTHET + p%UZ(index2) = UZ + p%Rindex(index2) = Rindex + p%Zindex(index2) = Zindex + p%geomweight(index2,:) = geomweight + + END SUBROUTINE exchange_parts + + SUBROUTINE change_parts_allocation(p, sizedifference) + implicit none + TYPE(particles), INTENT(INOUT):: p + INTEGER,INTENT(IN) :: sizedifference + CALL change_array_size_int(p%Rindex, sizedifference) + CALL change_array_size_int(p%Zindex, sizedifference) + CALL change_array_size_int(p%partindex, sizedifference) + CALL change_array_size_dp(p%ER,sizedifference) + CALL change_array_size_dp(p%EZ,sizedifference) + CALL change_array_size_dp(p%pot,sizedifference) + CALL change_array_size_dp(p%potxt,sizedifference) + CALL change_array_size_dp(p%R,sizedifference) + CALL change_array_size_dp(p%Z,sizedifference) + CALL change_array_size_dp(p%THET,sizedifference) + CALL change_array_size_dp(p%BR,sizedifference) + CALL change_array_size_dp(p%BZ,sizedifference) + CALL change_array_size_dp2(p%geomweight,sizedifference) + CALL change_array_size_dp_ptr(p%UR,sizedifference) + CALL change_array_size_dp_ptr(p%URold,sizedifference) + CALL change_array_size_dp_ptr(p%UZ,sizedifference) + CALL change_array_size_dp_ptr(p%UZold,sizedifference) + CALL change_array_size_dp_ptr(p%UTHET,sizedifference) + CALL change_array_size_dp_ptr(p%UTHETold,sizedifference) + CALL change_array_size_dp_ptr(p%Gamma,sizedifference) + CALL change_array_size_dp_ptr(p%Gammaold,sizedifference) + p%Nploc=MIN(p%Nploc,size(p%R)) + END SUBROUTINE change_parts_allocation + + SUBROUTINE change_array_size_dp(arr, sizedifference) + implicit none + REAL(kind=db), ALLOCATABLE, INTENT(INOUT):: arr(:) + INTEGER, INTENT(IN):: sizedifference + REAL(kind=db), ALLOCATABLE:: temp(:) + INTEGER:: current_size, new_size + if(allocated(arr)) THEN + current_size=size(arr) + new_size=current_size+sizedifference + ALLOCATE(temp(new_size)) + temp(1:min(current_size,new_size))=arr(1:min(current_size,new_size)) + DEALLOCATE(arr) + CALL move_alloc(temp, arr) + END IF + END SUBROUTINE change_array_size_dp + + SUBROUTINE change_array_size_dp2(arr, sizedifference) + implicit none + REAL(kind=db), ALLOCATABLE, INTENT(INOUT):: arr(:,:) + INTEGER, INTENT(IN):: sizedifference + REAL(kind=db), ALLOCATABLE:: temp(:,:) + INTEGER:: current_size, new_size + if(allocated(arr)) THEN + current_size=size(arr,1) + new_size=current_size+sizedifference + ALLOCATE(temp(new_size,0:size(arr,2)-1)) + temp(1:min(current_size,new_size),:)=arr(1:min(current_size,new_size),:) + DEALLOCATE(arr) + CALL move_alloc(temp, arr) + END IF + END SUBROUTINE change_array_size_dp2 + + SUBROUTINE change_array_size_dp_ptr(arr, sizedifference) + implicit none + REAL(kind=db), POINTER, INTENT(INOUT):: arr(:) + INTEGER, INTENT(IN):: sizedifference + REAL(kind=db), CONTIGUOUS, POINTER:: temp(:) + INTEGER:: current_size, new_size + if(associated(arr)) THEN + current_size=size(arr) + new_size=current_size+sizedifference + ALLOCATE(temp(new_size)) + temp(1:min(current_size,new_size))=arr(1:min(current_size,new_size)) + DEALLOCATE(arr) + arr=> temp + END IF + END SUBROUTINE change_array_size_dp_ptr + + SUBROUTINE change_array_size_int(arr, sizedifference) + implicit none + INTEGER, ALLOCATABLE, INTENT(INOUT):: arr(:) + INTEGER, INTENT(IN):: sizedifference + INTEGER, ALLOCATABLE:: temp(:) + INTEGER:: current_size, new_size + + if(allocated(arr)) THEN + current_size=size(arr) + new_size=current_size+sizedifference + ALLOCATE(temp(new_size)) + temp(1:min(current_size,new_size))=arr(1:min(current_size,new_size)) + DEALLOCATE(arr) + CALL move_alloc(temp,arr) + END IF + END SUBROUTINE change_array_size_int + + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Move particle with index sourceindex to particle with index destindex. +!> !WARNING! This will overwrite particle at destindex. +! +!> @param [in] sourceindex index in parts of the particle to move. +!> @param [in] destindex index in parts of the moved particle destination. +!--------------------------------------------------------------------------- + SUBROUTINE move_part(p, sourceindex, destindex) + !! This will destroy particle at destindex + INTEGER, INTENT(IN) :: destindex, sourceindex + TYPE(particles), INTENT(INOUT)::p + + IF(sourceindex .eq. destindex) RETURN + IF(sourceindex .le. 0 .or. destindex .le. 0) RETURN + ! Move part at sourceindex in part at destindex + Call copy_part(p,sourceindex,destindex,p) + + END SUBROUTINE move_part + + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Copy particle with index sourceindex in particles sourcep to particle with index destindex in particles destp. +!> !WARNING! This will overwrite particle at destp(destindex). +! +!> @param [inout] sourcep Structure of source particles. +!> @param [in] sourceindex index in parts of the particle to move. +!> @param [in] destindex index in parts of the moved particle destination. +!> @param [inout] destp Structure of source particles. +!--------------------------------------------------------------------------- + SUBROUTINE copy_part(sourcep, sourceindex, destindex, destp) + !! This will destroy particle at destindex + INTEGER, INTENT(IN) :: destindex, sourceindex + TYPE(particles), INTENT(IN)::sourcep + TYPE(particles), INTENT(INOUT)::destp + + IF(sourceindex .le. 0 .or. destindex .le. 0) RETURN + IF( destindex .gt. size(destp%R,1)) RETURN + ! Move part at sourceindex in part at destindex + destp%partindex(destindex) = sourcep%partindex(sourceindex) + destp%Gamma(destindex) = sourcep%Gamma(sourceindex) + destp%Gammaold(destindex) = sourcep%Gammaold(sourceindex) + destp%R(destindex) = sourcep%R(sourceindex) + destp%Z(destindex) = sourcep%Z(sourceindex) + destp%THET(destindex) = sourcep%THET(sourceindex) + destp%UR(destindex) = sourcep%UR(sourceindex) + destp%UTHET(destindex) = sourcep%UTHET(sourceindex) + destp%UZ(destindex) = sourcep%UZ(sourceindex) + destp%URold(destindex) = sourcep%URold(sourceindex) + destp%UTHETold(destindex) = sourcep%UTHETold(sourceindex) + destp%UZold(destindex) = sourcep%UZold(sourceindex) + destp%Rindex(destindex) = sourcep%Rindex(sourceindex) + destp%Zindex(destindex) = sourcep%Zindex(sourceindex) + destp%geomweight(destindex,:) = sourcep%geomweight(sourceindex,:) + destp%pot(destindex) = sourcep%pot(sourceindex) + destp%potxt(destindex) = sourcep%potxt(sourceindex) + + END SUBROUTINE copy_part + !________________________________________________________________________________ + + SUBROUTINE destroy_parts(p) + TYPE(particles) :: p + p%Nploc=0 + IF(ALLOCATED(p%Z)) DEALLOCATE(p%Z) + IF(ALLOCATED(p%R)) DEALLOCATE(p%R) + IF(ALLOCATED(p%THET)) DEALLOCATE(p%THET) + IF(ALLOCATED(p%BZ)) DEALLOCATE(p%BZ) + IF(ALLOCATED(p%BR)) DEALLOCATE(p%BR) + IF(ASSOCIATED(p%UR)) DEALLOCATE(p%UR) + IF(Associated(p%URold)) DEALLOCATE(p%URold) + IF(Associated(p%UZ)) DEALLOCATE(p%UZ) + IF(Associated(p%UZold)) DEALLOCATE(p%UZold) + IF(Associated(p%UTHET)) DEALLOCATE(p%UTHET) + IF(Associated(p%UTHETold)) DEALLOCATE(p%UTHETold) + IF(Associated(p%Gamma)) DEALLOCATE(p%Gamma) + IF(Associated(p%Gammaold)) DEALLOCATE(p%Gammaold) + IF(ALLOCATED(p%Rindex)) DEALLOCATE(p%Rindex) + IF(ALLOCATED(p%Zindex)) DEALLOCATE(p%Zindex) + IF(ALLOCATED(p%partindex)) DEALLOCATE(p%partindex) + if(allocated(p%geomweight)) Deallocate(p%geomweight) + if(allocated(p%moments)) Deallocate(p%moments) + END SUBROUTINE + !________________________________________________________________________________ + SUBROUTINE clean_beam(partslist) + ! + INTEGER:: i + type(particles):: partslist(:) + + Do i=1,size(partslist,1) + CALL destroy_parts(partslist(i)) + END DO + ! + END SUBROUTINE clean_beam + !________________________________________________________________________________ + SUBROUTINE swappointer( pointer1, pointer2) + REAL(kind=db), DIMENSION(:), POINTER, INTENT(inout):: pointer1, pointer2 + REAL(kind=db), DIMENSION(:), POINTER:: temppointer + temppointer=>pointer1 + pointer1=>pointer2 + pointer2=>temppointer + END SUBROUTINE swappointer + + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Deallocate recursively a linked_paticle linked list +! +!> @param [in] l_p linked_part particle to be dallocated. +!--------------------------------------------------------------------------- + RECURSIVE SUBROUTINE destroy_linked_parts(l_p) + TYPE(linked_part), POINTER :: l_p + + IF(associated(l_p%next)) call destroy_linked_parts(l_p%next) + deallocate(l_p) + END subroutine destroy_linked_parts + +END MODULE particletypes \ No newline at end of file diff --git a/src/psupply_mod.f90 b/src/psupply_mod.f90 new file mode 100644 index 0000000..2abc182 --- /dev/null +++ b/src/psupply_mod.f90 @@ -0,0 +1,306 @@ +module psupply + + use constants + implicit none + + type power_supply + logical :: active = .false. ! is the power supply active + real(kind=db):: geomcapacitor ! capacitance of the metalic vessel normalised to the neutral density in vessel used in the neutcol module + real(kind=db):: PSresistor ! internal resistance of the power supply normalised to the actual neutral density in vessel + real(kind=db):: targetbias ! Set voltage on the power supply + real(kind=db):: bias ! current voltage on the power supply + integer :: nbbounds ! number of boundaries defined in the geometry + integer :: lststp=0 ! previous step on which the bias was updated + real(kind=db):: current(3)=0 ! current collected on the boundaries normalised to the simulated collision neutral density set in neutcol module + ! 1 is at time i-2nbhdt, 2 is at time i-nbhdt and 3 is at time i + integer, allocatable:: bdpos(:) ! sign of each boundary for collected charge to determine direction of current + real(kind=db),allocatable:: charge(:) ! Charge collected on each boundary and per nbdt + real(kind=db),allocatable:: biases(:) ! Actual potentials at each boundary + integer :: nbhdt = 10 ! half of the number of time steps between each calls to RK4 + real(kind=db):: expdens ! [m-3] experimental neutral density + real(kind=db):: neutcoldens ! [m-3] neutral density used in neutcol module + end type + + type(power_supply):: the_ps + + contains + ! read the input parameters from the input file and setup the necesary variables for the module to work + subroutine psupply_init(fileid,cstep,nbbounds,neutcoldens,rstbias) + use splinebound + use basic, only: phinorm, tnorm, mpirank, qnorm, potinn, potout + use constants + use mpi + use geometry + use weighttypes + use fields + integer:: fileid, cstep, nbbounds, istat, nbhdt, ierr,i + real(kind=db),OPTIONAL, INTENT(IN):: rstbias + real(kind=db):: neutcoldens ! [m-3] + real(kind=db):: expneutdens ! [m-3] + real(kind=db):: PsResistor ! [Ohm] + real(kind=db):: geomcapacitor ! [F] + real(kind=db):: targetbias ! [V] + integer, allocatable:: bdpos(:) + character(len=1000) :: line + logical :: active = .false. + + NAMELIST /psupplyparams/ expneutdens, PsResistor, geomcapacitor, targetbias, nbhdt, active, bdpos + + + the_ps%lststp=cstep + the_ps%nbbounds=nbbounds + + allocate(the_ps%bdpos(nbbounds),bdpos(nbbounds)) + allocate(the_ps%charge(nbbounds),the_ps%biases(nbbounds)) + the_ps%bdpos=0 + bdpos=0 + the_ps%charge=0 + the_ps%biases=0 + the_ps%current=0 + + ! read the input parameters from file + Rewind(fileid) + READ(fileid, psupplyparams, iostat=istat) + + if (istat.gt.0) then + backspace(fileid) + read(fileid,fmt='(A)') line + write(*,'(A)') & + 'Invalid line in pssupplyparams: '//trim(line) + call MPI_Abort(MPI_COMM_WORLD, -1, ierr) + stop + end if + + ! save the parameters on output + IF(mpirank .eq. 0) THEN + WRITE(*, psupplyparams) + END IF + + ! rescale the targetbias set on the power supply + the_ps%targetbias=abs(targetbias)/phinorm + + the_ps%bdpos=bdpos + + ! save the experimental neutral density + the_ps%expdens=expneutdens + ! save the neutral collision density + the_ps%neutcoldens=neutcoldens + + if(present(rstbias))then + ! Initialize the current bias from the input parameters + the_ps%bias=rstbias + else + if (the_domain%nbsplines.gt.0) then + do i=1,the_ps%nbbounds + if(the_ps%bdpos(i) .lt. 0)then + the_ps%bias=-the_domain%boundaries(i)%Dirichlet_val + exit + end if + end do + else + the_ps%bias=(potout-potinn) + end if + end if + + ! set the initial bias + where(the_ps%bdpos .lt. 0) + the_ps%biases=the_ps%bias*the_ps%bdpos + end where + + ! Normalise resistor and capacitor to adapt to experimental pressure + the_ps%PSresistor = PSresistor*the_ps%expdens/the_ps%neutcoldens*qnorm/(tnorm*phinorm) + the_ps%geomcapacitor = geomcapacitor*phinorm/qnorm + + the_ps%nbhdt = nbhdt + the_ps%active = active + + ! Initialize the biases + if (the_domain%nbsplines.gt.0) then + do i=1,the_ps%nbbounds + the_domain%boundaries(i)%Dirichlet_val=the_ps%biases(i) + end do + else + potinn=-the_ps%biases(1) + Potout=0 + end if + + ! recalculate gtilde to adapt for the new biases + CALL total_gtilde(vec1, vec2, gtilde, gridwgeom) + call comp_gradgtilde + + end subroutine + + ! save to the result file the parameters of this module read from the input + Subroutine psupply_diag(File_handle, str) + use mpi + Use futils + use basic, only: tnorm, phinorm, qnorm + implicit none + Integer:: File_handle + Character(len=*):: str + CHARACTER(len=256):: grpname + Integer:: ierr, mpirank + + CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) + + IF(mpirank .eq. 0 .and. the_ps%active) THEN + + Write(grpname,'(a,a)') trim(str),"/psupply" + If(.not. isgroup(File_handle, trim(grpname))) THEN + CALL creatg(File_handle, trim(grpname)) + END IF + Call attach(File_handle, trim(grpname), "expdens", the_ps%expdens) + Call attach(File_handle, trim(grpname), "targetbias", the_ps%targetbias*phinorm) + Call attach(File_handle, trim(grpname), "PSresistor", the_ps%PSresistor/the_ps%expdens*the_ps%neutcoldens/qnorm*(tnorm*phinorm)) + Call attach(File_handle, trim(grpname), "geomcapacitor", the_ps%geomcapacitor/phinorm*qnorm) + Call attach(File_handle, trim(grpname), "nbhdt", the_ps%nbhdt) + Call putarr(File_handle,trim(grpname)//"/bdpos", the_ps%bdpos) + END IF + End subroutine psupply_diag + + ! gneral routine called from stepon to update the psupply bias + subroutine psupply_step(ps,p,cstep) + use particletypes + use geometry + use weighttypes + use fields + use basic, only: Potinn, potout + type(power_supply):: ps + type(particles):: p(:) + integer:: cstep, i + if (.not. ps%active ) return + + ! calculate the charge collected on each boundary due to the contribution of each specie + call add_charge(ps,p,cstep) + + ! calculate the current flowing between the electrodes due to the cloud + call calc_current(ps,cstep) + + ! calculate the bias at the new time step + call updt_bias(ps,cstep) + + if(mod(cstep-ps%lststp,2*ps%nbhdt) .ne. 0) return + + ! update the bias on the geometry for the Dirichlet b.c. + if (the_domain%nbsplines.gt.0) then + do i=1,ps%nbbounds + the_domain%boundaries(i)%Dirichlet_val=ps%biases(i) + end do + else + potinn=-ps%biases(1) + Potout=0 + end if + ! recalculate gtilde to adapt for the new biases + CALL total_gtilde(vec1, vec2, gtilde, gridwgeom) + call comp_gradgtilde + end subroutine + + + ! calculates the current flowing between the electrodes due to the cloud + subroutine calc_current(ps,cstep) + use geometry + use basic, only: phinorm, dt + use fields + type(power_supply):: ps + integer:: cstep + + if(mod(cstep-ps%lststp,ps%nbhdt).eq.0) then + ! communicate the charge accumulation in this timestep + call reduce_charge(ps) + if(mod(cstep-ps%lststp,ps%nbhdt*2).eq.0)then + + ! calculates the current by adding the contribution of each boundary + if (mpirank .eq. 0)then + ps%current(3)=sum(-ps%charge*ps%bdpos)/(ps%nbhdt*dt) + end if + ps%lststp=cstep + else + ! calculates the current by adding the contribution of each boundary + if (mpirank .eq. 0)then + ps%current(2)=sum(-ps%charge*ps%bdpos)/(ps%nbhdt*dt) + end if + end if + ps%charge=0 + end if + + + end subroutine + + ! calculate the charge deposited by each specie on the electrodes (used to calculate the resulting current) + subroutine add_charge(ps,p, cstep) + use particletypes + use basic, only: qnorm + type(power_supply):: ps + type(particles):: p(:) + integer::cstep, i + + do i=1,size(p,1) + if(.not. p(i)%is_field) cycle + !Add the normalised contribution of each specie + ps%charge=ps%charge+p(i)%nblost(5:)*p(i)%weight*p(i)%q/qnorm + end do + end subroutine + + ! Time integrate the ODE of the actual bias between the accelerating electrodes + ! and broadcast it to all the workers + subroutine updt_bias(ps,cstep) + use basic, only: dt, phinorm + implicit none + type(power_supply):: ps + integer:: cstep + real(kind=db):: bias,k1,k2,k3,k4, hdeltat + + if(mod(cstep-ps%lststp,2*ps%nbhdt) .ne. 0) return + + ! half delta t + hdeltat=dt*ps%nbhdt/(ps%PSresistor*ps%geomcapacitor) + bias=ps%bias + + ! we update the bias using RK4 + k1=-(bias+ps%current(1)*ps%PSresistor-ps%targetbias) + k2=-(bias+hdeltat*k1+ps%current(2)*ps%PSresistor-ps%targetbias) + k3=-(bias+hdeltat*k2+ps%current(2)*ps%PSresistor-ps%targetbias) + k4=-(bias+2*hdeltat*k3+ps%current(3)*ps%PSresistor-ps%targetbias) + + ps%bias=bias+(k1+2*k2+2*k3+k4)*2*hdeltat/6 + + Write(*,*) " new bias ", ps%bias*phinorm + where (ps%bdpos .lt. 0) + ps%biases=-ps%bias + end where + + ! broadcast the bias to all the mpi processes + call bcast_bias(ps) + + ps%current(1)=ps%current(3) + + end subroutine updt_bias + + + ! gather on node 0 the collected charge on each metallic boundary + subroutine reduce_charge(ps) + use mpi + use mpihelper + use basic, ONLY: mpirank + type(power_supply):: ps + integer:: ierr + + if(mpirank .eq. 0) then + call MPI_REDUCE(MPI_IN_PLACE,ps%charge,ps%nbbounds,db_type,db_sum_op,0,MPI_COMM_WORLD,ierr) + Write(*,*) "curr charge ", ps%charge + else + call MPI_REDUCE(ps%charge,ps%charge,ps%nbbounds,db_type,db_sum_op,0,MPI_COMM_WORLD,ierr) + end if + end subroutine + + ! broadcast to all the nodes the new bias imposed by the power supply on the electrodes + subroutine bcast_bias(ps) + use mpi + use mpihelper + type(power_supply):: ps + integer:: ierr + + call MPI_BCAST(ps%biases,ps%nbbounds,db_type,0,MPI_COMM_WORLD,ierr) + end subroutine + +end module psupply \ No newline at end of file diff --git a/src/random.f b/src/random.f new file mode 100644 index 0000000..a6ca773 --- /dev/null +++ b/src/random.f @@ -0,0 +1,376 @@ +* Version 1.0 of random number routines +* Author: Charles Karney +* Date: 1999-08-05 10:44:51 -0400 +* + function random(p,r) + implicit none + DOUBLE PRECISION ulp2 + parameter(ulp2=2.0d0**(-47-1)) + REAL ulps + DOUBLE PRECISION mult + parameter(ulps=2.0**(-23),mult=2.0d0**23) + DOUBLE PRECISION random + REAL srandom + integer p + DOUBLE PRECISION r(0:100-1) + external rand_batch + if(p.GE.100)then + call rand_batch(p,r(0)) + end if + random=r(p)+ulp2 + p=p+1 + return + entry srandom(p,r) + if(p.GE.100)then + call rand_batch(p,r(0)) + end if + srandom=(int(mult*r(p))+0.5)*ulps + p=p+1 + return + end +* + subroutine random_array(y,n,p,r) + implicit none + DOUBLE PRECISION ulp2 + parameter(ulp2=2.0d0**(-47-1)) + REAL ulps + DOUBLE PRECISION mult + parameter(ulps=2.0**(-23),mult=2.0d0**23) + integer n + DOUBLE PRECISION y(0:n-1) + REAL ys(0:n-1) + integer p + DOUBLE PRECISION r(0:100-1) + integer i,k,j + external rand_batch + if(n.LE.0)return + k=min(n,100-p) + do i=0,k-1 + y(i)=r(i+p)+ulp2 + end do + p=p+(k) + do j=k,n-1,100 + call rand_batch(p,r(0)) + do i=j,min(j+100,n)-1 + y(i)=r(i-j+p)+ulp2 + end do + p=p+(min(100,n-j)) + end do + return + entry srandom_array(ys,n,p,r) + if(n.LE.0)return + k=min(n,100-p) + do i=0,k-1 + ys(i)=(int(mult*r(i+p))+0.5)*ulps + end do + p=p+(k) + do j=k,n-1,100 + call rand_batch(p,r(0)) + do i=j,min(j+100,n)-1 + ys(i)=(int(mult*r(i-j+p))+0.5)*ulps + end do + p=p+(min(100,n-j)) + end do + return + end +* + subroutine rand_batch(p,r) + implicit none + integer p + DOUBLE PRECISION r(0:100-1) + integer i + DOUBLE PRECISION w(0:1009-100-1) + DOUBLE PRECISION tmp + do i=0,63-1 + tmp=r(i)+r(i+100-63) + w(i)=tmp-int(tmp) + end do + do i=63,100-1 + tmp=r(i)+w(i-63) + w(i)=tmp-int(tmp) + end do + do i=100,1009-100-1 + tmp=w(i-100)+w(i-63) + w(i)=tmp-int(tmp) + end do + do i=1009-100,1009-100+63-1 + tmp=w(i-100)+w(i-63) + r(i-1009+100)=tmp-int(tmp) + end do + do i=1009-100+63,1009-1 + tmp=w(i-100)+r(i-1009+100-63) + r(i-1009+100)=tmp-int(tmp) + end do + p=0 + return + end +* + subroutine random_init(seed,p,r) + implicit none + integer b + DOUBLE PRECISION del,ulp + parameter(del=2.0d0**(-14),ulp=2.0d0**(-47),b=2**14) + integer seed(0:8-1) + integer p + DOUBLE PRECISION r(0:100-1) + integer i,j,t,s(0:8-1) + logical even + integer z(0:8-1) + integer a0,a1,a2,a3,a4,a5,a6,c0 + data a0,a1,a2,a3,a4,a5,a6,c0/15661,678,724,5245,13656,11852,29,1/ + do i=0,8-1 + s(i)=seed(i) + end do + even=.TRUE. + even=even.AND.(mod(s(7),2).EQ.0) + r(0)=(((s(7)*del+s(6))*del+s(5))*del+int(s(4)/512))*512*del + do j=1,100-1 + t=c0+a0*s(0) + z(0)=mod(t,b) + t=int(t/b)+a0*s(1)+a1*s(0) + z(1)=mod(t,b) + t=int(t/b)+a0*s(2)+a1*s(1)+a2*s(0) + z(2)=mod(t,b) + t=int(t/b)+a0*s(3)+a1*s(2)+a2*s(1)+a3*s(0) + z(3)=mod(t,b) + t=int(t/b)+a0*s(4)+a1*s(3)+a2*s(2)+a3*s(1)+a4*s(0) + z(4)=mod(t,b) + t=int(t/b)+a0*s(5)+a1*s(4)+a2*s(3)+a3*s(2)+a4*s(1)+a5*s(0) + z(5)=mod(t,b) + t=int(t/b)+a0*s(6)+a1*s(5)+a2*s(4)+a3*s(3)+a4*s(2)+a5*s(1)+a6*s(0) + z(6)=mod(t,b) + t=int(t/b)+a0*s(7)+a1*s(6)+a2*s(5)+a3*s(4)+a4*s(3)+a5*s(2)+a6*s(1) + z(7)=mod(t,b) + do i=0,8-1 + s(i)=z(i) + end do + even=even.AND.(mod(s(7),2).EQ.0) + r(j)=(((s(7)*del+s(6))*del+s(5))*del+int(s(4)/512))*512*del + end do + if(even)then + t=c0+a0*s(0) + z(0)=mod(t,b) + t=int(t/b)+a0*s(1)+a1*s(0) + z(1)=mod(t,b) + t=int(t/b)+a0*s(2)+a1*s(1)+a2*s(0) + z(2)=mod(t,b) + t=int(t/b)+a0*s(3)+a1*s(2)+a2*s(1)+a3*s(0) + z(3)=mod(t,b) + t=int(t/b)+a0*s(4)+a1*s(3)+a2*s(2)+a3*s(1)+a4*s(0) + z(4)=mod(t,b) + t=int(t/b)+a0*s(5)+a1*s(4)+a2*s(3)+a3*s(2)+a4*s(1)+a5*s(0) + z(5)=mod(t,b) + t=int(t/b)+a0*s(6)+a1*s(5)+a2*s(4)+a3*s(3)+a4*s(2)+a5*s(1)+a6*s(0) + z(6)=mod(t,b) + t=int(t/b)+a0*s(7)+a1*s(6)+a2*s(5)+a3*s(4)+a4*s(3)+a5*s(2)+a6*s(1) + z(7)=mod(t,b) + do i=0,8-1 + s(i)=z(i) + end do + j=int((s(8-1)*100)/b) + r(j)=r(j)+(ulp) + end if + p=100 + return + end +* + subroutine decimal_to_seed(decimal,seed) + implicit none + integer b + parameter(b=2**14) + character*(*)decimal + integer seed(0:8-1) + integer i,ten(0:8-1),c(0:8-1),ch + data ten/10,7*0/ + external rand_axc + do i=0,8-1 + seed(i)=0 + c(i)=0 + end do + do i=1,len(decimal) + ch=ichar(decimal(i:i)) + if(ch.GE.ichar('0').AND.ch.LE.ichar('9'))then + c(0)=ch-ichar('0') + call rand_axc(ten,seed,c) + end if + end do + return + end +* + subroutine string_to_seed(string,seed) + implicit none + integer b + parameter(b=2**14) + character*(*)string + integer seed(0:8-1) + integer t,i,k,unity(0:8-1),c(0:8-1),ch + data unity/1,7*0/ + external rand_axc + do i=0,8-1 + seed(i)=0 + c(i)=0 + end do + do i=1,len(string) + ch=ichar(string(i:i)) + if(ch.GT.ichar(' ').AND.ch.LT.127)then + t=mod(seed(0),2)*(b/2) + do k=0,8-1 + seed(k)=int(seed(k)/2) + if(k.LT.8-1)then + seed(k)=seed(k)+(mod(seed(k+1),2)*(b/2)) + else + seed(k)=seed(k)+(t) + end if + end do + c(0)=ch + call rand_axc(unity,seed,c) + end if + end do + return + end +* + subroutine set_random_seed(time,seed) + implicit none + integer time(8) + integer seed(0:8-1) + character*21 c + external decimal_to_seed + write(c(1:8),'(i4.4,2i2.2)')time(1),time(2),time(3) + write(c(9:12),'(i1.1,i3.3)') (1-sign(1,time(4)))/2,abs(time(4)) + write(c(13:21),'(3i2.2,i3.3)')time(5),time(6),time(7),time(8) + call decimal_to_seed(c,seed) + return + end +* + subroutine seed_to_decimal(seed,decimal) + implicit none + integer pow,decbase,b + parameter(pow=4,decbase=10**pow,b=2**14) + character*(*)decimal + integer seed(0:8-1) + integer z(0:8-1),i,t,j,k + character*36 str + k=-1 + do i=0,8-1 + z(i)=seed(i) + if(z(i).GT.0)k=i + end do + str=' ' + i=9 +90000 continue + i=i-1 + t=0 + do j=k,0,-1 + z(j)=z(j)+t*b + t=mod(z(j),decbase) + z(j)=int(z(j)/decbase) + end do + if(z(max(0,k)).EQ.0)k=k-1 + if(k.GE.0)then + write(str(pow*i+1:pow*(i+1)),'(i4.4)')t + else + write(str(pow*i+1:pow*(i+1)),'(i4)')t + end if + if(k.GE.0)goto 90000 + if(len(decimal).GT.len(str))then + decimal(:len(decimal)-len(str))=' ' + decimal(len(decimal)-len(str)+1:)=str + else + decimal=str(len(str)-len(decimal)+1:) + end if + return + end +* + subroutine rand_next_seed(n,ax,cx,y) + implicit none + integer n,ax(0:8-1),cx(0:8-1) + integer y(0:8-1) + integer a(0:8-1),c(0:8-1),z(0:8-1),t(0:8-1),m,i + data z/8*0/ + external rand_axc + if(n.EQ.0)return + m=n + do i=0,8-1 + a(i)=ax(i) + c(i)=cx(i) + end do +90000 continue + if(mod(m,2).GT.0)then + call rand_axc(a,y,c) + end if + m=int(m/2) + if(m.EQ.0)return + do i=0,8-1 + t(i)=c(i) + end do + call rand_axc(a,c,t) + do i=0,8-1 + t(i)=a(i) + end do + call rand_axc(t,a,z) + goto 90000 + end +* + subroutine next_seed3(n0,n1,n2,seed) + implicit none + integer n0,n1,n2 + integer seed(0:8-1) + integer af0(0:8-1),cf0(0:8-1) + integer ab0(0:8-1),cb0(0:8-1) + integer af1(0:8-1),cf1(0:8-1) + integer ab1(0:8-1),cb1(0:8-1) + integer af2(0:8-1),cf2(0:8-1) + integer ab2(0:8-1),cb2(0:8-1) + data af0/15741,8689,9280,4732,12011,7130,6824,12302/ + data cf0/16317,10266,1198,331,10769,8310,2779,13880/ + data ab0/9173,9894,15203,15379,7981,2280,8071,429/ + data cb0/8383,3616,597,12724,15663,9639,187,4866/ + data af1/8405,4808,3603,6718,13766,9243,10375,12108/ + data cf1/13951,7170,9039,11206,8706,14101,1864,15191/ + data ab1/6269,3240,9759,7130,15320,14399,3675,1380/ + data cb1/15357,5843,6205,16275,8838,12132,2198,10330/ + data af2/445,10754,1869,6593,385,12498,14501,7383/ + data cf2/2285,8057,3864,10235,1805,10614,9615,15522/ + data ab2/405,4903,2746,1477,3263,13564,8139,2362/ + data cb2/8463,575,5876,2220,4924,1701,9060,5639/ + external rand_next_seed + if(n2.GT.0)then + call rand_next_seed(n2,af2,cf2,seed) + else if(n2.LT.0)then + call rand_next_seed(-n2,ab2,cb2,seed) + end if + if(n1.GT.0)then + call rand_next_seed(n1,af1,cf1,seed) + else if(n1.LT.0)then + call rand_next_seed(-n1,ab1,cb1,seed) + end if + entry next_seed(n0,seed) + if(n0.GT.0)then + call rand_next_seed(n0,af0,cf0,seed) + else if(n0.LT.0)then + call rand_next_seed(-n0,ab0,cb0,seed) + end if + return + end +* + subroutine rand_axc(a,x,c) + implicit none + integer b + parameter(b=2**14) + integer a(0:8-1),c(0:8-1) + integer x(0:8-1) + integer z(0:8-1),i,j,t + t=0 + do i=0,8-1 + t=c(i)+int(t/b) + do j=0,i + t=t+(a(j)*x(i-j)) + end do + z(i)=mod(t,b) + end do + do i=0,8-1 + x(i)=z(i) + end do + return + end +* diff --git a/src/random.h b/src/random.h new file mode 100644 index 0000000..ac33242 --- /dev/null +++ b/src/random.h @@ -0,0 +1,2 @@ + integer ran_k,ran_s,ran_c + parameter (ran_k=100,ran_s=8,ran_c=34) diff --git a/src/random_mod.f90 b/src/random_mod.f90 new file mode 100644 index 0000000..9a37315 --- /dev/null +++ b/src/random_mod.f90 @@ -0,0 +1,21 @@ +MODULE random + +! +! Random module for the random number generator (RNG) +! + + +! This module, together with the file "random.h", allow to use +! a high-quality, fast, parallel RNG developed by Charles Karney in Princeton University + + + INCLUDE "random.h" + + CHARACTER(ran_c) :: random_seed_str = '2.7182818' + INTEGER, allocatable, DIMENSION(:,:) :: seed ! nbprocs*ran_s size + + INTEGER, PARAMETER :: n_rand = 1 + INTEGER, allocatable:: ran_index(:) ! nbporcs size + DOUBLE PRECISION, allocatable :: ran_array(:,:) ! nbprocs*ran_k size + +END MODULE random \ No newline at end of file diff --git a/src/randother.f b/src/randother.f new file mode 100644 index 0000000..629e5a8 --- /dev/null +++ b/src/randother.f @@ -0,0 +1,123 @@ +* Version 1.1 of random number routines +* Author: Charles Karney +* Date: 1999-08-06 14:18:03 -0400 +* + subroutine random_gauss(y,n,p,r) + implicit none + integer p + DOUBLE PRECISION r(0:100-1) + integer n + DOUBLE PRECISION y(0:n-1) + integer i + DOUBLE PRECISION pi,theta,z + DOUBLE PRECISION random + external random_array,random + data pi/3.14159265358979323846264338328d0/ + REAL ys(0:n-1) + REAL spi,stheta,sz + REAL srandom + external srandom_array,srandom + data spi/3.14159265358979323846264338328/ + if(n.LE.0)return + call random_array(y,n,p,r(0)) + do i=0,int(n/2)*2-1,2 + theta=pi*(2.0d0*y(i)-1.0d0) + z=sqrt(-2.0d0*log(y(i+1))) + y(i)=z*cos(theta) + y(i+1)=z*sin(theta) + end do + if(mod(n,2).EQ.0)return + theta=pi*(2.0d0*y(n-1)-1.0d0) + z=sqrt(-2.0d0*log(random(p,r(0)))) + y(n-1)=z*cos(theta) + return + entry srandom_gauss(ys,n,p,r) + if(n.LE.0)return + call srandom_array(ys,n,p,r(0)) + do i=0,int(n/2)*2-1,2 + stheta=spi*(2.0*ys(i)-1.0) + sz=sqrt(-2.0*log(ys(i+1))) + ys(i)=sz*cos(stheta) + ys(i+1)=sz*sin(stheta) + end do + if(mod(n,2).EQ.0)return + stheta=spi*(2.0*ys(n-1)-1.0) + sz=sqrt(-2.0*srandom(p,r(0))) + ys(n-1)=sz*cos(stheta) + return + end +* + subroutine random_isodist(v,n,p,r) + implicit none + integer p + DOUBLE PRECISION r(0:100-1) + integer n + DOUBLE PRECISION v(0:3*n-1) + integer i + DOUBLE PRECISION pi,costheta,phi + external random_array + data pi/3.14159265358979323846264338328d0/ + REAL vs(0:3*n-1) + REAL spi,scostheta,sphi + external srandom_array + data spi/3.14159265358979323846264338328/ + if(n.LE.0)return + call random_array(v(n),2*n,p,r(0)) + do i=0,n-1 + costheta=2.0d0*v(n+2*i)-1.0d0 + phi=pi*(2.0d0*v(n+2*i+1)-1.0d0) + v(3*i)=cos(phi)*sqrt(1.0d0-costheta**2) + v(3*i+1)=sin(phi)*sqrt(1.0d0-costheta**2) + v(3*i+2)=costheta + end do + return + entry srandom_isodist(vs,n,p,r) + if(n.LE.0)return + call srandom_array(vs(n),2*n,p,r(0)) + do i=0,n-1 + scostheta=2.0*vs(n+2*i)-1.0 + sphi=spi*(2.0*vs(n+2*i+1)-1.0) + vs(3*i)=cos(sphi)*sqrt(1.0-scostheta**2) + vs(3*i+1)=sin(sphi)*sqrt(1.0-scostheta**2) + vs(3*i+2)=scostheta + end do + return + end +* + subroutine random_cosdist(v,n,p,r) + implicit none + integer p + DOUBLE PRECISION r(0:100-1) + integer n + DOUBLE PRECISION v(0:3*n-1) + integer i + DOUBLE PRECISION pi,costheta2,phi + external random_array + data pi/3.14159265358979323846264338328d0/ + REAL vs(0:2*n-1) + REAL spi,scostheta2,sphi + external srandom_array + data spi/3.14159265358979323846264338328/ + if(n.LE.0)return + call random_array(v(n),2*n,p,r(0)) + do i=0,n-1 + costheta2=v(n+2*i) + phi=pi*(2.0d0*v(n+2*i+1)-1.0d0) + v(3*i)=cos(phi)*sqrt(1.0d0-costheta2) + v(3*i+1)=sin(phi)*sqrt(1.0d0-costheta2) + v(3*i+2)=sqrt(costheta2) + end do + return + entry srandom_cosdist(vs,n,p,r) + if(n.LE.0)return + call srandom_array(vs(n),2*n,p,r(0)) + do i=0,n-1 + scostheta2=vs(n+2*i) + sphi=spi*(2.0*vs(n+2*i+1)-1.0) + vs(3*i)=cos(sphi)*sqrt(1.0-scostheta2) + vs(3*i+1)=sin(sphi)*sqrt(1.0-scostheta2) + vs(3*i+2)=sqrt(scostheta2) + end do + return + end +* diff --git a/src/resume.f90 b/src/resume.f90 index 2bf4a52..b59b155 100644 --- a/src/resume.f90 +++ b/src/resume.f90 @@ -1,55 +1,76 @@ SUBROUTINE resume ! ! Resume from previous run ! Use beam Use basic Use fields Use sort - Use maxwellsource + Use maxwsrce + Use geometry + Use neutcol IMPLICIT NONE ! ! Local vars and arrays - INTEGER, DIMENSION(:), ALLOCATABLE:: displs,sendcounts INTEGER:: i !________________________________________________________________________________ WRITE(*,'(a)') ' Resume from previous run' !________________________________________________________________________________ ! ! Open and read initial conditions from restart file CALL chkrst(0) - - ! TODO: change subroutine to take into account multiple species +! If we want to start a new run with a new file IF (newres) THEN + ! we start time and step from 0 cstep=0 time=0 END IF -! Compute Self consistent electric field + qnorm=abs(partslist(1)%weight*partslist(1)%q) + CALL fields_init + call timera(0, "read_geom") + call read_geom(lu_in, rnorm, splrz, Potinn, Potout) + call timera(1, "read_geom") +! Initialize the magnetic field and the electric field solver + call mag_init + CALL fields_start + call boundary_loss(partslist(1)) + call localisation(partslist(1)) + + ! Add the requested test particles and read them from input files + IF (nbaddtestspecies .gt. 0) THEN + Do i=1,nbaddtestspecies + CALL load_part_file(partslist(nbspecies+i),addedtestspecfile(i)) + call boundary_loss(partslist(nbspecies+i)) + call localisation(partslist(nbspecies+i)) + END DO + nbspecies=nbspecies+nbaddtestspecies + END IF + IF(mpisize .gt. 1) THEN - CALL distribpartsonprocs(partslist(1)) + CALL calc_Zbounds(partslist(1), Zbounds, femorder) DO i=1,nbspecies CALL keep_mpi_self_parts(partslist(i),Zbounds) + call collectparts(partslist(i)) END DO END IF + + + CALL bound(partslist(1)) + call boundary_loss(partslist(1)) CALL localisation(partslist(1)) - ! Init Electric and Magnetic Fields - CALL fields_comm_init - WRITE(*,*) "Fields initialized" - CALL rhscon(partslist(1)) - WRITE(*,*) "Initial rhs computed" - CALL poisson - WRITE(*,*) "Initial field solved" - CALL EForcescomp(partslist(1)) - WRITE(*,*) "Initial forces computed" - IF (newres) THEN - CALL diagnostics - WRITE(*,*) "Initial beam%diagnostics done!" - END IF - IF (nlmaxwellsource) CALL maxwellsource_init(lu_in, time) + ! Allocate the variables for MPI communications + CALL fields_comm_init(Zbounds) + ! Solve Poisson for the initial conditions + CALL rhscon(partslist) + CALL poisson(splrz) + ! Compute the fields at the particles positions + CALL EFieldscompatparts(partslist(1)) + if(mpirank .eq. 0) WRITE(*,*) "Initial forces computed" + ! END SUBROUTINE resume diff --git a/src/sort_mod.f90 b/src/sort_mod.f90 index f317085..d695b21 100644 --- a/src/sort_mod.f90 +++ b/src/sort_mod.f90 @@ -1,83 +1,133 @@ !------------------------------------------------------------------------------ ! EPFL/Swiss Plasma Center !------------------------------------------------------------------------------ ! ! MODULE: sort ! !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> Module containing particle sorting algorithms. !------------------------------------------------------------------------------ MODULE sort IMPLICIT NONE CONTAINS !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> -!> @brief Sorts the particles according to their z position using quick sort algorithm +!> @brief Sorts the particles according to their Z position using quick sort algorithm ! -!> @param[in] leftlimit the particles variable needing to be allocated. -!> @param[in] right limit the maximum number of particles that will be stored in this variable +!> @param[inout] p the particles array that need to be sorted +!> @param[in] leftlimit the left limit index for the sub array considered. +!> @param[in] rightlimit the right limit index for the sub array considered. !--------------------------------------------------------------------------- RECURSIVE SUBROUTINE quicksortparts(p, leftlimit, rightlimit) Use beam, ONLY: particles, exchange_parts USE constants TYPE(particles), INTENT(INOUT):: p INTEGER,INTENT(IN):: leftlimit, rightlimit REAL(kind=db):: pivot INTEGER::i, cnt, mid IF(leftlimit .ge. rightlimit) RETURN ! Impossible indices, return mid=(leftlimit+rightlimit)/2 ! Compute middle index IF(p%Z(mid).lt.p%Z(leftlimit)) CALL exchange_parts(p,leftlimit,mid) IF(p%Z(rightlimit).lt.p%Z(leftlimit)) CALL exchange_parts(p,leftlimit,rightlimit) IF(p%Z(mid).lt.p%Z(rightlimit)) CALL exchange_parts(p,rightlimit,mid) ! Store the pivot point for comparison pivot=p%Z(rightlimit) cnt=leftlimit ! Move all parts with Z smaller than pivot to the left of pivot DO i=leftlimit, rightlimit IF(p%Z(i) .le. pivot) THEN CALL exchange_parts(p, i,cnt) cnt=cnt+1 END IF END DO ! Quicksort the sub-arrays CALL quicksortparts(p, leftlimit,cnt-2) CALL quicksortparts(p, cnt,rightlimit) END SUBROUTINE quicksortparts !--------------------------------------------------------------------------- !> @author !> Guillaume Le Bars EPFL/SPC ! ! DESCRIPTION: !> -!> @brief Sorts the particles according to their index in the poisson solver grid. +!> @brief Sorts the particles according to their linear index in the poisson solver grid. ! -!> @param[in] leftlimit the particles variable needing to be allocated. -!> @param[in] right limit the maximum number of particles that will be stored in this variable +!> @param[inout] p the particles array that need to be sorted +!> @param[in] leftlimit the left limit index for the sub array considered. +!> @param[in] rightlimit the right limit index for the sub array considered. !--------------------------------------------------------------------------- - SUBROUTINE gridsort(p) - USE beam + RECURSIVE SUBROUTINE gridsort(p, leftlimit, rightlimit) + Use beam, ONLY: particles, exchange_parts + USE constants TYPE(particles), INTENT(INOUT):: p - !CALL creat_parts - IF(p%Nploc .gt. 0 ) THEN + INTEGER,INTENT(IN):: leftlimit, rightlimit + REAL(kind=db):: pivot + INTEGER::i, cnt, mid + IF(leftlimit .ge. rightlimit) RETURN ! Impossible indices, return + + mid=(leftlimit+rightlimit)/2 ! Compute middle index + + IF(linindex(p,mid).lt.linindex(p,leftlimit)) CALL exchange_parts(p,leftlimit,mid) + IF(linindex(p,rightlimit).lt.linindex(p,leftlimit)) CALL exchange_parts(p,leftlimit,rightlimit) + IF(linindex(p,mid).lt.linindex(p,rightlimit)) CALL exchange_parts(p,rightlimit,mid) - END IF + ! Store the pivot point for comparison + pivot=linindex(p,rightlimit) + + cnt=leftlimit + + ! Move all parts with Z smaller than pivot to the left of pivot + DO i=leftlimit, rightlimit + IF(linindex(p,i) .le. pivot) THEN + CALL exchange_parts(p, i,cnt) + cnt=cnt+1 + END IF + END DO + + ! Quicksort the sub-arrays + CALL gridsort(p, leftlimit,cnt-2) + CALL gridsort(p, cnt,rightlimit) END SUBROUTINE gridsort -END MODULE sort \ No newline at end of file +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief Computes the linear index of a given particle in the Poisson solver 2D grid +! +!> @param[in] p the particles array where the particle is stored +!> @param[in] pid the index in the particle array of the particle of interest +!> @param[out] linindex the computed linear index. +!--------------------------------------------------------------------------- + + FUNCTION linindex(p,pid) + USE beam, ONLY: particles + USE basic, ONLY: nz + INTEGER :: linindex, pid + TYPE(particles):: p + + linindex=p%Zindex(pid)+p%rindex(pid)*nz + + END FUNCTION linindex + +END MODULE sort + diff --git a/src/splinebound_mod.f90 b/src/splinebound_mod.f90 new file mode 100644 index 0000000..46893f1 --- /dev/null +++ b/src/splinebound_mod.f90 @@ -0,0 +1,805 @@ +MODULE splinebound + USE constants + USE bsplines + USE forSISL, only: newCurve, freeCurve, freeIntCurve, writeSISLcurve, writeSISLpoints + Use forSISLdata + + IMPLICIT NONE + + INTEGER(SELECTED_INT_KIND(4)), PARAMETER :: bd=-1, bd_Dirichletconst=0, bd_Dirichletvar=1, bd_Neumann=2 + TYPE spline_boundary + ! all curves assume right handedness to set which side of the curve is inside or outside + type(SISLCurve):: curve + Real(kind=db):: Dirichlet_val !< Value for the dirichlet boundary condition created by this boundary + Real(kind=db):: epsge=1.0e-5 !< geometric resolution used for calculating distances + Real(kind=db):: epsce=1.0e-9 !< value of weight below which it is 0 + INTEGER(kind(bd)):: type=bd_Dirichletconst !< type of boundary conditions + END TYPE spline_boundary + + type spline_domain + integer:: nbsplines = 0 !< number of spline boundaries in the domain + type(spline_boundary), allocatable:: boundaries(:) !< List of boundaries in the domain + Real(kind=db):: dist_extent=0.1 !< distance used for the merging with the plateau function for the weight + type(cellkind), ALLOCATABLE:: cellk(:,:) !< Precomputed parameters at each cell for faster weight computation + type(spline2d), pointer:: splrz => null() !< Pointer to the main spline grid used for the FEM solver + Integer:: nb1 !< Number of grid points in the 1st dimension + Integer:: nb2 !< Number of grid points in the 2nd dimension + real(kind=db), ALLOCATABLE:: x1(:) !< Grid points in first direction for weight interpolation + real(kind=db), ALLOCATABLE:: x2(:) !< Grid points in 2nd direction for weight interpolation + real(kind=db), ALLOCATABLE:: dx1(:) !< inverse cell width in first direction for weight interpolation + real(kind=db), ALLOCATABLE:: dx2(:) !< inverse cell width in 2nd direction for weight interpolation + type(SISLsurf):: Dirdomweight !< structure storing precalculated geometric weight for faster evaluation + type(SISLsurf):: totdomweight !< structure storing precalculated total weight for faster evaluation + end type spline_domain + + type cellkind + integer:: spldirkind=0 !< -1 outside (return -1) no dist to calculate; 0 boundary calculate dist with linked boundaries; 1 inside (return 1) no dist to calculate + integer:: spltotkind=0 !< -1 outside (return -1) no dist to calculate; 0 boundary calculate dist with linked boundaries; 1 inside (return 1) no dist to calculate + integer:: linkedboundaries(2)=0 !< stores the spline curve indices in the spline_domain of the spline boundaries that are at a distance lower than dist_extent + integer:: leftknot(2)=0 !< for each linkedboundary saves a lower initial guess for calculating the distance + real(kind=db):: lguess(2)=-1 !< Spline parameter left limit as start guess + real(kind=db):: rguess(2)=-1 !< Spline parameter right limit as start guess + end type cellkind + +CONTAINS + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Reads a spline domain from the namelist or from a h5 file. Needs to be called for the initialization of the module +!> @param[in] Fileid Text file id of the input file containing namelists +!> @param[out] spldom spline domain +!> @param[in] splrz bspline structure used by the FEM comming form bspline library +!> @param[in] rnorm distance normalization constant +!> @param[in] phinorm electric potential normalization constant +!--------------------------------------------------------------------------- + subroutine read_splinebound(Fileid, spldom, splrz, rnorm, Phinorm) + use mpi + Integer:: Fileid + type(spline_domain):: spldom + type(spline2d):: splrz + real(kind=db):: rnorm, phinorm + Integer:: nbsplines, istat, mpirank, ierr + real(kind=db):: dist_extent, Potinn, Potout, epsge, epsce + Character(len=128):: h5fname="", line + real(kind=db) :: Dvals(30)=0 + + integer:: nbpoints + real(kind=db),allocatable:: cpoints(:,:) + real(kind=db):: dz, dr, ctr, Lz, ra, rb + integer:: degree=4, i + namelist /spldomain/ nbsplines, dist_extent, h5fname, dr, Lz, ra, rb, epsge, epsce, Dvals + + CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) + + REWIND(fileid) + READ(fileid,spldomain, iostat=istat) + + if (istat.gt.0) then + if(mpirank .eq. 0) then + backspace(fileid) + read(fileid,fmt='(A)') line + write(*,'(A)') & + 'Invalid line in geomparams: '//trim(line) + end if + call MPI_Abort(MPI_COMM_WORLD, -1, ierr) + stop + end if + + if(mpirank .eq. 0) WRITE(*, spldomain) + + Dvals=Dvals/phinorm + dist_extent=dist_extent/rnorm + + if (.not. trim(h5fname)=='' ) then + call setspline_domain(spldom, splrz, dist_extent, 0) + call splinebound_readh5domain(h5fname,spldom, rnorm, phinorm) + call classifycells(spldom) + do i=1,spldom%nbsplines + spldom%boundaries(i)%Dirichlet_val=Dvals(i) + end do + return + end if + + nbpoints=600 + allocate(cpoints(2,nbpoints)) + + + nbsplines=2 + call setspline_domain(spldom, splrz, dist_extent, nbsplines) + + dz=(splrz%sp1%knots(splrz%sp1%nints)-splrz%sp1%knots(0))/(nbpoints-1) + Lz=Lz/rnorm + ctr=(splrz%sp1%knots(splrz%sp1%nints)+splrz%sp1%knots(0))/2 + do i=1,nbpoints + cpoints(1,i)=splrz%sp1%knots(0)+(i-1)*dz + end do + + cpoints(2,:)=ra/rnorm + call setspline_boundary(spldom%boundaries(1), cpoints, degree, Dvals(1), epsge, epsce) + + !spldom%boundaries(1)%type=bd_Neumann + + do i=1,nbpoints + cpoints(1,i)=splrz%sp1%knots(0)+(nbpoints-i)*dz + end do + dr=dr/rnorm + cpoints(2,:)=rb/rnorm + do i=1,nbpoints + if(((cpoints(1,i)-ctr)/Lz)**2.le.1) then + cpoints(2,i)=rb/rnorm+dr*sqrt(1-(cpoints(1,i)-ctr)**2/Lz**2) + endif + end do + + call setspline_boundary(spldom%boundaries(2), cpoints, degree, Dvals(2), epsge, epsce) + do i=1,spldom%nbsplines + spldom%boundaries(i)%Dirichlet_val=Dvals(i) + end do + call classifycells(spldom) + end subroutine + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Saves the spline boundaries to the result file +!> @param[in] File_handle futils h5 file id +!> @param[in] curr_grp groupname under which the boundaries must be saved +!> @param[in] spldom spline domain +!--------------------------------------------------------------------------- + Subroutine splinebound_diag(File_handle, curr_grp, spldom) + use mpi + Use futils + Use basic, ONLY: rnorm, phinorm + Integer:: File_handle + type(spline_domain):: spldom + Character(len=*):: curr_grp + CHARACTER(len=128):: grpname + Integer:: ierr, mpirank, i + + CALL MPI_COMM_RANK(MPI_COMM_WORLD, mpirank, ierr) + + IF(mpirank .eq. 0) THEN + + Write(grpname,'(a,a)') trim(curr_grp),"/geometry_spl" + If(.not. isgroup(File_handle, trim(grpname))) THEN + CALL creatg(File_handle, trim(grpname)) + END IF + Call attach(File_handle, trim(grpname), "dist_extent",spldom%dist_extent) + Call attach(File_handle, trim(grpname), "nbsplines", spldom%nbsplines) + do i=1,spldom%nbsplines + Write(grpname,'(a,a,i2.2)') trim(curr_grp),"/geometry_spl/",i + If(.not. isgroup(File_handle, trim(grpname))) THEN + CALL creatg(File_handle, trim(grpname)) + END IF + Call attach(File_handle, trim(grpname), "Dirichlet_val", spldom%boundaries(i)%Dirichlet_val*phinorm) + Call attach(File_handle, trim(grpname), "order", spldom%boundaries(i)%curve%ik) + Call attach(File_handle, trim(grpname), "kind", spldom%boundaries(i)%curve%ikind) + Call attach(File_handle, trim(grpname), "dim", spldom%boundaries(i)%curve%idim) + CALL putarr(File_handle, TRIM(grpname)//"/pos", spldom%boundaries(i)%curve%ecoef*rnorm) + CALL putarr(File_handle, TRIM(grpname)//"/knots", spldom%boundaries(i)%curve%et) + end do + + END IF + + End subroutine splinebound_diag + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Read a spline boundary domain from an h5 file structure +!> @param[out] spldom new spline domain +!> @param[in] filename filename of the h5 file +!> @param[in] rnorm distance normalization constant +!> @param[in] phinorm electric potential normalization constant +!--------------------------------------------------------------------------- + + subroutine splinebound_readh5domain(filename, spldom, rnorm, phinorm) + use futils + use forSISL + implicit none + Character(len=*),intent(in) :: filename + type(spline_domain),intent(inout) :: spldom + integer:: h5id, i + real(kind=db):: rnorm, phinorm + CHARACTER(len=128):: grpname + + integer:: periodic + integer:: order, dim, bdtype + INTEGER:: posrank, posdim(2), err + real(kind=db):: Dval, epsge, epsce + real(kind=db),allocatable:: points(:,:) + + call openf(filename, h5id,'r','d') + + call getatt(h5id, '/geometry_spl/','nbsplines', spldom%nbsplines) + + ! prepare memory + if (allocated(spldom%boundaries)) then + do i=1,size(spldom%boundaries,1) + call free_bsplinecurve(spldom%boundaries(i)) + end do + DEALLOCATE(spldom%boundaries) + end if + allocate(spldom%boundaries(spldom%nbsplines)) + + ! Read each boundary curve individually + do i=1,spldom%nbsplines + Write(grpname,'(a,i2.2)') "/geometry_spl/",i + If(.not. isgroup(h5id, trim(grpname))) THEN + Write(*,*) "Error the geometry definition file is invalid" + END IF + + periodic=0 + + Call getatt(h5id, trim(grpname), "Dirichlet_val", Dval) + Call getatt(h5id, trim(grpname), "epsge", epsge) + Call getatt(h5id, trim(grpname), "epsce", epsce) + Call getatt(h5id, trim(grpname), "order", order) + Call getatt(h5id, trim(grpname), "dim", dim) + err=0 + Call getatt(h5id, trim(grpname), "periodic", periodic,err) + if(err .lt.0) periodic=0 + + + CALL getdims(h5id, TRIM(grpname)//"/pos", posrank, posdim) + allocate(points(posdim(1),posdim(2))) + CALL getarr(h5id, TRIM(grpname)//"/pos", points) + + points=points/rnorm + + Call setspline_boundary(spldom%boundaries(i),transpose(points), order-1, Dval/phinorm, epsge,epsce, periodic) + bdtype=bd + err=0 + Call getatt(h5id, trim(grpname), "type", bdtype,err) + if(err.ge.0) spldom%boundaries(i)%type=bdtype + deallocate(points) + end do + + call closef(h5id) + + end subroutine splinebound_readh5domain + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> initialize a spline domain and allocate the necessary memory +!> @param[out] spldom new spline domain +!> @param[in] splrz bspline structure used by the FEM comming form bspline library +!> @param[in] dist_extent normalized characteristic fall lenght of the weight +!> @param[in] nb_splines number of boundary splines to allocate +!--------------------------------------------------------------------------- + + subroutine setspline_domain(spldom,splrz,dist_extent, nb_splines) + type(spline_domain):: spldom + type(spline2d), TARGET:: splrz + real(kind=db):: dist_extent + integer:: nb_splines, nb1, nb2 + + ! Store the grid parameters to speed-up calculations + nb1=splrz%sp1%nints + nb2=splrz%sp2%nints + spldom%nb1=nb1 + spldom%nb2=nb2 + + spldom%splrz=>splrz + allocate(spldom%cellk(0:nb1-1,0:nb2-1)) + allocate(spldom%x1(0:nb1)) + allocate(spldom%x2(0:nb2)) + allocate(spldom%dx1(0:nb1-1)) + allocate(spldom%dx2(0:nb2-1)) + + spldom%x1(0:)=splrz%sp1%knots(0:nb1) + spldom%x2(0:)=splrz%sp2%knots(0:nb2) + + spldom%dx1(0:)=1/(spldom%x1(1:nb1)-spldom%x1(0:nb1-1)) + spldom%dx2(0:)=1/(spldom%x2(1:nb2)-spldom%x2(0:nb2-1)) + + !Prepare structures to host singular spline boundaries + spldom%nbsplines=nb_splines + if(spldom%nbsplines.gt. 0) allocate(spldom%boundaries(nb_splines)) + + spldom%dist_extent=dist_extent + + end subroutine setspline_domain + + + subroutine setspline_boundary(b_curve, cpoints, degree, D_val, epsge, epsce, periodic) + Use bsplines + use forSISL,ONLY: newcurve, s1630 + use mpi + type(spline_boundary):: b_curve + Real(kind=db):: cpoints(:,:) + Real(REAL64),ALLOCATABLE:: points(:) + Real(REAL64):: astpar + integer:: degree + integer, optional:: periodic + Integer:: order, ierr + Real(kind=db):: D_val + Real(kind=db),OPTIONAL :: epsge, epsce + Integer:: nbpoints,i, dim, jstat, bsptype + integer:: period + + period=0 + if(present(periodic))period=periodic + nbpoints= size(cpoints,2) + dim=size(cpoints,1) + order=degree+1 + if(nbpoints .lt. order) then + WRITE(*,'(a,i3,a,i5)') "Error: the number of points", nbpoints, " is insuficient for the required order ", order + CALL mpi_finalize(ierr) + call EXIT(-1) + end if + + allocate(points(dim*nbpoints)) + points=reshape(cpoints,(/dim*nbpoints/)) + !CALL newCurve(nbpoints, order, knots, points, kind, dim, copy, b_curve%curve) + bsptype=1 ! open boundaries b-spline + if(period.gt.0) bsptype=-1 ! closed periodic curve + astpar=0.0 ! starting parameter for the knots vector + CALL s1630(points, nbpoints, astpar, bsptype, dim, order, b_curve%curve, jstat) + if (jstat > 0 ) WRITE(*,*) "Warning ", jstat," in curve initialisation s1630 for splineweight" + if (jstat < 0 ) WRITE(*,*) "Error ", jstat," in curve initialisation s1630 for splineweight" + b_curve%Dirichlet_val=D_val + if(present(epsge)) b_curve%epsge=epsge + if(present(epsce)) b_curve%epsce=epsce + + end subroutine setspline_boundary + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Calculates the Dirichlet boundary weight from a given spline domain +!> @param[in] spldom spline domain containing the information on the boundary conditions +!> @param[in] x1(:) array of axial positions where the weights are evaluated +!> @param[in] x2(:) array of radial positions where the weights are evaluated +!> @param[out] w(:,0:) matrix of weights with first index corresponding to the position and second index to the derivative +!--------------------------------------------------------------------------- + + SUBROUTINE spline_w(spldom,x1,x2,w) + use forSISL,ONLY: s1424 + type(spline_domain):: spldom + Real(kind=db), INTENT(IN):: x2(:),x1(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Integer:: k,sstat,l + type(cellkind):: cellk + REAL(real64):: wtmp(4), point(2) + Integer,allocatable::i(:),j(:) + + allocate(i(size(x2,1)),j(size(x2,1))) + + call getindex(x1, x2, spldom, i, j) + + Do k=1,size(x2,1) + + cellk=spldom%cellk(i(k),j(k)) + + SELECT CASE (cellk%spldirkind) + CASE(-1) + w(k,:)=0 + w(k,0)=-1 + CYCLE + CASE(1) + w(k,:)=0 + w(k,0)=1 + CYCLE + CASE DEFAULT + !call splineweight(spldom%boundaries(cellk%linkedboundaries(1)), x1(k), x2(k), w(k,:),spldom%dist_extent,rguess=cellk%rguess(1),lguess=cellk%lguess(1)) + point=(/x1(k),x2(k)/) + call s1424(spldom%Dirdomweight,1,1,point,cellk%leftknot(1),cellk%leftknot(2),wtmp,sstat) + if (sstat > 0 ) WRITE(*,*) "Warning ",sstat," in distance calculation s1424 for splineweight" + if (sstat < 0 ) WRITE(*,*) "Error ",sstat," in distance calculation s1424 for splineweight" + w(k,:)=wtmp(1:size(w,2)) + END SELECT + end DO + End SUBROUTINE spline_w + + !--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Calculates the total geometric weight from a given spline domain +!> @param[in] spldom spline domain containing the information on the boundary conditions +!> @param[in] x1(:) array of axial positions where the weights are evaluated +!> @param[in] x2(:) array of radial positions where the weights are evaluated +!> @param[out] w(:,0:) matrix of weights with first index corresponding to the position and second index to the derivative +!--------------------------------------------------------------------------- + + SUBROUTINE spline_wtot(spldom,x1,x2,w,idwall) + use forSISL,ONLY: s1424 + type(spline_domain):: spldom + Real(kind=db), INTENT(IN):: x2(:),x1(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + INTEGER, optional, INTENT(OUT):: idwall(:) + Integer:: k,sstat,l + type(cellkind):: cellk + REAL(real64):: wtmp(4), point(2) + Integer,allocatable::i(:),j(:) + + allocate(i(size(x2,1)),j(size(x2,1))) + + call getindex(x1, x2, spldom, i, j) + + Do k=1,size(x2,1) + + cellk=spldom%cellk(i(k),j(k)) + if(present(idwall)) idwall(k)=cellk%linkedboundaries(2) + SELECT CASE (cellk%spltotkind) + CASE(-1) + w(k,:)=0 + w(k,0)=-1 + CYCLE + CASE(1) + w(k,:)=0 + w(k,0)=1 + CYCLE + CASE DEFAULT + !call splineweight(spldom%boundaries(cellk%linkedboundaries(1)), x1(k), x2(k), w(k,:),spldom%dist_extent,rguess=cellk%rguess(1),lguess=cellk%lguess(1)) + point=(/x1(k),x2(k)/) + if(size(w,2).gt.1) then + call s1424(spldom%totdomweight,1,1,point,cellk%leftknot(1),cellk%leftknot(2),wtmp,sstat) + else + call s1424(spldom%totdomweight,0,0,point,cellk%leftknot(1),cellk%leftknot(2),wtmp,sstat) + end if + if (sstat > 0 ) WRITE(*,*) "Warning ",sstat," in distance calculation s1424 for splinedomweight" + if (sstat < 0 ) WRITE(*,*) "Error ",sstat," in distance calculation s1424 for splinedomweight" + w(k,:)=wtmp(1:size(w,2)) + END SELECT + end DO + End SUBROUTINE spline_wtot + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Calculates the interpolation in the domain of the Dirichlet boundary conditions from a given spline domain +!> @param[in] spldom spline domain containing the information on the boundary conditions +!> @param[in] z(:) array of axial positions where the weights are evaluated +!> @param[in] r(:) array of radial positions where the weights are evaluated +!> @param[out] g(:,0:) matrix of boundary interpolations g with first index corresponding to the position and second index to the derivative +!--------------------------------------------------------------------------- + SUBROUTINE spline_g(spldom,x1,x2,g,w) + use forSISL,ONLY: s1424 + type(spline_domain):: spldom + Real(kind=db), INTENT(IN):: x2(:),x1(:) + Real(kind=db), INTENT(OUT):: g(:,0:) + Real(kind=db), INTENT(IN),OPTIONAL::w(:,0:) + REAL(real64),allocatable:: gtmp(:,:) + Integer:: k,sstat,sizew + Integer,allocatable::i(:),j(:) + type(cellkind):: cellk + + + allocate(gtmp(size(x2,1),0:size(g,2)-1)) + allocate(i(size(x2,1)),j(size(x2,1))) + + call getindex(x1, x2, spldom, i, j) + + + + if(present(w)) then + gtmp=w + else + Do k=1,size(x2,1) + + cellk=spldom%cellk(i(k),j(k)) + call s1424(spldom%Dirdomweight,1,1,(/x1(k),x2(k)/),cellk%leftknot(1),cellk%leftknot(2),gtmp(k,:),sstat) + if (sstat > 0 ) WRITE(*,*) "Warning ",sstat," in distance calculation s1424 for splineweight" + if (sstat < 0 ) WRITE(*,*) "Error ",sstat," in distance calculation s1424 for splineweight" + end DO + end if + + Do k=1,size(x2,1) + cellk=spldom%cellk(i(k),j(k)) + if(cellk%spldirkind.eq.0)then + if(gtmp(k,0) .ge. 0) then + if(size(g,2) .gt. 1) then + g(k,1:2)=-gtmp(k,1:2)*spldom%boundaries(cellk%linkedboundaries(1))%Dirichlet_val + end if + g(k,0)=(1-gtmp(k,0))*spldom%boundaries(cellk%linkedboundaries(1))%Dirichlet_val + else + g(k,0)=spldom%boundaries(cellk%linkedboundaries(1))%Dirichlet_val + if(size(g,2).gt. 1) then + g(k,1:2)=0 + end if + end if + else + g(k,:)=0 + end if + end DO + + End SUBROUTINE spline_g + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Evaluates the geometric weight induced by the spline curve defined by b_curve at position (z,r) +!> @param[in] b_curve spline_boundary containing the spline curve parameters +!> @param[in] z axial position where the weight is evaluated +!> @param[in] r radial position where the weight is evaluated +!> @param[out] weight(:) weight index defines the order of derivation by r or z +!> @param[in] h distance from the spline at which the weight is 1 +!> @param[out] distance unscaled distance between evaluation point and spline b_curve +!> @param[inout] leftknot initial guess for the closest spline knot of the points (r,z) +!--------------------------------------------------------------------------- + + subroutine splineweight(b_curve, z, r, weight, h, distance, guess, lguess, rguess) + Use forSISL, ONLY: s1227,s1221, s1774 + type(spline_boundary):: b_curve + Real(kind=db)::r,z + Real(kind=db):: weight(0:) + Real(kind=db),OPTIONAL:: distance + real(kind=db),OPTIONAL:: guess + real(kind=db),OPTIONAL:: lguess + real(kind=db),OPTIONAL:: rguess + integer:: sstatus, der, left,siz + real(kind=db):: h, d, tpos, proj + real(kind=real64):: curvepos(2*b_curve%curve%idim) + real(kind=db):: leftpar, rightpar,guesspar + + + + weight=0 + der=1 + sstatus=-1 + guesspar=-1.0_db + if(present(lguess) .and. present(rguess)) then + leftpar=lguess + rightpar=rguess + guesspar=(lguess+rguess)/2 + call s1774(b_curve%curve,(/z,r/),b_curve%curve%idim,b_curve%epsge,leftpar,rightpar,guesspar,tpos,sstatus) + if (sstatus < 0 ) WRITE(*,*) "Error ",sstatus," in distance calculation s1774 for splineweight at ", z, r + else + call dist(b_curve,(/z,r/),d,tpos) + end if + ! position and derivative wrt r,z + call s1221(b_curve%curve,der,tpos,left,curvepos,sstatus) + if (sstatus > 0 ) WRITE(*,*) "Warning ",sstatus," in distance calculation s1227 for splineweight at ", z, r + if (sstatus < 0 ) WRITE(*,*) "Error ",sstatus," in distance calculation s1227 for splineweight at ", z, r + d=sqrt((curvepos(1)-z)**2+(curvepos(2)-r)**2) + + weight(0)=1-max((h-d)/h,0.0_db)**3 + curvepos(3:4)=curvepos(3:4)/sqrt(curvepos(3)**2+curvepos(4)**2) + ! if the projection of the distance vector on the normal is negative, the weight is negative + proj=(-(z-curvepos(1))*curvepos(4)+(r-curvepos(2))*curvepos(3)) + if (proj .lt. 0 .or. abs(abs(proj) -sqrt((z-curvepos(1))**2+(r-curvepos(2))**2)).gt.1e-10) weight(0)=-weight(0) + !if (proj .lt. 0 ) weight(0)=-weight(0) + siz=size(weight,1) + if (size(weight,1).gt.1 .and. abs(weight(0)) .lt. 1) then + weight(1)=-3*curvepos(4)*(h-d)**2/h**3 + weight(2)=+3*curvepos(3)*(h-d)**2/h**3 + end if + if(present(distance)) distance=d + if(present(guess)) guess=tpos + end subroutine + +!--------------------------------------------------------------------------- +!> @author +!> Guillaume Le Bars EPFL/SPC +! +! DESCRIPTION: +!> +!> @brief +!> Calculates the closest distance between the point and the selected spline b_curve +!> @param[in] b_curve spline_boundary containing the spline curve parameters +!> @param[in] point(:) array containing the position from which to calculate the distance +!> @param[out] distance distance from the point to the spline +!> @param[in] pos parameter value of the closest point on the spline +!--------------------------------------------------------------------------- + + subroutine dist(b_curve, point, distance, pos) + Use forSISL, ONLY: s1957,s1953, s1221,s1227 + type(spline_boundary):: b_curve + Real(kind=db):: point(:) + real(kind=db):: distance + Real(kind=db),optional::pos + REAL(real64):: posres, epsco, epsge,curvepos(2),d,distmin + REAL(real64),allocatable::intpar(:) + integer:: numintpt, sstat, numintcu,i,left,sstatus + type(SISLIntCurve),ALLOCATABLE:: intcurve(:) + + epsco=1.0e-15 + epsge=1.0e-15 + + !epsco=0 + !epsge=b_curve%epsge + + numintpt=0 + sstatus=0 + distmin=HUGE(d) + call s1953(b_curve%curve,point,b_curve%curve%idim,epsco,epsge,numintpt,intpar,numintcu,intcurve,sstatus) + if (sstatus > 0 ) WRITE(*,*) "Warning ",sstatus," in distance calculation s1953 for splineweight at ", point(1), point(2) + if (sstatus < 0 ) WRITE(*,*) "Error ",sstatus," in distance calculation s1953 for splineweight at ",point(1), point(2) + if(numintpt .gt. 1) then + Do i=1,numintpt + call s1227(b_curve%curve,0,intpar(i),left,curvepos,sstatus) + if (sstatus > 0 ) WRITE(*,*) "Warning ",sstatus," in distance calculation s1221 for splineweight at ", point(1), point(2) + if (sstatus < 0 ) WRITE(*,*) "Error ",sstatus," in distance calculation s1221 for splineweight at ",point(1), point(2) + d=(curvepos(1)-point(1))**2+(curvepos(2)-point(2))**2 + if(d .lt. distmin) then + distmin=d + posres=intpar(i) + end if + end do + else if(numintpt .gt. 0) then + posres=intpar(1) + end if + if(numintcu.ge.1) then + posres=0.5*(intcurve(1)%epar1(1)+intcurve(1)%epar1(2)) + end if + if (present(pos)) pos=posres + END subroutine + + SUBROUTINE classify(x1, x2, cellk, spldom, wpredir, wpretot) + real(kind=db), INTENT(IN):: x2(2), x1(2) + type(cellkind), intent(INOUT):: cellk + type(spline_domain)::spldom + Real(kind=db):: zeval(4),reval(4), wpretot, wpredir + real(kind=db), allocatable:: guess(:,:), w(:,:,:) + Real(kind=db):: dmin, insidedir, insidetot, distance + integer:: i,k + + allocate(guess(spldom%nbsplines,4)) + allocate(w(0:2,spldom%nbsplines,4)) + w=0 + cellk%spldirkind=0 + guess=-1.0_db + dmin=HUGE(spldom%dist_extent) + cellk%linkedboundaries=0 + zeval=(/ x1(1),x1(2),x1(1),x1(2) /) + reval=(/ x2(1),x2(1),x2(2),x2(2) /) + insidedir=1 + do i=1,spldom%nbsplines + do k=1,4 + ! calculate the weight for each spline boundaries at each cell corner + call splineweight(spldom%boundaries(i),zeval(k),reval(k),w(:,i,k),spldom%dist_extent,distance,guess(i,k)) + ! We find the closest boundary to this point + if(distance .lt. dmin) then + ! If we are close enough we check if we are below dist_extent and need to calculate the distance each time + if(distance .lt. spldom%dist_extent) then + if(spldom%boundaries(i)%type .eq. bd_Dirichletconst .or. spldom%boundaries(i)%type .eq.bd_Dirichletvar) then + cellk%linkedboundaries(1)=i + cellk%spldirkind=0 + end if + cellk%linkedboundaries(2)=i + cellk%spltotkind=0 + end if + dmin=distance + ! Otherwise we define the interior by the closest spline + if(spldom%boundaries(i)%type .eq. bd_Dirichletconst .or. spldom%boundaries(i)%type .eq.bd_Dirichletvar) then + insidedir=w(0,i,k) + end if + insidetot=w(0,i,k) + end if + end do + end do + if(cellk%linkedboundaries(1) .gt. 0) then + i=cellk%linkedboundaries(1) + cellk%lguess(1)=minval(guess(i,:),1,guess(i,:).ge.0) + cellk%rguess(1)=maxval(guess(i,:),1) + wpredir=w(0,i,1) + else + cellk%spldirkind=sign(1,int(insidedir)) + wpredir=insidedir + end if + + if(cellk%linkedboundaries(2) .gt. 0) then + i=cellk%linkedboundaries(2) + wpretot=w(0,i,1) + else + cellk%spltotkind=sign(1,int(insidetot)) + wpretot=insidetot + end if + end subroutine + + subroutine classifycells(spldom) + use forSISL, ONLY: s1537, s1424 + type(spline_domain):: spldom + integer:: i,j,sstat + type(cellkind):: cellk + real(kind=db), allocatable:: wpretot(:,:,:), wpredir(:,:,:) + + allocate(wpretot(1:1,0:spldom%nb1,0:spldom%nb2)) + allocate(wpredir(1:1,0:spldom%nb1,0:spldom%nb2)) + + wpretot=0 + wpredir=0 + !$OMP PARALLEL DO private(i,j) + do i=0,spldom%nb1-1 + !DIR$ UNROLL + do j=0,spldom%nb2-1 + call classify(spldom%x1(i:i+1),spldom%x2(j:j+1),spldom%cellk(i,j),spldom, wpredir(1,i,j),wpretot(1,i,j)) + end do + end do + !$OMP END PARALLEL DO + + + i=spldom%nb1 + !$OMP PARALLEL DO private(j,cellk) + do j=0,spldom%nb2 + cellk=spldom%cellk(min(i,spldom%nb1-1),min(j,spldom%nb2-1)) + If(abs(cellk%spldirkind) .eq. 1) Then + wpredir(1,i,j)=cellk%spldirkind + else + call splineweight(spldom%boundaries(cellk%linkedboundaries(1)), spldom%x1(i),spldom%x2(j), wpredir(:,i,j),spldom%dist_extent) + end IF + If(abs(cellk%spltotkind) .eq. 1) Then + wpretot(1,i,j)=cellk%spltotkind + else + call splineweight(spldom%boundaries(cellk%linkedboundaries(2)), spldom%x1(i),spldom%x2(j), wpretot(:,i,j),spldom%dist_extent) + end IF + end do + !$OMP END PARALLEL DO + + !$OMP PARALLEL DO private(i,cellk) + do i=0,spldom%nb1 + j=spldom%nb2 + cellk=spldom%cellk(min(i,spldom%nb1-1),min(j,spldom%nb2-1)) + If(abs(cellk%spldirkind) .eq. 1) Then + wpredir(1,i,j)=cellk%spldirkind + else + call splineweight(spldom%boundaries(cellk%linkedboundaries(1)), spldom%x1(i),spldom%x2(j), wpredir(:,i,j),spldom%dist_extent) + end IF + If(abs(cellk%spltotkind) .eq. 1) Then + wpretot(1,i,j)=cellk%spltotkind + else + call splineweight(spldom%boundaries(cellk%linkedboundaries(2)), spldom%x1(i),spldom%x2(j), wpretot(:,i,j),spldom%dist_extent) + end IF + end do + !$OMP END PARALLEL DO + call s1537(reshape(wpredir(1,:,:),(/(spldom%nb1+1)*(spldom%nb2+1)/)),spldom%nb1+1,spldom%nb2+1,1,spldom%x1,spldom%x2,0,0,0,0,4,4,1,1,spldom%Dirdomweight,sstat) + if (sstat > 0 ) WRITE(*,*) "Warning ",sstat," in distance calculation s1537 for splineweight" + if (sstat < 0 ) WRITE(*,*) "Error ",sstat," in distance calculation s1537 for splineweight" + + call s1537(reshape(wpretot(1,:,:),(/(spldom%nb1+1)*(spldom%nb2+1)/)),spldom%nb1+1,spldom%nb2+1,1,spldom%x1,spldom%x2,0,0,0,0,4,4,1,1,spldom%totdomweight,sstat) + if (sstat > 0 ) WRITE(*,*) "Warning ",sstat," in distance calculation s1537 for splineweight" + if (sstat < 0 ) WRITE(*,*) "Error ",sstat," in distance calculation s1537 for splineweight" + + end subroutine + + subroutine getindex(x1,x2,spldom, i, j) + use distrib, ONLY: closest + type(spline_domain):: spldom + real(kind=db):: x1(:), x2(:) + integer:: i(:),j(:) + call locintv(spldom%splrz%sp1,x1, i) + call locintv(spldom%splrz%sp2,x2, j) + end subroutine + + + subroutine free_bsplinecurve(b_curve) + type(spline_boundary):: b_curve + call freeCurve(b_curve%curve) + !call freeIntCurve(b_curve%intcurve) + end subroutine +END MODULE splinebound + diff --git a/src/start.f90 b/src/start.f90 index 30269d5..0cc883c 100644 --- a/src/start.f90 +++ b/src/start.f90 @@ -1,13 +1,52 @@ SUBROUTINE start ! ! Start or restart the run ! -USE basic,ONLY: mpirank +USE basic,ONLY: mpirank, nlmaxwellsource, time, lu_in, Zbounds, nbspecies, cstep, nlres +Use maxwsrce +Use geometry +Use neutcol +Use beam +Use fields +use mpi +use psupply IMPLICIT NONE ! ! Local vars and arrays +INTEGER:: i, nbbounds !________________________________________________________________________________ IF(mpirank.eq.0) WRITE(*,'(a/)') '=== Start or restart the run ===' !________________________________________________________________________________ ! + + IF (cstep .eq. 0) THEN + CALL partdiagnostics + Do i=1,nbspecies + if(.not. partslist(i)%calc_moments) CYCLE + CALL momentsdiag(partslist(i)) + End do + END IF + + ! Initialize electron neutral collisions + CALL neutcol_init(lu_in, partslist(1)) + + ! resize nblost array to adapt for correct number of boundaries + nbbounds=2 + if(the_domain%nbsplines .gt. 0) nbbounds=the_domain%nbsplines + Do i=1,nbspecies + if( allocated(partslist(i)%nblost)) deallocate(partslist(i)%nblost) + allocate(partslist(i)%nblost(4+nbbounds)) + partslist(i)%nblost=0 + end do + + ! Initialize the external power supply + if(nlres .and. the_ps%active)then + write(*,*) "the_ps is active" + call psupply_init(lu_in, cstep, nbbounds, neutdens, the_ps%bias) + else + write(*,*) "use default psupply init" + call psupply_init(lu_in, cstep, nbbounds, neutdens) + end if + ! Activate the source if present + IF (nlmaxwellsource) CALL maxwsrce_init(lu_in, time, Zbounds) END SUBROUTINE start diff --git a/src/stepon.f90 b/src/stepon.f90 index 7d33669..03951ad 100644 --- a/src/stepon.f90 +++ b/src/stepon.f90 @@ -1,42 +1,101 @@ SUBROUTINE stepon ! ! Advance one time step ! - USE basic - USE constants - USE fields - USE beam - USE maxwellsource + USE basic + USE constants + USE fields + USE beam + USE maxwsrce + USE celldiag + USE neutcol + USE sort + Use psupply - INTEGER:: i + INTEGER:: i + + DO i=1,nbspecies + ! Boundary conditions for plasma particles outside the plasma region + CALL bound(partslist(i)) + call boundary_loss(partslist(i)) + + ! Localisation of particles in cells (calculation of the r and z indices) + CALL localisation(partslist(i)) + END DO + + + ! Cell diag quantities + IF(modulo(step,itcelldiag).eq. 0 .or. nlend) THEN + CALL celldiag_save(time, fidres) + END IF + +! We compute collisions on the main particles + IF(modulo(step,itcol).eq. 0) THEN + CALL neutcol_step(partslist) + END IF + ! The particles are injected by the source - IF (nlmaxwellsource) CALL maxwellsource_inject(time) + CALL maxwsrce_inject(time) + +! Sort particles for faster rhscon run time +! DO i=1,nbspecies +! IF(modulo(step,it2d) .eq. 0) THEN +! CALL gridsort(partslist(i),1,partslist(i)%Nploc) +! END IF +! END DO + +! Assemble right hand side of Poisson equation + CALL rhscon(partslist) + + if (.not. nlfreezephi) THEN +! Solve Poisson equation + CALL poisson(splrz) + end if + + + DO i=1,nbspecies + + ! Compute the electric field at the particle position + CALL EFieldscompatparts(partslist(i)) + + ! Compute the magnetic field at the particle position + call comp_mag_p(partslist(i)) + + ! Solve Newton eq. and advance velocity by delta t + CALL comp_velocity(partslist(i)) + + ! Compute the energy of added particles + CALL calc_newparts_energy(partslist(i)) + END DO + + ! Calculate main physical quantities + CALL partdiagnostics + + IF (modulo(step,it2d).eq. 0 .or. nlend) THEN + Do i=1,nbspecies + if(partslist(i)%calc_moments) CALL momentsdiag(partslist(i)) + End do + END IF - DO i=1,nbspecies - ! Boundary conditions for plasma particles outside the plasam region - CALL bound(partslist(i)) + ! update the power supply voltage if necessary + call psupply_step(the_ps,partslist,cstep) - ! Localisation of particles in cells - CALL localisation(partslist(i)) + ! Save variables to file + CALL diagnose(step) - ! Assemble right hand side - CALL rhscon(partslist(i)) - END DO -! Solve Poisson equation - CALL poisson + Do i=1,nbspecies + ! Calculate new positions of particles at time t+delta t + CALL push(partslist(i)) + END DO - DO i=1,nbspecies - ! Compute the electric field at the particle position - CALL EForcescomp(partslist(i)) - ! Solve Newton eq. and advance velocity by delta t - CALL comp_velocity(partslist(i)) - ! Calculate new positions of particles - CALL push(partslist(i)) - END DO -! Calculate main physical quantities - CALL diagnostics + ! We recalculate the mpi axial boundaries and we adapt them if necessary + IF(modulo(step,50) .eq. 0) THEN + CALL calc_Zbounds(partslist(1),Zbounds, femorder) + CALL fields_comm_init(Zbounds) + CALL maxwsrce_calcfreq(Zbounds) + END IF END SUBROUTINE stepon diff --git a/src/tesend.f90 b/src/tesend.f90 index 2a9352c..ead066a 100644 --- a/src/tesend.f90 +++ b/src/tesend.f90 @@ -1,74 +1,75 @@ SUBROUTINE tesend ! ! Test for run completion ! USE basic ! IMPLICIT NONE ! ! Local vars and arrays CHARACTER(len=*), PARAMETER :: stop_file = 'mystop' LOGICAL :: mlexist REAL(kind=db) :: eltime, step_time, tremain - INTEGER :: rem_steps + INTEGER :: rem_steps, reason !________________________________________________________________________________ !!$ WRITE(*,'(a/)') '=== Test for run completion ===' !________________________________________________________________________________ ! 1. Some processors had set nlend ! IF( nlend ) THEN WRITE(*,'(/a)') 'NLEND set to .TRUE.!' RETURN END IF !________________________________________________________________________________ ! 2. NRUN modified through "stop file" ! + + CALL MPI_BARRIER(MPI_COMM_WORLD, ierr) INQUIRE(file=stop_file, exist=mlexist) IF( mlexist ) THEN IF(mpirank .eq. 0) THEN OPEN(lu_stop, file=stop_file) - READ(lu_stop,*) rem_steps ! Modify remaining steps "on the fly" - CALL MPI_BARRIER(MPI_COMM_WORLD, ierr) + READ(lu_stop,*,IOSTAT=reason) rem_steps ! Modify remaining steps "on the fly" + if(reason .ne. 0 ) rem_steps=10 ! We reached end of file CALL MPI_Bcast(rem_steps, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) - WRITE(*,'(/a,i4,a)') 'Stop file found: will exit in', rem_steps, ' steps' + WRITE(*,'(/,/a,i6,a,i8,/,/)') '############ Stop file found: will exit in', rem_steps, ' steps at',step+rem_steps CLOSE(lu_stop, status='delete') ELSE - CALL MPI_BARRIER(MPI_COMM_WORLD, ierr) CALL MPI_Bcast(rem_steps, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) END IF nrun = step + rem_steps END IF !________________________________________________________________________________ ! 3. Test on NRUN ! nlend = step .GE. nrun IF ( nlend ) THEN WRITE(*,'(/a)') 'NRUN steps done' RETURN END IF !________________________________________________________________________________ ! 4. Test on TMAX ! nlend = time .GE. tmax IF ( nlend ) THEN WRITE(*,'(/a)') 'TMAX reached' RETURN END IF !________________________________________________________________________________ ! 5. Test on time allocated to job ! CALL timera(-1, '', eltime) ! Current elapsed time tremain = job_time - eltime ! CALL timera(1, 'Main loop', step_time) step_time = 1.2 * step_time / step ! Averaged time per step + 20% ! nlend = tremain .LT. (step_time+extra_time) IF( nlend ) THEN WRITE(*,'(/a,f8.3)') 'Allocated Job time exhausted:, remaining time =', tremain END IF RETURN ! nlend = .FALSE. !________________________________________________________________________________ END SUBROUTINE tesend diff --git a/src/weighttypes_mod.f90 b/src/weighttypes_mod.f90 new file mode 100644 index 0000000..a0743f0 --- /dev/null +++ b/src/weighttypes_mod.f90 @@ -0,0 +1,452 @@ +MODULE weighttypes + USE constants + use splinebound + IMPLICIT NONE + + Real(kind=db),save:: z_0=0, r_0=0, invr_r=0, invr_z=0, r_a=0, r_b=0, z_r=1, r_r=1, alpha=0,z_a=0,z_b=0 + Real(kind=db),save:: r_bLeft=0, r_bRight=0 + Real(kind=db), save:: Phidown=0,Phiup=0 + Real(kind=db), save:: L_r=0.16, L_z=0.24 + Integer, save::Interior=-1 + Integer, save:: above1=1, above2=-1 + type(spline_domain):: the_domain + + contains + + SUBROUTINE geom_w2(z,r,w,wupper) + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: walltmp(size(w,1),0:size(w,2)-1), elliptmp(size(w,1),0:size(w,2)-1) + + call cyllweight(z,r,walltmp, r_a, above1) + call ellipsewall(z,r,elliptmp,r_b,above2,r_0, z_0, invr_z, invr_r, Interior) + + if( present(wupper) ) then + wupper=elliptmp + w=walltmp + else + call Combine(elliptmp, walltmp, w, -1) + end if + + End SUBROUTINE geom_w2 + + SUBROUTINE geom_w3(z,r,w,wupper) + ! Defines the geometric weight for two facing ellipses of parallel boundaries with line prolongations + !-----\__/------ + ! + !---\______/---- + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: belowtmp(size(w,1),0:size(w,2)-1), abovetmp(size(w,1),0:size(w,2)-1) + + ! Weight functions necessary for calculation of top and bottom boundaries + ! Ellipse and bottom cylinder combination + call ellipsewall(z,r,belowtmp,r_a,1,r_b, z_0, 1/(z_r+r_b-r_a), 1/(z_r+r_b-r_a), 1) + ! Ellipse and top cylinder combination + call ellipsewall(z,r,abovetmp,r_b,-1,r_b, z_0, invr_z, invr_r, -1) + + if( present(wupper) ) then + wupper=abovetmp + w=belowtmp + else + call Combine(belowtmp, abovetmp, w, -1) + end if + + End SUBROUTINE geom_w3 + + SUBROUTINE geom_w4(z,r,w,wupper) + + ! Defines the geometric weight for two facing ellipses of same dimensions with line prolongations + !-----\__/------ + ! + !-----\__/------ + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: belowtmp(size(w,1),0:size(w,2)-1), abovetmp(size(w,1),0:size(w,2)-1) + + ! Weight functions necessary for calculation of g + ! Ellipse and bottom cylinder combination + call ellipsewall(z,r,belowtmp,r_a,1,r_a, z_0, invr_z, invr_r, 1) + ! Ellipse and top cylinder combination + call ellipsewall(z,r,abovetmp,r_b,-1,r_b, z_0, invr_z, invr_r, -1) + + if( present(wupper) ) then + wupper=abovetmp + w=belowtmp + else + call Combine(belowtmp, abovetmp, w, -1) + end if + + End SUBROUTINE geom_w4 + + SUBROUTINE geom_w5(z,r,w, wupper) + + ! Defines the geometric weight for a central cylinder and a tilted upper cylinder with elliptic region + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: w1(size(w,1),0:size(w,2)-1), w2(size(w,1),0:size(w,2)-1), w3(size(w,1),0:size(w,2)-1) + + + ! tilted wall + call tiltedwall(z,r,w2,r_0,z_0, alpha, above2) + ! calculate upper right boundary + call cyllweight(z,r,w1,max(r_bRight,r_bLeft),above2) + ! Intersection between slanted wall and upper right wall + call Combine(w1,w2, w3, -1) + + ! calculate upper left boundary + call cyllweight(z,r,w1,min(r_bRight,r_bLeft),above2) + ! Union between previous weight w3 and upper left wall + call Combine(w1,w3, w2, 1) + + ! tilted ellipse + call tiltedellipse(z,r,w1,r_0,z_0, invr_z, invr_r, Interior, alpha) + ! Intersection of ellipse and previous weight + call Combine(w1, w2, w3, Interior) + + ! calculate coaxial insert + call cyllweight(z,r,w1,r_a,above1) + + if( present(wupper) ) then + wupper=w3 + w=w1 + else + ! gives total weight + call Combine(w1, w3, w, -1) + end if + + End SUBROUTINE geom_w5 + + SUBROUTINE geom_w6(z,r,w, wupper) + use basic, ONLY: zgrid, nz + ! Defines the geometric weight for a central cylinder and a tilted upper cylinder with elliptic region + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: w1(size(w,1),0:size(w,2)-1), w2(size(w,1),0:size(w,2)-1), w3(size(w,1),0:size(w,2)-1) + + + ! tilted wall + call tiltedwall(z,r,w2,r_0,z_0, alpha, above2) + ! calculate upper right boundary + call cyllweight(z,r,w1,max(r_bRight,r_bLeft),above2) + ! Intersection between slanted wall and upper right wall + call Combine(w1,w2, w3, -1) + + ! calculate upper left boundary + call cyllweight(z,r,w1,min(r_bRight,r_bLeft),above2) + ! Union between previous weight w3 and upper left wall + call Combine(w1,w3, w2, 1) + + ! tilted ellipse + call tiltedellipse(z,r,w1,r_0,z_0, invr_z, invr_r, Interior, alpha) + ! Intersection of ellipse and previous weight + call Combine(w1, w2, w3, Interior) + + ! tilted ellipse + call discweight(z,r, w1, zgrid(nz),-1) + ! Intersection of ellipse and previous weight + call Combine(w1, w3, w2, -1) + + ! calculate coaxial insert + call cyllweight(z,r,w1,r_a,above1) + + if( present(wupper) ) then + wupper=w2 + w=w1 + else + ! gives total weight + call Combine(w1, w2, w, -1) + end if + + End SUBROUTINE geom_w6 + + + SUBROUTINE geom_w7(z,r,w, wupper) + use basic, ONLY: zgrid, nz + ! Defines the geometric weight for a central cylinder and a tilted upper cylinder with elliptic region + ! and left and right dirichlet boundaries + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: w1(size(w,1),0:size(w,2)-1), w2(size(w,1),0:size(w,2)-1), w3(size(w,1),0:size(w,2)-1) + + + ! tilted wall + call tiltedwall(z,r,w2,r_0,z_0, alpha, above2) + ! calculate upper right boundary + call cyllweight(z,r,w1,max(r_bRight,r_bLeft),above2) + ! Intersection between slanted wall and upper right wall + call Combine(w1,w2, w3, -1) + + ! calculate upper left boundary + call cyllweight(z,r,w1,min(r_bRight,r_bLeft),above2) + ! Union between previous weight w3 and upper left wall + call Combine(w1,w3, w2, 1) + + ! tilted ellipse + call tiltedellipse(z,r,w1,r_0,z_0, invr_z, invr_r, Interior, alpha) + ! Intersection of ellipse and previous weight + call Combine(w1, w2, w3, Interior) + + ! Right Dirichlet + call discweight(z,r, w1, zgrid(nz),-1) + ! Intersection of ellipse and previous weight + call Combine(w1, w3, w2, -1) + + ! Left Dirichlet + call discweight(z,r, w1, zgrid(0),1) + ! Intersection of ellipse and previous weight + call Combine(w1, w2, w3, -1) + + ! calculate coaxial insert + call cyllweight(z,r,w1,r_a,above1) + + if( present(wupper) ) then + wupper=w3 + w=w1 + else + ! gives total weight + call Combine(w1, w3, w, -1) + end if + + End SUBROUTINE geom_w7 + + SUBROUTINE geom_w8(z,r,w, wupper) + use basic, ONLY: zgrid, nz + ! Defines the geometric weight for a central cylinder and a tilted upper cylinder with elliptic region + ! and left and right dirichlet boundaries + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: w1(size(w,1),0:size(w,2)-1), w2(size(w,1),0:size(w,2)-1), w3(size(w,1),0:size(w,2)-1) + + + ! tilted wall + call tiltedwall(z,r,w2,r_0,z_0, alpha, above2) + ! calculate upper right boundary + call cyllweight(z,r,w1,max(r_bRight,r_bLeft),above2) + ! Intersection between slanted wall and upper right wall + call Combine(w1,w2, w3, -1) + + ! calculate upper left boundary + call cyllweight(z,r,w1,min(r_bRight,r_bLeft),above2) + ! Union between previous weight w3 and upper left wall + call Combine(w1,w3, w2, 1) + + ! tilted ellipse + call tiltedellipse(z,r,w1,r_0,z_0, invr_z, invr_r, Interior, alpha) + ! Intersection of ellipse and previous weight + call Combine(w1, w2, w3, Interior) + + ! Left Dirichlet + call discweight(z,r, w1, zgrid(0),1) + ! Intersection of ellipse and previous weight + call Combine(w1, w3, w2, -1) + + ! calculate coaxial insert + call cyllweight(z,r,w1,r_a,above1) + ! Intersection of ellipse and previous weight + call Combine(w1, w2, w3, -1) + + + ! Right Dirichlet + call discweight(z,r, w1, zgrid(nz),-1) + + + if( present(wupper) ) then + wupper=w3 + w=w1 + else + ! gives total weight + call Combine(w1, w3, w, -1) + end if + + End SUBROUTINE geom_w8 + + SUBROUTINE geom_w10(z,r,w, wupper) + use basic, ONLY: zgrid, nz + ! Defines the geometric weight for a central cylinder and a tilted upper cylinder with elliptic region + ! and left and right dirichlet boundaries + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + Real(kind=db):: w1(size(w,1),0:size(w,2)-1), w2(size(w,1),0:size(w,2)-1), w3(size(w,1),0:size(w,2)-1), w4(size(w,1),0:size(w,2)-1) + + ! Right Dirichlet + call discweight(z,r, w1, z_b,-1) + + ! Left Dirichlet + call discweight(z,r, w2, z_a,1) + + ! Union between previous weight w3 and upper left wall + call Combine(w1,w2, w3, -1) + + ! calculate upper radial boundary + call cyllweight(z,r,w1,r_b,-1) + + ! calculate lower radial boundary + call cyllweight(z,r,w2,r_a,1) + + ! Union between previous weight w3 and upper left wall + call Combine(w1,w2, w4, -1) + + + + + + if( present(wupper) ) then + wupper=w4 + w=w3 + else + ! gives total weight + call Combine(w3, w4, w, -1) + end if + + End SUBROUTINE geom_w10 + + + SUBROUTINE cyllweight(z,r,w, r_lim, above) + Real(kind=db):: z(:),r(:),w(:,0:), r_lim + Integer:: above + + w(:,0)=above*(r-r_lim) ! weight at position r,z + + If(size(w,2) .gt. 1) then ! first derivative + w(:,1)=0 ! z derivative of w + w(:,2)=above ! r derivative of w + End If + End subroutine cyllweight + + SUBROUTINE discweight(z,r,w, z_lim, right) + Real(kind=db):: z(:),r(:),w(:,0:), z_lim + Integer:: right + + w(:,0)=right*(z-z_lim) ! weight at position r,z + + If(size(w,2) .gt. 1) then ! first derivative + w(:,1)=right ! z derivative of w + w(:,2)=0 ! r derivative of w + End If + End subroutine discweight + + + SUBROUTINE tiltedwall(z,r,w,r0,z0,alpha,left) + Real(kind=db):: z(:),r(:),w(:,0:) + Integer:: left + Real(kind=db):: slope, r0, z0, alpha + + slope=tan(alpha) + + w(:,0)=(r-r0-slope*(z-z0)) + If(size(w,2) .gt. 1) then ! first derivative + w(:,1)= -slope ! z derivative of w + w(:,2)= 1 ! r derivative of w + End If + + w=left*w + + end SUBROUTINE + + SUBROUTINE ellipsewall(z,r,w,r_lim,above,r_c, z_c, invrz, invrr, Inside) + Real(kind=db):: z(:),r(:),w(:,0:) + Integer:: Inside, above + Real(kind=db):: r_lim, r_c,z_c,invrz,invrr + Real(kind=db):: walltmp(size(w,1),0:size(w,2)-1), elliptmp(size(w,1),0:size(w,2)-1) + + call cyllweight(z,r,walltmp, r_lim, above) + call ellipseweight(z,r,elliptmp,r_c, z_c, invrz, invrr, Inside) + + call combine( walltmp, elliptmp, w, Inside) + + END SUBROUTINE ellipsewall + + Subroutine ellipseweight(z,r,w, r_c, z_c, invrz, invrr, Inside) + Real(kind=db):: z(:),r(:),w(:,0:) + Real(kind=db):: r_c,z_c,invrz,invrr + Integer:: Inside + Real(kind=db):: D(size(r,1)) + + D=sqrt((r-r_c)**2*invrr**2+(z-z_c)**2*invrz**2) + w(:,0)=Inside*(1-D) ! weight at position r,z + + If(size(w,2) .gt. 1) then ! first derivative + w(:,1)=-(z-z_c)*invrz**2/D*Inside ! z derivative of w + w(:,2)=-(r-r_c)*invrr**2/D*Inside ! r derivative of w + End If + End subroutine ellipseweight + + Subroutine tiltedellipse(z,r,w, r_c, z_c, invrz, invrr, Inside, alpha) + Real(kind=db):: z(:),r(:),w(:,0:) + Real(kind=db):: r_c,z_c,invrz,invrr, alpha, cosa, sina + Real(kind=db):: deltar(size(r,1)), deltaz(size(z,1)) + Integer:: Inside + Real(kind=db):: D(size(r,1)) + + cosa=cos(alpha) + sina=sin(alpha) + + deltar=(r-r_c) + deltaz=(z-z_c) + D=sqrt(((deltaz*cosa+deltar*sina)*invrz)**2+((deltaz*sina-deltar*cosa)*invrr)**2) + w(:,0)=1-D ! weight at position r,z + + If(size(w,2) .gt. 1) then ! first derivative + w(:,1)=(-cosa*(deltaz*cosa+deltar*sina)*invrz**2-sina*(deltaz*sina-deltar*cosa)*invrr**2)/D ! z derivative of w + w(:,2)=(-sina*(deltaz*cosa+deltar*sina)*invrz**2+cosa*(deltaz*sina-deltar*cosa)*invrr**2)/D ! r derivative of w + End If + + w=w*Inside + End subroutine tiltedellipse + + + SUBROUTINE combine(w1, w2, w, union) + ! Combines two weights w1 and w2 into w + ! If union is 1 this defines the union of the geometric domains + ! If union is -1 this defines the intersection of the geometric domains + Real(kind=db):: w1(:,0:), w2(:,0:), w(:,0:) + Integer:: union ! if 1 defines the union ow weights, if -1 defines intersection + Real(kind=db):: squareroot(size(w,1)), denom(size(w,1)) + + denom=w1(:,0)**2+w2(:,0)**2 + squareroot=union*sqrt(denom) + + w(:,0)=w1(:,0)+w2(:,0)+squareroot ! weight at position r,z + + If(size(w,2) .gt. 1) then ! first derivative + w(:,1)=w1(:,1)+w2(:,1)+(w1(:,1)*w1(:,0)+w2(:,1)*w2(:,0))/squareroot ! z derivative of w + w(:,2)=w1(:,2)+w2(:,2)+(w1(:,2)*w1(:,0)+w2(:,2)*w2(:,0))/squareroot ! r derivative of w + End If + END SUBROUTINE + + SUBROUTINE geom_spline(z,r,w,wupper) + Use splinebound, ONLY: spline_w + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + Real(kind=db), OPTIONAL:: wupper(:,0:) + + call spline_w(the_domain,z,r,w) + End SUBROUTINE geom_spline + + SUBROUTINE geom_splinetot(z,r,w,idwall) + Use splinebound, ONLY: spline_w + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: w(:,0:) + INTEGER, optional, INTENT(OUT):: idwall(:) + + call spline_wtot(the_domain,z,r,w,idwall) + End SUBROUTINE geom_splinetot + + SUBROUTINE gspline(z,r,g,w) + Use splinebound, ONLY: spline_g + Real(kind=db), INTENT(IN):: r(:),z(:) + Real(kind=db), INTENT(OUT):: g(:,0:) + Real(kind=db), INTENT(IN),OPTIONAL::w(:,0:) + + call spline_g(the_domain,z,r,g,w) + End SUBROUTINE gspline +END MODULE weighttypes \ No newline at end of file diff --git a/src/xg_mod.f90 b/src/xg_mod.f90 index 0631f59..19fe503 100644 --- a/src/xg_mod.f90 +++ b/src/xg_mod.f90 @@ -1,119 +1,115 @@ MODULE xg USE constants USE basic, ONLY : nr, nz, nrun, zgrid, rgrid, nplasma, itgraph, resfile USE beam, ONLY: partslist USE fields, ONLY: nrank, vec1, vec2 IMPLICIT NONE ! REAL(kind=db) :: tstep=0.0_db, dnorm=1.0_db REAL(kind=db) :: zb1, zb2, rb1, rb2 REAL(kind=db), ALLOCATABLE :: pzxg(:), prxg(:), pthetxg(:) INTEGER :: nhist, maxhist REAL(kind=db), ALLOCATABLE :: thist(:), tepot(:), tekin(:), tetot(:), terror(:) INTEGER :: x0=250, xsize = 410, y0=0, ysize=335 ! CONTAINS SUBROUTINE initw ! ! Initialize Xgrafix ! ! Particle phase space ! ALLOCATE (pzxg(partslist(1)%Nploc)) ALLOCATE (prxg(partslist(1)%Nploc)) ALLOCATE (pthetxg(partslist(1)%Nploc)) ! ! Bounds of space domain ! zb1 = zgrid(0) zb2 = zgrid(nz) rb1 = rgrid(0) rb2 = rgrid(nr) ! ! Time history ! nhist = 0 maxhist=nrun/itgraph+1 ALLOCATE(thist(0:maxhist)) ALLOCATE(tepot(0:maxhist)) ALLOCATE(tekin(0:maxhist)) ALLOCATE(tetot(0:maxhist)) ALLOCATE(terror(0:maxhist)) ! CALL updt_xg_var ! CALL xginit(3,'espic2d:'//TRIM(resfile),'espic2d'//TRIM(resfile)//'.o','espic2d'//TRIM(resfile)//'.dump','N4','N5','N6',tstep) ! ! Particle phase space plots ! pzxg(:) = partslist(1)%uz(1:partslist(1)%Nploc) prxg(:) = partslist(1)%ur(1:partslist(1)%Nploc) pthetxg(:) = partslist(1)%uthet(1:partslist(1)%Nploc) ! Es potential ! CALL xgset2d('linlin','Z','Z-R phase space','open',x0, y0, dnorm, dnorm, & & .FALSE., .FALSE., ZB1, ZB2, RB1, RB2) CALL xgscat2d(partslist(1)%Z,partslist(1)%R,partslist(1)%Nploc,1) ! CALL xgset2d('linlin','PZ','PZ-PR phase space','open',x0+2*xsize, y0, 1._db, 1._db, & & .TRUE., .TRUE., 0._db, 1._db, 0._db, 1._db) CALL xgscat2d(pzxg, prxg, partslist(1)%Nploc,1) ! CALL xgset2d('linlin','PZ','PZ-PTHET phase space','open',x0+2*xsize, y0+ysize, 1._db, 1._db, & & .TRUE., .TRUE., 0._db, 1._db, 0._db, 1._db) CALL xgscat2d(pzxg, pthetxg, partslist(1)%Nploc,1) ! CALL xgset2d('linlin','Z','Z-PZ phase space','open',x0+xsize, y0, dnorm, 1.0_db, & & .FALSE., .TRUE., ZB1, ZB2, 0._db, 1._db) CALL xgscat2d(partslist(1)%Z, pzxg, partslist(1)%Nploc,1) ! ! Time history ! CALL xgset2d('linlin','T','Energies', 'open', x0, y0+ysize, 1._db, 1._db, & & .TRUE., .TRUE., 0._db, 1._db, 0._db, 1._db) CALL xgcurve(thist, tepot, nhist, 1) CALL xgcurve(thist, tekin, nhist, 2) CALL xgcurve(thist, tetot, nhist, 3) ! CALL xgset2d('linlog','T','Relative error in Etot', 'open', x0+xsize, y0+ysize, 1._db, 1._db, & & .TRUE., .TRUE., 0._db, 1._db, 0._db, 1._db) CALL xgcurve(thist, terror, nhist, 1) ! ! Xgrafix control ! CALL xgevent CALL xgupdate END SUBROUTINE initw !-------------------------------------------------------------------------------- SUBROUTINE updt_xg_var ! ! Update XG variables ! - USE basic, ONLY : cstep, dt + USE basic, ONLY : cstep, dt, tnorm USE basic, ONLY : omegap, omegac USE beam, ONLY : ekin, epot, etot, etot0 ! ! Phase space tstep = REAL(cstep,db) pzxg(:) = partslist(1)%uz(1:partslist(1)%Nploc) prxg(:) = partslist(1)%ur(1:partslist(1)%Nploc) pthetxg(:) = partslist(1)%uthet(1:partslist(1)%Nploc) ! ! Time history ! - IF(omegac.GT.omegap) THEN - thist(nhist) = nhist*itgraph*dt*omegac - ELSE - thist(nhist) = nhist*itgraph*dt*omegap - END IF + thist(nhist) = nhist*itgraph*dt*tnorm tekin(nhist) = ekin tepot(nhist) = epot tetot(nhist) = etot terror(nhist) = ABS((etot-etot0)/etot) ! To be dispalyed on log scale! nhist = nhist+1 ! END SUBROUTINE updt_xg_var END MODULE xg diff --git a/wk/Ar_ela_cross_sec.in b/wk/Ar_ela_cross_sec.in new file mode 100644 index 0000000..49de924 --- /dev/null +++ b/wk/Ar_ela_cross_sec.in @@ -0,0 +1,363 @@ +!LXCat, www.lxcat.net +!Generated on 13 Apr 2022. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on April 13, 2022. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: Transcribed from S.F. Biagi's Fortran Magboltz version 8.97 (Sept 2011). +! Data are based in part on the calculations of Zatsarinny and Bartschat. See BSR database on this site. +! +!********************************************************** Ar ********************************************************** +! +!ELASTIC +!Ar +! 1.360000e-5 +!SPECIES: e / Ar +!PROCESS: E + Ar -> E + Ar, Elastic +!PARAM.: m/M = 0.0000136, complete set +!COMMENT: testing 11/02 elastic momentum transfer from Magboltz 8.97 Sept 2011. Note that the energy +!COMMENT: resolution in the LXCat tables for energy < 1 eV is less than for the original data. +!UPDATED: 2021-02-11 18:17:56 +!COLUMNS: Energy (eV) | Cross section (m2) +!----------------------------- + 0.000000e+0 6.298400e-20 + 1.000000e-3 6.298400e-20 + 2.000000e-3 5.835000e-20 + 5.000000e-3 4.977500e-20 + 1.000000e-2 4.113000e-20 + 1.741900e-2 3.290600e-20 + 3.514200e-2 2.144900e-20 + 5.317400e-2 1.474100e-20 + 7.151900e-2 1.032600e-20 + 9.018400e-2 7.273200e-21 + 1.091700e-1 5.117000e-21 + 1.285000e-1 3.589000e-21 + 1.481500e-1 2.519100e-21 + 1.681500e-1 1.793200e-21 + 1.885000e-1 1.330700e-21 + 2.092100e-1 1.073000e-21 + 2.302700e-1 9.762900e-22 + 2.517000e-1 1.007000e-21 + 2.735000e-1 1.139000e-21 + 2.956900e-1 1.351700e-21 + 3.182600e-1 1.628900e-21 + 3.412200e-1 1.957400e-21 + 3.645800e-1 2.326700e-21 + 3.883500e-1 2.728200e-21 + 4.125400e-1 3.155200e-21 + 4.371400e-1 3.602100e-21 + 4.621800e-1 4.064500e-21 + 4.876500e-1 4.539100e-21 + 5.135600e-1 5.023100e-21 + 5.399300e-1 5.514600e-21 + 5.667500e-1 6.012300e-21 + 5.940400e-1 6.515300e-21 + 6.218100e-1 7.023300e-21 + 6.500600e-1 7.536300e-21 + 6.788000e-1 8.054600e-21 + 7.080500e-1 8.579000e-21 + 7.378000e-1 9.110500e-21 + 7.680700e-1 9.650300e-21 + 7.988700e-1 1.020000e-20 + 8.302100e-1 1.076200e-20 + 8.620900e-1 1.133700e-20 + 8.945200e-1 1.192800e-20 + 9.275200e-1 1.253700e-20 + 9.611000e-1 1.316800e-20 + 9.952600e-1 1.382100e-20 + 1.030000e+0 1.431600e-20 + 1.065400e+0 1.479100e-20 + 1.101400e+0 1.527500e-20 + 1.138000e+0 1.576700e-20 + 1.175200e+0 1.626700e-20 + 1.213100e+0 1.677000e-20 + 1.251600e+0 1.727100e-20 + 1.290900e+0 1.778100e-20 + 1.330800e+0 1.830000e-20 + 1.371400e+0 1.882800e-20 + 1.412700e+0 1.936500e-20 + 1.454700e+0 1.991100e-20 + 1.497500e+0 2.046700e-20 + 1.541000e+0 2.107400e-20 + 1.585200e+0 2.169300e-20 + 1.630300e+0 2.232400e-20 + 1.676100e+0 2.296500e-20 + 1.722700e+0 2.358000e-20 + 1.770100e+0 2.416500e-20 + 1.818400e+0 2.476000e-20 + 1.867500e+0 2.536600e-20 + 1.917400e+0 2.598200e-20 + 1.968200e+0 2.660800e-20 + 2.020000e+0 2.729100e-20 + 2.072600e+0 2.805900e-20 + 2.126100e+0 2.884100e-20 + 2.180500e+0 2.963600e-20 + 2.235900e+0 3.044500e-20 + 2.292300e+0 3.126800e-20 + 2.349700e+0 3.210500e-20 + 2.408000e+0 3.295700e-20 + 2.467400e+0 3.382400e-20 + 2.527800e+0 3.472800e-20 + 2.589200e+0 3.567400e-20 + 2.651700e+0 3.663700e-20 + 2.715400e+0 3.761600e-20 + 2.780100e+0 3.861300e-20 + 2.845900e+0 3.962700e-20 + 2.912900e+0 4.065900e-20 + 2.981100e+0 4.170900e-20 + 3.050400e+0 4.275600e-20 + 3.121000e+0 4.381500e-20 + 3.192800e+0 4.489100e-20 + 3.265800e+0 4.598700e-20 + 3.340100e+0 4.710200e-20 + 3.415700e+0 4.823600e-20 + 3.492600e+0 4.938900e-20 + 3.570900e+0 5.056300e-20 + 3.650500e+0 5.175800e-20 + 3.731500e+0 5.297300e-20 + 3.813900e+0 5.420900e-20 + 3.897800e+0 5.546700e-20 + 3.983100e+0 5.674700e-20 + 4.069900e+0 5.832800e-20 + 4.158200e+0 6.000600e-20 + 4.248100e+0 6.171300e-20 + 4.339500e+0 6.345000e-20 + 4.432500e+0 6.521800e-20 + 4.527100e+0 6.701600e-20 + 4.623400e+0 6.884500e-20 + 4.721400e+0 7.070600e-20 + 4.821000e+0 7.260000e-20 + 4.922400e+0 7.452600e-20 + 5.025600e+0 7.651200e-20 + 5.130600e+0 7.861100e-20 + 5.237300e+0 8.074700e-20 + 5.346000e+0 8.292000e-20 + 5.456500e+0 8.513100e-20 + 5.569000e+0 8.738000e-20 + 5.683400e+0 8.966900e-20 + 5.799900e+0 9.199700e-20 + 5.918300e+0 9.436600e-20 + 6.038800e+0 9.673800e-20 + 6.161400e+0 9.906700e-20 + 6.286200e+0 1.014400e-19 + 6.413100e+0 1.038500e-19 + 6.542200e+0 1.063000e-19 + 6.673600e+0 1.088000e-19 + 6.807300e+0 1.113400e-19 + 6.943300e+0 1.139200e-19 + 7.081600e+0 1.163100e-19 + 7.365700e+0 1.208500e-19 + 7.659600e+0 1.255500e-19 + 7.964000e+0 1.304200e-19 + 8.279000e+0 1.354600e-19 + 8.605100e+0 1.406800e-19 + 8.942600e+0 1.460800e-19 + 9.292000e+0 1.513800e-19 + 9.653700e+0 1.568100e-19 + 1.002800e+1 1.621700e-19 + 1.061400e+1 1.656900e-19 + 1.256800e+1 1.620300e-19 + 1.304400e+1 1.586500e-19 + 1.353800e+1 1.547000e-19 + 1.404900e+1 1.505600e-19 + 1.457800e+1 1.458000e-19 + 1.512500e+1 1.408700e-19 + 1.569200e+1 1.357700e-19 + 1.627800e+1 1.305000e-19 + 1.657900e+1 1.277900e-19 + 1.688500e+1 1.250300e-19 + 1.719700e+1 1.222300e-19 + 1.751400e+1 1.193700e-19 + 1.783600e+1 1.164700e-19 + 1.816500e+1 1.137700e-19 + 1.849800e+1 1.112600e-19 + 1.883800e+1 1.087100e-19 + 1.918400e+1 1.061200e-19 + 1.953500e+1 1.034900e-19 + 1.989300e+1 1.008000e-19 + 2.062700e+1 9.717800e-20 + 2.138700e+1 9.375800e-20 + 2.217400e+1 9.021700e-20 + 2.257800e+1 8.840100e-20 + 2.298800e+1 8.655300e-20 + 2.340600e+1 8.467200e-20 + 2.383100e+1 8.275900e-20 + 2.426400e+1 8.081300e-20 + 2.470400e+1 7.883200e-20 + 2.515200e+1 7.704500e-20 + 2.607100e+1 7.428800e-20 + 2.702200e+1 7.143400e-20 + 2.751000e+1 6.996900e-20 + 2.800700e+1 6.848000e-20 + 2.851200e+1 6.696400e-20 + 2.902600e+1 6.542100e-20 + 2.954900e+1 6.385200e-20 + 3.008100e+1 6.235400e-20 + 3.117400e+1 6.038700e-20 + 3.230400e+1 5.835200e-20 + 3.347500e+1 5.624600e-20 + 3.468600e+1 5.406500e-20 + 3.530800e+1 5.294600e-20 + 3.594000e+1 5.180800e-20 + 3.658400e+1 5.064900e-20 + 3.723800e+1 4.947100e-20 + 3.790500e+1 4.827200e-20 + 3.858200e+1 4.705200e-20 + 3.927200e+1 4.581100e-20 + 3.997300e+1 4.454800e-20 + 4.141300e+1 4.315800e-20 + 4.290400e+1 4.174200e-20 + 4.444600e+1 4.027600e-20 + 4.604400e+1 3.875900e-20 + 4.686300e+1 3.798000e-20 + 4.769700e+1 3.718800e-20 + 4.854500e+1 3.638200e-20 + 4.940800e+1 3.556200e-20 + 5.028600e+1 3.480000e-20 + 5.208800e+1 3.353800e-20 + 5.395400e+1 3.223200e-20 + 5.491100e+1 3.156200e-20 + 5.588500e+1 3.088000e-20 + 5.687600e+1 3.018700e-20 + 5.788400e+1 2.948100e-20 + 5.891000e+1 2.876300e-20 + 5.995400e+1 2.803200e-20 + 6.101500e+1 2.744100e-20 + 6.209600e+1 2.684700e-20 + 6.319500e+1 2.624300e-20 + 6.431300e+1 2.562800e-20 + 6.545100e+1 2.500200e-20 + 6.660800e+1 2.436500e-20 + 6.778600e+1 2.371800e-20 + 6.898400e+1 2.305900e-20 + 7.020300e+1 2.244900e-20 + 7.270600e+1 2.182400e-20 + 7.529600e+1 2.117600e-20 + 7.797700e+1 2.050600e-20 + 8.075200e+1 1.977400e-20 + 8.217600e+1 1.934700e-20 + 8.362500e+1 1.891200e-20 + 8.509900e+1 1.847000e-20 + 8.659900e+1 1.802000e-20 + 8.812500e+1 1.756200e-20 + 8.967800e+1 1.709700e-20 + 9.125700e+1 1.674900e-20 + 9.449900e+1 1.610000e-20 + 9.616300e+1 1.576700e-20 + 9.785500e+1 1.542900e-20 + 9.957700e+1 1.508500e-20 + 1.031100e+2 1.465100e-20 + 1.067700e+2 1.424200e-20 + 1.105600e+2 1.381800e-20 + 1.144800e+2 1.337800e-20 + 1.185400e+2 1.292400e-20 + 1.227400e+2 1.245300e-20 + 1.270900e+2 1.201600e-20 + 1.315900e+2 1.162000e-20 + 1.362500e+2 1.121000e-20 + 1.410700e+2 1.078600e-20 + 1.435400e+2 1.056800e-20 + 1.460600e+2 1.034700e-20 + 1.486200e+2 1.012100e-20 + 1.538800e+2 9.829200e-21 + 1.593200e+2 9.589700e-21 + 1.649600e+2 9.341800e-21 + 1.707900e+2 9.085200e-21 + 1.768300e+2 8.819600e-21 + 1.830800e+2 8.544600e-21 + 1.895500e+2 8.260000e-21 + 1.962400e+2 7.965300e-21 + 2.031700e+2 7.711100e-21 + 2.103500e+2 7.510200e-21 + 2.177800e+2 7.302300e-21 + 2.254600e+2 7.087000e-21 + 2.334200e+2 6.864200e-21 + 2.416600e+2 6.633500e-21 + 2.545600e+2 6.317800e-21 + 2.681500e+2 6.073200e-21 + 2.824700e+2 5.815600e-21 + 2.975400e+2 5.544300e-21 + 3.134100e+2 5.312200e-21 + 3.301300e+2 5.078200e-21 + 3.477400e+2 4.831700e-21 + 3.662800e+2 4.637200e-21 + 3.858100e+2 4.441900e-21 + 4.063800e+2 4.252100e-21 + 4.355200e+2 4.033600e-21 + 4.587300e+2 3.859500e-21 + 4.831700e+2 3.676200e-21 + 5.089200e+2 3.501000e-21 + 5.360300e+2 3.351800e-21 + 5.645900e+2 3.194800e-21 + 5.946600e+2 3.029400e-21 + 6.263400e+2 2.894700e-21 + 6.596900e+2 2.761200e-21 + 6.948200e+2 2.620700e-21 + 7.318200e+2 2.472700e-21 + 7.575800e+2 2.369700e-21 + 7.842400e+2 2.263100e-21 + 8.118300e+2 2.164500e-21 + 8.404000e+2 2.078800e-21 + 8.699600e+2 1.990100e-21 + 9.005700e+2 1.898300e-21 + 9.322500e+2 1.803200e-21 + 9.650500e+2 1.704800e-21 + !----------------------------- +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ No newline at end of file diff --git a/wk/Ar_io_cross_sec.in b/wk/Ar_io_cross_sec.in new file mode 100644 index 0000000..123e5ba --- /dev/null +++ b/wk/Ar_io_cross_sec.in @@ -0,0 +1,301 @@ +!LXCat, www.lxcat.net +!Generated on 13 Apr 2022. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on April 13, 2022. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: Transcribed from S.F. Biagi's Fortran Magboltz version 8.97 (Sept 2011). +! Data are based in part on the calculations of Zatsarinny and Bartschat. See BSR database on this site. +! +!********************************************************** Ar ********************************************************** +! +!IONIZATION +!Ar -> Ar^+ +! 1.576000e+1 +!SPECIES: e / Ar +!PROCESS: E + Ar -> E + E + Ar+, Ionization +!PARAM.: E = 15.76 eV, complete set +!COMMENT: from Magboltz 8.97 Sept 2011. +!UPDATED: 2011-10-20 14:25:01 +!COLUMNS: Energy (eV) | Cross section (m2) +!----------------------------- + 1.576000e+1 0.000000e+0 + 1.576100e+1 1.289600e-24 + 1.577700e+1 2.246400e-23 + 1.579500e+1 4.531800e-23 + 1.581300e+1 6.857100e-23 + 1.583100e+1 9.222900e-23 + 1.585000e+1 1.163000e-22 + 1.586900e+1 1.407900e-22 + 1.588800e+1 1.657000e-22 + 1.590800e+1 1.910500e-22 + 1.592800e+1 2.168500e-22 + 1.594800e+1 2.430900e-22 + 1.596900e+1 2.697900e-22 + 1.599000e+1 2.969500e-22 + 1.601100e+1 3.242500e-22 + 1.603300e+1 3.517200e-22 + 1.605500e+1 3.796700e-22 + 1.607800e+1 4.081100e-22 + 1.610100e+1 4.370500e-22 + 1.612400e+1 4.664800e-22 + 1.614800e+1 4.964300e-22 + 1.617200e+1 5.269100e-22 + 1.619700e+1 5.579100e-22 + 1.622200e+1 5.894500e-22 + 1.624700e+1 6.215400e-22 + 1.627300e+1 6.542000e-22 + 1.630000e+1 6.874200e-22 + 1.632600e+1 7.212200e-22 + 1.635400e+1 7.556000e-22 + 1.638100e+1 7.905900e-22 + 1.641000e+1 8.261900e-22 + 1.643800e+1 8.624000e-22 + 1.646800e+1 8.992500e-22 + 1.649700e+1 9.367400e-22 + 1.652800e+1 9.782000e-22 + 1.655800e+1 1.020700e-21 + 1.659000e+1 1.063900e-21 + 1.662200e+1 1.107900e-21 + 1.665400e+1 1.152700e-21 + 1.668700e+1 1.198200e-21 + 1.672100e+1 1.244600e-21 + 1.675500e+1 1.291700e-21 + 1.679000e+1 1.339700e-21 + 1.682500e+1 1.388500e-21 + 1.686100e+1 1.438100e-21 + 1.689800e+1 1.488600e-21 + 1.693500e+1 1.540000e-21 + 1.697300e+1 1.592300e-21 + 1.701100e+1 1.646200e-21 + 1.705000e+1 1.702700e-21 + 1.709000e+1 1.760200e-21 + 1.713100e+1 1.818600e-21 + 1.717200e+1 1.878100e-21 + 1.721400e+1 1.938600e-21 + 1.725700e+1 2.000200e-21 + 1.730100e+1 2.062800e-21 + 1.734500e+1 2.126600e-21 + 1.739000e+1 2.191400e-21 + 1.743600e+1 2.257400e-21 + 1.748200e+1 2.324500e-21 + 1.753000e+1 2.394600e-21 + 1.757800e+1 2.467000e-21 + 1.762700e+1 2.540600e-21 + 1.767700e+1 2.615600e-21 + 1.772800e+1 2.691800e-21 + 1.778000e+1 2.769300e-21 + 1.783200e+1 2.848300e-21 + 1.788600e+1 2.928500e-21 + 1.794000e+1 3.010200e-21 + 1.799600e+1 3.093300e-21 + 1.805200e+1 3.178900e-21 + 1.810900e+1 3.266100e-21 + 1.816800e+1 3.354800e-21 + 1.822700e+1 3.445000e-21 + 1.828700e+1 3.536800e-21 + 1.834900e+1 3.630200e-21 + 1.841100e+1 3.725300e-21 + 1.847500e+1 3.821900e-21 + 1.854000e+1 3.922700e-21 + 1.860600e+1 4.026700e-21 + 1.867300e+1 4.132600e-21 + 1.874100e+1 4.240300e-21 + 1.881000e+1 4.349800e-21 + 1.888100e+1 4.461300e-21 + 1.895200e+1 4.574700e-21 + 1.902500e+1 4.691200e-21 + 1.910000e+1 4.811500e-21 + 1.917500e+1 4.934000e-21 + 1.925200e+1 5.058600e-21 + 1.933000e+1 5.185400e-21 + 1.941000e+1 5.314400e-21 + 1.949100e+1 5.445600e-21 + 1.957400e+1 5.579100e-21 + 1.965700e+1 5.715000e-21 + 1.974300e+1 5.853200e-21 + 1.983000e+1 5.993800e-21 + 1.991800e+1 6.136900e-21 + 2.000800e+1 6.283200e-21 + 2.009900e+1 6.440500e-21 + 2.019200e+1 6.600400e-21 + 2.028700e+1 6.763200e-21 + 2.038300e+1 6.928800e-21 + 2.048100e+1 7.097300e-21 + 2.058100e+1 7.249400e-21 + 2.068200e+1 7.399400e-21 + 2.078500e+1 7.552100e-21 + 2.089000e+1 7.707400e-21 + 2.099700e+1 7.865500e-21 + 2.110600e+1 8.020000e-21 + 2.121600e+1 8.176900e-21 + 2.132900e+1 8.336600e-21 + 2.144300e+1 8.499100e-21 + 2.155900e+1 8.669200e-21 + 2.167800e+1 8.846900e-21 + 2.179800e+1 9.027600e-21 + 2.192100e+1 9.211600e-21 + 2.204600e+1 9.385900e-21 + 2.217300e+1 9.540700e-21 + 2.230200e+1 9.698200e-21 + 2.243300e+1 9.858500e-21 + 2.256700e+1 1.002800e-20 + 2.270300e+1 1.020800e-20 + 2.284100e+1 1.039000e-20 + 2.298200e+1 1.057600e-20 + 2.312500e+1 1.075000e-20 + 2.327100e+1 1.092500e-20 + 2.341900e+1 1.110300e-20 + 2.357000e+1 1.128400e-20 + 2.372400e+1 1.146800e-20 + 2.388000e+1 1.165600e-20 + 2.403900e+1 1.184600e-20 + 2.420000e+1 1.204000e-20 + 2.436500e+1 1.223800e-20 + 2.453200e+1 1.243800e-20 + 2.470200e+1 1.264300e-20 + 2.487500e+1 1.285000e-20 + 2.505200e+1 1.305200e-20 + 2.523100e+1 1.323100e-20 + 2.541300e+1 1.341300e-20 + 2.559900e+1 1.361900e-20 + 2.578800e+1 1.384500e-20 + 2.598000e+1 1.407600e-20 + 2.617500e+1 1.426600e-20 + 2.637400e+1 1.445500e-20 + 2.657600e+1 1.464800e-20 + 2.678200e+1 1.484300e-20 + 2.699200e+1 1.504200e-20 + 2.720500e+1 1.524500e-20 + 2.742200e+1 1.545000e-20 + 2.764200e+1 1.566000e-20 + 2.786700e+1 1.587300e-20 + 2.809500e+1 1.609500e-20 + 2.832700e+1 1.632700e-20 + 2.856300e+1 1.656300e-20 + 2.880400e+1 1.680400e-20 + 2.904900e+1 1.704900e-20 + 2.929700e+1 1.729700e-20 + 2.955100e+1 1.755100e-20 + 2.980800e+1 1.780800e-20 + 3.007000e+1 1.805600e-20 + 3.033700e+1 1.827000e-20 + 3.060900e+1 1.848700e-20 + 3.088500e+1 1.870800e-20 + 3.116600e+1 1.893200e-20 + 3.145100e+1 1.916100e-20 + 3.203800e+1 1.962800e-20 + 3.264500e+1 2.008400e-20 + 3.327400e+1 2.055500e-20 + 3.392400e+1 2.104300e-20 + 3.459800e+1 2.148900e-20 + 3.529500e+1 2.194200e-20 + 3.601700e+1 2.240700e-20 + 3.714700e+1 2.291600e-20 + 3.833700e+1 2.340100e-20 + 4.002300e+1 2.390500e-20 + 4.278200e+1 2.445600e-20 + 4.584100e+1 2.496700e-20 + 5.170000e+1 2.553800e-20 + 5.573300e+1 2.608800e-20 + 6.020600e+1 2.662900e-20 + 6.430500e+1 2.720300e-20 + 7.067100e+1 2.776700e-20 + 7.895400e+1 2.835800e-20 + 1.263200e+2 2.778400e-20 + 1.428500e+2 2.715800e-20 + 1.544000e+2 2.653600e-20 + 1.643800e+2 2.598100e-20 + 1.750800e+2 2.544600e-20 + 1.865500e+2 2.477400e-20 + 1.956900e+2 2.418000e-20 + 2.053100e+2 2.366700e-20 + 2.189300e+2 2.306700e-20 + 2.297900e+2 2.258900e-20 + 2.412200e+2 2.208600e-20 + 2.532700e+2 2.156900e-20 + 2.659500e+2 2.106200e-20 + 2.793000e+2 2.052800e-20 + 2.933700e+2 1.996500e-20 + 3.081900e+2 1.942200e-20 + 3.237900e+2 1.889100e-20 + 3.402200e+2 1.833200e-20 + 3.516600e+2 1.795700e-20 + 3.695700e+2 1.749100e-20 + 3.884400e+2 1.700100e-20 + 4.015700e+2 1.665900e-20 + 4.151700e+2 1.630600e-20 + 4.292400e+2 1.594000e-20 + 4.438000e+2 1.556100e-20 + 4.588800e+2 1.522200e-20 + 4.744900e+2 1.491000e-20 + 4.906400e+2 1.458700e-20 + 5.246800e+2 1.400500e-20 + 5.611500e+2 1.342200e-20 + 6.002200e+2 1.279700e-20 + 6.421000e+2 1.225300e-20 + 6.869600e+2 1.166900e-20 + 7.350400e+2 1.111500e-20 + 7.865500e+2 1.054800e-20 + 8.417500e+2 1.011200e-20 + 9.008900e+2 9.703500e-21 + 9.642700e+2 9.240800e-21 +!----------------------------- +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/wk/H2_ela_cross_sec.in b/wk/H2_ela_cross_sec.in new file mode 100644 index 0000000..566b005 --- /dev/null +++ b/wk/H2_ela_cross_sec.in @@ -0,0 +1,265 @@ +!LXCat, www.lxcat.net +!Generated on 07 Oct 2021. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on October 7, 2021. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: Transcribed from S.F. Biagi's FORTRAN code Magboltz, version 8.97, Oct 2012 assuming the rotational levels in the ground +! state are in thermal equilibrium at 300K, but neglecting superelastic collisions. Therefore calculations using these +! cross sections should be limited to E/N > 1 Td. We have not transcribed the H2 temperature dependent attachment cross +! section included in MagBoltz. +! +!********************************************************** H2 ********************************************************** +! +!ELASTIC +!H2 +! 2.720000e-4 +!SPECIES: e / H2 +!PROCESS: E + H2 -> E + H2, Elastic +!PARAM.: m/M = 0.000272, complete set +!COMMENT: elastic MOMENTUM-TRANSFER CROSS SECTION. +!UPDATED: 2012-10-11 09:34:55 +!COLUMNS: Energy (eV) | Cross section (m2) +!----------------------------- + 1.000000e-4 7.241000e-20 + 3.514217e-2 8.750300e-20 + 7.151931e-2 9.828000e-20 + 1.091748e-1 1.064900e-19 + 1.481536e-1 1.132000e-19 + 1.885022e-1 1.188200e-19 + 2.302688e-1 1.233500e-19 + 2.735031e-1 1.275600e-19 + 3.182567e-1 1.314800e-19 + 3.645831e-1 1.352300e-19 + 4.125375e-1 1.389900e-19 + 4.621772e-1 1.425100e-19 + 5.135612e-1 1.460700e-19 + 5.667511e-1 1.494700e-19 + 6.218101e-1 1.526900e-19 + 7.378008e-1 1.585300e-19 + 8.620871e-1 1.642500e-19 + 9.952623e-1 1.699000e-19 + 1.137962e+0 1.748600e-19 + 1.371374e+0 1.790000e-19 + 2.126079e+0 1.742300e-19 + 2.349654e+0 1.693100e-19 + 2.589219e+0 1.637700e-19 + 2.845918e+0 1.573500e-19 + 2.981072e+0 1.539700e-19 + 3.120975e+0 1.504800e-19 + 3.265795e+0 1.468600e-19 + 3.415704e+0 1.431100e-19 + 3.570882e+0 1.392300e-19 + 3.731513e+0 1.352100e-19 + 3.897788e+0 1.310600e-19 + 4.069907e+0 1.271400e-19 + 4.248075e+0 1.236600e-19 + 4.432503e+0 1.200700e-19 + 4.623413e+0 1.163400e-19 + 4.821032e+0 1.124900e-19 + 5.025596e+0 1.086300e-19 + 5.237348e+0 1.055600e-19 + 5.456542e+0 1.023800e-19 + 5.683439e+0 9.909000e-20 + 5.918310e+0 9.568500e-20 + 6.161434e+0 9.248200e-20 + 6.413102e+0 8.933600e-20 + 6.673615e+0 8.608000e-20 + 6.943282e+0 8.270900e-20 + 7.222426e+0 7.977600e-20 + 7.511380e+0 7.688600e-20 + 7.810489e+0 7.389500e-20 + 8.120108e+0 7.091900e-20 + 8.440609e+0 6.803500e-20 + 8.772372e+0 6.504900e-20 + 9.115795e+0 6.218900e-20 + 9.471285e+0 5.970100e-20 + 9.839269e+0 5.712500e-20 + 1.022018e+1 5.473400e-20 + 1.061449e+1 5.246700e-20 + 1.102264e+1 5.012000e-20 + 1.144515e+1 4.769000e-20 + 1.188250e+1 4.517600e-20 + 1.233521e+1 4.318700e-20 + 1.280384e+1 4.135200e-20 + 1.328894e+1 3.945200e-20 + 1.379108e+1 3.748500e-20 + 1.431087e+1 3.544900e-20 + 1.484893e+1 3.334200e-20 + 1.540590e+1 3.174100e-20 + 1.598244e+1 3.030700e-20 + 1.657924e+1 2.882300e-20 + 1.719701e+1 2.728700e-20 + 1.783649e+1 2.569700e-20 + 1.849845e+1 2.435500e-20 + 1.918366e+1 2.307100e-20 + 1.989296e+1 2.174100e-20 + 2.062719e+1 2.069000e-20 + 2.138721e+1 1.965900e-20 + 2.217395e+1 1.859200e-20 + 2.298833e+1 1.748800e-20 + 2.383133e+1 1.634500e-20 + 2.470396e+1 1.516100e-20 + 2.560725e+1 1.430300e-20 + 2.654229e+1 1.360000e-20 + 2.751018e+1 1.287200e-20 + 2.851209e+1 1.211900e-20 + 2.954921e+1 1.133900e-20 + 3.062278e+1 1.075200e-20 + 3.173407e+1 1.031000e-20 + 3.288442e+1 9.852000e-21 + 3.407519e+1 9.378100e-21 + 3.530781e+1 8.887500e-21 + 3.658374e+1 8.379700e-21 + 3.790451e+1 7.854000e-21 + 3.927170e+1 7.309900e-21 + 4.068694e+1 6.884700e-21 + 4.215191e+1 6.596100e-21 + 4.366836e+1 6.297300e-21 + 4.523810e+1 5.988100e-21 + 4.686301e+1 5.668000e-21 + 4.854502e+1 5.336600e-21 + 5.028614e+1 5.012800e-21 + 5.208844e+1 4.778500e-21 + 5.395409e+1 4.536000e-21 + 5.588529e+1 4.284900e-21 + 5.788437e+1 4.025000e-21 + 5.995369e+1 3.756000e-21 + 6.209573e+1 3.582300e-21 + 6.431306e+1 3.405000e-21 + 6.660830e+1 3.221300e-21 + 6.898420e+1 3.031300e-21 + 7.144360e+1 2.867700e-21 + 7.398942e+1 2.722600e-21 + 7.662471e+1 2.572400e-21 + 7.935261e+1 2.416900e-21 + 8.217638e+1 2.286400e-21 + 8.509938e+1 2.160700e-21 + 8.812509e+1 2.030600e-21 + 9.125714e+1 1.918600e-21 + 9.449926e+1 1.837500e-21 + 9.785531e+1 1.753600e-21 + 1.013293e+2 1.671300e-21 + 1.049254e+2 1.593600e-21 + 1.086478e+2 1.513200e-21 + 1.125011e+2 1.430000e-21 + 1.164898e+2 1.343800e-21 + 1.206186e+2 1.254600e-21 + 1.248925e+2 1.162300e-21 + 1.293167e+2 1.109600e-21 + 1.338963e+2 1.056100e-21 + 1.386368e+2 1.000700e-21 + 1.435440e+2 9.434100e-22 + 1.486236e+2 8.840800e-22 + 1.538817e+2 8.360200e-22 + 1.593245e+2 7.911700e-22 + 1.649587e+2 7.447400e-22 + 1.707908e+2 6.966800e-22 + 1.768279e+2 6.519100e-22 + 1.830772e+2 6.174100e-22 + 1.895461e+2 5.817100e-22 + 1.962423e+2 5.447400e-22 + 2.031738e+2 5.131500e-22 + 2.103489e+2 4.886100e-22 + 2.177762e+2 4.632100e-22 + 2.254644e+2 4.369100e-22 + 2.334229e+2 4.096900e-22 + 2.416610e+2 3.815200e-22 + 2.501886e+2 3.526300e-22 + 2.590160e+2 3.355100e-22 + 2.681535e+2 3.177800e-22 + 2.776121e+2 2.994300e-22 + 2.874032e+2 2.804400e-22 + 2.975383e+2 2.607800e-22 + 3.080295e+2 2.462000e-22 + 3.188895e+2 2.329500e-22 + 3.301311e+2 2.192400e-22 + 3.417678e+2 2.050400e-22 + 3.538134e+2 1.918700e-22 + 3.662823e+2 1.816500e-22 + 3.791894e+2 1.710600e-22 + 3.925501e+2 1.601100e-22 + 4.063803e+2 1.503000e-22 + 4.206965e+2 1.420000e-22 + 4.355158e+2 1.334000e-22 + 4.508559e+2 1.246200e-22 + 4.667351e+2 1.176400e-22 + 4.831724e+2 1.104000e-22 + 5.001872e+2 1.029500e-22 + 5.178000e+2 9.796300e-23 + 5.360318e+2 9.280300e-23 + 5.549043e+2 8.746200e-23 + 5.744399e+2 8.193300e-23 + 5.946621e+2 7.621100e-23 + 6.155950e+2 7.189300e-23 + 6.372635e+2 6.799300e-23 + 6.596934e+2 6.395500e-23 + 6.829116e+2 5.977600e-23 + 7.069458e+2 5.586000e-23 + 7.318245e+2 5.284900e-23 + 7.575776e+2 4.973300e-23 + 7.842356e+2 4.650700e-23 + 8.118305e+2 4.359400e-23 + 8.403951e+2 4.116600e-23 + 8.699636e+2 3.865300e-23 + 9.005711e+2 3.606500e-23 + 9.322543e+2 3.410000e-23 + 9.650509e+2 3.206700e-23 +!----------------------------- \ No newline at end of file diff --git a/wk/H2_io_cross_sec.in b/wk/H2_io_cross_sec.in new file mode 100644 index 0000000..2a023c3 --- /dev/null +++ b/wk/H2_io_cross_sec.in @@ -0,0 +1,204 @@ +!LXCat, www.lxcat.net +!Generated on 07 Oct 2021. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on October 7, 2021. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: Transcribed from S.F. Biagi's FORTRAN code Magboltz, version 8.97, Oct 2012 assuming the rotational levels in the ground +! state are in thermal equilibrium at 300K, but neglecting superelastic collisions. Therefore calculations using these +! cross sections should be limited to E/N > 1 Td. We have not transcribed the H2 temperature dependent attachment cross +! section included in MagBoltz. +! +!********************************************************** H2 ********************************************************** +! +!IONIZATION +!H2 -> +ionized H2 +! 1.541800e+1 +!SPECIES: e / H2 +!PROCESS: E + H2 -> E + E + +ionized H2, Ionization +!PARAM.: E = 15.418 eV, complete set +!COMMENT: IONISATION. +!UPDATED: 2012-10-11 17:32:03 +!COLUMNS: Energy (eV) | Cross section (m2) +!----------------------------- + 1.541800e+1 0.000000e+0 + 1.541900e+1 5.068700e-26 + 1.545314e+1 1.781300e-23 + 1.548952e+1 3.625100e-23 + 1.552718e+1 5.533800e-23 + 1.556615e+1 7.509500e-23 + 1.560650e+1 9.554700e-23 + 1.564827e+1 1.167200e-22 + 1.569150e+1 1.386300e-22 + 1.573626e+1 1.613200e-22 + 1.578258e+1 1.848000e-22 + 1.583054e+1 2.091000e-22 + 1.588018e+1 2.342700e-22 + 1.593156e+1 2.603100e-22 + 1.598475e+1 2.872700e-22 + 1.603981e+1 3.191300e-22 + 1.609680e+1 3.536600e-22 + 1.615580e+1 3.894200e-22 + 1.621687e+1 4.264200e-22 + 1.628009e+1 4.647300e-22 + 1.634553e+1 5.043900e-22 + 1.641326e+1 5.454400e-22 + 1.648338e+1 5.879300e-22 + 1.655596e+1 6.329200e-22 + 1.663109e+1 6.798000e-22 + 1.670887e+1 7.283300e-22 + 1.678937e+1 7.785700e-22 + 1.687271e+1 8.305700e-22 + 1.695897e+1 8.844000e-22 + 1.704827e+1 9.389600e-22 + 1.714070e+1 9.944200e-22 + 1.723638e+1 1.051800e-21 + 1.733543e+1 1.111300e-21 + 1.743795e+1 1.172800e-21 + 1.754408e+1 1.239100e-21 + 1.765394e+1 1.311600e-21 + 1.776765e+1 1.386700e-21 + 1.788537e+1 1.464300e-21 + 1.800722e+1 1.544300e-21 + 1.813335e+1 1.620000e-21 + 1.826392e+1 1.698400e-21 + 1.839907e+1 1.779400e-21 + 1.853898e+1 1.865700e-21 + 1.868380e+1 1.961300e-21 + 1.883370e+1 2.060200e-21 + 1.898888e+1 2.162700e-21 + 1.914951e+1 2.253700e-21 + 1.931579e+1 2.346800e-21 + 1.948791e+1 2.443200e-21 + 1.966607e+1 2.553000e-21 + 1.985050e+1 2.667300e-21 + 2.004141e+1 2.784000e-21 + 2.023903e+1 2.898600e-21 + 2.044360e+1 3.017300e-21 + 2.065535e+1 3.130800e-21 + 2.087454e+1 3.244800e-21 + 2.110144e+1 3.362700e-21 + 2.133631e+1 3.484900e-21 + 2.157943e+1 3.612900e-21 + 2.183110e+1 3.748800e-21 + 2.209162e+1 3.882100e-21 + 2.236128e+1 4.006200e-21 + 2.264043e+1 4.143000e-21 + 2.292938e+1 4.293300e-21 + 2.322849e+1 4.426000e-21 + 2.353811e+1 4.557500e-21 + 2.385861e+1 4.705000e-21 + 2.419037e+1 4.850000e-21 + 2.453379e+1 4.992200e-21 + 2.488929e+1 5.120100e-21 + 2.525727e+1 5.262900e-21 + 2.563818e+1 5.412500e-21 + 2.603249e+1 5.561000e-21 + 2.644064e+1 5.699800e-21 + 2.686315e+1 5.843500e-21 + 2.730050e+1 5.992200e-21 + 2.775321e+1 6.146100e-21 + 2.822184e+1 6.291000e-21 + 2.870694e+1 6.424400e-21 + 2.920908e+1 6.562500e-21 + 2.972887e+1 6.705400e-21 + 3.026693e+1 6.842700e-21 + 3.082390e+1 6.973600e-21 + 3.199724e+1 7.249400e-21 + 3.325449e+1 7.500900e-21 + 3.460166e+1 7.755300e-21 + 3.604519e+1 8.006300e-21 + 3.759195e+1 8.222900e-21 + 3.924933e+1 8.436200e-21 + 4.102525e+1 8.626400e-21 + 4.292818e+1 8.805200e-21 + 4.496721e+1 8.996900e-21 + 4.949319e+1 9.242600e-21 + 5.468970e+1 9.438800e-21 + 9.477061e+1 9.206400e-21 + 1.132733e+2 8.821400e-21 + 1.279191e+2 8.445800e-21 + 1.447347e+2 8.094800e-21 + 1.640416e+2 7.656900e-21 + 1.803767e+2 7.242800e-21 + 1.984952e+2 6.898600e-21 + 2.185918e+2 6.557700e-21 + 2.408824e+2 6.195900e-21 + 2.570790e+2 5.936700e-21 + 2.744340e+2 5.659100e-21 + 3.028212e+2 5.340100e-21 + 3.234475e+2 5.121500e-21 + 3.455491e+2 4.887200e-21 + 3.692314e+2 4.682300e-21 + 3.946074e+2 4.474200e-21 + 4.217983e+2 4.260000e-21 + 4.509338e+2 4.035300e-21 + 4.821531e+2 3.879200e-21 + 5.156052e+2 3.677600e-21 + 5.514498e+2 3.423900e-21 + 5.898579e+2 3.262600e-21 + 6.310130e+2 3.127000e-21 + 6.751114e+2 2.969600e-21 + 7.223638e+2 2.820800e-21 + 7.729956e+2 2.691000e-21 + 8.272485e+2 2.555500e-21 + 8.853816e+2 2.425100e-21 + 9.476723e+2 2.266100e-21 +!----------------------------- \ No newline at end of file diff --git a/wk/He_ela_cross_sec.in b/wk/He_ela_cross_sec.in new file mode 100644 index 0000000..20c9d2b --- /dev/null +++ b/wk/He_ela_cross_sec.in @@ -0,0 +1,115 @@ +!LXCat, www.lxcat.net +!Generated on 17 Feb 2022. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on February 17, 2022. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: Transcribed from S.F. Biagi's FORTRAN code Magboltz, version 8.97, Sept 2011. +! Based in part on K Bartschat 1998 and Ralchenko et al 2008 (after 30 eV). +! +!********************************************************** He ********************************************************** +! +!ELASTIC +!He +! 1.360000e-4 +!SPECIES: e / He +!PROCESS: E + He -> E + He, Elastic +!PARAM.: m/M = 0.000136, complete set +!COMMENT: From Biagi's elastic momentum transfer. +!UPDATED: 2015-03-12 13:44:44 +!COLUMNS: Energy (eV) | Cross section (m2) +!----------------------------- + 0.000000e+0 4.903500e-20 + 1.000000e-4 4.903500e-20 + 3.514000e-2 5.501140e-20 + 7.152000e-2 5.747600e-20 + 1.091700e-1 5.896700e-20 + 1.371370e+0 6.938560e-20 + 5.918310e+0 6.025320e-20 + 8.440610e+0 5.209010e-20 + 1.102264e+1 4.444340e-20 + 1.379108e+1 3.734320e-20 + 1.657924e+1 3.184430e-20 + 1.918366e+1 2.766530e-20 + 2.217395e+1 2.383470e-20 + 2.560725e+1 1.998990e-20 + 2.954921e+1 1.667870e-20 + 3.407519e+1 1.385490e-20 + 3.790451e+1 1.190580e-20 + 4.215191e+1 1.024370e-20 + 4.686301e+1 8.806780e-21 + 5.588529e+1 6.702370e-21 + 6.660830e+1 5.073960e-21 + 7.935261e+1 3.808260e-21 + 9.449926e+1 2.878530e-21 + 1.125011e+2 2.204960e-21 + 1.338963e+2 1.622750e-21 + 1.593245e+2 1.224330e-21 + 1.895461e+2 9.142570e-22 + 2.254644e+2 6.756040e-22 + 2.681535e+2 4.949030e-22 + 3.188895e+2 3.701550e-22 + 3.791894e+2 2.748810e-22 + 4.508559e+2 2.023320e-22 + 5.360318e+2 1.477860e-22 + 6.372635e+2 1.101690e-22 + 7.575776e+2 8.165410e-23 + 9.005711e+2 5.983320e-23 +!----------------------------- diff --git a/wk/He_io_cross_sec.in b/wk/He_io_cross_sec.in new file mode 100644 index 0000000..773ed5e --- /dev/null +++ b/wk/He_io_cross_sec.in @@ -0,0 +1,186 @@ +!LXCat, www.lxcat.net +!Generated on 17 Feb 2022. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on February 17, 2022. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: Transcribed from S.F. Biagi's FORTRAN code Magboltz, version 8.97, Sept 2011. +! Based in part on K Bartschat 1998 and Ralchenko et al 2008 (after 30 eV). +! +!********************************************************** He ********************************************************** +! +! IONIZATION +! He -> He^+ +! 2.458740e+1 +! SPECIES: e / He +! PROCESS: E + He -> E + E + He+, Ionization +! PARAM.: E = 24.5874 eV, complete set +! UPDATED: 2011-10-21 15:05:18 +! COLUMNS: Energy (eV) | Cross section (m2) +! ----------------------------- + 2.458739e+1 0.000000e+0 + 2.458800e+1 9.936770e-27 + 2.462253e+1 3.491990e-24 + 2.465891e+1 7.106710e-24 + 2.469657e+1 1.084840e-23 + 2.473554e+1 1.472170e-23 + 2.477589e+1 1.873100e-23 + 2.481766e+1 2.288130e-23 + 2.486089e+1 2.717740e-23 + 2.490565e+1 3.162440e-23 + 2.495197e+1 3.622780e-23 + 2.499993e+1 4.099290e-23 + 2.504957e+1 4.694820e-23 + 2.510095e+1 5.311430e-23 + 2.515414e+1 5.949700e-23 + 2.520920e+1 6.610410e-23 + 2.526619e+1 7.294340e-23 + 2.532519e+1 8.002300e-23 + 2.538626e+1 8.735140e-23 + 2.544948e+1 9.493740e-23 + 2.551492e+1 1.029390e-22 + 2.558265e+1 1.117450e-22 + 2.565277e+1 1.208600e-22 + 2.572535e+1 1.302960e-22 + 2.580049e+1 1.400630e-22 + 2.587826e+1 1.501740e-22 + 2.595876e+1 1.606390e-22 + 2.604210e+1 1.716410e-22 + 2.612836e+1 1.832010e-22 + 2.621766e+1 1.951660e-22 + 2.631009e+1 2.075520e-22 + 2.640577e+1 2.203740e-22 + 2.650482e+1 2.336360e-22 + 2.660734e+1 2.471690e-22 + 2.671347e+1 2.611780e-22 + 2.682333e+1 2.756790e-22 + 2.693705e+1 2.906900e-22 + 2.705476e+1 3.056810e-22 + 2.717661e+1 3.205460e-22 + 2.730274e+1 3.359350e-22 + 2.743331e+1 3.518640e-22 + 2.756846e+1 3.680790e-22 + 2.770837e+1 3.845870e-22 + 2.785319e+1 4.016760e-22 + 2.800310e+1 4.193590e-22 + 2.815827e+1 4.373600e-22 + 2.831890e+1 4.559930e-22 + 2.848518e+1 4.752810e-22 + 2.865730e+1 4.965050e-22 + 2.883547e+1 5.185980e-22 + 2.901989e+1 5.414270e-22 + 2.921080e+1 5.647180e-22 + 2.940842e+1 5.888280e-22 + 2.961299e+1 6.124290e-22 + 2.982474e+1 6.357210e-22 + 3.004393e+1 6.601840e-22 + 3.027083e+1 6.869580e-22 + 3.050570e+1 7.146730e-22 + 3.074883e+1 7.433610e-22 + 3.100049e+1 7.730530e-22 + 3.126101e+1 8.011890e-22 + 3.153067e+1 8.304970e-22 + 3.180982e+1 8.623190e-22 + 3.209877e+1 8.950620e-22 + 3.239788e+1 9.285620e-22 + 3.270750e+1 9.624100e-22 + 3.302800e+1 9.971360e-22 + 3.335976e+1 1.034290e-21 + 3.370319e+1 1.070320e-21 + 3.405868e+1 1.105870e-21 + 3.442666e+1 1.142670e-21 + 3.480758e+1 1.180760e-21 + 3.520188e+1 1.220190e-21 + 3.561004e+1 1.261000e-21 + 3.603254e+1 1.302930e-21 + 3.646989e+1 1.342290e-21 + 3.692261e+1 1.383030e-21 + 3.739123e+1 1.425210e-21 + 3.787633e+1 1.468870e-21 + 3.837847e+1 1.512170e-21 + 3.889827e+1 1.556350e-21 + 3.943632e+1 1.602090e-21 + 3.999329e+1 1.649430e-21 + 4.056983e+1 1.691030e-21 + 4.116663e+1 1.734000e-21 + 4.178440e+1 1.778480e-21 + 4.242388e+1 1.824520e-21 + 4.308584e+1 1.872180e-21 + 4.377105e+1 1.921520e-21 + 4.448035e+1 1.972590e-21 + 4.521458e+1 2.024160e-21 + 4.597460e+1 2.074320e-21 + 4.676134e+1 2.126250e-21 + 4.757572e+1 2.180000e-21 + 4.841872e+1 2.235640e-21 + 5.632146e+1 2.645500e-21 + 7.000000e+1 3.130000e-21 + 8.000000e+1 3.320000e-21 + 9.000000e+1 3.440000e-21 + 1.000000e+2 3.510000e-21 + 1.500000e+2 3.460000e-21 + 2.000000e+2 3.240000e-21 + 3.000000e+2 2.900000e-21 + 4.309677e+2 2.362320e-21 + 5.000000e+2 2.200000e-21 + 6.401824e+2 1.811600e-21 + 9.568417e+2 1.357680e-21 +!----------------------------- +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ No newline at end of file diff --git a/wk/Ne_ela_cross_sec.in b/wk/Ne_ela_cross_sec.in new file mode 100644 index 0000000..60d84b4 --- /dev/null +++ b/wk/Ne_ela_cross_sec.in @@ -0,0 +1,235 @@ +!LXCat, www.lxcat.net +!Generated on 01 Jun 2021. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on June 1, 2021. +!- BSR database, www.lxcat.net, retrieved on June 1, 2021. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: From Biagi's Magboltz v8.9. +! +!********************************************************** Ne ********************************************************** +! +!ELASTIC +!Ne +! 2.715000e-5 +!SPECIES: e / Ne +!PROCESS: E + Ne -> E + Ne, Elastic +!PARAM.: m/M = 0.00002715, complete set +!COMMENT: Extracted from MagBoltz version 8.9. Note that the energy resolution in the LXCat tables +!COMMENT: for energy < 1 eV is less than for the original data. +!UPDATED: 2014-10-03 16:41:16 +!COLUMNS: Energy (eV) | Cross section (m2) +!----------------------------- + 0.000000e+0 1.742650e-21 + 1.000000e-4 1.742650e-21 + 3.514000e-2 4.681770e-21 + 7.152000e-2 6.159750e-21 + 1.091700e-1 7.328590e-21 + 1.481500e-1 8.322300e-21 + 1.885000e-1 9.195310e-21 + 2.302700e-1 9.976620e-21 + 2.735000e-1 1.068410e-20 + 3.182600e-1 1.132980e-20 + 3.645800e-1 1.192230e-20 + 4.125400e-1 1.246820e-20 + 4.621800e-1 1.297250e-20 + 5.135600e-1 1.343950e-20 + 5.667500e-1 1.387240e-20 + 6.218100e-1 1.427440e-20 + 6.788000e-1 1.464780e-20 + 7.378000e-1 1.499500e-20 + 7.988700e-1 1.531790e-20 + 8.620900e-1 1.561860e-20 + 9.275200e-1 1.589860e-20 + 9.952600e-1 1.615980e-20 + 1.065380e+0 1.641400e-20 + 1.137960e+0 1.667600e-20 + 1.213090e+0 1.692620e-20 + 1.371370e+0 1.724270e-20 + 1.540970e+0 1.755460e-20 + 1.722700e+0 1.779690e-20 + 1.917430e+0 1.807610e-20 + 2.126080e+0 1.830090e-20 + 2.467370e+0 1.857390e-20 + 2.715350e+0 1.881540e-20 + 2.981070e+0 1.908110e-20 + 3.265800e+0 1.928610e-20 + 3.570880e+0 1.949960e-20 + 3.897790e+0 1.972850e-20 + 4.248070e+0 2.002330e-20 + 4.623410e+0 2.036110e-20 + 5.025600e+0 2.071790e-20 + 5.456540e+0 2.101960e-20 + 5.918310e+0 2.134280e-20 + 6.413100e+0 2.168920e-20 + 6.943280e+0 2.206030e-20 + 7.511380e+0 2.250910e-20 + 7.810490e+0 2.274840e-20 + 8.120110e+0 2.300150e-20 + 8.440610e+0 2.327230e-20 + 8.772370e+0 2.354300e-20 + 9.115790e+0 2.378110e-20 + 9.471290e+0 2.402990e-20 + 9.839270e+0 2.428750e-20 + 1.022018e+1 2.455410e-20 + 1.061449e+1 2.483010e-20 + 1.102264e+1 2.511310e-20 + 1.188250e+1 2.560910e-20 + 1.233521e+1 2.587030e-20 + 1.280384e+1 2.614070e-20 + 1.328894e+1 2.642050e-20 + 1.431087e+1 2.685390e-20 + 1.540590e+1 2.723530e-20 + 1.657924e+1 2.761790e-20 + 1.783649e+1 2.790180e-20 + 1.918366e+1 2.820600e-20 + 3.790451e+1 2.792570e-20 + 4.215191e+1 2.736960e-20 + 4.366836e+1 2.706630e-20 + 4.523810e+1 2.675240e-20 + 4.686301e+1 2.642740e-20 + 4.854502e+1 2.609100e-20 + 5.028614e+1 2.571990e-20 + 5.208844e+1 2.521520e-20 + 5.395409e+1 2.469290e-20 + 5.588529e+1 2.415210e-20 + 5.788437e+1 2.359240e-20 + 5.995369e+1 2.301300e-20 + 6.209573e+1 2.262280e-20 + 6.431306e+1 2.222360e-20 + 6.660830e+1 2.181050e-20 + 6.898420e+1 2.138280e-20 + 7.144360e+1 2.101440e-20 + 7.398942e+1 2.068710e-20 + 7.662471e+1 2.034830e-20 + 7.935261e+1 1.978860e-20 + 8.217638e+1 1.917470e-20 + 8.509938e+1 1.853930e-20 + 8.812509e+1 1.788150e-20 + 9.125714e+1 1.720060e-20 + 9.449926e+1 1.649580e-20 + 9.785531e+1 1.576620e-20 + 1.013293e+2 1.515820e-20 + 1.049254e+2 1.477460e-20 + 1.086478e+2 1.437760e-20 + 1.125011e+2 1.396660e-20 + 1.164898e+2 1.354110e-20 + 1.206186e+2 1.310070e-20 + 1.248925e+2 1.264480e-20 + 1.293167e+2 1.217290e-20 + 1.338963e+2 1.174930e-20 + 1.386368e+2 1.132270e-20 + 1.435440e+2 1.088100e-20 + 1.486236e+2 1.042390e-20 + 1.538817e+2 1.004770e-20 + 1.593245e+2 9.693900e-21 + 1.649587e+2 9.327680e-21 + 1.707908e+2 8.962040e-21 + 1.768279e+2 8.672260e-21 + 1.830772e+2 8.372290e-21 + 1.895461e+2 8.061790e-21 + 1.962423e+2 7.740370e-21 + 2.031738e+2 7.451460e-21 + 2.103489e+2 7.206070e-21 + 2.177762e+2 6.952050e-21 + 2.254644e+2 6.689120e-21 + 2.334229e+2 6.416940e-21 + 2.416610e+2 6.135190e-21 + 2.501886e+2 5.845810e-21 + 2.590160e+2 5.649850e-21 + 2.681535e+2 5.446990e-21 + 2.776121e+2 5.237010e-21 + 2.874031e+2 5.019650e-21 + 2.975383e+2 4.794650e-21 + 3.080295e+2 4.597070e-21 + 3.188895e+2 4.403770e-21 + 3.301311e+2 4.203670e-21 + 3.417678e+2 3.996530e-21 + 3.538134e+2 3.801190e-21 + 3.662823e+2 3.641590e-21 + 3.791894e+2 3.476380e-21 + 3.925501e+2 3.305360e-21 + 4.063803e+2 3.154490e-21 + 4.206965e+2 3.029940e-21 + 4.355158e+2 2.901010e-21 + 4.508559e+2 2.767550e-21 + 4.667351e+2 2.629400e-21 + 4.831724e+2 2.486400e-21 + 5.001872e+2 2.338990e-21 + 5.178000e+2 2.243880e-21 + 5.360318e+2 2.145430e-21 + 5.549043e+2 2.043520e-21 + 5.744399e+2 1.938020e-21 + 5.946621e+2 1.828820e-21 + 6.155950e+2 1.742300e-21 + 6.372635e+2 1.662130e-21 + 6.596934e+2 1.579130e-21 + 6.829117e+2 1.493230e-21 + 7.069458e+2 1.411940e-21 + 7.318245e+2 1.347260e-21 + 7.575776e+2 1.280300e-21 + 7.842356e+2 1.210990e-21 + 8.118305e+2 1.147170e-21 + 8.403951e+2 1.092040e-21 + 8.699636e+2 1.034970e-21 + 9.005711e+2 9.761600e-22 + 9.322543e+2 9.295860e-22 + 9.650509e+2 8.813750e-22 +!----------------------------- diff --git a/wk/Ne_io_cross_sec.in b/wk/Ne_io_cross_sec.in new file mode 100644 index 0000000..418e7d2 --- /dev/null +++ b/wk/Ne_io_cross_sec.in @@ -0,0 +1,278 @@ +!LXCat, www.lxcat.net +!Generated on 01 Jun 2021. All rights reserved. +! +!RECOMMENDED REFERENCE FORMAT +!- Biagi database, www.lxcat.net, retrieved on June 1, 2021. +!- BSR database, www.lxcat.net, retrieved on June 1, 2021. +!Be aware that some databases and solvers can additionally have instructions how to reference corresponding data. +!Please check below in the headers of databases. +! +!CROSS SECTION DATA FORMAT +!In downloaded files, each collision process is defined by a block consisting of +!1st line +!Keyword in capitals indicating the type of the collision. Possible collision types are elastic, effective, excitation, +!ionization, or attachment (capital letters required, key words are case sensitive), where "elastic" is used to denote +!the elastic momentum transfer cross section and where "effective" denotes the total momentum transfer cross section (sum +!of elastic momentum transfer and total inelastic cross sections). The latter is useful for solving the Boltzmann +!equation in the 2-term approximation. +!2nd line +!Name of the target particle species. This name is a character string, freely chosen by the user, e.g. "Ar". Optionally +!for excitation processes, the name of the corresponding excited state can be specified on the same line, separated from +!the first name either by arrow "->" (dash + greater than) or by double-head arrow "<->" (less than + dash + +!greater than), e.g. "Ar -> Ar*" and "Ar <-> Ar*", respectively. In the later case BOLSIG+ will automatically +!define the inverse superelastic process, constructing the superelastic cross-section by detailed balancing, and +!considering the indicated excited state as the target. In this case, the ratio of statistical weights must be input in +!the 3rd line (see below). +!3rd line +!For elastic and effective collisions, the ratio of the electron mass to the target particle mass. For excitation or +!ionization collisions, the electron energy loss (nominally the threshold energy) in eV. For attachment, the 3rd line is +!missing. In case of an excitation process where an excited state has been indicated on the 2nd line using double-head +!arrow "<->", the 3rd line must specify also ratio of the statistical weights of the final state to the initial state +!as the second parameter in 3rd line this is needed by BOLSIG+ to calculate the de-excitation cross-section. The +!statistical weight ratio, if given, will also be used by the automatic superelastics option in BOLSIG+. If this ratio is +!not provided then BOLSIG+ will assume it unity. +!from 4th line (optionally) +!User comments and reference information, maximum 100 lines. The only constraint on format is that these comment lines +!must not start with a number. +!Finally +!Table of the cross section as a function of energy. The table starts and ends by a line of dashes "------" (at least 5), +!and has otherwise two numbers per line: the energy in eV and the cross section in m2. +! +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +!DATABASE: Biagi (transcription of data from SF Biagi's Fortran code, Magboltz.) +!PERMLINK: www.lxcat.net/Biagi +!DESCRIPTION: These data were transcribed from S.F. Biagi's FORTRAN code, MagBoltz, versions 8.9 and later. The +! exact version number is given for each species. At this time, the Biagi database contains cross +! sections for rare gases, for a few simple molecules, and for SF6. The transcription of cross sections +! for other gases is in progress. These data were compiled for use a Monte Carlo simulation (or +! multi-term Boltzmann code), but their use in a 2-term Boltzmann solver gives reasonably accurate +! results in most cases. The the LXCat data tables do not always have the same energy resolution as the +! original data in the MagBoltz code. This limited energy resolution can introduce small (1%) +! differences in the calculated drift velocity at low E/N. +! For history and detailed notes, see http://consult.cern.ch/writeup/magboltz/ +! +! Oct 2011 : update of Ar and Kr data taken from MagBoltz version 8.97 Nov 2011 : small changes in Xe +! ionization cross sections, taken from MagBoltz v8.97 +! March 2015: cross sections for SF6 were transcribed from Magboltz version 10.6 from February 2014. +!CONTACT: email Stephen.biagi@@hotmail.co.uk +!HOW TO REFERENCE: Fortran program, MAGBOLTZ, S.F. Biagi. The version number of MAGBOLTZ from which the cross sections +! were transcribed is provided in the comment for each species. This version number must be included in +! the reference. +!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +! +!************************************************************************************************************************ +! +!COMMENT: From Biagi's Magboltz v8.9. +! +!********************************************************** Ne ********************************************************** +!IONIZATION +!Ne -> Ne^+ +! 2.156500e+1 +!SPECIES: e / Ne +!PROCESS: E + Ne -> E + E + Ne+, Ionization +!PARAM.: E = 21.565 eV, complete set +!UPDATED: 2010-11-26 13:30:45 +!COLUMNS: Energy (eV) | Cross section (m2) +!----------------------------- + 2.156454e+1 0.000000e+0 + 2.156600e+1 7.118920e-27 + 2.159968e+1 2.501740e-24 + 2.163606e+1 5.091400e-24 + 2.167372e+1 7.772070e-24 + 2.171269e+1 1.054690e-23 + 2.175304e+1 1.341930e-23 + 2.179481e+1 1.639270e-23 + 2.183804e+1 1.947050e-23 + 2.188280e+1 2.265650e-23 + 2.192912e+1 2.595440e-23 + 2.197708e+1 2.936820e-23 + 2.202672e+1 3.388550e-23 + 2.207810e+1 3.943500e-23 + 2.213129e+1 4.517950e-23 + 2.218635e+1 5.112590e-23 + 2.224334e+1 5.728120e-23 + 2.230234e+1 6.365290e-23 + 2.236341e+1 7.024850e-23 + 2.242663e+1 7.707580e-23 + 2.249207e+1 8.414310e-23 + 2.255980e+1 9.145870e-23 + 2.262992e+1 9.903150e-23 + 2.270250e+1 1.068700e-22 + 2.277764e+1 1.149850e-22 + 2.285541e+1 1.233840e-22 + 2.293591e+1 1.320790e-22 + 2.301925e+1 1.410400e-22 + 2.310551e+1 1.501840e-22 + 2.319481e+1 1.596500e-22 + 2.328724e+1 1.694480e-22 + 2.338292e+1 1.795900e-22 + 2.348197e+1 1.900890e-22 + 2.358449e+1 2.014630e-22 + 2.369062e+1 2.133490e-22 + 2.380048e+1 2.256530e-22 + 2.391420e+1 2.383900e-22 + 2.403191e+1 2.517010e-22 + 2.415376e+1 2.658360e-22 + 2.427989e+1 2.804680e-22 + 2.441046e+1 2.956130e-22 + 2.454561e+1 3.111090e-22 + 2.468552e+1 3.267780e-22 + 2.483034e+1 3.429980e-22 + 2.498025e+1 3.597870e-22 + 2.513542e+1 3.768970e-22 + 2.529605e+1 3.945660e-22 + 2.546233e+1 4.128560e-22 + 2.563445e+1 4.323270e-22 + 2.581262e+1 4.526380e-22 + 2.599704e+1 4.736630e-22 + 2.618795e+1 4.988100e-22 + 2.638557e+1 5.248960e-22 + 2.659014e+1 5.511770e-22 + 2.680189e+1 5.774340e-22 + 2.702108e+1 6.046140e-22 + 2.724798e+1 6.327490e-22 + 2.748285e+1 6.618730e-22 + 2.772597e+1 6.920210e-22 + 2.797764e+1 7.232280e-22 + 2.823816e+1 7.548170e-22 + 2.850782e+1 7.874470e-22 + 2.878697e+1 8.212230e-22 + 2.907592e+1 8.564140e-22 + 2.937503e+1 8.935040e-22 + 2.968465e+1 9.318960e-22 + 3.000515e+1 9.716410e-22 + 3.033691e+1 1.012950e-21 + 3.068034e+1 1.055700e-21 + 3.103583e+1 1.099960e-21 + 3.140381e+1 1.145770e-21 + 3.178473e+1 1.193200e-21 + 3.217903e+1 1.242380e-21 + 3.258719e+1 1.293400e-21 + 3.300969e+1 1.346210e-21 + 3.344704e+1 1.400880e-21 + 3.389976e+1 1.457470e-21 + 3.436838e+1 1.512360e-21 + 3.485348e+1 1.568150e-21 + 3.535562e+1 1.625900e-21 + 3.587542e+1 1.685670e-21 + 3.641347e+1 1.748580e-21 + 3.697044e+1 1.814030e-21 + 3.754698e+1 1.881770e-21 + 3.814378e+1 1.951890e-21 + 3.876155e+1 2.024480e-21 + 3.940103e+1 2.099620e-21 + 4.006299e+1 2.176550e-21 + 4.074820e+1 2.247810e-21 + 4.145750e+1 2.321580e-21 + 4.219173e+1 2.397940e-21 + 4.295175e+1 2.476980e-21 + 4.373849e+1 2.558800e-21 + 4.455287e+1 2.643500e-21 + 4.539587e+1 2.731960e-21 + 4.626850e+1 2.824460e-21 + 4.717179e+1 2.920210e-21 + 4.810683e+1 3.019320e-21 + 4.907472e+1 3.121920e-21 + 5.007663e+1 3.227660e-21 + 5.111375e+1 3.331380e-21 + 5.218732e+1 3.438730e-21 + 5.329861e+1 3.549860e-21 + 5.444896e+1 3.664900e-21 + 5.563973e+1 3.773740e-21 + 5.687235e+1 3.877280e-21 + 5.814828e+1 3.984460e-21 + 5.946906e+1 4.095400e-21 + 6.083624e+1 4.206900e-21 + 6.225148e+1 4.320120e-21 + 6.371645e+1 4.437320e-21 + 6.523290e+1 4.556770e-21 + 6.680264e+1 4.669790e-21 + 6.842755e+1 4.786780e-21 + 7.010956e+1 4.906790e-21 + 7.185068e+1 5.014740e-21 + 7.365299e+1 5.126490e-21 + 7.551863e+1 5.239040e-21 + 7.744983e+1 5.347190e-21 + 7.944891e+1 5.459140e-21 + 8.151823e+1 5.564390e-21 + 8.366028e+1 5.669350e-21 + 8.587760e+1 5.778000e-21 + 8.817284e+1 5.890470e-21 + 9.054874e+1 6.000300e-21 + 9.300814e+1 6.091300e-21 + 9.555396e+1 6.185500e-21 + 9.818925e+1 6.283000e-21 + 1.009172e+2 6.379350e-21 + 1.037409e+2 6.469710e-21 + 1.066639e+2 6.563250e-21 + 1.096896e+2 6.660070e-21 + 1.128217e+2 6.734900e-21 + 1.160638e+2 6.809470e-21 + 1.194198e+2 6.886660e-21 + 1.228938e+2 6.949200e-21 + 1.264899e+2 7.010330e-21 + 1.302124e+2 7.072970e-21 + 1.340656e+2 7.126920e-21 + 1.380543e+2 7.182760e-21 + 1.421831e+2 7.240560e-21 + 1.464571e+2 7.300400e-21 + 1.508812e+2 7.359690e-21 + 1.554608e+2 7.410070e-21 + 1.602014e+2 7.460200e-21 + 1.651085e+2 7.465110e-21 + 1.701881e+2 7.470190e-21 + 1.754462e+2 7.475450e-21 + 1.808891e+2 7.478220e-21 + 1.865232e+2 7.466950e-21 + 1.923554e+2 7.457640e-21 + 1.983925e+2 7.451610e-21 + 2.046417e+2 7.435150e-21 + 2.111106e+2 7.414450e-21 + 2.178068e+2 7.393020e-21 + 2.247383e+2 7.370840e-21 + 2.319134e+2 7.331280e-21 + 2.393407e+2 7.289690e-21 + 2.470290e+2 7.246640e-21 + 2.549874e+2 7.190100e-21 + 2.632255e+2 7.124200e-21 + 2.717532e+2 7.055970e-21 + 2.805805e+2 6.983120e-21 + 2.897180e+2 6.906370e-21 + 2.991767e+2 6.826920e-21 + 3.089677e+2 6.759020e-21 + 3.191028e+2 6.690100e-21 + 3.295941e+2 6.618760e-21 + 3.404540e+2 6.544910e-21 + 3.516957e+2 6.466100e-21 + 3.633323e+2 6.370670e-21 + 3.753779e+2 6.271900e-21 + 3.878468e+2 6.169660e-21 + 4.007539e+2 6.065630e-21 + 4.141146e+2 5.988140e-21 + 4.279448e+2 5.907920e-21 + 4.422610e+2 5.824890e-21 + 4.570804e+2 5.738930e-21 + 4.724205e+2 5.649960e-21 + 4.882997e+2 5.557860e-21 + 5.047369e+2 5.467260e-21 + 5.217518e+2 5.385590e-21 + 5.393646e+2 5.301050e-21 + 5.575963e+2 5.213540e-21 + 5.764688e+2 5.122950e-21 + 5.960045e+2 5.029180e-21 + 6.162267e+2 4.938600e-21 + 6.371595e+2 4.846500e-21 + 6.588280e+2 4.751160e-21 + 6.812580e+2 4.652460e-21 + 7.044762e+2 4.553440e-21 + 7.285103e+2 4.464510e-21 + 7.533891e+2 4.372460e-21 + 7.791421e+2 4.277170e-21 + 8.058002e+2 4.180860e-21 + 8.333951e+2 4.089800e-21 + 8.619597e+2 3.995530e-21 + 8.915281e+2 3.897960e-21 + 9.221357e+2 3.816870e-21 + 9.538188e+2 3.740830e-21 + 9.866154e+2 3.662120e-21 +!----------------------------- \ No newline at end of file