diff options
Diffstat (limited to 'openEMS/matlab')
116 files changed, 14008 insertions, 0 deletions
diff --git a/openEMS/matlab/AR_estimate.m b/openEMS/matlab/AR_estimate.m new file mode 100644 index 0000000..ab6e293 --- /dev/null +++ b/openEMS/matlab/AR_estimate.m @@ -0,0 +1,115 @@ +function [val_ar t_ar f_val_ar EC] = AR_estimate( t, val, freq, nu, mu, expand_factor) +% [val_ar t_ar f_val_ar EC] = AR_estimate( t, val, freq, < nu, mu, expand_factor >) +% +% apply autoregressive signal model to improve dft results +% +% t : time vector +% val : time domain values +% freq : frequency vector for dft +% +% optional +% nu : AR order (default 40) +% mu : number of timesteps to train the model (default 3*nu) +% expand_factor : increase signal length by this factor (default 5) +% +% return values: +% val_ar: AR estimated time signal +% t_ar: time vector +% f_val_ar: FD transformed AR estimated signal +% EC: error code +% 0 --> no error +% 1 --> input error: t and val mismatch +% 2 --> input error: mu has to be larger than 2*nu +% 3 --> inout error: expand_factor has to be larger than 1 +% 10 --> AR error: signal is to short for AR estimate --> decrease AR order +% 11 --> AR error: estimated signal appears to be unstable --> use a different mu +% +% openEMS matlab interface +% ----------------------- +% Author: Thorsten Liebig, 2011 +% +% See also ReadUI, DFT_time2freq + +EC = 0; +val_ar = []; +t_ar = []; +f_val_ar = []; + + +if numel(t) ~= numel(val) + if (nargout<4) + error 'numel(t) ~= numel(val)' + else + EC = 1; + return + end +end + +if (nargin<4) + nu = 40; +end +if (nargin<5) + mu = 3*nu; +end +if (nargin<6) + expand_factor=5; +end + +if (mu<=2*nu) + if (nargout<4) + error 'mu has to be larger than 2*nu' + else + EC = 2; + return + end +end + +if (expand_factor<=1) + if (nargout<4) + error 'expand_factor has to be larger than 1' + else + EC = 3; + return + end +end + +dt = t(2)-t(1); + +M = numel(t); + +if (M<0.6*mu) + if (nargout<4) + error 'signal is to short for AR estimate --> decrease AR order' + else + EC = 10; + return + end +end + +for n=1:mu-nu + b(n) = val(end-n+1); + for m=1:nu + A(n,m)=val(end-n+1-m); + end +end + +a = ((A'*A)\A')*b'; + +val_ar = val; +t_ar = t; +for k=M:expand_factor*M + val_ar(k) = 0; + t_ar(k) = t_ar(k-1)+dt; + val_ar(k) = sum(a.*val_ar(k-(1:nu))'); +end + +if (max(val_ar(M:end)) > max(val)) + if (nargout<4) + error 'estimated signal appears to be unstable --> use a different mu' + else + EC = 11; + return + end +end + +f_val_ar = DFT_time2freq(t_ar, val_ar, freq); diff --git a/openEMS/matlab/Add2Queue.m b/openEMS/matlab/Add2Queue.m new file mode 100644 index 0000000..a80bdc9 --- /dev/null +++ b/openEMS/matlab/Add2Queue.m @@ -0,0 +1,48 @@ +function [queue] = Add2Queue(queue,func_name, func_args, varargin) +% function [queue] = Add2Queue(queue,func_name, func_args, varargin) +% +% Use this function to add a funtion to the queue. +% +% For more details see: InitQueue +% +% See also: InitQueue, FinishQueue, ResultsQueue, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if isfield(queue,'jobs') + jobnum = numel(queue.jobs)+1; +else + jobnum = 1; +end + +running = numel(queue.jobs_finished) - sum(queue.jobs_finished); + +while (running>=queue.maxThreads) + [queue running] = CheckQueue(queue); +end + + +if (queue.verbose>=1) + disp(['Add2Queue: Job #' num2str(jobnum) ' starting...']); +end + +queue.jobs_finished(jobnum) = 0; + +queue.jobs{jobnum}.argsfile = [tempname '.mat']; +save(queue.jobs{jobnum}.argsfile,'func_args'); + +queue.jobs{jobnum}.nargout = nargout(func_name); +queue.jobs{jobnum}.outargsfile = [tempname '.mat']; + +queue.jobs{jobnum}.command = [queue.bin queue.bin_options ' "load(''' queue.jobs{jobnum}.argsfile ''');' ... + queue.DependPath ... + 'err=[];' ... + 'try;' ... + '[outargs{1:' num2str(queue.jobs{jobnum}.nargout) '}]=' func_name '(func_args{:});' ... + 'catch err;outargs=0;end;' ... + 'save(''-V7'',''' queue.jobs{jobnum}.outargsfile ''',''outargs'',''err'');' ... + 'exit;"']; + +[queue.jobs{jobnum}.pid, queue.jobs{jobnum}.filenames] = queue_addProcess( queue.jobs{jobnum}.command ); diff --git a/openEMS/matlab/AddCPWPort.m b/openEMS/matlab/AddCPWPort.m new file mode 100644 index 0000000..ba4fc4a --- /dev/null +++ b/openEMS/matlab/AddCPWPort.m @@ -0,0 +1,285 @@ +function [CSX,port] = AddCPWPort( CSX, prio, portnr, materialname, start, stop, gap_width, dir, evec, varargin ) +% [CSX,port] = AddCPWPort( CSX, prio, portnr, materialname, start, stop, gap_width, dir, evec, varargin ) +% +% CSX: CSX-object created by InitCSX() +% prio: priority for excitation and probe boxes +% portnr: (integer) number of the port +% materialname: property for the CPW (created by AddMetal()) +% start: 3D start rowvector for port definition +% stop: 3D end rowvector for port definition +% gap_width: width of the CPW gap (left and right) +% dir: direction of wave propagation (choices: 0, 1, 2 or 'x','y','z') +% evec: excitation vector, which defines the direction of the e-field (must be the same as used in AddExcitation()) +% +% variable input: +% varargin: optional additional excitations options, see also AddExcitation +% 'ExcitePort' true/false to make the port an active feeding port (default +% is false) +% 'FeedShift' shift to port from start by a given distance in drawing +% units. Default is 0. Only active if 'ExcitePort' is set! +% 'Feed_R' Specifiy a lumped port resistance. Default is no lumped +% port resistance --> port has to end in an ABC. +% 'MeasPlaneShift' Shift the measurement plane from start t a given distance +% in drawing units. Default is the middle of start/stop. +% 'PortNamePrefix' a prefix to the port name +% +% Important: The mesh has to be already set and defined by DefineRectGrid! +% +% example: +% CSX = AddMetal( CSX, 'metal' ); %create a PEC called 'metal' +% start = [0 -width/2 0]; +% stop = [length +width/2 0]; +% [CSX,port] = AddCPWPort( CSX, 0, 1, 'metal', start, stop, gap_width, 'x', ... +% [0 0 -1], 'ExcitePort', true, 'Feed_R', 50 ) +% Explanation: +% - this defines a stripline in x-direction (dir='x') +% --> the wave travels along the x-direction +% - with an e-field excitation in -z-direction (evec=[0 0 -1]) +% - the excitation is active and placed at x=start(1) ('ExcitePort', true) +% - a 50 Ohm lumped port resistance is placed at x=start(1) ('Feed_R', 50) +% - the width-direction is determined by the cross product of the +% direction of propagtion (dir='x') and the excitation vector +% (evec=[0 0 -1]), in this case it is the y-direction +% - the stripline-metal is created in a xy-plane at a height at z=start(3) +% --> The upper and lower reference plane (ground) must be defined by +% the user +% +% Thorsten Liebig <thorsten.liebig@gmx.de> (c) 2014 +% +% See also InitCSX DefineRectGrid AddMetal AddMaterial AddExcitation calcPort + +%% validate arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%check mesh +if ~isfield(CSX,'RectilinearGrid') + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end +if (~isfield(CSX.RectilinearGrid,'XLines') || ~isfield(CSX.RectilinearGrid,'YLines') || ~isfield(CSX.RectilinearGrid,'ZLines')) + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end + +% check dir +dir = DirChar2Int(dir); + +% check evec +if ~(evec(1) == evec(2) == 0) && ~(evec(1) == evec(3) == 0) && ~(evec(2) == evec(3) == 0) || (sum(evec) == 0) + error 'evec must have exactly one component ~= 0' +end +evec0 = evec ./ sum(evec); % evec0 is a unit vector + +%set defaults +feed_shift = 0; +feed_R = inf; %(default is open, no resitance) +excite = false; +measplanepos = nan; +PortNamePrefix = ''; + +excite_args = {}; + +%% read optional arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'FeedShift')==1); + feed_shift = varargin{n+1}; + if (numel(feed_shift)>1) + error 'FeedShift must be a scalar value' + end + elseif (strcmp(varargin{n},'Feed_R')==1); + feed_R = varargin{n+1}; + if (numel(feed_shift)>1) + error 'Feed_R must be a scalar value' + end + elseif (strcmp(varargin{n},'MeasPlaneShift')==1); + measplanepos = varargin{n+1}; + if (numel(feed_shift)>1) + error 'MeasPlaneShift must be a scalar value' + end + elseif (strcmp(varargin{n},'ExcitePort')==1); + if ischar(varargin{n+1}) + warning('CSXCAD:AddCPWPort','depreceated: a string as excite option is no longer supported and will be removed in the future, please use true or false'); + if ~isempty(excite) + excite = true; + else + excite = false; + end + else + excite = varargin{n+1}; + end + elseif (strcmpi(varargin{n},'PortNamePrefix')) + PortNamePrefix = varargin{n+1}; + else + excite_args{end+1} = varargin{n}; + excite_args{end+1} = varargin{n+1}; + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% normalize start and stop +nstart = min( [start;stop] ); +nstop = max( [start;stop] ); + +% determine index (1, 2 or 3) of propagation (length of CPW) +idx_prop = dir + 1; + +% determine index (1, 2 or 3) of width of CPW +dir = [0 0 0]; +dir(idx_prop) = 1; +idx_height = abs(cross(dir,evec0)) * [1;2;3]; + +% determine index (1, 2 or 3) of height +idx_width = abs(evec0) * [1;2;3]; + + +if (start(idx_height)~=stop(idx_height)) + error('openEMS:AddCPWPort','start/stop in height direction must be equal'); +end + +% direction of propagation +if stop(idx_prop)-start(idx_prop) > 0 + direction = +1; +else + direction = -1; +end + +% create the metal/material for the CPW +SL_start = start; +SL_stop = stop; +CSX = AddBox( CSX, materialname, prio, SL_start, SL_stop ); + +if isnan(measplanepos) + measplanepos = (nstart(idx_prop)+nstop(idx_prop))/2; +else + measplanepos = start(idx_prop)+direction*measplanepos; +end + +% calculate position of the voltage probes +mesh{1} = sort(CSX.RectilinearGrid.XLines); +mesh{2} = sort(CSX.RectilinearGrid.YLines); +mesh{3} = sort(CSX.RectilinearGrid.ZLines); +meshlines = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), measplanepos, 'nearest' ); +meshlines = mesh{idx_prop}(meshlines-1:meshlines+1); % get three lines (approx. at center) +if direction == -1 + meshlines = fliplr(meshlines); +end +SL_w2 = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), (nstart(idx_width)+nstop(idx_width))/2, 'nearest' ); +SL_w2 = mesh{idx_width}(SL_w2); % get e-line at center of CPW (SL_width/2) +v1_start(idx_prop) = meshlines(1); +v1_start(idx_width) = (nstart(idx_width)+nstop(idx_width))/2; +v1_start(idx_height) = start(idx_height); +v1_stop = v1_start; +v2_start = v1_start; +v2_stop = v1_stop; +v2_start(idx_prop) = meshlines(2); +v2_stop(idx_prop) = meshlines(2); +v3_start = v2_start; +v3_stop = v2_stop; +v3_start(idx_prop) = meshlines(3); +v3_stop(idx_prop) = meshlines(3); + +width_add_start = [0 0 0]; +width_add_stop = [0 0 0]; +width_add_start(idx_width) = (nstop(idx_width)-nstart(idx_width))/2; +width_add_stop(idx_width) = (nstop(idx_width)-nstart(idx_width))/2+gap_width; + +weight = 0.5; +% create the voltage-probes +port.U_filename{1,1} = [PortNamePrefix 'port_ut' num2str(portnr) 'A1']; +CSX = AddProbe( CSX, port.U_filename{1,1}, 0, 'weight', -1*weight ); +CSX = AddBox( CSX, port.U_filename{1,1}, prio, v1_start-width_add_start, v1_stop-width_add_stop); + +port.U_filename{1,2} = [PortNamePrefix 'port_ut' num2str(portnr) 'A2']; +CSX = AddProbe( CSX, port.U_filename{1,2}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{1,2}, prio, v1_start+width_add_start, v1_stop+width_add_stop); + + +port.U_filename{2,1} = [PortNamePrefix 'port_ut' num2str(portnr) 'B1']; +CSX = AddProbe( CSX, port.U_filename{2,1}, 0, 'weight', -1*weight ); +CSX = AddBox( CSX, port.U_filename{2,1}, prio, v2_start-width_add_start, v2_stop-width_add_stop ); + +port.U_filename{2,2} = [PortNamePrefix 'port_ut' num2str(portnr) 'B2']; +CSX = AddProbe( CSX, port.U_filename{2,2}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{2,2}, prio, v2_start+width_add_start, v2_stop+width_add_stop ); + + +port.U_filename{3,1} = [PortNamePrefix 'port_ut' num2str(portnr) 'C1']; +CSX = AddProbe( CSX, port.U_filename{3,1}, 0, 'weight', -1*weight ); +CSX = AddBox( CSX, port.U_filename{3,1}, prio, v3_start-width_add_start, v3_stop-width_add_stop ); + +port.U_filename{3,2} = [PortNamePrefix 'port_ut' num2str(portnr) 'C2']; +CSX = AddProbe( CSX, port.U_filename{3,2}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{3,2}, prio, v3_start+width_add_start, v3_stop+width_add_stop ); + +% calculate position of the current probes +idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstart(idx_width), 'nearest' ); +i1_start(idx_width) = mesh{idx_width}(idx) - diff(mesh{idx_width}(idx-1:idx))/2; +idx = interp1( mesh{idx_height}, 1:numel(mesh{idx_height}), start(idx_height), 'nearest' ); +i1_start(idx_height) = mesh{idx_height}(idx-1) - diff(mesh{idx_height}(idx-2:idx-1))/2; +i1_stop(idx_height) = mesh{idx_height}(idx+1) + diff(mesh{idx_height}(idx+1:idx+2))/2; +i1_start(idx_prop) = sum(meshlines(1:2))/2; +i1_stop(idx_prop) = i1_start(idx_prop); +idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstop(idx_width), 'nearest' ); +i1_stop(idx_width) = mesh{idx_width}(idx) + diff(mesh{idx_width}(idx:idx+1))/2; +i2_start = i1_start; +i2_stop = i1_stop; +i2_start(idx_prop) = sum(meshlines(2:3))/2; +i2_stop(idx_prop) = i2_start(idx_prop); + +% create the curr-probes +weight = direction; +port.I_filename{1} = [PortNamePrefix 'port_it' num2str(portnr) 'A']; +CSX = AddProbe( CSX, port.I_filename{1}, 1, 'weight', weight ); +CSX = AddBox( CSX, port.I_filename{1}, prio, i1_start, i1_stop ); +port.I_filename{2} = [PortNamePrefix 'port_it' num2str(portnr) 'B']; +CSX = AddProbe( CSX, port.I_filename{2}, 1,'weight', weight ); +CSX = AddBox( CSX, port.I_filename{2}, prio, i2_start, i2_stop ); + +% create port structure +port.LengthScale = 1; +if ((CSX.ATTRIBUTE.CoordSystem==1) && (idx_prop==2)) + port.LengthScale = SL_stop(idx_height); +end +port.nr = portnr; +port.type = 'CPW'; +port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; +port.v_delta = diff(meshlines)*port.LengthScale; +port.i_delta = diff( meshlines(1:end-1) + diff(meshlines)/2 )*port.LengthScale; +port.direction = direction; +port.excite = 0; +port.measplanepos = abs(v2_start(idx_prop) - start(idx_prop))*port.LengthScale; +% port + +% create excitation (if enabled) and port resistance +meshline = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), start(idx_prop) + feed_shift*direction, 'nearest' ); +ex_start(idx_prop) = mesh{idx_prop}(meshline) ; +ex_start(idx_width) = (nstart(idx_width)+nstop(idx_width))/2; +ex_start(idx_height) = nstart(idx_height); +ex_stop(idx_prop) = ex_start(idx_prop); +ex_stop(idx_width) = (nstart(idx_width)+nstop(idx_width))/2; +ex_stop(idx_height) = nstop(idx_height); + +port.excite = 0; +if excite + port.excite = 1; + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_1_' num2str(portnr)], 0, evec, excite_args{:} ); + CSX = AddBox( CSX, [PortNamePrefix 'port_excite_1_' num2str(portnr)], prio, ex_start-width_add_start, ex_stop-width_add_stop ); + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_2_' num2str(portnr)], 0, -evec, excite_args{:} ); + CSX = AddBox( CSX, [PortNamePrefix 'port_excite_2_' num2str(portnr)], prio, ex_start+width_add_start, ex_stop+width_add_stop ); +end + +%% CPW resitance at start of CPW line +ex_start(idx_prop) = start(idx_prop); +ex_stop(idx_prop) = ex_start(idx_prop); + +if (feed_R > 0) && ~isinf(feed_R) + CSX = AddLumpedElement( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], idx_width-1, 'R', 2*feed_R ); + CSX = AddBox( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], prio, ex_start-width_add_start, ex_stop-width_add_stop ); + CSX = AddBox( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], prio, ex_start+width_add_start, ex_stop+width_add_stop ); +elseif isinf(feed_R) + % do nothing --> open port +elseif feed_R == 0 + %port "resistance" as metal + CSX = AddBox( CSX, materialname, prio, ex_start-width_add_start, ex_stop-width_add_stop ); + CSX = AddBox( CSX, materialname, prio, ex_start+width_add_start, ex_stop+width_add_stop ); +else + error('openEMS:AddCPWPort','CPW port with resitance <= 0 it not possible'); +end +end diff --git a/openEMS/matlab/AddCircWaveGuidePort.m b/openEMS/matlab/AddCircWaveGuidePort.m new file mode 100644 index 0000000..2d2e7ff --- /dev/null +++ b/openEMS/matlab/AddCircWaveGuidePort.m @@ -0,0 +1,109 @@ +function [CSX,port] = AddCircWaveGuidePort( CSX, prio, portnr, start, stop, radius, mode_name, pol_ang, exc_amp, varargin ) +% function [CSX,port] = AddCircWaveGuidePort( CSX, prio, portnr, start, stop, radius, mode_name, pol_ang, exc_amp, varargin ) +% +% Create a circular waveguide port, including an optional excitation and probes +% +% Note: - The excitation will be located at the start position in the given direction +% - The voltage and current probes at the stop position in the given direction +% +% input: +% CSX: complete CSX structure (must contain a mesh) +% prio: priority of primitives +% start: start coordinates of waveguide port box +% stop: stop coordinates of waveguide port box +% radius: circular waveguide radius (in meter) +% mode_name: mode name, e.g. 'TE11' or 'TM21' +% pol_ang: polarization angle (e.g. 0 = horizontal, pi/2 = vertical) +% exc_amp: excitation amplitude (set 0 to be passive) +% +% optional (key/values): +% varargin: optional additional excitations options, see also AddExcitation +% 'PortNamePrefix': a prefix to the port name +% +% output: +% CSX: modified CSX structure +% port: port structure to use with calcPort +% +% example: +% % create a TE11 circular waveguide mode, using cylindircal coordinates +% start=[mesh.r(1) mesh.a(1) 0 ]; +% stop =[mesh.r(end) mesh.a(end) 100]; +% [CSX,port] = AddCircWaveGuidePort( CSX, 99, 1, start, stop, 320e-3, 'TE11', 0, 1); +% +% openEMS matlab interface +% ----------------------- +% (c) 2013 Thorsten Liebig (thorsten.liebig@gmx.de) +% +% See also InitCSX, AddExcitation, calcWGPort, calcPort + +if (~strcmpi(mode_name(1:2),'TE')) + error 'currently only TE type modes are supported' +end + +if (nargin<9) + exc_amp = 0; +end +if (nargin<8) + pol_ang = 0; +end + +pnm = 0; +n = str2double(mode_name(3)); +m = str2double(mode_name(4)); + +% values by David M. Pozar, Microwave Engineering, third edition +if ((n==0) && (m==1)) + pnm = 3.832; +elseif ((n==1) && (m==1)) + pnm = 1.841; +elseif ((n==2) && (m==1)) + pnm = 3.054; +elseif ((n==0) && (m==2)) + pnm = 7.016; +elseif ((n==1) && (m==2)) + pnm = 5.331; +elseif ((n==2) && (m==2)) + pnm = 6.706; +elseif ((n==0) && (m==3)) + pnm = 10.174; +elseif ((n==1) && (m==3)) + pnm = 8.536; +elseif ((n==2) && (m==3)) + pnm = 9.970; +else + error 'invalid TE_nm mode' +end + +if ~isfield(CSX,'RectilinearGrid') + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end + +unit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; +kc = pnm/radius; +kc_draw = kc*unit; + +angle = ['a-' num2str(pol_ang)]; +% functions by David M. Pozar, Microwave Engineering, third edition +% electric field mode profile +func_Er = [ num2str(-1/kc_draw^2,15) '/rho*cos(' angle ')*j1(' num2str(kc_draw,15) '*rho)']; +func_Ea = [ num2str(1/kc_draw,15) '*sin(' angle ')*0.5*(j0(' num2str(kc_draw,15) '*rho)-jn(2,' num2str(kc_draw,15) '*rho))']; + +% magnetic field mode profile +func_Hr = [ num2str(-1/kc_draw,15) '*sin(' angle ')*0.5*(j0(' num2str(kc_draw,15) '*rho)-jn(2,' num2str(kc_draw,15) '*rho))']; +func_Ha = [ num2str(-1/kc_draw^2,15) '/rho*cos(' angle ')*j1(' num2str(kc_draw,15) '*rho)']; + +if (CSX.ATTRIBUTE.CoordSystem==1) + func_E = {func_Er, func_Ea, 0}; + func_H = {func_Hr, func_Ha, 0}; +else + func_Ex = ['(' func_Er '*cos(a) - ' func_Ea '*sin(a) ) * (rho<' num2str(radius/unit) ')']; + func_Ey = ['(' func_Er '*sin(a) + ' func_Ea '*cos(a) ) * (rho<' num2str(radius/unit) ')']; + func_E = {func_Ex, func_Ey, 0}; + + func_Hx = ['(' func_Hr '*cos(a) - ' func_Ha '*sin(a) ) * (rho<' num2str(radius/unit) ')']; + func_Hy = ['(' func_Hr '*sin(a) + ' func_Ha '*cos(a) ) * (rho<' num2str(radius/unit) ')']; + func_H = {func_Hx, func_Hy, 0}; +end + +[CSX,port] = AddWaveGuidePort( CSX, prio, portnr, start, stop, 2, func_E, func_H, kc, exc_amp, varargin{:} ); + diff --git a/openEMS/matlab/AddCoaxialPort.m b/openEMS/matlab/AddCoaxialPort.m new file mode 100644 index 0000000..45c0d2b --- /dev/null +++ b/openEMS/matlab/AddCoaxialPort.m @@ -0,0 +1,232 @@ +function [CSX,port] = AddCoaxialPort( CSX, prio, portnr, pec_name, materialname, start, stop, dir, r_i, r_o, r_os, varargin ) +% function [CSX,port] = AddCoaxialPort( CSX, prio, portnr, pec_name, materialname, start, stop, dir, r_i, r_o, r_os, varargin ) +% +% CSX: CSX-object created by InitCSX() +% prio: priority for excitation and probe boxes +% portnr: (integer) number of the port +% pec_name: metal property for coaxial inner/outer conductor (created by AddMetal()) +% materialname: substrate property for coaxial line (created by AddMaterial()) +% Note: this may be empty for an "air filled" coaxial line +% start: 3D start rowvector for coaxial cable axis +% stop: 3D end rowvector for coaxial cable axis +% dir: direction of wave propagation (choices: 0, 1, 2 or 'x','y','z') +% r_i: inner coaxial radius (in drawing unit) +% r_o: outer coaxial radius (in drawing unit) +% r_os: outer shell coaxial radius (in drawing unit) +% +% variable input: +% varargin: optional additional excitations options, see also AddExcitation +% 'ExciteAmp' excitation amplitude of transversal electric field profile, +% set to 0 (default) for a passive port +% 'FeedShift' shift to port from start by a given distance in drawing +% units. Default is 0. Only active if 'ExciteAmp' is set! +% 'Feed_R' Specifiy a lumped port resistance. Default is no lumped +% port resistance --> port has to end in an ABC. +% 'MeasPlaneShift' Shift the measurement plane from start t a given distance +% in drawing units. Default is the middle of start/stop. +% 'PortNamePrefix' a prefix to the port name +% +% the mesh must be already initialized +% +% example: +% +% openEMS matlab interface +% ----------------------- +% Thorsten Liebig <thorsten.liebig@gmx.de> (c) 2013 +% +% See also InitCSX AddMetal AddMaterial AddExcitation calcPort + +%% validate arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%check mesh +if ~isfield(CSX,'RectilinearGrid') + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end +if (~isfield(CSX.RectilinearGrid,'XLines') || ~isfield(CSX.RectilinearGrid,'YLines') || ~isfield(CSX.RectilinearGrid,'ZLines')) + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end + +% check dir +dir = DirChar2Int(dir); + +%set defaults +feed_shift = 0; +feed_R = inf; %(default is open, no resitance) +excite_amp = 0; +measplanepos = nan; +PortNamePrefix = ''; + +excite_args = {}; + +%% read optional arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'FeedShift')==1); + feed_shift = varargin{n+1}; + if (numel(feed_shift)>1) + error 'FeedShift must be a scalar value' + end + elseif (strcmp(varargin{n},'Feed_R')==1); + feed_R = varargin{n+1}; + if (numel(feed_shift)>1) + error 'Feed_R must be a scalar value' + end + elseif (strcmp(varargin{n},'MeasPlaneShift')==1); + measplanepos = varargin{n+1}; + if (numel(feed_shift)>1) + error 'MeasPlaneShift must be a scalar value' + end + elseif (strcmp(varargin{n},'ExciteAmp')==1); + excite_amp = varargin{n+1}; + elseif (strcmpi(varargin{n},'PortNamePrefix')) + PortNamePrefix = varargin{n+1}; + else + excite_args{end+1} = varargin{n}; + excite_args{end+1} = varargin{n+1}; + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% determine index (1, 2 or 3) of propagation (length of MSL) +idx_prop_n = dir + 1; +idx_prop_nP = mod((dir+1),3)+1; +idx_prop_nPP = mod((dir+2),3)+1; + +% direction of propagation +if stop(idx_prop_n)-start(idx_prop_n) > 0 + direction = +1; +else + direction = -1; +end + +% create the metal for the coaxial line +CSX = AddCylinder( CSX, pec_name, prio, start, stop, r_i ); +CSX = AddCylindricalShell( CSX, pec_name, prio, start, stop, 0.5*(r_o+r_os), r_os-r_o ); + +% create the material filling for the coaxial line +if (~isempty(materialname)) + CSX = AddCylindricalShell( CSX, materialname, prio-1, start, stop, 0.5*(r_o+r_i), r_o-r_i ); +end + +if isnan(measplanepos) + measplanepos = (start(idx_prop_n)+stop(idx_prop_n))/2; +else + measplanepos = start(idx_prop_n)+direction*measplanepos; +end + +% calculate position of the voltage probes +mesh{1} = sort(unique(CSX.RectilinearGrid.XLines)); +mesh{2} = sort(unique(CSX.RectilinearGrid.YLines)); +mesh{3} = sort(unique(CSX.RectilinearGrid.ZLines)); +meshlines = interp1( mesh{idx_prop_n}, 1:numel(mesh{idx_prop_n}), measplanepos, 'nearest' ); +meshlines = mesh{idx_prop_n}(meshlines-1:meshlines+1); % get three lines (approx. at center) +if direction == -1 + meshlines = fliplr(meshlines); +end +v1_start(idx_prop_n) = meshlines(1); +v1_start(idx_prop_nP) = start(idx_prop_nP)+r_i; +v1_start(idx_prop_nPP) = start(idx_prop_nPP); +v1_stop = v1_start; +v1_stop(idx_prop_nP) = start(idx_prop_nP)+r_o; +v2_start = v1_start; +v2_stop = v1_stop; +v2_start(idx_prop_n) = meshlines(2); +v2_stop(idx_prop_n) = meshlines(2); +v3_start = v2_start; +v3_stop = v2_stop; +v3_start(idx_prop_n) = meshlines(3); +v3_stop(idx_prop_n) = meshlines(3); + +% calculate position of the current probes +i1_start(idx_prop_n) = 0.5*(meshlines(1)+meshlines(2)); +i1_start(idx_prop_nP) = start(idx_prop_nP)-r_i-0.1*(r_o-r_i); +i1_start(idx_prop_nPP) = start(idx_prop_nPP)-r_i-0.1*(r_o-r_i); +i1_stop = i1_start; +i1_stop(idx_prop_nP) = start(idx_prop_nP)+r_i+0.1*(r_o-r_i); +i1_stop(idx_prop_nPP) = start(idx_prop_nPP)+r_i+0.1*(r_o-r_i); + +i2_start = i1_start; +i2_stop = i1_stop; +i2_start(idx_prop_n) = 0.5*(meshlines(2)+meshlines(3)); +i2_stop(idx_prop_n) = 0.5*(meshlines(2)+meshlines(3)); + +% create the probes +port.U_filename{1} = [PortNamePrefix 'port_ut' num2str(portnr) 'A']; +weight = 1; +CSX = AddProbe( CSX, port.U_filename{1}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{1}, prio, v1_start, v1_stop ); +port.U_filename{2} = [PortNamePrefix 'port_ut' num2str(portnr) 'B']; +CSX = AddProbe( CSX, port.U_filename{2}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{2}, prio, v2_start, v2_stop ); +port.U_filename{3} = [PortNamePrefix 'port_ut' num2str(portnr) 'C']; +CSX = AddProbe( CSX, port.U_filename{3}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{3}, prio, v3_start, v3_stop ); + +weight = direction; +port.I_filename{1} = [PortNamePrefix 'port_it' num2str(portnr) 'A']; +CSX = AddProbe( CSX, port.I_filename{1}, 1, 'weight', weight ); +CSX = AddBox( CSX, port.I_filename{1}, prio, i1_start, i1_stop ); +port.I_filename{2} = [PortNamePrefix 'port_it' num2str(portnr) 'B']; +CSX = AddProbe( CSX, port.I_filename{2}, 1,'weight', weight ); +CSX = AddBox( CSX, port.I_filename{2}, prio, i2_start, i2_stop ); + +% create port structure +port.LengthScale = 1; +port.nr = portnr; +port.type = 'Coaxial'; +port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; +port.v_delta = diff(meshlines)*port.LengthScale; +port.i_delta = diff( meshlines(1:end-1) + diff(meshlines)/2 )*port.LengthScale; +port.direction = direction; +port.excite = 0; +port.measplanepos = abs(v2_start(idx_prop_n) - start(idx_prop_n))*port.LengthScale; + +port.r_i = r_i; +port.r_o = r_o; + +% create excitation (if enabled) and port resistance +meshline = interp1( mesh{idx_prop_n}, 1:numel(mesh{idx_prop_n}), start(idx_prop_n) + feed_shift*direction, 'nearest' ); +min_cell_prop = min(diff(mesh{idx_prop_n})); +ex_start = start; +ex_start(idx_prop_n) = mesh{idx_prop_n}(meshline) - 0.01*min_cell_prop; +ex_stop = ex_start; +ex_stop(idx_prop_n) = mesh{idx_prop_n}(meshline) + 0.01*min_cell_prop; + +port.excite = 0; +if (excite_amp~=0) + dir_names={'x','y','z'}; + nameX = ['(' dir_names{idx_prop_nP} '-' num2str(start(idx_prop_nP)) ')']; + nameY = ['(' dir_names{idx_prop_nPP} '-' num2str(start(idx_prop_nPP)) ')']; + + func_Ex = [ nameX '/(' nameX '*' nameX '+' nameY '*' nameY ') * (sqrt(' nameX '*' nameX '+' nameY '*' nameY ')<' num2str(r_o) ') * (sqrt(' nameX '*' nameX '+' nameY '*' nameY ')>' num2str(r_i) ')']; + func_Ey = [ nameY '/(' nameX '*' nameX '+' nameY '*' nameY ') * (sqrt(' nameX '*' nameX '+' nameY '*' nameY ')<' num2str(r_o) ') * (sqrt(' nameX '*' nameX '+' nameY '*' nameY ')>' num2str(r_i) ')']; + + func_E{idx_prop_n} = 0; + func_E{idx_prop_nP} = func_Ex; + func_E{idx_prop_nPP} = func_Ey; + + port.excite = 1; + evec = [1 1 1]; + evec(idx_prop_n) = 0; + + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], 0, evec, excite_args{:} ); + CSX = SetExcitationWeight(CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], func_E ); + CSX = AddCylindricalShell(CSX,[PortNamePrefix 'port_excite_' num2str(portnr)],0 ,ex_start,ex_stop,0.5*(r_i+r_o),(r_o-r_i)); +end + +%% resitance at start of coaxial line +ex_start = start; +ex_stop = stop; +ex_stop(idx_prop_n) = ex_start(idx_prop_n); + +if (feed_R > 0) && ~isinf(feed_R) + error 'feed_R not yet implemented' +elseif isinf(feed_R) + % do nothing --> open port +elseif feed_R == 0 + %port "resistance" as metal + CSX = AddBox( CSX, pec_name, prio, ex_start, ex_stop ); + CSX = AddCylindricalShell(CSX, pec_name, prio ,ex_start, ex_stop, 0.5*(r_i+r_o),(r_o-r_i)); +else + error('openEMS:AddMSLPort','MSL port with resitance <= 0 it not possible'); +end +end diff --git a/openEMS/matlab/AddCurvePort.m b/openEMS/matlab/AddCurvePort.m new file mode 100644 index 0000000..6ea99aa --- /dev/null +++ b/openEMS/matlab/AddCurvePort.m @@ -0,0 +1,182 @@ +function [CSX,port] = AddCurvePort( CSX, prio, portnr, R, start, stop, excite, varargin ) +%[CSX,port] = AddCurvePort( CSX, prio, portnr, R, start, stop [, excite, varargin] ) +% +% Creates a curve port (1-dimensional). +% The mesh must already be initialized. +% +% input: +% CSX: CSX-object created by InitCSX() +% prio: priority for excitation, metal, sheet and probe boxes +% portnr: (integer) number of the port +% R: internal resistance of the port +% start: 3D start rowvector for port definition +% stop: 3D end rowvector for port definition +% excite (optional): if true, the port will be switched on (see AddExcitation()) +% Note: for legacy support a string will be accepted +% optional (key/values): +% varargin: optional additional excitations options, see also AddExcitation +% 'PortNamePrefix': a prefix to the port name +% +% output: +% CSX: +% port: +% +% example: +% start = [0 0 0]; stop = [0 0 12]; +% this defines a lumped port in z-direction +% the excitation/probe is placed between start(1) and stop(1) +% +% (C) 2010 Sebastian Held <sebastian.held@uni-due.de> +% See also InitCSX AddExcitation + +port.type='Lumped'; +port.nr=portnr; + +PortNamePrefix = ''; + +varargin_tmp = varargin; +for n=1:2:numel(varargin_tmp) + if strcmpi('PortNamePrefix',varargin_tmp{n}) + PortNamePrefix = varargin_tmp{n+1}; + varargin([n n+1]) = []; + end +end + +% make row vector +start = reshape( start, 1, [] ); +stop = reshape( stop , 1, [] ); + +% get grid +mesh{1} = sort(unique(CSX.RectilinearGrid.XLines)); +mesh{2} = sort(unique(CSX.RectilinearGrid.YLines)); +mesh{3} = sort(unique(CSX.RectilinearGrid.ZLines)); +unit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; + +% find port direction +dir = abs(stop - start); +[dummy,dir] = max(dir); + +% other directions +dir1 = mod(dir,3)+1; +dir2 = mod(dir+1,3)+1; + +% normalize start and stop +if start(dir) < stop(dir) + nstart = start; + nstop = stop; +else + nstart = stop; + nstop = start; +end + +% snap to grid +start_idx = zeros(1,3); +stop_idx = zeros(1,3); +for n=1:3 + start_idx(n) = interp1( mesh{n}, 1:numel(mesh{n}), nstart(n), 'nearest' ); + stop_idx(n) = interp1( mesh{n}, 1:numel(mesh{n}), nstop(n), 'nearest' ); +end + +% calculate position +port_start_idx = start_idx; +port_stop_idx = stop_idx; +if abs(start_idx(dir) - stop_idx(dir)) ~= 1 + % calc port position + idx = interp1( mesh{dir}, 1:numel(mesh{dir}), (nstart(dir)+nstop(dir))/2, 'nearest' ); + idx1 = interp1( mesh{dir1}, 1:numel(mesh{dir1}), (nstart(dir1)+nstop(dir1))/2, 'nearest' ); + idx2 = interp1( mesh{dir2}, 1:numel(mesh{dir2}), (nstart(dir2)+nstop(dir2))/2, 'nearest' ); + port_start_idx(dir) = idx; + port_start_idx(dir1) = idx1; + port_start_idx(dir2) = idx2; + port_stop_idx(dir) = idx+1; + port_stop_idx(dir1) = idx1; + port_stop_idx(dir2) = idx2; + metalname = [PortNamePrefix 'port' num2str(portnr) '_PEC']; + CSX = AddMetal( CSX, metalname ); + CSX = AddCurve( CSX, metalname, prio, [nstart.' [mesh{1}(port_start_idx(1));mesh{2}(port_start_idx(2));mesh{3}(port_start_idx(3))]] ); + CSX = AddCurve( CSX, metalname, prio, [nstop.' [mesh{1}(port_stop_idx(1));mesh{2}(port_stop_idx(2));mesh{3}(port_stop_idx(3))]] ); +end + +% calculate position of resistive material +delta1_n = mesh{dir1}(port_start_idx(dir1)) - mesh{dir1}(port_start_idx(dir1)-1); +delta1_p = mesh{dir1}(port_start_idx(dir1)+1) - mesh{dir1}(port_start_idx(dir1)); +delta2_n = mesh{dir2}(port_start_idx(dir2)) - mesh{dir2}(port_start_idx(dir2)-1); +delta2_p = mesh{dir2}(port_start_idx(dir2)+1) - mesh{dir2}(port_start_idx(dir2)); +m_start = zeros(1,3); +m_stop = zeros(1,3); +for n=1:3 + m_start(n) = mesh{n}(port_start_idx(n)); + m_stop(n) = mesh{n}(port_stop_idx(n)); +end +m_start(dir1) = m_start(dir1) - delta1_n/2; +m_stop(dir1) = m_stop(dir1) + delta1_p/2; +m_start(dir2) = m_start(dir2) - delta2_n/2; +m_stop(dir2) = m_stop(dir2) + delta2_p/2; + +% calculate position of the voltage probe & excitation +v_start = [mesh{1}(port_start_idx(1)), mesh{2}(port_start_idx(2)), mesh{3}(port_start_idx(3))]; +v_stop = [mesh{1}(port_stop_idx(1)), mesh{2}(port_stop_idx(2)), mesh{3}(port_stop_idx(3))]; + +% calculate position of the current probe +i_start = m_start; +i_stop = m_stop; +i_start(dir) = (i_start(dir)+i_stop(dir))/2; +i_stop(dir) = i_start(dir); + +% create the probes +port.U_filename = [PortNamePrefix 'port_ut' num2str(portnr)]; +weight = -1; +CSX = AddProbe( CSX, port.U_filename, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename, prio, v_start, v_stop ); +port.I_filename = [PortNamePrefix 'port_it' num2str(portnr)]; +weight = 1; +CSX = AddProbe( CSX, port.I_filename, 1, 'weight', weight ); +CSX = AddBox( CSX, port.I_filename, prio, i_start, i_stop ); + +% create port structure +port.drawingunit = unit; +% port.start = start; +% port.stop = stop; +% port.v_start = v_start; +% port.v_stop = v_stop; +% port.i_start = i_start; +% port.i_stop = i_stop; +% port.dir = dir; +% port.direction = direction; +% port.idx_cal = idx_cal; +% port.idx1 = idx1; +% port.idx1 = idx1; + +if (nargin < 7) + excite = false; +end + +% legacy support, will be removed at some point +if ischar(excite) + warning('CSXCAD:AddCurvePort','depreceated: a string as excite option is no longer supported and will be removed in the future, please use true or false'); + if ~isempty(excite) + excite = true; + else + excite = false; + end +end + +port.excite = excite; + +% create excitation +if (excite) + % excitation of this port is enabled + port.excite = 1; + e_start = v_start; + e_stop = v_stop; + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], 0, start_idx ~= stop_idx, varargin{:}); + CSX = AddBox( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], prio, e_start, e_stop ); +end + +port.Feed_R = R; +if (R>0 && (~isinf(R))) + CSX = AddLumpedElement( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], dir-1, 'R', R); + CSX = AddBox( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], prio, v_start, v_stop ); +elseif (R==0) + CSX = AddBox(CSX,metalname, prio, v_start, v_stop); +end
\ No newline at end of file diff --git a/openEMS/matlab/AddLumpedPort.m b/openEMS/matlab/AddLumpedPort.m new file mode 100644 index 0000000..ad6d5fa --- /dev/null +++ b/openEMS/matlab/AddLumpedPort.m @@ -0,0 +1,129 @@ +function [CSX, port] = AddLumpedPort( CSX, prio, portnr, R, start, stop, dir, excite, varargin ) +% [CSX, port] = AddLumpedPort( CSX, prio, portnr, R, start, stop, dir, excite, varargin ) +% +% Add a lumped port as an excitation. +% +% A lumped port consists of an excitation, a lumped resistor, a voltage and +% current probe. +% +% CSX: CSX-object created by InitCSX() +% prio: priority for substrate and probe boxes +% portnr: (integer) number of the port +% R: internal resistance of the port (lumped element) +% start: 3D start rowvector for port definition +% stop: 3D end rowvector for port definition +% dir: direction/amplitude of port (e.g.: [1 0 0], [0 1 0] or [0 0 1]) +% excite (optional): if true, the port will be switched on (see AddExcitation()) +% Note: for legacy support a string will be accepted +% V_Probe_Weight: additional weigth for the voltage probes +% I_Probe_Weight: additional weigth for the current probes +% optional (key/values): +% 'PortNamePrefix': an prefix to the port name +% varargin (optional): additional excitations options, see also AddExcitation +% +% example: +% start = [0 -width/2 0]; +% stop = [0 width/2 height]; +% [CSX] = AddLumpedPort(CSX, 5 ,1 , 50, start, stop, [0 0 1], true); +% %this defines an active lumped port in z-direction with a 50 Ohm port impedence +% +% openEMS matlab interface +% ----------------------- +% Sebastian Held <sebastian.held@gmx.de> +% Jun 1 2010 +% Thorsten Liebig +% Jul 13 2011 +% +% See also InitCSX AddExcitation + +% check dir + +port.type='Lumped'; +port.nr=portnr; + +V_Probe_Weight = 1; +I_Probe_Weight = 1; + +if (dir(1)~=0) && (dir(2) == 0) && (dir(3)==0) + n_dir = 1; +elseif (dir(1)==0) && (dir(2) ~= 0) && (dir(3)==0) + n_dir = 2; +elseif (dir(1)==0) && (dir(2) == 0) && (dir(3)~=0) + n_dir = 3; +else + error 'dir must have exactly one component ~= 0' +end + +PortNamePrefix = ''; + +varargin_tmp = varargin; +for n=1:2:numel(varargin_tmp) + if strcmpi('PortNamePrefix',varargin_tmp{n}) + PortNamePrefix = varargin_tmp{n+1}; + varargin([n n+1]) = []; + elseif strcmpi('V_Probe_Weight',varargin_tmp{n}) + V_Probe_Weight = varargin_tmp{n+1}; + elseif strcmpi('I_Probe_Weight',varargin_tmp{n}) + I_Probe_Weight = varargin_tmp{n+1}; + end +end + +if (stop(n_dir)==start(n_dir)) + error 'start/stop in excitation direction in must not be equal' +end + +if (stop(n_dir)-start(n_dir)) > 0 + direction = +1; +else + direction = -1; +end +port.direction = direction; + +port.Feed_R = R; +if (R>0 && (~isinf(R))) + CSX = AddLumpedElement(CSX,[PortNamePrefix 'port_resist_' int2str(portnr)], n_dir-1, 'Caps', 1, 'R', R); + CSX = AddBox(CSX,[PortNamePrefix 'port_resist_' int2str(portnr)], prio, start, stop); +elseif (R<=0) + CSX = AddMetal(CSX,[PortNamePrefix 'port_resist_' int2str(portnr)]); + CSX = AddBox(CSX,[PortNamePrefix 'port_resist_' int2str(portnr)], prio, start, stop); +end + +if (nargin < 8) + excite = false; +end + +% legacy support, will be removed at some point +if ischar(excite) + warning('CSXCAD:AddLumpedPort','depreceated: a string as excite option is no longer supported and will be removed in the future, please use true or false'); + if ~isempty(excite) + excite = true; + else + excite = false; + end +end + +port.excite = excite; +% create excitation +if (excite) + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], 0, -dir*direction, varargin{:}); + CSX = AddBox( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], prio, start, stop ); +end + +u_start = 0.5*(start + stop); +u_stop = 0.5*(start + stop); +u_start(n_dir) = start(n_dir); +u_stop(n_dir) = stop(n_dir); + +port.U_filename = [PortNamePrefix 'port_ut' int2str(portnr)]; +CSX = AddProbe(CSX, port.U_filename, 0, 'weight', -direction*V_Probe_Weight); +CSX = AddBox(CSX, port.U_filename, prio, u_start, u_stop); + +i_start = start; +i_stop = stop; +i_start(n_dir) = 0.5*(start(n_dir)+stop(n_dir)); +i_stop(n_dir) = 0.5*(start(n_dir)+stop(n_dir)); + +port.I_filename = [PortNamePrefix 'port_it' int2str(portnr)]; +CSX = AddProbe(CSX, port.I_filename, 1, 'weight', direction*I_Probe_Weight, 'NormDir', n_dir-1); +CSX = AddBox(CSX, port.I_filename, prio, i_start, i_stop); + diff --git a/openEMS/matlab/AddMRStub.m b/openEMS/matlab/AddMRStub.m new file mode 100644 index 0000000..432fa6a --- /dev/null +++ b/openEMS/matlab/AddMRStub.m @@ -0,0 +1,73 @@ +function CSX = AddMRStub( CSX, materialname, prio, MSL_width, len, alpha, resolution, orientation, normVector, position ) +% CSX = AddMRStub( CSX, materialname, prio, MSL_width, len, alpha, +% resolution, orientation, normVector, position ) +% +% Microstrip Radial Stub +% +% CSX: CSX-object created by InitCSX() +% materialname: property for the MSL (created by AddMetal() or AddMaterial()) +% prio: priority +% MSL_width: width of the MSL to connect the stub to +% len: length of the radial stub +% alpha: angle subtended by the radial stub (degrees) +% resolution: discrete angle spacing (degrees) +% orientation: angle of main direction of the radial stub (degrees) +% normVector: normal vector of the stub +% position: position of the end of the MSL +% +% This radial stub definition is equivalent to the one Agilent ADS uses. +% +% example: +% CSX = AddMRStub( CSX, 'PEC', 10, 1000, 5900, 30, 1, -90, [0 0 1], [0 -10000 254] ); +% +% +% Sebastian Held <sebastian.held@gmx.de> +% Jun 1 2010 +% +% See also InitCSX AddMetal AddMaterial + +% check normVector +if ~(normVector(1) == normVector(2) == 0) && ... + ~(normVector(1) == normVector(3) == 0) && ... + ~(normVector(2) == normVector(3) == 0) || (sum(normVector) == 0) + error 'normVector must have exactly one component ~= 0' +end +normVector = normVector ./ sum(normVector); % normVector is now a unit vector + +% convert angles to radians +alpha_rad = alpha/180*pi; +orientation_rad = orientation/180*pi; +resolution_rad = resolution/180*pi; + +% +% build stub at origin (0,0,0) and translate/rotate it later +% + +D = 0.5 * MSL_width / sin(alpha_rad/2); +R = cos(alpha_rad/2) * D; + +% point at the center of the MSL +p(1,1) = 0; +p(2,1) = -MSL_width/2; +p(1,2) = 0; +p(2,2) = MSL_width/2; + +for a = alpha_rad/2 : -resolution_rad : -alpha_rad/2 + p(1,end+1) = cos(a) * (D+len) - R; + p(2,end) = sin(a) * (D+len); +end + +% rotate +rot = [cos(-orientation_rad), -sin(-orientation_rad); sin(-orientation_rad), cos(-orientation_rad)]; +p = (p.' * rot).'; + +% translate +idx_elevation = [1 2 3]; +idx_elevation = idx_elevation(normVector>0); +dim1 = mod( idx_elevation, 3 ) + 1; +dim2 = mod( idx_elevation+1, 3 ) + 1; +p(1,:) = p(1,:) + position(dim1); +p(2,:) = p(2,:) + position(dim2); + +elevation = position(idx_elevation); +CSX = AddPolygon( CSX, materialname, prio, normVector, elevation, p ); diff --git a/openEMS/matlab/AddMSLPort.m b/openEMS/matlab/AddMSLPort.m new file mode 100644 index 0000000..462db14 --- /dev/null +++ b/openEMS/matlab/AddMSLPort.m @@ -0,0 +1,265 @@ +function [CSX,port] = AddMSLPort( CSX, prio, portnr, materialname, start, stop, dir, evec, varargin ) +% [CSX,port] = AddMSLPort( CSX, prio, portnr, materialname, start, stop, dir, evec, varargin ) +% +% CSX: CSX-object created by InitCSX() +% prio: priority for excitation and probe boxes +% portnr: (integer) number of the port +% materialname: property for the MSL (created by AddMetal()) +% start: 3D start rowvector for port definition +% stop: 3D end rowvector for port definition +% dir: direction of wave propagation (choices: 0, 1, 2 or 'x','y','z') +% evec: excitation vector, which defines the direction of the e-field (must be the same as used in AddExcitation()) +% +% variable input: +% varargin: optional additional excitations options, see also AddExcitation +% 'ExcitePort' true/false to make the port an active feeding port (default +% is false) +% 'FeedShift' shift to port from start by a given distance in drawing +% units. Default is 0. Only active if 'ExcitePort' is set! +% 'Feed_R' Specifiy a lumped port resistance. Default is no lumped +% port resistance --> port has to end in an ABC. +% 'MeasPlaneShift' Shift the measurement plane from start t a given distance +% in drawing units. Default is the middle of start/stop. +% 'PortNamePrefix' a prefix to the port name +% +% Important: The mesh has to be already set and defined by DefineRectGrid! +% +% example: +% CSX = AddMetal( CSX, 'metal' ); %create a PEC called 'metal' +% start = [0 -width/2 height]; +% stop = [length +width/2 0 ]; +% [CSX,port] = AddMSLPort( CSX, 0, 1, 'metal', start, stop, 'x', [0 0 -1], ... +% 'ExcitePort', true, 'Feed_R', 50 ) +% Explanation: +% - this defines a MSL in x-direction (dir='x') +% --> the wave travels along the x-direction +% - with an e-field excitation in -z-direction (evec=[0 0 -1]) +% - the excitation is active and placed at x=start(1) ('ExcitePort', true) +% - a 50 Ohm lumped port resistance is placed at x=start(1) ('Feed_R', 50) +% - the width-direction is determined by the cross product of the +% direction of propagtion (dir='x') and the excitation vector +% (evec=[0 0 -1]), in this case it is the y-direction +% - the MSL-metal is created in a xy-plane at a height at z=start(3) +% --> It is important to define the MSL height in the start coordinate! +% - the ground (xy-plane, not defined by the port) is assumed at z=stop(3) +% --> The reference plane (ground) is defined in the stop coordinate! +% +% Sebastian Held <sebastian.held@gmx.de> May 13 2010 +% Thorsten Liebig <thorsten.liebig@gmx.de> (c) 2011-2013 +% +% See also InitCSX DefineRectGrid AddMetal AddMaterial AddExcitation calcPort + +%% validate arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%check mesh +if ~isfield(CSX,'RectilinearGrid') + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end +if (~isfield(CSX.RectilinearGrid,'XLines') || ~isfield(CSX.RectilinearGrid,'YLines') || ~isfield(CSX.RectilinearGrid,'ZLines')) + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end + +% check dir +dir = DirChar2Int(dir); + +% check evec +if ~(evec(1) == evec(2) == 0) && ~(evec(1) == evec(3) == 0) && ~(evec(2) == evec(3) == 0) || (sum(evec) == 0) + error 'evec must have exactly one component ~= 0' +end +evec0 = evec ./ sum(evec); % evec0 is a unit vector + +%set defaults +feed_shift = 0; +feed_R = inf; %(default is open, no resitance) +excite = false; +measplanepos = nan; +PortNamePrefix = ''; + +excite_args = {}; + +%% read optional arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'FeedShift')==1); + feed_shift = varargin{n+1}; + if (numel(feed_shift)>1) + error 'FeedShift must be a scalar value' + end + elseif (strcmp(varargin{n},'Feed_R')==1); + feed_R = varargin{n+1}; + if (numel(feed_shift)>1) + error 'Feed_R must be a scalar value' + end + elseif (strcmp(varargin{n},'MeasPlaneShift')==1); + measplanepos = varargin{n+1}; + if (numel(feed_shift)>1) + error 'MeasPlaneShift must be a scalar value' + end + elseif (strcmp(varargin{n},'ExcitePort')==1); + if ischar(varargin{n+1}) + warning('CSXCAD:AddMSLPort','depreceated: a string as excite option is no longer supported and will be removed in the future, please use true or false'); + if ~isempty(excite) + excite = true; + else + excite = false; + end + else + excite = varargin{n+1}; + end + elseif (strcmpi(varargin{n},'PortNamePrefix')) + PortNamePrefix = varargin{n+1}; + else + excite_args{end+1} = varargin{n}; + excite_args{end+1} = varargin{n+1}; + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% normalize start and stop +nstart = min( [start;stop] ); +nstop = max( [start;stop] ); + +% determine index (1, 2 or 3) of propagation (length of MSL) +idx_prop = dir + 1; + +% determine index (1, 2 or 3) of width of MSL +dir = [0 0 0]; +dir(idx_prop) = 1; +idx_width = abs(cross(dir,evec0)) * [1;2;3]; + +% determine index (1, 2 or 3) of height +idx_height = abs(evec0) * [1;2;3]; + +% direction of propagation +if stop(idx_prop)-start(idx_prop) > 0 + direction = +1; +else + direction = -1; +end + +% direction of propagation +if stop(idx_height)-start(idx_height) > 0 + upsidedown = +1; +else + upsidedown = -1; +end + +% create the metal/material for the MSL +MSL_start = start; +MSL_stop = stop; +MSL_stop(idx_height) = MSL_start(idx_height); +CSX = AddBox( CSX, materialname, prio, MSL_start, MSL_stop ); + +if isnan(measplanepos) + measplanepos = (nstart(idx_prop)+nstop(idx_prop))/2; +else + measplanepos = start(idx_prop)+direction*measplanepos; +end + +% calculate position of the voltage probes +mesh{1} = sort(CSX.RectilinearGrid.XLines); +mesh{2} = sort(CSX.RectilinearGrid.YLines); +mesh{3} = sort(CSX.RectilinearGrid.ZLines); +meshlines = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), measplanepos, 'nearest' ); +meshlines = mesh{idx_prop}(meshlines-1:meshlines+1); % get three lines (approx. at center) +if direction == -1 + meshlines = fliplr(meshlines); +end +MSL_w2 = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), (nstart(idx_width)+nstop(idx_width))/2, 'nearest' ); +MSL_w2 = mesh{idx_width}(MSL_w2); % get e-line at center of MSL (MSL_width/2) +v1_start(idx_prop) = meshlines(1); +v1_start(idx_width) = MSL_w2; +v1_start(idx_height) = start(idx_height); +v1_stop = v1_start; +v1_stop(idx_height) = stop(idx_height); +v2_start = v1_start; +v2_stop = v1_stop; +v2_start(idx_prop) = meshlines(2); +v2_stop(idx_prop) = meshlines(2); +v3_start = v2_start; +v3_stop = v2_stop; +v3_start(idx_prop) = meshlines(3); +v3_stop(idx_prop) = meshlines(3); + +% calculate position of the current probes +idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstart(idx_width), 'nearest' ); +i1_start(idx_width) = mesh{idx_width}(idx) - diff(mesh{idx_width}(idx-1:idx))/2; +idx = interp1( mesh{idx_height}, 1:numel(mesh{idx_height}), start(idx_height), 'nearest' ); +i1_start(idx_height) = mesh{idx_height}(idx-1) - diff(mesh{idx_height}(idx-2:idx-1))/2; +i1_stop(idx_height) = mesh{idx_height}(idx+1) + diff(mesh{idx_height}(idx+1:idx+2))/2; +i1_start(idx_prop) = sum(meshlines(1:2))/2; +i1_stop(idx_prop) = i1_start(idx_prop); +idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstop(idx_width), 'nearest' ); +i1_stop(idx_width) = mesh{idx_width}(idx) + diff(mesh{idx_width}(idx:idx+1))/2; +i2_start = i1_start; +i2_stop = i1_stop; +i2_start(idx_prop) = sum(meshlines(2:3))/2; +i2_stop(idx_prop) = i2_start(idx_prop); + +% create the probes +port.U_filename{1} = [PortNamePrefix 'port_ut' num2str(portnr) 'A']; +% weight = sign(stop(idx_height)-start(idx_height)) +weight = upsidedown; +CSX = AddProbe( CSX, port.U_filename{1}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{1}, prio, v1_start, v1_stop ); +port.U_filename{2} = [PortNamePrefix 'port_ut' num2str(portnr) 'B']; +CSX = AddProbe( CSX, port.U_filename{2}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{2}, prio, v2_start, v2_stop ); +port.U_filename{3} = [PortNamePrefix 'port_ut' num2str(portnr) 'C']; +CSX = AddProbe( CSX, port.U_filename{3}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{3}, prio, v3_start, v3_stop ); + +weight = direction; +port.I_filename{1} = [PortNamePrefix 'port_it' num2str(portnr) 'A']; +CSX = AddProbe( CSX, port.I_filename{1}, 1, 'weight', weight ); +CSX = AddBox( CSX, port.I_filename{1}, prio, i1_start, i1_stop ); +port.I_filename{2} = [PortNamePrefix 'port_it' num2str(portnr) 'B']; +CSX = AddProbe( CSX, port.I_filename{2}, 1,'weight', weight ); +CSX = AddBox( CSX, port.I_filename{2}, prio, i2_start, i2_stop ); + +% create port structure +port.LengthScale = 1; +if ((CSX.ATTRIBUTE.CoordSystem==1) && (idx_prop==2)) + port.LengthScale = MSL_stop(idx_height); +end +port.nr = portnr; +port.type = 'MSL'; +port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; +port.v_delta = diff(meshlines)*port.LengthScale; +port.i_delta = diff( meshlines(1:end-1) + diff(meshlines)/2 )*port.LengthScale; +port.direction = direction; +port.excite = 0; +port.measplanepos = abs(v2_start(idx_prop) - start(idx_prop))*port.LengthScale; +% port + +% create excitation (if enabled) and port resistance +meshline = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), start(idx_prop) + feed_shift*direction, 'nearest' ); +ex_start(idx_prop) = mesh{idx_prop}(meshline) ; +ex_start(idx_width) = nstart(idx_width); +ex_start(idx_height) = nstart(idx_height); +ex_stop(idx_prop) = ex_start(idx_prop); +ex_stop(idx_width) = nstop(idx_width); +ex_stop(idx_height) = nstop(idx_height); + +port.excite = 0; +if excite + port.excite = 1; + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], 0, evec, excite_args{:} ); + CSX = AddBox( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], prio, ex_start, ex_stop ); +end + +%% MSL resitance at start of MSL line +ex_start(idx_prop) = start(idx_prop); +ex_stop(idx_prop) = ex_start(idx_prop); + +if (feed_R > 0) && ~isinf(feed_R) + CSX = AddLumpedElement( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], idx_height-1, 'R', feed_R ); + CSX = AddBox( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], prio, ex_start, ex_stop ); +elseif isinf(feed_R) + % do nothing --> open port +elseif feed_R == 0 + %port "resistance" as metal + CSX = AddBox( CSX, materialname, prio, ex_start, ex_stop ); +else + error('openEMS:AddMSLPort','MSL port with resitance <= 0 it not possible'); +end +end diff --git a/openEMS/matlab/AddPML.m b/openEMS/matlab/AddPML.m new file mode 100644 index 0000000..4e7ba80 --- /dev/null +++ b/openEMS/matlab/AddPML.m @@ -0,0 +1,92 @@ +function mesh = AddPML( mesh, numcells, CoordSystem ) +% mesh = AddPML( mesh, numcells, <CoordSystem> ) +% +% Adds equidistant cells to the specified directions of the simulation +% area. This is used to put a PML (perfectly matched layer) absorber there. +% Remember: this function only adds space for the PML, the boundary +% conditions need to be set correctly to really add PML material. +% +% The mesh is sorted and duplicate lines are removed. +% +% input: +% mesh: mesh structure +% numcells: 1x6 vector (xmin,xmax,ymin,ymax,zmin,zmax) with number of +% cells to add to this direction +% CoordSystem (optional): set to 1 in case of cylindrical mesh using +% mesh.r, mesh.a and mesh.z +% +% output: +% mesh: new mesh with the added lines +% +% example: +% % some fixed mesh lines +% mesh.x = [-100 0 100]; +% mesh.y = [-100 0 100]; +% mesh.z = [0 500]; +% mesh = DetectEdges(CSX, mesh); %detect edges +% mesh = SmoothMesh(mesh, c0/fmax/20/unit); % smooth the mesh +% +% mesh = AddPML(mesh, 8); % add 8 lines to all directions +% % or +% mesh = AddPML(mesh, [0 0 0 0 8 8]); % add 8 lines in both z-directions +% +% See also DefineRectGrid, SmoothMesh, DetectEdges +% +% openEMS matlab interface +% ----------------------- +% Sebastian Held <sebastian.held@uni-due.de> + +% check +error( nargchk(2,3,nargin) ); + +if (numel(numcells)==1) + numcells = ones(6,1)*numcells; +end + +numcells = reshape( numcells, 1, [] ); +if numel(numcells) ~= 6 + error( 'argument numcells needs to have exactly 6 elements' ); +end + +if (nargin<3) + CoordSystem = 0; +end + +dir_names = 'xyz'; +if (CoordSystem==1) + dir_names = 'raz'; +end + +mesh.(dir_names(1)) = unique(sort(mesh.(dir_names(1)))); +mesh.(dir_names(2)) = unique(sort(mesh.(dir_names(2)))); +mesh.(dir_names(3)) = unique(sort(mesh.(dir_names(3)))); + +% xmin +delta = mesh.(dir_names(1))(2) - mesh.(dir_names(1))(1); +start = mesh.(dir_names(1))(1) - numcells(1)*delta; +mesh.(dir_names(1)) = [start:delta:(mesh.(dir_names(1))(1)-delta), mesh.(dir_names(1))]; + +% xmax +delta = mesh.(dir_names(1))(end) - mesh.(dir_names(1))(end-1); +stop = mesh.(dir_names(1))(end) + numcells(2)*delta; +mesh.(dir_names(1)) = [mesh.(dir_names(1)), (mesh.(dir_names(1))(end)+delta):delta:stop]; + +% ymin +delta = mesh.(dir_names(2))(2) - mesh.(dir_names(2))(1); +start = mesh.(dir_names(2))(1) - numcells(3)*delta; +mesh.(dir_names(2)) = [start:delta:(mesh.(dir_names(2))(1)-delta), mesh.(dir_names(2))]; + +% ymax +delta = mesh.(dir_names(2))(end) - mesh.(dir_names(2))(end-1); +stop = mesh.(dir_names(2))(end) + numcells(4)*delta; +mesh.(dir_names(2)) = [mesh.(dir_names(2)), (mesh.(dir_names(2))(end)+delta):delta:stop]; + +% zmin +delta = mesh.(dir_names(3))(2) - mesh.(dir_names(3))(1); +start = mesh.(dir_names(3))(1) - numcells(5)*delta; +mesh.(dir_names(3)) = [start:delta:(mesh.(dir_names(3))(1)-delta), mesh.(dir_names(3))]; + +% zmax +delta = mesh.(dir_names(3))(end) - mesh.(dir_names(3))(end-1); +stop = mesh.(dir_names(3))(end) + numcells(6)*delta; +mesh.(dir_names(3)) = [mesh.(dir_names(3)), (mesh.(dir_names(3))(end)+delta):delta:stop]; diff --git a/openEMS/matlab/AddRectWaveGuidePort.m b/openEMS/matlab/AddRectWaveGuidePort.m new file mode 100644 index 0000000..8233f77 --- /dev/null +++ b/openEMS/matlab/AddRectWaveGuidePort.m @@ -0,0 +1,92 @@ +function [CSX,port] = AddRectWaveGuidePort( CSX, prio, portnr, start, stop, dir, a, b, mode_name, exc_amp, varargin ) +% function [CSX,port] = AddRectWaveGuidePort( CSX, prio, portnr, start, stop, dir, a, b, mode_name, exc_amp, varargin ) +% +% Create a rectangular waveguide port, including an optional excitation and probes +% +% Note: - The excitation will be located at the start position in the given direction +% - The voltage and current probes at the stop position in the given direction +% +% input: +% CSX: complete CSX structure (must contain a mesh) +% prio: priority of primitives +% start: start coordinates of waveguide port box +% stop: stop coordinates of waveguide port box +% dir: direction of port (0/1/2 or 'x'/'y'/'z'-direction) +% a,b: rectangular waveguide width and height (in meter) +% mode_name: mode name, e.g. 'TE11' or 'TM21' +% exc_amp: excitation amplitude (set 0 to be passive) +% +% optional (key/values): +% varargin: optional additional excitations options, see also AddExcitation +% 'PortNamePrefix': a prefix to the port name +% +% output: +% CSX: modified CSX structure +% port: port structure to use with calcPort +% +% example: +% % create a TE10 circular waveguide mode, using cylindircal coordinates +% start=[mesh.r(1) mesh.a(1) 0 ]; +% stop =[mesh.r(end) mesh.a(end) 100]; +% [CSX,port] = AddCircWaveGuidePort( CSX, 99, 1, start, stop, 320e-3, 'TE11', 0, 1); +% +% openEMS matlab interface +% ----------------------- +% (c) 2013 Thorsten Liebig (thorsten.liebig@gmx.de) +% +% See also InitCSX, AddExcitation, calcWGPort, calcPort + +if (~strcmpi(mode_name(1:2),'TE')) + error 'currently only TE type modes are supported' +end + +if (nargin<10) + exc_amp = 0; +end + +m = str2double(mode_name(3)); +n = str2double(mode_name(4)); + +% values by David M. Pozar, Microwave Engineering, third edition +kc = sqrt((m*pi/a)^2 + (n*pi/b)^2); + +if ~isfield(CSX,'RectilinearGrid') + error 'mesh needs to be defined! Use DefineRectGrid() first!'; + if (~isfield(CSX.RectilinearGrid,'XLines') || ~isfield(CSX.RectilinearGrid,'YLines') || ~isfield(CSX.RectilinearGrid,'ZLines')) + error 'mesh needs to be defined! Use DefineRectGrid() first!'; + end +end + +unit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; + +dir = DirChar2Int(dir); +dir_names={'x','y','z'}; + +dirP = mod((dir+1),3)+1; +dirPP = mod((dir+2),3)+1; +nameX = ['(' dir_names{dirP} '-' num2str(start(dirP)) ')']; +nameY = ['(' dir_names{dirPP} '-' num2str(start(dirPP)) ')']; + +%convert a&b to drawing units +a = a/unit; +b = b/unit; +% functions by David M. Pozar, Microwave Engineering, third edition +% electric field mode profile +func_Ex = [num2str( n/b) '*cos(' num2str(m*pi/a) '*' nameX ')*sin(' num2str(n*pi/b) '*' nameY ')']; +func_Ey = [num2str(-m/a) '*sin(' num2str(m*pi/a) '*' nameX ')*cos(' num2str(n*pi/b) '*' nameY ')']; + +% magnetic field mode profile +func_Hx = [num2str(m/a) '*sin(' num2str(m*pi/a) '*' nameX ')*cos(' num2str(n*pi/b) '*' nameY ')']; +func_Hy = [num2str(n/b) '*cos(' num2str(m*pi/a) '*' nameX ')*sin(' num2str(n*pi/b) '*' nameY ')']; + + +func_E{dir+1} = 0; +func_E{dirP} = func_Ex; +func_E{dirPP} = func_Ey; + +func_H{dir+1} = 0; +func_H{dirP} = func_Hx; +func_H{dirPP} = func_Hy; + +[CSX,port] = AddWaveGuidePort( CSX, prio, portnr, start, stop, dir, func_E, func_H, kc, exc_amp, varargin{:} ); + diff --git a/openEMS/matlab/AddStripLinePort.m b/openEMS/matlab/AddStripLinePort.m new file mode 100644 index 0000000..70dd065 --- /dev/null +++ b/openEMS/matlab/AddStripLinePort.m @@ -0,0 +1,283 @@ +function [CSX,port] = AddStripLinePort( CSX, prio, portnr, materialname, start, stop, height, dir, evec, varargin ) +% [CSX,port] = AddStripLinePort( CSX, prio, portnr, materialname, start, stop, height, dir, evec, varargin ) +% +% CSX: CSX-object created by InitCSX() +% prio: priority for excitation and probe boxes +% portnr: (integer) number of the port +% materialname: property for the MSL (created by AddMetal()) +% start: 3D start rowvector for port definition +% stop: 3D end rowvector for port definition +% height: height of the stripline (top and bottom) +% dir: direction of wave propagation (choices: 0, 1, 2 or 'x','y','z') +% evec: excitation vector, which defines the direction of the e-field (must be the same as used in AddExcitation()) +% +% variable input: +% varargin: optional additional excitations options, see also AddExcitation +% 'ExcitePort' true/false to make the port an active feeding port (default +% is false) +% 'FeedShift' shift to port from start by a given distance in drawing +% units. Default is 0. Only active if 'ExcitePort' is set! +% 'Feed_R' Specifiy a lumped port resistance. Default is no lumped +% port resistance --> port has to end in an ABC. +% 'MeasPlaneShift' Shift the measurement plane from start t a given distance +% in drawing units. Default is the middle of start/stop. +% 'PortNamePrefix' a prefix to the port name +% +% Important: The mesh has to be already set and defined by DefineRectGrid! +% +% example: +% CSX = AddMetal( CSX, 'metal' ); %create a PEC called 'metal' +% start = [0 -width/2 0]; +% stop = [length +width/2 0]; +% [CSX,port] = AddStripLinePort( CSX, 0, 1, 'metal', start, stop, height, 'x', ... +% [0 0 -1], 'ExcitePort', true, 'Feed_R', 50 ) +% Explanation: +% - this defines a stripline in x-direction (dir='x') +% --> the wave travels along the x-direction +% - with an e-field excitation in -z-direction (evec=[0 0 -1]) +% - the excitation is active and placed at x=start(1) ('ExcitePort', true) +% - a 50 Ohm lumped port resistance is placed at x=start(1) ('Feed_R', 50) +% - the width-direction is determined by the cross product of the +% direction of propagtion (dir='x') and the excitation vector +% (evec=[0 0 -1]), in this case it is the y-direction +% - the stripline-metal is created in a xy-plane at a height at z=start(3) +% --> The upper and lower reference plane (ground) must be defined by +% the user +% +% Thorsten Liebig <thorsten.liebig@gmx.de> (c) 2013 +% +% See also InitCSX DefineRectGrid AddMetal AddMaterial AddExcitation calcPort + +%% validate arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%check mesh +if ~isfield(CSX,'RectilinearGrid') + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end +if (~isfield(CSX.RectilinearGrid,'XLines') || ~isfield(CSX.RectilinearGrid,'YLines') || ~isfield(CSX.RectilinearGrid,'ZLines')) + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end + +% check dir +dir = DirChar2Int(dir); + +% check evec +if ~(evec(1) == evec(2) == 0) && ~(evec(1) == evec(3) == 0) && ~(evec(2) == evec(3) == 0) || (sum(evec) == 0) + error 'evec must have exactly one component ~= 0' +end +evec0 = evec ./ sum(evec); % evec0 is a unit vector + +%set defaults +feed_shift = 0; +feed_R = inf; %(default is open, no resitance) +excite = false; +measplanepos = nan; +PortNamePrefix = ''; + +excite_args = {}; + +%% read optional arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'FeedShift')==1); + feed_shift = varargin{n+1}; + if (numel(feed_shift)>1) + error 'FeedShift must be a scalar value' + end + elseif (strcmp(varargin{n},'Feed_R')==1); + feed_R = varargin{n+1}; + if (numel(feed_shift)>1) + error 'Feed_R must be a scalar value' + end + elseif (strcmp(varargin{n},'MeasPlaneShift')==1); + measplanepos = varargin{n+1}; + if (numel(feed_shift)>1) + error 'MeasPlaneShift must be a scalar value' + end + elseif (strcmp(varargin{n},'ExcitePort')==1); + if ischar(varargin{n+1}) + warning('CSXCAD:AddMSLPort','depreceated: a string as excite option is no longer supported and will be removed in the future, please use true or false'); + if ~isempty(excite) + excite = true; + else + excite = false; + end + else + excite = varargin{n+1}; + end + elseif (strcmpi(varargin{n},'PortNamePrefix')) + PortNamePrefix = varargin{n+1}; + else + excite_args{end+1} = varargin{n}; + excite_args{end+1} = varargin{n+1}; + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% normalize start and stop +nstart = min( [start;stop] ); +nstop = max( [start;stop] ); + +% determine index (1, 2 or 3) of propagation (length of MSL) +idx_prop = dir + 1; + +% determine index (1, 2 or 3) of width of MSL +dir = [0 0 0]; +dir(idx_prop) = 1; +idx_width = abs(cross(dir,evec0)) * [1;2;3]; + +% determine index (1, 2 or 3) of height +idx_height = abs(evec0) * [1;2;3]; + +if (start(idx_height)~=stop(idx_height)) + error('openEMS:AddStripLinePort','start/stop in height direction must be equal'); +end + +% direction of propagation +if stop(idx_prop)-start(idx_prop) > 0 + direction = +1; +else + direction = -1; +end + +% create the metal/material for the MSL +SL_start = start; +SL_stop = stop; +CSX = AddBox( CSX, materialname, prio, SL_start, SL_stop ); + +if isnan(measplanepos) + measplanepos = (nstart(idx_prop)+nstop(idx_prop))/2; +else + measplanepos = start(idx_prop)+direction*measplanepos; +end + +% calculate position of the voltage probes +mesh{1} = sort(CSX.RectilinearGrid.XLines); +mesh{2} = sort(CSX.RectilinearGrid.YLines); +mesh{3} = sort(CSX.RectilinearGrid.ZLines); +meshlines = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), measplanepos, 'nearest' ); +meshlines = mesh{idx_prop}(meshlines-1:meshlines+1); % get three lines (approx. at center) +if direction == -1 + meshlines = fliplr(meshlines); +end +SL_w2 = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), (nstart(idx_width)+nstop(idx_width))/2, 'nearest' ); +SL_w2 = mesh{idx_width}(SL_w2); % get e-line at center of MSL (SL_width/2) +v1_start(idx_prop) = meshlines(1); +v1_start(idx_width) = SL_w2; +v1_start(idx_height) = start(idx_height); +v1_stop = v1_start; +v1_stop(idx_height) = v1_start(idx_height); +v2_start = v1_start; +v2_stop = v1_stop; +v2_start(idx_prop) = meshlines(2); +v2_stop(idx_prop) = meshlines(2); +v3_start = v2_start; +v3_stop = v2_stop; +v3_start(idx_prop) = meshlines(3); +v3_stop(idx_prop) = meshlines(3); + +height_vector = [0 0 0]; +height_vector(idx_height) = height; + +weight = 0.5; +% create the voltage-probes +port.U_filename{1,1} = [PortNamePrefix 'port_ut' num2str(portnr) 'A1']; +CSX = AddProbe( CSX, port.U_filename{1,1}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{1,1}, prio, v1_start, v1_stop+height_vector); + +port.U_filename{1,2} = [PortNamePrefix 'port_ut' num2str(portnr) 'A2']; +CSX = AddProbe( CSX, port.U_filename{1,2}, 0, 'weight', -1*weight ); +CSX = AddBox( CSX, port.U_filename{1,2}, prio, v1_start, v1_stop-height_vector); + + +port.U_filename{2,1} = [PortNamePrefix 'port_ut' num2str(portnr) 'B1']; +CSX = AddProbe( CSX, port.U_filename{2,1}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{2,1}, prio, v2_start, v2_stop+height_vector ); + +port.U_filename{2,2} = [PortNamePrefix 'port_ut' num2str(portnr) 'B2']; +CSX = AddProbe( CSX, port.U_filename{2,2}, 0, 'weight', -1*weight ); +CSX = AddBox( CSX, port.U_filename{2,2}, prio, v2_start, v2_stop-height_vector ); + + +port.U_filename{3,1} = [PortNamePrefix 'port_ut' num2str(portnr) 'C1']; +CSX = AddProbe( CSX, port.U_filename{3,1}, 0, 'weight', weight ); +CSX = AddBox( CSX, port.U_filename{3,1}, prio, v3_start, v3_stop+height_vector ); + +port.U_filename{3,2} = [PortNamePrefix 'port_ut' num2str(portnr) 'C2']; +CSX = AddProbe( CSX, port.U_filename{3,2}, 0, 'weight', -1*weight ); +CSX = AddBox( CSX, port.U_filename{3,2}, prio, v3_start, v3_stop-height_vector ); + +% calculate position of the current probes +idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstart(idx_width), 'nearest' ); +i1_start(idx_width) = mesh{idx_width}(idx) - diff(mesh{idx_width}(idx-1:idx))/2; +idx = interp1( mesh{idx_height}, 1:numel(mesh{idx_height}), start(idx_height), 'nearest' ); +i1_start(idx_height) = mesh{idx_height}(idx-1) - diff(mesh{idx_height}(idx-2:idx-1))/2; +i1_stop(idx_height) = mesh{idx_height}(idx+1) + diff(mesh{idx_height}(idx+1:idx+2))/2; +i1_start(idx_prop) = sum(meshlines(1:2))/2; +i1_stop(idx_prop) = i1_start(idx_prop); +idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstop(idx_width), 'nearest' ); +i1_stop(idx_width) = mesh{idx_width}(idx) + diff(mesh{idx_width}(idx:idx+1))/2; +i2_start = i1_start; +i2_stop = i1_stop; +i2_start(idx_prop) = sum(meshlines(2:3))/2; +i2_stop(idx_prop) = i2_start(idx_prop); + +% create the curr-probes +weight = direction; +port.I_filename{1} = [PortNamePrefix 'port_it' num2str(portnr) 'A']; +CSX = AddProbe( CSX, port.I_filename{1}, 1, 'weight', weight ); +CSX = AddBox( CSX, port.I_filename{1}, prio, i1_start, i1_stop ); +port.I_filename{2} = [PortNamePrefix 'port_it' num2str(portnr) 'B']; +CSX = AddProbe( CSX, port.I_filename{2}, 1,'weight', weight ); +CSX = AddBox( CSX, port.I_filename{2}, prio, i2_start, i2_stop ); + +% create port structure +port.LengthScale = 1; +if ((CSX.ATTRIBUTE.CoordSystem==1) && (idx_prop==2)) + port.LengthScale = SL_stop(idx_height); +end +port.nr = portnr; +port.type = 'StripLine'; +port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; +port.v_delta = diff(meshlines)*port.LengthScale; +port.i_delta = diff( meshlines(1:end-1) + diff(meshlines)/2 )*port.LengthScale; +port.direction = direction; +port.excite = 0; +port.measplanepos = abs(v2_start(idx_prop) - start(idx_prop))*port.LengthScale; +% port + +% create excitation (if enabled) and port resistance +meshline = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), start(idx_prop) + feed_shift*direction, 'nearest' ); +ex_start(idx_prop) = mesh{idx_prop}(meshline) ; +ex_start(idx_width) = nstart(idx_width); +ex_start(idx_height) = nstart(idx_height); +ex_stop(idx_prop) = ex_start(idx_prop); +ex_stop(idx_width) = nstop(idx_width); +ex_stop(idx_height) = nstop(idx_height); + +port.excite = 0; +if excite + port.excite = 1; + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_1_' num2str(portnr)], 0, evec, excite_args{:} ); + CSX = AddBox( CSX, [PortNamePrefix 'port_excite_1_' num2str(portnr)], prio, ex_start, ex_stop+height_vector ); + CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_2_' num2str(portnr)], 0, -evec, excite_args{:} ); + CSX = AddBox( CSX, [PortNamePrefix 'port_excite_2_' num2str(portnr)], prio, ex_start, ex_stop-height_vector ); +end + +%% MSL resitance at start of MSL line +ex_start(idx_prop) = start(idx_prop); +ex_stop(idx_prop) = ex_start(idx_prop); + +if (feed_R > 0) && ~isinf(feed_R) + CSX = AddLumpedElement( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], idx_height-1, 'R', 2*feed_R ); + CSX = AddBox( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], prio, ex_start, ex_stop+height_vector ); + CSX = AddBox( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], prio, ex_start, ex_stop-height_vector ); +elseif isinf(feed_R) + % do nothing --> open port +elseif feed_R == 0 + %port "resistance" as metal + CSX = AddBox( CSX, materialname, prio, ex_start, ex_stop+height_vector ); + CSX = AddBox( CSX, materialname, prio, ex_start, ex_stop-height_vector ); +else + error('openEMS:AddMSLPort','MSL port with resitance <= 0 it not possible'); +end +end diff --git a/openEMS/matlab/AddWaveGuidePort.m b/openEMS/matlab/AddWaveGuidePort.m new file mode 100644 index 0000000..1a17db2 --- /dev/null +++ b/openEMS/matlab/AddWaveGuidePort.m @@ -0,0 +1,119 @@ +function [CSX,port] = AddWaveGuidePort( CSX, prio, portnr, start, stop, dir, E_WG_func, H_WG_func, kc, exc_amp, varargin ) +% function [CSX,port] = AddWaveGuidePort( CSX, prio, portnr, start, stop, dir, E_WG_func, H_WG_func, kc, exc_amp, varargin ) +% +% Create a waveguide port, including an optional excitation and probes +% +% Note: - The excitation will be located at the start position in the given direction +% - The voltage and current probes at the stop position in the given direction +% +% parameter: +% CSX: complete CSX structure (must contain a mesh) +% prio: priority of primitives +% start: start coordinates of waveguide port box +% stop: stop coordinates of waveguide port box +% dir: direction of port (0/1/2 or 'x'/'y'/'z'-direction) +% E_WG_func: electric field mode profile function as a string +% H_WG_func: magnetic field mode profile function as a string +% kc: cutoff wavenumber (defined by the waveguide dimensions) +% exc_amp: excitation amplitude (set 0 to be passive) +% +% optional (key/values): +% varargin: optional additional excitations options, see also AddExcitation +% 'PortNamePrefix': a prefix to the port name +% +% output: +% CSX: modified CSX structure +% port: port structure to use with calcPort +% +% example: +% % create a TE11 circular waveguide mode, using cylindircal coordinates +% p11 = 1.841; +% kc = p11 / radius; % cutoff wavenumber with radius in meter +% kc_draw = kc*unit; % cutoff wavenumber in drawing units +% +% % electric field mode profile +% func_E{1} = [ num2str(-1/kc_draw^2,15) '/rho*cos(a)*j1(' num2str(kc_draw,15) '*rho)']; +% func_E{2} = [ num2str(1/kc_draw,15) '*sin(a)*0.5*(j0(' num2str(kc_draw,15) '*rho)-jn(2,' num2str(kc_draw,15) '*rho))']; +% func_E{3} = 0; +% +% % magnetic field mode profile +% func_H{1} = [ '-1*' num2str(1/kc_draw,15) '*sin(a)*0.5*(j0(' num2str(kc_draw,15) '*rho)-jn(2,' num2str(kc_draw,15) '*rho))']; +% func_H{2} = [ num2str(-1/kc_draw^2,15) '/rho*cos(a)*j1(' num2str(kc_draw,15) '*rho)']; +% func_H{3} = 0; +% +% start=[mesh.r(1) mesh.a(1) 0 ]; +% stop =[mesh.r(end) mesh.a(end) 100]; +% [CSX, port{1}] = AddWaveGuidePort(CSX, 0, 1, start, stop, 2, func_E, func_H, kc, 1); +% +% openEMS matlab interface +% ----------------------- +% (c) 2013 Thorsten Liebig (thorsten.liebig@gmx.de) +% +% See also InitCSX, AddExcitation, calcWGPort, calcPort + +%check mesh +if ~isfield(CSX,'RectilinearGrid') + error 'mesh needs to be defined! Use DefineRectGrid() first!'; +end + +dir = DirChar2Int(dir); + +port.type='WaveGuide'; +port.nr=portnr; +port.kc = kc; +port.dir = dir; +port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit; + +PortNamePrefix = ''; + +varargin_tmp = varargin; +for n=1:2:numel(varargin_tmp) + if strcmpi('PortNamePrefix',varargin_tmp{n}) + PortNamePrefix = varargin_tmp{n+1}; + varargin([n n+1]) = []; + end +end + +% matlab adressing +dir = dir + 1; +dir_sign = sign(stop(dir) - start(dir)); +if (dir_sign==0) + dir_sign = 1; +end + +port.direction = dir_sign; + +E_WG_func{dir} = 0; +H_WG_func{dir} = 0; + +port.excite = 0; +if (exc_amp~=0) + if (start(dir)==stop(dir)) + error 'if waveguide port is to be excited, the length in propagation direction must not be zero' + end + e_start = start; + e_stop = stop; + e_stop(dir) = e_start(dir); + port.excite = 1; + port.excitepos = e_start(dir); + e_vec = [1 1 1]*exc_amp; + e_vec(dir) = 0; + exc_name = [PortNamePrefix 'port_excite_' num2str(portnr)]; + CSX = AddExcitation( CSX, exc_name, 0, e_vec, varargin{:}); + CSX = SetExcitationWeight(CSX, exc_name, E_WG_func ); + CSX = AddBox( CSX, exc_name, prio, e_start, e_stop); +end + +% voltage/current planes +m_start = start; +m_stop = stop; +m_start(dir) = stop(dir); + +port.measplanepos = m_start(dir); +port.U_filename = [PortNamePrefix 'port_ut' int2str(portnr)]; +CSX = AddProbe(CSX, port.U_filename, 10, 'ModeFunction', E_WG_func); +CSX = AddBox(CSX, port.U_filename, 0 ,m_start, m_stop); + +port.I_filename = [PortNamePrefix 'port_it' int2str(portnr)]; +CSX = AddProbe(CSX, port.I_filename, 11, 'ModeFunction', H_WG_func, 'weight', dir_sign); +CSX = AddBox(CSX, port.I_filename, 0 ,m_start, m_stop); diff --git a/openEMS/matlab/AnalyzeNF2FF.m b/openEMS/matlab/AnalyzeNF2FF.m new file mode 100644 index 0000000..bc88b72 --- /dev/null +++ b/openEMS/matlab/AnalyzeNF2FF.m @@ -0,0 +1,231 @@ +function [E_theta,E_phi,Prad,Dmax] = AnalyzeNF2FF( Sim_Path, nf2ff, f, theta, phi, r ) +% [E_theta,E_phi,Prad,Dmax] = AnalyzeNF2FF( Sim_Path, filenames_E, filenames_H, f, theta, phi, r ) +% +% calculates the farfield via a near field to far field transformation +% +% input: +% Sim_Path: simulation directory +% nf2ff: structure on filenames etc. as created by CreateNF2FFBox +% f: frequency (Hz) for far field calculation +% theta: (degrees) vector of discrete theta values to calculate the far field for +% phi: (degrees) vector of discrete phi values to calculate the far field for +% r: (optional) Radius (m) at which the E-fields are calculated (default: 1 m) +% +% output: +% E_theta: E_theta(theta,phi); theta component of the electric field strength at radius r +% E_phi: E_phi(theta,phi); phi component of the electric field strength at radius r +% Prad: time averaged radiated power +% Dmax: maximum directivity +% +% example: +% see examples/NF2FF/infDipol.m +% +% See also CreateNF2FFBox +% +% (C) 2010 Sebastian Held <sebastian.held@gmx.de> +% (C) 2011 Thorsten Liebig <thorsten.liebig@gmx.de> + +% check arguments +error( nargchk(6,6,nargin) ); +if ~isscalar(f) + error 'Currently only one frequency is supported. Call this function multiple times.' +end + +warning('openEMS:AnalyzeNF2FF','This function is deprecated, use CalcNF2FF instead'); + +filenames_E = nf2ff.filenames_E; +filenames_H = nf2ff.filenames_H; + +if (~isrow(nf2ff.directions)) + nf2ff.directions = nf2ff.directions'; +end + +% read time domain field data and transform into frequency domain +for n=find(nf2ff.directions==1) + [Ef{n}, E_mesh{n}] = ReadHDF5Dump( [Sim_Path '/' filenames_E{n} '.h5'], 'Frequency', f ); + + if (Ef{n}.FD.frequency(1) ~= f) + error 'frequency mismach' + end + + %clear out time domain data + if isfield(Ef{n},'TD') + Ef{n} = rmfield(Ef{n},'TD'); + end + + [Hf{n}, H_mesh{n}] = ReadHDF5Dump( [Sim_Path '/' filenames_H{n} '.h5'], 'Frequency', f ); + %clear out time domain data + if isfield(Hf{n},'TD') + Hf{n} = rmfield(Hf{n},'TD'); + end + + % reshape mesh into row vector + mesh{n}.x = reshape( E_mesh{n}.lines{1}, 1, [] ); + mesh{n}.y = reshape( E_mesh{n}.lines{2}, 1, [] ); + mesh{n}.z = reshape( E_mesh{n}.lines{3}, 1, [] ); +end + +% create a normal vector for every plane +% FIXME!!! this is dependent upon the order of filenames_* +n = {}; +for a=1:6 + temp = [(a<=2), ((a>=3)&&(a<=4)), (a>=5)]; + n{a} = temp - 2*mod(a,2)*temp; +end + + +physical_constants + +k = 2*pi*f/c0; +center = [0 0 0]; +Umax = 0; + +phi_idx = 0; +for phi_deg_aufpunkt = phi + phi_rad_aufpunkt = phi_deg_aufpunkt/180*pi; % radiant + phi_idx = phi_idx + 1; + + theta_idx = 0; + for theta_deg_aufpunkt = theta + theta_rad_aufpunkt = theta_deg_aufpunkt/180*pi; % radiant + theta_idx = theta_idx + 1; + + N_theta = 0; + N_phi = 0; + L_theta = 0; + L_phi = 0; + for a=find(nf2ff.directions==1) + [N_theta_,N_phi_,L_theta_,L_phi_] = process_plane( k, n{a}, center, mesh{a}, Ef{a}.FD.values{1}, Hf{a}.FD.values{1}, theta_rad_aufpunkt, phi_rad_aufpunkt ); + N_theta = N_theta + N_theta_; N_phi = N_phi + N_phi_; + L_theta = L_theta + L_theta_; L_phi = L_phi + L_phi_; + end + + % E-fields + erg_E_theta = -1i*k*exp(-1i*k*r) / (4*pi*r)*(L_phi+Z0*N_theta); + erg_E_phi = 1i*k*exp(-1i*k*r) / (4*pi*r)*(L_theta-Z0*N_phi); + + % output + E_theta(theta_idx,phi_idx) = erg_E_theta; + E_phi(theta_idx,phi_idx) = erg_E_phi; + + % directivity + U = r^2/(2*Z0) * sum(abs([erg_E_theta erg_E_phi]).^2); + Umax = max( [Umax U] ); + end +end + +% power +Prad = 0; +for a=find(nf2ff.directions==1) + [~,~,~,~,P] = process_plane( k, n{a}, center, mesh{a}, Ef{a}.FD.values{1}, Hf{a}.FD.values{1}, theta_rad_aufpunkt, phi_rad_aufpunkt ); + Prad = Prad + P; +end + +% directivity +Dmax = 4*pi*Umax / Prad; + + +% integrate over one plane +function [N_theta,N_phi,L_theta,L_phi,Prad] = process_plane( k, n, center, mesh, E_field, H_field, theta_rad_aufpunkt, phi_rad_aufpunkt ) +% [N_theta,N_phi,L_theta,L_phi,Prad] = process_plane( k, n, center, mesh, E_field, H_field, theta_rad_aufpunkt, phi_rad_aufpunkt ) +% +% k: wave number +% n: normal vector of the plane +% center: correction coordinates for the center of the antenna +% mesh: mesh info +% E_field: E field array ?x?x?x3 +% H_field: H field array ?x?x?x3 + +% speed up +sin__theta_rad_aufpunkt = sin(theta_rad_aufpunkt); +cos__theta_rad_aufpunkt = cos(theta_rad_aufpunkt); +sin__phi_rad_aufpunkt = sin(phi_rad_aufpunkt); +cos__phi_rad_aufpunkt = cos(phi_rad_aufpunkt); + +if abs(n(1)) == 1 + % x-plane + x = mesh.x(1); + [y z] = ndgrid( mesh.y, mesh.z ); + coord1 = mesh.y.'; + coord2 = mesh.z.'; + Ex = squeeze( E_field(1,:,:,1) ); + Ey = squeeze( E_field(1,:,:,2) ); + Ez = squeeze( E_field(1,:,:,3) ); + Hx = squeeze( H_field(1,:,:,1) ); + Hy = squeeze( H_field(1,:,:,2) ); + Hz = squeeze( H_field(1,:,:,3) ); +elseif abs(n(2)) == 1 + % y-plane + y = mesh.y(1); + [x z] = ndgrid( mesh.x, mesh.z ); + coord1 = mesh.x.'; + coord2 = mesh.z.'; + Ex = squeeze( E_field(:,1,:,1) ); + Ey = squeeze( E_field(:,1,:,2) ); + Ez = squeeze( E_field(:,1,:,3) ); + Hx = squeeze( H_field(:,1,:,1) ); + Hy = squeeze( H_field(:,1,:,2) ); + Hz = squeeze( H_field(:,1,:,3) ); +elseif abs(n(3)) == 1 + % z-plane + z = mesh.z(1); + [x y] = ndgrid( mesh.x, mesh.y ); + coord1 = mesh.x.'; + coord2 = mesh.y.'; + Ex = squeeze( E_field(:,:,1,1) ); + Ey = squeeze( E_field(:,:,1,2) ); + Ez = squeeze( E_field(:,:,1,3) ); + Hx = squeeze( H_field(:,:,1,1) ); + Hy = squeeze( H_field(:,:,1,2) ); + Hz = squeeze( H_field(:,:,1,3) ); +end + +Jx = n(2) .* Hz - n(3) .* Hy; +Jy = n(3) .* Hx - n(1) .* Hz; +Jz = n(1) .* Hy - n(2) .* Hx; +Mx = -n(2) .* Ez + n(3) .* Ey; +My = -n(3) .* Ex + n(1) .* Ez; +Mz = -n(1) .* Ey + n(2) .* Ex; +r_cos_psi = x*sin__theta_rad_aufpunkt*cos__phi_rad_aufpunkt + y*sin__theta_rad_aufpunkt*sin__phi_rad_aufpunkt + z*cos__theta_rad_aufpunkt; +e_fkt = exp( +1i*k*r_cos_psi ); +N_theta = dbltrapz( ( Jx*cos__theta_rad_aufpunkt*cos__phi_rad_aufpunkt + Jy*cos__theta_rad_aufpunkt*sin__phi_rad_aufpunkt - Jz*sin__theta_rad_aufpunkt) .* e_fkt, coord1, coord2 ); +N_phi = dbltrapz( (-Jx*sin__phi_rad_aufpunkt + Jy*cos__phi_rad_aufpunkt) .* e_fkt, coord1, coord2 ); +L_theta = dbltrapz( ( Mx*cos__theta_rad_aufpunkt*cos__phi_rad_aufpunkt + My*cos__theta_rad_aufpunkt*sin__phi_rad_aufpunkt - Mz*sin__theta_rad_aufpunkt) .* e_fkt, coord1, coord2 ); +L_phi = dbltrapz( (-Mx*sin__phi_rad_aufpunkt + My*cos__phi_rad_aufpunkt) .* e_fkt, coord1, coord2 ); + +if nargout > 4 + % Prad requested + + % this is crap! recode it! + EH = zeros(size(Ex)); + for i1 = 1:numel(coord1) + for i2 = 1:numel(coord2) + E = [Ex(i1,i2) Ey(i1,i2) Ez(i1,i2)]; + H = [Hx(i1,i2) Hy(i1,i2) Hz(i1,i2)]; + EH(i1,i2) = real( dot(cross(E,conj(H)),n) ); + end + end + Prad = 0.5 * dbltrapz( EH, coord1, coord2 ); +end + + + + +function Q = dbltrapz(matrix,a,b) +%DBLTRAPZ Trapezoidal numerical integration in two dimensions. +% Z = DBLTRAPZ(MATRIX,A,B) computes an approximation of the double integral +% of MATRIX via the trapezoidal method (with respect to A and B). A and B must be +% column vectors of the same length. +% index like this: MATRIX(A,B) + +if nargin < 3, error('MATLAB:dblquad:NotEnoughInputs',... + 'Requires at least three inputs.'); end +if size(a,2) ~= 1, error('column vectors required'); end +if size(b,2) ~= 1, error('column vectors required'); end + +temp = zeros(size(b)); +for i = 1:length(b) + temp(i) = trapz( a, matrix(:,i) ); +end + +Q = trapz( b, temp ); diff --git a/openEMS/matlab/CalcNF2FF.m b/openEMS/matlab/CalcNF2FF.m new file mode 100644 index 0000000..c80b52e --- /dev/null +++ b/openEMS/matlab/CalcNF2FF.m @@ -0,0 +1,161 @@ +function nf2ff = CalcNF2FF(nf2ff, Sim_Path, freq, theta, phi, varargin) +% function nf2ff = CalcNF2FF(nf2ff, Sim_Path, freq, theta, phi, varargin) +% +% Calculate the near-field to far-field transformation created by +% CreateNF2FFBox +% +% IMPORTANT: +% Make sure to define the correct nf2ff phase center, aka. central antenna +% position! See optional parameter below!! Default is [0 0 0] +% +% parameter: +% nf2ff: data structure created by CreateNF2FFBox +% Sim_Path: path to simulation data +% freq: array of frequencies to analyse +% theta,phi: spherical coordinates to evaluate the far-field on (in radians) +% +% optional paramater: +% 'Center': nf2ff phase center, default is [0 0 0] +% !! Make sure the center is never outside of your nf2ff box!! +% Definition is the correct coordinate system necessary +% --> either Cartesian or cylindrical coordinates +% 'Mode': 'Mode', 0 -> read only, if data already exist (default) +% 'Mode', 1 -> calculate anyway, overwrite existing +% 'Mode', 2 -> read only, fail if not existing +% 'Outfile': alternative nf2ff result hdf5 file name +% default is: <nf2ff.name>.h5 +% 'Verbose': set verbose level for the nf2ff calculation 0-2 supported +% 'Radius': specify the radius for the nf2ff +% 'Eps_r': specify the relative electric permittivity for the nf2ff +% 'Mue_r': specify the relative magnetic permeability for the nf2ff +% +% 'Mirror': Add mirroring in a given direction (dir), with a given +% mirror type (PEC or PMC) and a mirror position in the given +% direction. +% Example: 'Mirror', {0, 'PMC', +100} +% +% See also: CreateNF2FFBox, ReadNF2FF +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig, 2012 + +mode = 0; + +filename = nf2ff.name; +nf2ff_xml.Planes = {}; + +nf2ff_xml.ATTRIBUTE.Outfile = [filename '.h5']; + +if (isfield(nf2ff,'Eps_r')) + nf2ff_xml.ATTRIBUTE.Eps_r = nf2ff.Eps_r; +end +if (isfield(nf2ff,'Mue_r')) + nf2ff_xml.ATTRIBUTE.Mue_r = nf2ff.Mue_r; +end + +for n=1:2:numel(varargin)-1 + if (strcmp(varargin{n},'Mode')) + mode = varargin{n+1}; + elseif (strcmp(varargin{n},'Mirror')) + if isfield(nf2ff_xml,'Mirror') + pos = length(nf2ff_xml.Mirror)+1; + else + pos = 1; + end + nf2ff_xml.Mirror{pos}.ATTRIBUTE.Dir=varargin{n+1}{1}; + nf2ff_xml.Mirror{pos}.ATTRIBUTE.Type=varargin{n+1}{2}; + nf2ff_xml.Mirror{pos}.ATTRIBUTE.Pos=varargin{n+1}{3}; + else + nf2ff_xml.ATTRIBUTE.(varargin{n})=varargin{n+1}; + end +end + +for (n=1:numel(nf2ff.filenames_E)) + if (nf2ff.directions(n)~=0) + files_E = dir([Sim_Path '/*' nf2ff.filenames_E{n} '.h5']); + files_H = dir([Sim_Path '/*' nf2ff.filenames_H{n} '.h5']); + if (numel(files_E)~=numel(files_H)) + error 'number of E/H planes mismatch!' + end + for fn = 1:numel(files_E) + nf2ff_xml.Planes{end+1}.ATTRIBUTE.E_Field = files_E(fn).name; + nf2ff_xml.Planes{end}.ATTRIBUTE.H_Field = files_H(fn).name; + end + end +end + +nf2ff_xml.ATTRIBUTE.freq = freq; +nf2ff_xml.theta = theta; +nf2ff_xml.phi = phi; + +nf2ff.xml = [Sim_Path '' filesep '' filename '.xml']; +nf2ff.hdf5 = [Sim_Path '' filesep '' nf2ff_xml.ATTRIBUTE.Outfile]; + +% create nf2ff structure +struct_2_xml(nf2ff.xml,nf2ff_xml,'nf2ff'); + +m_filename = mfilename('fullpath'); +dir_name = fileparts( m_filename ); + +if isunix + nf2ff_bin = searchBinary('nf2ff', ... + {[dir_name filesep '..' filesep 'nf2ff' filesep], ... + [dir_name filesep '..' filesep '..' filesep '..' filesep 'bin' filesep]}, 0); +else + nf2ff_bin = searchBinary('nf2ff.exe',[dir_name filesep '..' filesep], 0); +end + +if ((exist(nf2ff.hdf5,'file') && (mode==0)) || (mode==2)) + disp('CalcNF2FF: Reading nf2ff data only...') + nf2ff = ReadNF2FF(nf2ff); + + % verify read data + if ( (vectorEqual(nf2ff.freq,freq)==0) || (vectorEqual(nf2ff.theta,theta)==0) || (vectorEqual(nf2ff.phi,phi)==0) ) + error('openEMS:CalcNF2FF','data mismatch between read and requested data --> recalculate nf2ff --> Set Mode to 1 '); + end + return; +end + +savePath = pwd; +cd(Sim_Path); + +try + if (isempty(nf2ff_bin)) + error('openEMS:CalcNF2FF','nf2ff binary not found!'); + end + if isunix + % remove LD_LIBRARY_PATH set by matlab + system(['export LD_LIBRARY_PATH=; ' nf2ff_bin ' ' filename '.xml']); + else + system([nf2ff_bin ' ' filename '.xml']); + end + nf2ff.hdf5; + cd(savePath); +catch + cd(savePath); + error 'CalcNF2FF: failed' +end + +nf2ff = ReadNF2FF(nf2ff); + +% verify read data +if ( (vectorEqual(nf2ff.freq,freq)==0) || (vectorEqual(nf2ff.theta,theta)==0) || (vectorEqual(nf2ff.phi,phi)==0) ) + error('openEMS:CalcNF2FF','data mismatch between read and requested data --> THIS SHOULD NOT HAPPEN!'); +end + +function equal = vectorEqual(v1, v2, acc) +if (nargin<3) + acc = 1e-6; +end + +equal = 0; +if numel(v1)~=numel(v2) + return; +end + +if sum(abs((v1(:)-v2(:))/v1(:)) > acc)>0 + return; +end +equal = 1; +return diff --git a/openEMS/matlab/CheckQueue.m b/openEMS/matlab/CheckQueue.m new file mode 100644 index 0000000..748d706 --- /dev/null +++ b/openEMS/matlab/CheckQueue.m @@ -0,0 +1,60 @@ +function [queue running] = CheckQueue(queue, query_time) +% function [queue running] = CheckQueue(queue, <query_time>) +% +% Check the given queue for finished tasks. +% +% Parameter: +% query_time (optional): time interval to check for finished tasks +% (in seconds, default is 5) +% +% For more details see: InitQueue +% +% See also: InitQueue, ResultsQueue, Add2Queue, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if ~isfield(queue,'jobs') + running = 0; + return +end + +if (nargin<2) + query_time = 5; +end + +numJobs = numel(queue.jobs); + +pause(query_time); + +for n=1:numJobs + if (queue.jobs_finished(n)==0) + if (queue_checkProcess( queue.jobs{n}.pid, queue.jobs{n}.filenames)==0) + queue.jobs_finished(n)=1; + load(queue.jobs{n}.outargsfile); + if ~isempty(err) + disp(['Job with number ' num2str(n) ' failed to execute: Error message:']); + error(['CheckQueue:' err.message]); + end + queue.jobs{n}.outargs = outargs; + + % read in output and cleanup + [queue.jobs{n}.stdout,queue.jobs{n}.stderr] = queue_delProcess( queue.jobs{n}.pid, queue.jobs{n}.filenames ); + + % cleanup + delete( queue.jobs{n}.argsfile ); + clear queue.jobs{n}.argsfile; + delete( queue.jobs{n}.outargsfile ); + clear queue.jobs{n}.outargsfile; + + queue.jobs_finished(n) = 1; + + if (queue.verbose>=1) + disp(['CheckQueue: Job #' num2str(n) ' is finished!']); + end + end + end +end + +running = numel(queue.jobs_finished) - sum(queue.jobs_finished); diff --git a/openEMS/matlab/ConvertHDF5_VTK.m b/openEMS/matlab/ConvertHDF5_VTK.m new file mode 100644 index 0000000..545ba01 --- /dev/null +++ b/openEMS/matlab/ConvertHDF5_VTK.m @@ -0,0 +1,100 @@ +function ConvertHDF5_VTK(hdf_file, vtk_prefix, varargin) +% ConvertHDF5_VTK(hdf_file, vtk_prefix, varargin) +% +% Convert openEMS field data stored in the given hdf5 file to a vtk file. +% +% arguments: +% hdf_file: source hdf5 file +% vtk_prefix: output vtk files prefix +% +% optional arguments: +% 'TD_Dump': activate dump for time-domain data (default is off) +% 'FD_Dump': activate dump for frequency-domain data (default is on) +% 'NumPhase': number of phase to dump frequency domain data animation +% (default is 36 --> 10°) +% 'FieldName': field name written to vtk, e.g. 'E-Field' +% 'weight': field weighting +% +% for more optional aguments have a look at ReadHDF5Dump +% +% example: +% % read time-domian data from hdf5, perform dft and dump as vtk +% ConvertHDF5_VTK('Et.h5','Ef','NumPhase',18,'Frequency',1e9) +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5Dump Dump2VTK + +do_FD_dump = 1; +do_TD_dump = 0; +phase_N = 36; +weight = 1; + +fieldname = 'unknown'; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'TD_Dump')==1); + do_TD_dump = varargin{n+1}; + elseif (strcmp(varargin{n},'FD_Dump')==1); + do_FD_dump = varargin{n+1}; + elseif (strcmp(varargin{n},'NumPhase')==1); + phase_N = varargin{n+1}; + elseif (strcmp(varargin{n},'FieldName')==1); + fieldname = varargin{n+1}; + elseif (strcmp(varargin{n},'weight')==1); + weight = varargin{n+1}; + end +end + +[field mesh] = ReadHDF5Dump(hdf_file, varargin{:}); + +if ((do_TD_dump==0) && (do_FD_dump==0)) + warning('openEMS:ConvertHDF5_VTK','FD and TD dump disabled, nothing to be done...'); +end + +if (do_FD_dump) + if (~isfield(field,'FD')) + warning('openEMS:ConvertHDF5_VTK','no FD data found skipping frequency domian vtk dump...'); + else + %set weighting + if (numel(weight)~=numel(field.FD.frequency)) + FD_weight = ones(size(field.FD.frequency))*weight(1); + else + FD_weight = weight; + end + if (field.FD.DataType==1) % dump complex value FD data + ph = linspace(0,360,phase_N+1); + ph = ph(1:end-1); + for n = 1:numel(field.FD.frequency) + for p = ph + filename = [vtk_prefix '_' num2str(field.FD.frequency(n)) '_' num2str(p,'%03d') '.vtk' ]; + Dump2VTK(filename, real(FD_weight(n)*field.FD.values{n}*exp(1j*p*pi/180)), mesh, fieldname, varargin{:}); + end + filename = [vtk_prefix '_' num2str(field.FD.frequency(n)) '_abs.vtk' ]; + Dump2VTK(filename, abs(FD_weight(n)*field.FD.values{n}), mesh, fieldname, varargin{:}); + filename = [vtk_prefix '_' num2str(field.FD.frequency(n)) '_ang.vtk' ]; + Dump2VTK(filename, angle(FD_weight(n)*field.FD.values{n}), mesh, fieldname, varargin{:}); + end + else % dump real value FD data + for n = 1:numel(field.FD.frequency) + filename = [vtk_prefix '_' num2str(field.FD.frequency(n)) '.vtk' ]; + Dump2VTK(filename, real(FD_weight(n)*field.FD.values{n}), mesh, fieldname, varargin{:}); + end + end + end +end + +if (do_TD_dump) + if (~isfield(field,'TD')) + warning('openEMS:ConvertHDF5_VTK','no TD data found skipping time domian vtk dump...'); + else + disp('dumping time domain data...') + acc = ['%0' int2str(ceil(log10(numel(field.TD.time)+1))) 'd']; + for n = 1:numel(field.TD.time) + filename = [vtk_prefix '_TD_' num2str(n,acc) '.vtk' ]; + Dump2VTK(filename, abs(weight(1))*field.TD.values{n}, mesh, fieldname, varargin{:}); + end + end +end diff --git a/openEMS/matlab/CreateNF2FFBox.m b/openEMS/matlab/CreateNF2FFBox.m new file mode 100644 index 0000000..a97f0d2 --- /dev/null +++ b/openEMS/matlab/CreateNF2FFBox.m @@ -0,0 +1,94 @@ +function [CSX nf2ff] = CreateNF2FFBox(CSX, name, start, stop, varargin) +% function [CSX nf2ff] = CreateNF2FFBox(CSX, name, start, stop, varargin) +% +% create the dump boxes needed for the near field to far field transformation +% +% input: +% name: name of this nf2ff box +% start/stop: start/stop coordinates for the nf2ff box (this box has to +% enclose all radiating structures!) +% optional inputs: +% 'Directions': enable/disable specific directions, e.g. +% 'Directions',[1 1 0 0 1 1] +% -> disable nf2ff in +/-y direction +% 'Frequency': dump nf2ff in frequency domain, this will save disk-space +% but is less flexible, since only this frequencies can be +% used for the nf2ff calculations by CalcNF2FF +% See also AddDump for more information +% 'OptResolution': specify a dump resolution, this will save disk-space +% See also AddDump for more information +% e.g.: 'OptResolution', c0/max_freq/unit/15 +% +% example: +% see Tutorials/Simple_Patch_Antenna.m +% see Tutorials/Helical_Antenna.m +% +% See also CalcNF2FF +% +% (C) 2010 Sebastian Held <sebastian.held@gmx.de> +% (C) 2010-2012 Thorsten Liebig <thorsten.liebig@gmx.de> + +if (nargin<5) + directions = ones(6,1); +end + +directions = ones(6,1); +add_args = {}; +dump_type = 0; +dump_mode = 1; + +for n=1:numel(varargin)/2 + if strcmp(varargin{2*n-1},'Frequency') + add_args = {add_args{:}, 'Frequency', varargin{2*n}}; + dump_type = 10; + elseif strcmp(varargin{2*n-1},'Directions') + directions=varargin{2*n}; + else + add_args = {add_args{:}, varargin{2*n-1}, varargin{2*n}}; + end +end + +nf2ff.name = name; +nf2ff.filenames_E = {[name '_E_xn'],[name '_E_xp'],[name '_E_yn'],[name '_E_yp'],[name '_E_zn'],[name '_E_zp']}; +nf2ff.filenames_H = {[name '_H_xn'],[name '_H_xp'],[name '_H_yn'],[name '_H_yp'],[name '_H_zn'],[name '_H_zp']}; +nf2ff.directions = directions; + +if (isfield(CSX,'ATTRIBUTE')) + if (isfield(CSX.ATTRIBUTE,'CoordSystem')) + nf2ff.CoordSystem = CSX.ATTRIBUTE.CoordSystem; + end + if (isfield(CSX,'BackgroundMaterial')) + if (isfield(CSX.ATTRIBUTE,'Epsilon')) + nf2ff.Eps_r = CSX.ATTRIBUTE.BG_epsR; + end + if (isfield(CSX.ATTRIBUTE,'Mue')) + nf2ff.Mue_r = CSX.ATTRIBUTE.BG_mueR; + end + end +end + +for nd = 1:3 + pos = 2*nd-1; + if (directions(pos)) + l_start = start; + l_stop = stop; + l_stop(nd) = start(nd); + CSX = AddBox( AddDump(CSX,nf2ff.filenames_E{pos},'DumpType',dump_type,'DumpMode',dump_mode,'FileType',1,add_args{:}), nf2ff.filenames_E{pos}, 0, l_start, l_stop ); + CSX = AddBox( AddDump(CSX,nf2ff.filenames_H{pos},'DumpType',dump_type+1,'DumpMode',dump_mode,'FileType',1,add_args{:}), nf2ff.filenames_H{pos}, 0, l_start, l_stop ); + else + nf2ff.filenames_E{pos}=''; + nf2ff.filenames_H{pos}=''; + end + pos = 2*nd; + if (directions(pos)) + l_start = start; + l_stop = stop; + l_start(nd) = stop(nd); + CSX = AddBox( AddDump(CSX,nf2ff.filenames_E{pos},'DumpType',dump_type,'DumpMode',dump_mode,'FileType',1,add_args{:}), nf2ff.filenames_E{pos}, 0, l_start, l_stop ); + CSX = AddBox( AddDump(CSX,nf2ff.filenames_H{pos},'DumpType',dump_type+1,'DumpMode',dump_mode,'FileType',1,add_args{:}), nf2ff.filenames_H{pos}, 0, l_start, l_stop ); + else + nf2ff.filenames_E{pos}=''; + nf2ff.filenames_H{pos}=''; + end +end + diff --git a/openEMS/matlab/DFT_time2freq.m b/openEMS/matlab/DFT_time2freq.m new file mode 100644 index 0000000..ac84035 --- /dev/null +++ b/openEMS/matlab/DFT_time2freq.m @@ -0,0 +1,47 @@ +function f_val = DFT_time2freq( t, val, freq, signal_type ) +% f_val = DFT_time2freq( t, val, freq, signal_type ) +% +% computes the DFT at the given frequencies +% +% parameter: +% t : time vector +% val: data vector +% freq: DFT frequency vector +% signal_type: 'pulse' (default), 'periodic' +% +% return values: +% f_val: single-sided spectrum +% +% example: +% t=linspace(0,1,100); +% t_val=0.9*sin(2*pi*3*t); % sine wave; amplitude 0.9; frequency 3 Hz +% f=linspace(1,5,101); +% f_val=DFT_time2freq( t, t_val, f, 'periodic' ); +% interp1(f,abs(f_val),3) +% ans = 0.8910 +% plot( t, t_val ) +% plot( f, abs(f_val) ) + +if numel(t) ~= numel(val) + error 'numel(t) ~= numel(val)' +end + +if nargin<4 + signal_type = 'pulse'; +end + +f_val = zeros(1,numel(freq)); +for f_idx=1:numel(freq) + f_val(f_idx) = sum( val .* exp( -1i * 2*pi*freq(f_idx) * t ) ); +end + +if strcmpi(signal_type, 'pulse') + dt = t(2)-t(1); + f_val = f_val * dt; +elseif strcmpi(signal_type, 'periodic') + f_val = f_val / length(t); +else + error 'unknown signal type' +end + +f_val = f_val * 2; % single-sided spectrum diff --git a/openEMS/matlab/Dump2VTK.m b/openEMS/matlab/Dump2VTK.m new file mode 100644 index 0000000..e57b153 --- /dev/null +++ b/openEMS/matlab/Dump2VTK.m @@ -0,0 +1,179 @@ +function Dump2VTK(filename, fields, mesh, fieldname, varargin) +% Dump2VTK(filename, fields, mesh, fieldname, varargin) +% +% Dump fields extraced from an hdf5 file to a vtk file format +% +% possible arguments: +% 'NativeDump': 0 (default) / 1, dump in native coordinate system +% 'CloseAlpha': 0 (default) / 1, repeat first/last line in +% alpha-direction for a full cylindrical mesh +% +% example: +% +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5FieldData ReadHDF5Mesh GetField_TD2FD GetField_Interpolation + +NativeDump = 0; +CloseAlpha = 0; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'NativeDump')==1); + NativeDump = varargin{n+1}; + elseif (strcmp(varargin{n},'CloseAlpha')==1); + CloseAlpha = varargin{n+1}; + end +end + +x = mesh.lines{1}; +y = mesh.lines{2}; +z = mesh.lines{3}; + +fid = fopen(filename,'w+'); + +% set nan values to zero +ind = find(isnan(fields)); +if (~isempty(ind)) + warning('openEMS:Dump2VTK','field contains nan, setting to zero'); + fields(ind)=0; +end + +% set inf values to zero +ind = find(isinf(fields)); +if (~isempty(ind)) + warning('openEMS:Dump2VTK','field contains inf, setting to zero'); + fields(ind)=0; +end + +if ((CloseAlpha~=0) && (mesh.type==1) && (range(y)<2*pi)) + y(end+1) = y(1)+2*pi; + fields(:,end+1,:,:) = fields(:,1,:,:); +end + +if (mesh.type==0) %write cartesian mesh to vtk + fprintf(fid,'# vtk DataFile Version 2.0\n'); + fprintf(fid,'Rectilinear Grid by matlab-interface of openEMS\n'); + fprintf(fid,'ASCII\n'); + fprintf(fid,'DATASET RECTILINEAR_GRID\n'); + + fprintf(fid,'DIMENSIONS %d %d %d\n',numel(x),numel(y),numel(z)); + + fprintf(fid,'X_COORDINATES %d double\n',numel(x)); + fprintf(fid,'%e',x(1)); + for n=2:numel(x) + fprintf(fid,' %e',x(n)); + end + fprintf(fid,'\n'); + + fprintf(fid,'Y_COORDINATES %d double\n',numel(y)); + fprintf(fid,'%e',y(1)); + for n=2:numel(y) + fprintf(fid,' %e',y(n)); + end + fprintf(fid,'\n'); + + fprintf(fid,'Z_COORDINATES %d double\n',numel(z)); + fprintf(fid,'%e',z(1)); + for n=2:numel(z) + fprintf(fid,' %e',z(n)); + end + +elseif (mesh.type==1) %write cylindrical mesh to vtk + fprintf(fid,'# vtk DataFile Version 3.0\n'); + fprintf(fid,'Structured Grid by matlab-interface of openEMS\n'); + fprintf(fid,'ASCII\n'); + fprintf(fid,'DATASET STRUCTURED_GRID\n'); + + fprintf(fid,'DIMENSIONS %d %d %d\n',numel(x),numel(y),numel(z)); + + fprintf(fid,'POINTS %d double\n',numel(x)*numel(y)*numel(z)); + + for nz=1:numel(z) + for ny=1:numel(y) + for nx=1:numel(x) + fprintf(fid,'%e %e %e\n',x(nx)*cos(y(ny)),x(nx)*sin(y(ny)),z(nz)); + end + end + end + if ((ndims(fields)==4) && (NativeDump==0)) + [R A Z] = ndgrid(x,y,z); + sinA = sin(A); + cosA = cos(A); + field_CC(:,:,:,1) = fields(:,:,:,1) .* cosA - fields(:,:,:,2) .* sinA; + field_CC(:,:,:,2) = fields(:,:,:,1) .* sinA + fields(:,:,:,2) .* cosA; + field_CC(:,:,:,3) = fields(:,:,:,3); + fields = field_CC; + clear R A Z sinA cosA field_CC + end +elseif (mesh.type==2) %write spherical mesh to vtk + fprintf(fid,'# vtk DataFile Version 3.0\n'); + fprintf(fid,'Structured Grid by matlab-interface of openEMS\n'); + fprintf(fid,'ASCII\n'); + fprintf(fid,'DATASET STRUCTURED_GRID\n'); + + fprintf(fid,'DIMENSIONS %d %d %d\n',numel(x),numel(y),numel(z)); + + fprintf(fid,'POINTS %d double\n',numel(x)*numel(y)*numel(z)); + + for nz=1:numel(z) + for ny=1:numel(y) + for nx=1:numel(x) + fprintf(fid,'%e %e %e\n',... + x(nx)*sin(y(ny))*cos(z(nz)),... + x(nx)*sin(y(ny))*sin(z(nz)),... + x(nx)*cos(y(ny))); + end + end + end + + if ((ndims(fields)==4) && (NativeDump==0)) + [R T A] = ndgrid(x,y,z); + sinA = sin(A); + cosA = cos(A); + sinT = sin(T); + cosT = cos(T); + field_CC(:,:,:,1) = fields(:,:,:,1) .* sinT .* cosA + fields(:,:,:,2) .*cosT .* cosA - fields(:,:,:,3) .* sinA; + field_CC(:,:,:,2) = fields(:,:,:,1) .* sinT .* cosA + fields(:,:,:,2) .*cosT .* sinA + fields(:,:,:,3) .* cosA; + field_CC(:,:,:,3) = fields(:,:,:,1) .* cosT - fields(:,:,:,2) .*sinT; + fields = field_CC; + clear R A T sinA cosA sinT cosT field_CC + end +end + + +fprintf(fid,'\n\n'); + +fprintf(fid,'POINT_DATA %d\n',numel(x)*numel(y)*numel(z)); +% dump vector field data +if (size(fields,4)>1) + if (nargin>3) + fprintf(fid,['VECTORS ' fieldname ' double\n']); + else + fprintf(fid,'VECTORS field double\n'); + end + fclose(fid); + field_x = fields(:,:,:,1); + field_y = fields(:,:,:,2); + field_z = fields(:,:,:,3); + clear fields + dumpField(:,1) = field_x(:); + dumpField(:,2) = field_y(:); + dumpField(:,3) = field_z(:); + save('-ascii','-append',filename,'dumpField') + return +elseif (size(fields,4)==1) % scalar field + if (nargin>3) + fprintf(fid,['SCALARS ' fieldname ' double 1\nLOOKUP_TABLE default\n']); + else + fprintf(fid,'SCALARS field double 1\nLOOKUP_TABLE default\n'); + end + fclose(fid); + dumpField = fields(:); + save('-ascii','-append',filename,'dumpField') + return +end + +fclose(fid); diff --git a/openEMS/matlab/DumpFF2VTK.m b/openEMS/matlab/DumpFF2VTK.m new file mode 100644 index 0000000..8e6e2f8 --- /dev/null +++ b/openEMS/matlab/DumpFF2VTK.m @@ -0,0 +1,105 @@ +function DumpFF2VTK(filename, farfield, thetaRange, phiRange, varargin) +% DumpFF2VTK(filename, farfield, thetaRange, phiRange, varargin) +% +% Dump 3D far field pattern to a vtk file +% +% input: +% filename: filename of VTK file, existing file will be overwritten +% farfield: farfield in V/m +% thetaRange: theta range in deg +% phiRange: phi range in deg +% +% variable input: +% 'scale': - linear scale of plot, doesn't affect gain values +% 'logscale': - if set, show farfield with logarithmic scale +% - set the dB value for point of origin +% - values below will be clamped +% 'maxgain': - add max gain in dB to normalized farfield +% - only valid if logscale is set +% - default is 0dB +% +% example: +% DumpFF2VTK(filename, farfield, thetaRange, phiRange, ... +% 'scale', 2, 'logscale', -20, 'maxgain', 3) +% +% see also examples/NF2FF/infDipol.m +% +% See also CreateNF2FFBox, CalcNF2FF +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + + +% defaults +scale = 1; +maxgain = 0; +logscale = []; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'maxgain')==1); + maxgain = varargin{n+1}; + elseif (strcmp(varargin{n},'logscale')==1); + logscale = varargin{n+1}; + elseif (strcmp(varargin{n},'scale')==1); + scale = varargin{n+1}; + end +end + +if ~isempty(logscale) + farfield = 20*log10(farfield) + maxgain - logscale; + ind = find(farfield<0); + farfield(ind)=0; +else + % force 0 for linear plot + logscale = 0; +end + +t = thetaRange*pi/180; +a = phiRange*pi/180; + +fid = fopen(filename,'w+'); + +% set nan values to zero +ind = find(isnan(farfield)); +if (~isempty(ind)) + warning('openEMS:Dump2VTK','field contains nan, setting to zero'); + farfield(ind)=0; +end + +% set inf values to zero +ind = find(isinf(farfield)); +if (~isempty(ind)) + warning('openEMS:Dump2VTK','field contains inf, setting to zero'); + farfield(ind)=0; +end + + +fprintf(fid,'# vtk DataFile Version 3.0\n'); +fprintf(fid,'Structured Grid by matlab-interface of openEMS\n'); +fprintf(fid,'ASCII\n'); +fprintf(fid,'DATASET STRUCTURED_GRID\n'); + +fprintf(fid,'DIMENSIONS %d %d %d\n',1,numel(t),numel(a)); + +fprintf(fid,'POINTS %d double\n',numel(t)*numel(a)); + +for na=1:numel(phiRange) + for nt=1:numel(thetaRange) + fprintf(fid,'%e %e %e\n',... + scale*farfield(nt,na)*sin(t(nt))*cos(a(na)),... + scale*farfield(nt,na)*sin(t(nt))*sin(a(na)),... + scale*farfield(nt,na)*cos(t(nt))); + end +end + + + +fprintf(fid,'\n\n'); + +fprintf(fid,'POINT_DATA %d\n',numel(t)*numel(a)); + +fprintf(fid,['SCALARS gain double 1\nLOOKUP_TABLE default\n']); +fclose(fid); +dumpField = farfield(:) + logscale; +save('-ascii','-append',filename,'dumpField') diff --git a/openEMS/matlab/FFT_time2freq.m b/openEMS/matlab/FFT_time2freq.m new file mode 100644 index 0000000..a82c435 --- /dev/null +++ b/openEMS/matlab/FFT_time2freq.m @@ -0,0 +1,18 @@ +function [f,val] = FFT_time2freq( t, val ) +% [f,val] = FFT_time2freq( t, val ) +% +% Note: This function can only be used for pulse signals +% +% See also DFT_time2freq + +dt=t(2)-t(1); % timestep +L=numel(val); % signal length +NFFT = 2^nextpow2(L); % next power of 2 (makes fft fast) +%very fine freq resolution... NFFT = NFFT+100000; +val = fft( val, NFFT)*dt; +f = 1/(2*dt) * linspace(0,1,NFFT/2+1); + +val = 2*val(1:NFFT/2+1); % single-sided spectrum + +%correct phase for time-shifted signals +val = val .* exp(-1j*2*pi*f * t(1)); diff --git a/openEMS/matlab/FindFreeSSH.m b/openEMS/matlab/FindFreeSSH.m new file mode 100644 index 0000000..8fd6c22 --- /dev/null +++ b/openEMS/matlab/FindFreeSSH.m @@ -0,0 +1,87 @@ +function host = FindFreeSSH(host_list, Settings, wait_time, command) +% function host = FindFreeSSH(host_list, Settings, wait_time, command) +% +% Find a free ssh host not running openEMS +% +% internal function used by RunOpenEMS +% +% host_list: give a list of possible host +% +% wait_time: wait x seconds after not finding a free host and rechecking +% default: 600 seconds +% +% command: unix command to check for free host (empty result --> free) +% default: 'ps -e | grep openEMS' +% +% See also RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if (nargin<4) + % command which should return an empty string if host is available + command = 'ps -e | grep openEMS'; +end + +% 10 seconds ssh timeout +time_out = 10; + +if (nargin<3) + wait_time = 600; +end + +if ~isunix + ssh_command = [Settings.SSH.Putty.Path '/plink ']; + ssh_options = [' -i ' Settings.SSH.Putty.Key]; + command = ['"' command '"']; +else + ssh_command = 'ssh'; + ssh_options = ['-o ConnectTimeout=' num2str(time_out)]; + command = ['''' command '''']; +end + +if ischar(host_list) + fid=fopen(host_list); + if (fid==-1) + error('FindFreeSSH: cant open host file'); + end + clear host_list; + host_list = {}; + while 1 + line = fgetl(fid); + if ischar(line) + host_list{end+1} = line; + else + break; + end + end + fclose(fid); +elseif ~iscell(host_list) + error('FindFreeSSH: unknown host list format'); +end + +while 1 + for n = 1:numel(host_list) + host = host_list{n}; + [status, result] = unix([ssh_command ' ' ssh_options ' ' host ' ' command ]); + if (isempty(result) && status==1) + disp(['FindFreeSSH:: found a free host: ' host ]); + return + elseif (~isempty(result) && status==0) + disp(['FindFreeSSH:: ' host ' is busy running openEMS ... ' ]); + else + disp(['FindFreeSSH:: shh connection to ' host ' failed ... ' ]); + end + end + + host = ''; + + if (wait_time<=0) + warning('openEMS:FindFreeSSH',' unable to find a free host '); + return + end + + disp([' no free host found waiting for ' num2str(wait_time) ' seconds ... ']) + pause(wait_time) +end
\ No newline at end of file diff --git a/openEMS/matlab/FinishQueue.m b/openEMS/matlab/FinishQueue.m new file mode 100644 index 0000000..10020c5 --- /dev/null +++ b/openEMS/matlab/FinishQueue.m @@ -0,0 +1,40 @@ +function [queue] = FinishQueue(queue, query_time) +% function [queue] = FinishQueue(queue, <query_time>) +% +% Wait for the given queue to finish. +% +% Parameter: +% query_time (optional): time interval to check for finished tasks +% (in seconds, default is 5) +% +% For more details see: InitQueue +% +% See also: InitQueue, ResultsQueue, Add2Queue, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if ~isfield(queue,'jobs') + return +end + +if (nargin<2) + query_time = 5; +end + +numJobs = numel(queue.jobs); + +if (queue.verbose>=1) + disp(['FinishQueue: Waiting for ' num2str(sum(~queue.jobs_finished)) ' of ' num2str(numJobs) ' jobs to finish...']); +end + +running = numel(queue.jobs_finished) - sum(queue.jobs_finished); + +while sum(running)>0 + [queue running] = CheckQueue(queue, query_time); +end + +if (queue.verbose>=1) + disp(['FinishQueue: All jobs done!']) +end diff --git a/openEMS/matlab/GetField_Interpolation.m b/openEMS/matlab/GetField_Interpolation.m new file mode 100644 index 0000000..6682a83 --- /dev/null +++ b/openEMS/matlab/GetField_Interpolation.m @@ -0,0 +1,147 @@ +function [field_i mesh_i] = GetField_Interpolation(field, mesh, lines, varargin) +% [field_i mesh_i] = GetField_Interpolation(field, mesh, lines, varargin) +% +% Get an interpolated field, e.g. read by ReadHDF5Dump +% +% homogen interpolation given by a 3x1 vector: e.g. [21,1,101] +% +% abitrary interpolation on a given mesh: +% e.g.: mesh_interp{1} = linspace(0, 1,101) * 1e-3; +% mesh_interp{2} = linspace(0,0.5, 51) * 1e-3; +% mesh_interp{3} = linspace(0,0.2, 21) * 1e-3; +% +% example: +% [field mesh] = ReadHDF5Dump('Et.h5'); +% %interpolate on a mesh with 21x21x101 lines +% [field_i mesh_i] = GetField_Interpolation(field, mesh, [21 21 101]); +% or +% [field_i mesh_i] = GetField_Interpolation(field, mesh, mesh_interp); +% +% %or both steps in one with the same result: +% [field_i mesh_i] = ReadHDF5Dump('Et.h5','Interpolation', [21 21 101]); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5Dump ReadHDF5FieldData ReadHDF5Mesh + +if ((~iscell(lines) && ~isnumeric(lines)) || numel(lines)~=3) + error('openEMS:GetField_Interpolation: numLines for interpolation must be a vector...'); +end + +x = mesh.lines{1}; +y = mesh.lines{2}; +z = mesh.lines{3}; + +if (isnumeric(lines)) + if (lines(1)==0) + x_i = x; + else + x_i = linspace(x(1),x(end),lines(1)); + end + if (lines(2)==0) + y_i = y; + else + y_i = linspace(y(1),y(end),lines(2)); + end + if (lines(3)==0) + z_i = z; + else + z_i = linspace(z(1),z(end),lines(3)); + end +else + if isempty(lines{1}) + x_i = x; + else + x_i = lines{1}; + end + if isempty(lines{2}) + y_i = y; + else + y_i = lines{2}; + end + if isempty(lines{3}) + z_i = z; + else + z_i = lines{3}; + end +end + +field_i = field; +mesh_i = mesh; +mesh_i.lines{1} = x_i; +mesh_i.lines{2} = y_i; +mesh_i.lines{3} = z_i; + +% clear or create empty original indices list, since such do not make any +% sense with interpolated field values +mesh_i.original_indices = {}; + +if (isfield(field,'TD')) + field_i.TD = interpolate_fields(field.TD,x,y,z, x_i, y_i, z_i); + field_i.TD.time = field.TD.time; + field_i.TD.names= field.TD.names; +end + +if (isfield(field,'FD')) + field_i.FD = interpolate_fields(field.FD,x,y,z, x_i, y_i, z_i); + field_i.FD.frequency = field.FD.frequency; + field_i.FD.DataType = field.FD.DataType; +end + +return + +function field_i = interpolate_fields(field, x,y,z, x_i, y_i, z_i) + +% matlab cannot handle 3D data to be 2D data, workaround for these cases +if (numel(x)==1) + [Y Z] = ndgrid(y,z); + [Y_I Z_I] = ndgrid(y_i,z_i); + for n=1:numel(field.values) + field_i.values{n}(1,:,:,1) = interpn(Y,Z,squeeze(field.values{n}(1,:,:,1)),Y_I,Z_I); + if (size(field.values{n},4)>1) + field_i.values{n}(1,:,:,2) = interpn(Y,Z,squeeze(field.values{n}(1,:,:,2)),Y_I,Z_I); + field_i.values{n}(1,:,:,3) = interpn(Y,Z,squeeze(field.values{n}(1,:,:,3)),Y_I,Z_I); + end + end + return; +end + +if (numel(y)==1) + [X Z] = ndgrid(x,z); + [X_I Z_I] = ndgrid(x_i,z_i); + for n=1:numel(field.values) + field_i.values{n}(:,1,:,1) = interpn(X,Z,squeeze(field.values{n}(:,1,:,1)),X_I,Z_I); + if (size(field.values{n},4)>1) + field_i.values{n}(:,1,:,2) = interpn(X,Z,squeeze(field.values{n}(:,1,:,2)),X_I,Z_I); + field_i.values{n}(:,1,:,3) = interpn(X,Z,squeeze(field.values{n}(:,1,:,3)),X_I,Z_I); + end + end + return; +end + +if (numel(z)==1) + [X Y] = ndgrid(x,y); + [X_I Y_I] = ndgrid(x_i,y_i); + for n=1:numel(field.values) + field_i.values{n}(:,:,1,1) = interpn(X,Y,squeeze(field.values{n}(:,:,1,1)),X_I,Y_I); + if (size(field.values{n},4)>1) + field_i.values{n}(:,:,1,2) = interpn(X,Y,squeeze(field.values{n}(:,:,1,2)),X_I,Y_I); + field_i.values{n}(:,:,1,3) = interpn(X,Y,squeeze(field.values{n}(:,:,1,3)),X_I,Y_I); + end + end + return; +end + + +%real 3D case +[X Y Z] = ndgrid(x,y,z); +[X_I Y_I Z_I] = ndgrid(x_i,y_i,z_i); +for n=1:numel(field.values) + field_i.values{n}(:,:,:,1) = interpn(X,Y,Z,field.values{n}(:,:,:,1),X_I,Y_I,Z_I); + if (size(field.values{n},4)>1) + field_i.values{n}(:,:,:,2) = interpn(X,Y,Z,field.values{n}(:,:,:,2),X_I,Y_I,Z_I); + field_i.values{n}(:,:,:,3) = interpn(X,Y,Z,field.values{n}(:,:,:,3),X_I,Y_I,Z_I); + end +end diff --git a/openEMS/matlab/GetField_Range.m b/openEMS/matlab/GetField_Range.m new file mode 100644 index 0000000..8d489b9 --- /dev/null +++ b/openEMS/matlab/GetField_Range.m @@ -0,0 +1,71 @@ +function [field_i mesh_i] = GetField_Range(field, mesh, range) +% [field_i mesh_i] = GetField_Range(field, mesh, range) +% +% Get a field dump subset within a given mesh range +% +% example: +% % specify a mesh range +% range{1} = [0 150] * 1e-3; % x in range 0..150mm +% range{2} = [0]; % only one line close to y==0 +% range{3} = []; % no range restriction +% +% % read hdf data +% [field mesh] = ReadHDF5Dump('Et.h5'); +% % extract a ranged subset +% [field_i mesh_i] = GetField_Range(field, mesh, range); +% +% %or both steps in one with the same result: +% [field_i mesh_i] = ReadHDF5Dump('Et.h5','Range', range); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5Dump ReadHDF5FieldData ReadHDF5Mesh + +mesh_i = mesh; +for n=1:3 + if (numel(range{n})==0) + ind_range{n} = []; + + ind_range{n} = 1:numel( mesh.lines{n}); + + elseif (numel(range{n})==1) + ind_range{n} = find( mesh.lines{n}>=range{n}(1) , 1); + + if (isempty(ind_range{n})) + ind_range{n} = find( mesh.lines{n}>=range{n}(1) , 1, 'first'); + end + if (isempty(ind_range{n})) + ind_range{n} = find( mesh.lines{n}<=range{n}(2) , 1, 'last'); + end + + else + ind_range{n} = find( mesh.lines{n}>=range{n}(1) & mesh.lines{n}<=range{n}(2)); + end + + mesh_i.lines{n} = mesh.lines{n}(ind_range{n}); +end + +% store original indices +if (isfield(mesh_i,'original_indices')) + for n=1:3 + mesh_i.original_indices{n} = mesh_i.original_indices{n}(ind_range{n}); + end +else + mesh_i.original_indices = ind_range; +end + +field_i = field; + +if (isfield(field,'FD')) + for n=1:numel(field.FD.values) + field_i.FD.values{n} = field.FD.values{n}(ind_range{1},ind_range{2},ind_range{3},:); + end +end + +if (isfield(field,'TD')) + for n=1:numel(field.TD.values) + field_i.TD.values{n} = field.TD.values{n}(ind_range{1},ind_range{2},ind_range{3},:); + end +end diff --git a/openEMS/matlab/GetField_SubSampling.m b/openEMS/matlab/GetField_SubSampling.m new file mode 100644 index 0000000..9c792e0 --- /dev/null +++ b/openEMS/matlab/GetField_SubSampling.m @@ -0,0 +1,63 @@ +function [field_i mesh_i] = GetField_SubSampling(field, mesh, subsampling, varargin) +% [field_i mesh_i] = GetField_SubSampling(field, mesh, subsampling, varargin) +% +% Get a sub-sampled field, e.g. read by ReadHDF5Dump +% +% sub-sampling e.g. skipping every second line in x/r direction: [2 1 1] +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5Dump ReadHDF5FieldData ReadHDF5Mesh + +if (~isnumeric(subsampling) || numel(subsampling)~=3) + error('openEMS:GetField_Interpolation: numLines for interpolation must be a vector...'); +end + +x = mesh.lines{1}; +y = mesh.lines{2}; +z = mesh.lines{3}; + +ss_idx{1} = 1:subsampling(1):numel(x); +ss_idx{2} = 1:subsampling(2):numel(y); +ss_idx{3} = 1:subsampling(3):numel(z); + +x_i = x(ss_idx{1}); +y_i = y(ss_idx{2}); +z_i = z(ss_idx{3}); + +field_i = field; +mesh_i = mesh; +mesh_i.lines{1} = x_i; +mesh_i.lines{2} = y_i; +mesh_i.lines{3} = z_i; + +% store original indices +if (isfield(mesh_i,'original_indices')) + for n=1:3 + mesh_i.original_indices{n} = mesh_i.original_indices{n}(ss_idx{n}); + end +else + mesh_i.original_indices = ss_idx; +end + +if (isfield(field,'TD')) + field_i.TD = subsample_fields(field.TD,ss_idx); + field_i.TD.time = field.TD.time; + field_i.TD.names= field.TD.names; +end + +if (isfield(field,'FD')) + field_i.FD = subsample_fields(field.FD,ss_idx); + field_i.FD.frequency = field.FD.frequency; + field_i.FD.DataType = field.FD.DataType; +end + +return + +function field_i = subsample_fields(field, ss_idx) + +for n=1:numel(field.values) + field_i.values{n} = field.values{n}(ss_idx{1},ss_idx{2},ss_idx{3},:); +end diff --git a/openEMS/matlab/GetField_TD2FD.m b/openEMS/matlab/GetField_TD2FD.m new file mode 100644 index 0000000..014c6d6 --- /dev/null +++ b/openEMS/matlab/GetField_TD2FD.m @@ -0,0 +1,48 @@ +function field = GetField_TD2FD(field, freq) +% function field = GetField_TD2FD(field, freq) +% +% Transforms time-domain field data into the frequency domain +% Autocorrects the half-timestep offset of the H-field +% +% example: +% freq = linspace(0,1e9,100); %target frequency vector (Hz) +% field = ReadHDF5FieldData('tmp/Ht.h5'); +% field_FD = GetField_TD2FD(field, freq); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5FieldData + +if (~isfield(field,'TD')) + warning('openEMS:GetField_TD2FD','field has no time domain data... skipping FD transformation...'); + return +end + +t = field.TD.time; +dt = t(2)-t(1); + +clear field.FD + +field.FD.frequency = freq; + +for nf = 1:numel(freq) + field.FD.values{nf} = 0; +end + +numTS = numel(field.TD.values); + +for n=1:numTS + for nf = 1:numel(freq) + f = freq(nf); + field.FD.values{nf} = field.FD.values{nf} + field.TD.values{n}.*exp(-1i*2*pi*f*t(n)) * 2 * dt; + % t(n) is absolute time and therefore the half-timestep offset of + % the H-field is automatically compensated + % openEMS output: E-fields start at t=0 + % openEMS output: H-fields start at t=delta_t/2 + end +end + +field.FD.DataType=1; + diff --git a/openEMS/matlab/InitCylindricalFDTD.m b/openEMS/matlab/InitCylindricalFDTD.m new file mode 100644 index 0000000..9da649f --- /dev/null +++ b/openEMS/matlab/InitCylindricalFDTD.m @@ -0,0 +1,21 @@ +function FDTD = InitCylindricalFDTD(NrTS, endCrit, varargin) +% function FDTD = InitCylindricalFDTD(NrTS, endCrit, varargin) +% +% see also InitFDTD +% +% e.g FDTD = InitCylindricalFDTD(5e5,1e-6,'OverSampling',10) +% +% WARNING: This function is depreciated, use InitFDTD with 'CoordSystem',1 +% e.g.: InitFDTD(5e5,1e-6,'OverSampling',10, 'CoordSystem',1) +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +warning('InitCylindricalFDTD: This function is depreciated, use InitFDTD with ''CoordSystem'',1'); + +FDTD = InitFDTD(NrTS, endCrit, varargin{:}); + +FDTD.ATTRIBUTE.CylinderCoords=1; + + diff --git a/openEMS/matlab/InitFDTD.m b/openEMS/matlab/InitFDTD.m new file mode 100644 index 0000000..dba5120 --- /dev/null +++ b/openEMS/matlab/InitFDTD.m @@ -0,0 +1,64 @@ +function FDTD = InitFDTD(varargin) +% function FDTD = InitFDTD(varargin) +% +% Inititalize the FDTD data-structure. +% +% optional field arguments for usage with openEMS: +% NrTS: max. number of timesteps to simulate (e.g. default=1e9) +% EndCriteria: end criteria, e.g. 1e-5, simulations stops if energy has +% decayed by this value (<1e-4 is recommended, default=1e-5) +% MaxTime: max. real time in seconds to simulate +% OverSampling: nyquist oversampling of time domain dumps +% CoordSystem: choose coordinate system (0 Cartesian, 1 Cylindrical) +% MultiGrid: define a cylindrical sub-grid radius +% TimeStep: force to use a given timestep (dangerous!) +% TimeStepFactor: reduce the timestep by a given factor (>0 to <=1) +% TimeStepMethod: 1 or 3 chose timestep method (1=CFL, 3=Rennigs (default)) +% CellConstantMaterial: set to 1 to assume a material is constant inside +% a cell (material probing in cell center) +% +% examples: +% %default init with 1e9 max. timesteps and -50dB end-criteria +% FDTD = InitFDTD(); +% +% %init with 1e6 max. timesteps and -60dB end-criteria +% FDTD = InitFDTD('NrTS', 1e6, 'EndCriteria', 1e-6); +% +% %cylindrical FDTD simulation +% FDTD = InitFDTD('CoordSystem', 1); +% +% See also InitCSX +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig (c) 2010-2013 + +% default values +NrTS = 1e9; +endCrit = 1e-5; + +% legacy support +if ((nargin==1) && (isnumeric(varargin{1}))) + NrTS = varargin{1}; + warning('openEMS:InitFDTD',['Syntax for InitFDTD has changed, use: "InitFDTD(''NrTS'', ' num2str(NrTS) ')" instead! Legacy support enabled.']); +elseif ((nargin>1) && (isnumeric(varargin{1})) && (isnumeric(varargin{2}))) + NrTS = varargin{1}; + endCrit = varargin{2}; + varargin(1:2) = []; + warning('openEMS:InitFDTD',['Syntax for InitFDTD has changed, use: "InitFDTD(''NrTS'', ' num2str(NrTS) ', ''EndCriteria'', ' num2str(endCrit) ')" instead! Legacy support enabled.']); +end + +for n=1:numel(varargin)/2 + if strcmpi(varargin{2*n-1},'CoordSystem')==1 + FDTD.ATTRIBUTE.CylinderCoords=varargin{2*n}==1; + elseif strcmpi(varargin{2*n-1},'NrTS')==1 + NrTS=varargin{2*n}; + elseif strcmpi(varargin{2*n-1},'EndCriteria')==1 + endCrit=varargin{2*n}; + else + FDTD.ATTRIBUTE.(varargin{2*n-1})=varargin{2*n}; + end +end + +FDTD.ATTRIBUTE.NumberOfTimesteps=NrTS; +FDTD.ATTRIBUTE.endCriteria=endCrit; diff --git a/openEMS/matlab/InitQueue.m b/openEMS/matlab/InitQueue.m new file mode 100644 index 0000000..feec5cc --- /dev/null +++ b/openEMS/matlab/InitQueue.m @@ -0,0 +1,90 @@ +function [queue] = InitQueue(varargin) +% function [queue] = InitQueue(varargin) +% +% Use this function to initialize a queue to run one or more matlab scripts +% in parallel. +% This can be used to efficiently run an openEMS parameter sweep in parallel +% on multiple remote machines. +% +% Options: +% DependPath: Add multiple paths, your script may depend on +% UseOctave: Enable/Disable octave usage +% MaxThreads: max. number of parallel executions +% +% Note: +% - Currently only Linux/Unix is supported +% - By default Octave is used to spawn parallel functions (saves +% licenses), but this can be changed by: +% [queue] = InitQueue('UseOctave', 0); +% You may need to change this, if your script is not octave compatible +% - To efficiently run openEMS in parallel, you need to run it on several +% machines using a SSH.host_list setting --> See also RunOpenEMS +% +% Example: +% %serial version: +% for n=1:10 +% % manipulate parameter etc. +% [result1(n) result2(n)] = Parallel_Func_Name(param1, param2); +% end +% +% %parallel version: +% queue = InitQueue('DependPath',{'/opt/openEMS/CSXCAD/matlab', ... +% '/opt/openEMS/openEMS/matlab'}); +% for n=1:10 +% % manipulate parameter etc. +% queue = Add2Queue(queue, 'Parallel_Func_Name', {param1, param2}); +% end +% +% % wait for all to finish +% [queue] = FinishQueue(queue); +% +% % retrieve result +% for n=1:numel(stub_sweep) +% [result1(n) result2(n)] = ResultsQueue(queue,n); +% end +% +% See also: Add2Queue, FinishQueue, ResultsQueue, RunOpenEMS, +% RunOpenEMS_Parallel, FindFreeSSH +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if ~isunix + error 'your OS is not supported (Unix only)' +end + +queue.use_octave = exist('OCTAVE_VERSION','builtin') ~= 0; + +queue.verbose = 1; + +queue.maxThreads = Inf; + +% add current path +queue.DependPath = ['addpath(''' pwd ''');']; + +for n=1:2:nargin + if strcmp(varargin{n},'DependPath'); + for m=1:numel(varargin{n+1}) + queue.DependPath = [queue.DependPath 'addpath(''' varargin{n+1}{m} ''');']; + end + end + if strcmp(varargin{n},'UseOctave'); + queue.use_octave = varargin{n+1}; + end + if strcmp(varargin{n},'MaxThreads'); + queue.maxThreads = varargin{n+1}; + end +end + + +% set binaries and options +if (queue.use_octave) + queue.bin = ['export LD_LIBRARY_PATH=""; octave']; + queue.bin_options = [' --silent --eval']; +else + queue.bin = [matlabroot '/bin/matlab']; + queue.bin_options = [' -nodesktop -nosplash -r']; +end + +queue.jobs_finished = []; diff --git a/openEMS/matlab/PlotHDF5FieldData.m b/openEMS/matlab/PlotHDF5FieldData.m new file mode 100644 index 0000000..9aaf899 --- /dev/null +++ b/openEMS/matlab/PlotHDF5FieldData.m @@ -0,0 +1,94 @@ +function PlotHDF5FieldData(file, PlotArgs) +% function PlotHDF5FieldData(file, PlotArgs) +% +% e.g. +% PlotArgs.slice = {0 [10 20] 0}; +% PlotArgs.pauseTime=0.01; +% PlotArgs.component=2; +% PlotArgs.Limit = 'auto'; +% +% PlotHDF5FieldData('tmp/Et.h5',PlotArgs) +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +component = PlotArgs.component; + +if (isfield(PlotArgs,'pauseTime')) + pauseT = PlotArgs.pauseTime; +else + pauseT = 0.01; +end + +mesh = ReadHDF5Mesh(file); +fields = ReadHDF5FieldData(file); + +if (mesh.type==0) + % cartesian mesh + [X Y Z] = meshgrid(mesh.lines{1},mesh.lines{2},mesh.lines{3}); + for n=1:numel(fields.TD.values) + % since Matlab 7.1SP3 the field needs to be reordered + fields.TD.values{n} = permute(fields.TD.values{n},[2 1 3 4]); % reorder: y,x,z (or y,x) + end +else + disp(['PlotHDF5FieldData:: Error: unknown mesh type ' num2str(mesh.type)]); +end + +max_amp = 0; + +if (component>0) + for n=1:numel(fields.TD.values) + Field{n} = fields.TD.values{n}(:,:,:,component); + end +else + for n=1:numel(fields.TD.values) + fx = fields.TD.values{n}(:,:,:,1); + fy = fields.TD.values{n}(:,:,:,2); + fz = fields.TD.values{n}(:,:,:,3); + Field{n} = sqrt(fx.^2 + fy.^2 + fz.^2); + end +end + +for n=1:numel(Field) + amp = max(max(max(abs(Field{n})))); + if (amp>max_amp) + max_amp = amp; + end +end + +if (max_amp==0) + disp('max found amplitude was 0 --> nothing to plot'); + return +end + +for n=1:numel(Field) + if size(Field{n},3) > 1 + % Field is a volume + hsurfaces = slice(X,Y,Z, Field{n} , PlotArgs.slice{:}); + set(hsurfaces,'FaceColor','interp','EdgeColor','none'); + else + % Field is already a 2D cut + pcolor(X,Y,Field{n}); + shading( 'interp' ); + xlabel( 'x' ); + ylabel( 'y' ); + end + title(fields.TD.names{n}); + %view(3) + axis equal + if (isfield(PlotArgs,'Limit')) + if ~ischar(PlotArgs.Limit) + caxis(PlotArgs.Limit); + elseif strcmp(PlotArgs.Limit,'auto') + if (component>0) + caxis([-max_amp,max_amp]); + else + caxis([0,max_amp]); + end + end + end + + drawnow + pause(pauseT) +end diff --git a/openEMS/matlab/ReadHDF5Attribute.m b/openEMS/matlab/ReadHDF5Attribute.m new file mode 100644 index 0000000..3361ee9 --- /dev/null +++ b/openEMS/matlab/ReadHDF5Attribute.m @@ -0,0 +1,33 @@ +function attr = ReadHDF5Attribute(file, groupname, attr_name) +% attr = ReadHDF5Attribute(file, groupname, attr_name) +% +% internal function for openEMS to read hdf5 attributes +% +% See also: ReadHDF5ComplexData +% +% openEMS Matlab/Octave interface +% ----------------------- +% author: Thorsten Liebig, 2012 + + +if isOctave + if (exist('h5readatt_octave')==0) + warning('openEMS:ReadHDF5Attribute','function "h5readatt_octave" not found, trying to run "setup"'); + try + setup + catch + error('openEMS:ReadHDF5Attribute','running "setup" failed...'); + end + end + attr = double(h5readatt_octave(file,groupname,attr_name)); +else + %check for different matlab versions + if verLessThan('matlab','7.9') + attr = double(hdf5read(file,[groupname '/' attr_name])); + elseif verLessThan('matlab','7.12') + attr = double(hdf5read(file,groupname,attr_name)); + else + attr = double(h5readatt(file,groupname,attr_name)); + end + +end
\ No newline at end of file diff --git a/openEMS/matlab/ReadHDF5Dump.m b/openEMS/matlab/ReadHDF5Dump.m new file mode 100644 index 0000000..9fc8c43 --- /dev/null +++ b/openEMS/matlab/ReadHDF5Dump.m @@ -0,0 +1,92 @@ +function [field mesh] = ReadHDF5Dump(file, varargin) +%[field mesh] = ReadHDF5Dump(file, varargin) +% +% Read a hdf5 field dump, including an interpolation and frequency domain +% transformation. +% +% For more information about the output, refer to the help of +% ReadHDF5Mesh and ReadHDF5FieldData +% +% possible arguments: +% 'Range' see GetField_Range +% 'Interpolation' see GetField_Interpolation +% 'SubSampling' see GetField_SubSampling +% 'Frequency' see GetField_TD2FD +% 'CloseAlpha': 0 (default) / 1 +% +% example: +% [field mesh] = ReadHDF5Dump('Et.h5'); +% or +% [field mesh] = ReadHDF5Dump('Et.h5','Range',{[0 100],[-20 20],[50 90]}); +% or +% [field mesh] = ReadHDF5Dump('Et.h5','Interpolation',[21 1 101],'Frequency',300e6); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5Mesh ReadHDF5FieldData GetField_Interpolation GetField_SubSampling +% GetField_TD2FD GetField_Range + +field = ReadHDF5FieldData(file); +mesh = ReadHDF5Mesh(file); + +if (nargin<2) + return +end + +% evaluate arguments in a specific order +for n=1:2:(nargin-1) + if (strcmp(varargin{n},'Range')==1); + [field mesh] = GetField_Range(field, mesh, varargin{n+1}); + end +end + +for n=1:2:(nargin-1) + if (strcmp(varargin{n},'SubSampling')==1); + [field mesh] = GetField_SubSampling(field,mesh,varargin{n+1}); + end +end + +for n=1:2:(nargin-1) + if (strcmp(varargin{n},'Interpolation')==1); + [field mesh] = GetField_Interpolation(field,mesh,varargin{n+1}); + end +end + +for n=1:2:(nargin-1) + if (strcmp(varargin{n},'Frequency')==1); + field = GetField_TD2FD(field,varargin{n+1}); + end +end + +for n=1:2:(nargin-1) + if (strcmp(varargin{n},'CloseAlpha')==1); + if ((varargin{n+1}==1) && (mesh.type==1) && (range(mesh.lines{2})<2*pi)) + mesh.lines{2}(end+1)=mesh.lines{2}(1)+2*pi; + if (isfield(field,'TD')) + for n = 1:numel(field.TD.values) + field.TD.values{n}(:,end+1,:,:) = field.TD.values{n}(:,1,:,:); + end + end + if (isfield(field,'FD')) + for n = 1:numel(field.FD.values) + field.FD.values{n}(:,end+1,:,:) = field.FD.values{n}(:,1,:,:); + end + end + if (isfield(mesh,'original_indices')) + if (~isempty(mesh.original_indices)) + mesh.original_indices{2} = [mesh.original_indices{2} 1]; + end + else + mesh.original_indices = {1:numel(mesh.lines{1}),[1:numel(mesh.lines{2}) 1],[1:numel(mesh.lines{3})]}; + end + end + end +end + +end + +function rng = range(x) + rng = max(x)-min(x); +end
\ No newline at end of file diff --git a/openEMS/matlab/ReadHDF5FieldData.m b/openEMS/matlab/ReadHDF5FieldData.m new file mode 100644 index 0000000..c45f9ad --- /dev/null +++ b/openEMS/matlab/ReadHDF5FieldData.m @@ -0,0 +1,119 @@ +function hdf_fielddata = ReadHDF5FieldData(file) +% function hdf_fielddata = ReadHDF5FieldData(file) +% +% returns: +% % time domain data (if exist) +% hdf_fielddata.TD.time +% hdf_fielddata.TD.names +% hdf_fielddata.TD.values +% hdf_fielddata.TD.DataType (0 --> real value data) +% +% % frequency domain data (if exist) +% hdf_fielddata.FD.frequency +% hdf_fielddata.FD.values +% hdf_fielddata.FD.DataType (0 / 1 --> real / complex value data) +% +% example: values of timestep 12: +% hdf_fielddata.TD.values{12}: array (x,y,z,polarization) +% +% plot z-field component along y-direction for timestep 12: +% plot( hdf_fielddata.TD.values{12}(1,:,1,3) ) +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5Mesh ReadHDF5Dump + +if isOctave + hdf_fielddata = ReadHDF5FieldData_octave(file); + return +end + +info = hdf5info(file); +TD.names = {}; +hdf_fielddata = []; + +for n=1:numel(info.GroupHierarchy.Groups) + if strcmp(info.GroupHierarchy.Groups(n).Name,'/FieldData') + %found /FieldData, look for either TD or FD data + for nGroup=1:numel(info.GroupHierarchy.Groups(n).Groups) + %search and read TD data + if strcmp(info.GroupHierarchy.Groups(n).Groups(nGroup).Name,'/FieldData/TD') + for m=1:numel(info.GroupHierarchy.Groups(n).Groups(nGroup).Datasets) + TD.names{m} = info.GroupHierarchy.Groups(n).Groups(nGroup).Datasets(m).Name; + for a = 1:numel(info.GroupHierarchy.Groups(n).Groups(nGroup).Datasets(m).Attributes) + str = regexp(info.GroupHierarchy.Groups(n).Groups(nGroup).Datasets(m).Attributes(a).Name,'\w/*\w*','match'); + TD.(str{end})(m) = double(info.GroupHierarchy.Groups(n).Groups(nGroup).Datasets(m).Attributes(a).Value); + end + end + end + end + + end +end + +if (numel(TD.names)>0) + hdf_fielddata.TD=TD; + hdf_fielddata.TD.DataType = 0; %real value data + for n=1:numel(hdf_fielddata.TD.names) + hdf_fielddata.TD.values{n} = double(hdf5read(file,hdf_fielddata.TD.names{n})); + end +end + +% extract FD data +try + hdf_fielddata.FD.frequency = ReadHDF5Attribute(file,'/FieldData/FD','frequency'); +catch err +% disp(err) + return +end + +for n=1:numel(hdf_fielddata.FD.frequency) + try + hdf_fielddata.FD.values{n} = double(hdf5read(file,['/FieldData/FD/f' int2str(n-1) '_real']) + 1i*hdf5read(file,['/FieldData/FD/f' int2str(n-1) '_imag'])); + hdf_fielddata.FD.DataType = 1; %complex value data + catch + try + hdf_fielddata.FD.values{n} = double(hdf5read(file,['/FieldData/FD/f' int2str(n-1)])); + hdf_fielddata.FD.DataType = 0; %real value data + catch + error('openEMS:ReadHDF5FieldData','FD data invalid...') + end + end +end + +function hdf_fielddata = ReadHDF5FieldData_octave(file) +hdf = load( '-hdf5', file ); +if ~isfield(hdf,'FieldData') + error('no field data found') +end +if isfield(hdf.FieldData,'TD') + %read TD data + hdf_fielddata_names = fieldnames(hdf.FieldData.TD); + for n=1:numel(hdf_fielddata_names) + hdf_fielddata.TD.values{n} = hdf.FieldData.TD.(hdf_fielddata_names{n}); + hdf_fielddata.TD.names{n} = ['/FieldData/TD/' hdf_fielddata_names{n}(2:end)]; + hdf_fielddata.TD.time(n) = ReadHDF5Attribute(file, hdf_fielddata.TD.names{n},'time'); + end + hdf_fielddata.TD.DataType = 0; %real value data +end +if isfield(hdf.FieldData,'FD') + %read FD data + hdf_fielddata.FD.frequency = ReadHDF5Attribute(file,'/FieldData/FD/','frequency'); + try %try reading complex data + for n=1:numel(hdf_fielddata.FD.frequency) + hdf_fielddata.FD.values{n} = double(hdf.FieldData.FD.(['f' int2str(n-1) '_real']) +1i*hdf.FieldData.FD.(['f' int2str(n-1) '_imag']) ); + end + hdf_fielddata.FD.DataType = 1; %complex value data + catch + try %try reading real value data + for n=1:numel(hdf_fielddata.FD.frequency) + hdf_fielddata.FD.values{n} = double(hdf.FieldData.FD.(['f' int2str(n-1)])); + end + hdf_fielddata.FD.DataType = 0; %real value data + catch + error('openEMS:ReadHDF5FieldData','FD data invalid...') + end + end +end diff --git a/openEMS/matlab/ReadHDF5Mesh.m b/openEMS/matlab/ReadHDF5Mesh.m new file mode 100644 index 0000000..b276391 --- /dev/null +++ b/openEMS/matlab/ReadHDF5Mesh.m @@ -0,0 +1,80 @@ +function hdf_mesh = ReadHDF5Mesh(file) +% function hdf_mesh = ReadHDF5Mesh(file) +% +% Get the raw mesh data stored in the hdf5 dump file created by openEMS +% +% returns: +% hdf_mesh.type (0-> cartesian, 1-> cylindrical mesh type) +% hdf_mesh.names (e.g. 'Mesh/y') +% hdf_mesh.lines (e.g. [0,1,2,3,4]) +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also ReadHDF5FieldData + +isOctave = exist('OCTAVE_VERSION','builtin') ~= 0; +if isOctave + hdf_mesh = ReadHDF5Mesh_octave(file); + return +end + +info = hdf5info(file); + +for n=1:numel(info.GroupHierarchy.Groups) + if strcmp(info.GroupHierarchy.Groups(n).Name,'/Mesh') + for m=1:numel(info.GroupHierarchy.Groups(n).Datasets) + names{m} = info.GroupHierarchy.Groups(n).Datasets(m).Name; + end + end +end + +hdf_mesh.names = names; +for n=1:numel(names) + hdf_mesh.lines{n} = double(hdf5read(file,names{n})); +end + +if (strcmp(names{1},'/Mesh/alpha')) + % alpha and rho are in the wrong order, flip to have rho, alpha, z + hdf_mesh.names(1:2) = fliplr(hdf_mesh.names(1:2)); + hdf_mesh.lines(1:2) = fliplr(hdf_mesh.lines(1:2)); + hdf_mesh.type=1; + return +end +if (strcmp(names{1},'/Mesh/phi')) + % reorder coordinates + hdf_mesh.names = hdf_mesh.names([2 3 1]); + hdf_mesh.lines = hdf_mesh.lines([2 3 1]); + hdf_mesh.type=2; + return +end + +hdf_mesh.type=0; + + +function hdf_mesh = ReadHDF5Mesh_octave(file) +hdf = load( '-hdf5', file ); +hdf_mesh.names = fieldnames(hdf.Mesh); +hdf_mesh.type = 0; % cartesian mesh +for n=1:numel(hdf_mesh.names) + hdf_mesh.lines{n} = hdf.Mesh.(hdf_mesh.names{n}); + hdf_mesh.names{n} = ['/Mesh/' hdf_mesh.names{n}]; + if strcmp(hdf_mesh.names{n},'/Mesh/alpha') + hdf_mesh.type = 1; % cylindrical mesh + end + if strcmp(hdf_mesh.names{n},'/Mesh/phi') + hdf_mesh.type = 2; % cylindrical mesh + end +end + +if (hdf_mesh.type==1) + % alpha and rho are in the wrong order, flip to have rho, alpha, z + hdf_mesh.names(1:2) = fliplr(hdf_mesh.names(1:2)); + hdf_mesh.lines(1:2) = fliplr(hdf_mesh.lines(1:2)); +end +if (hdf_mesh.type==2) + % alpha and rho are in the wrong order, flip to have rho, alpha, z + hdf_mesh.names = hdf_mesh.names([2 3 1]); + hdf_mesh.lines = hdf_mesh.lines([2 3 1]); +end diff --git a/openEMS/matlab/ReadUI.m b/openEMS/matlab/ReadUI.m new file mode 100644 index 0000000..23fe342 --- /dev/null +++ b/openEMS/matlab/ReadUI.m @@ -0,0 +1,107 @@ +function UI = ReadUI(files, path, freq, varargin) +% function UI = ReadUI(files, path, freq, varargin) +% +% read current and voltages from multiple files found in path +% +% returns voltages/currents in time and frequency-domain +% +% remarks on the frequency-domain: +% - all signals are assumed to start at t=0 +% - currents that e.g. start at t = +delta_t/2 will be phase shifted by +% exp(-j*w*t(1)) +% +% optional parameter: +% freq: frequency-domain values will be calculated according to 'freq' +% if 'freq' is not given, a (zero padded) FFT will be used +% +% optional key,value pairs: +% 'AR' : auto-regressive model to improve FD accuracy +% values: order to use within an AR model or 'auto' +% +% % examples: +% U = ReadUI({'ut1_1','ut1_2'},'tmp' ); +% I = ReadUI('it1' ,'tmp',[0.5e9 1e9 1.5e9]); +% +% % using the auto-regressive model +% U = ReadUI('port_ut1' , 'tmp', 'AR', 'auto'); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig +% +% See also DFT_time2freq, AR_estimate + +if (nargin<2) + path =''; +end + +AR_order = 0; +SignalType = 'pulse'; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'AR')==1) + AR_order = varargin{n+1}; + elseif strcmpi(varargin{n},'SignalType') + SignalType = varargin{n+1}; + else + warning('CSXCAD:ReadUI', ['"' varargin{n} '" is an unknown argument']); + end +end + +if strcmpi(SignalType,'periodic') && AR_order>0 + error 'auto-regressive model not compatible with periodic signals' +end + +if (ischar(files)) + filenames{1}=files; +else + filenames=files; +end + +UI.TD = {}; +UI.FD = {}; +for n=1:numel(filenames) + tmp = load( fullfile(path,filenames{n}) ); + t = tmp(:,1)'; + val = tmp(:,2)'; + + UI.TD{n}.t = t; + UI.TD{n}.val = val; + + if (numel(tmp(1,:))>2) + UI.TD{n}.additional = tmp(:,3:end)'; + end + + if (nargin<3) || isempty(freq) + if strcmpi(SignalType,'periodic') + warning 'ReadUI: periodic signal type not supported by FFT' + end + [UI.FD{n}.f,UI.FD{n}.val] = FFT_time2freq( t,val ); + else + UI.FD{n}.f = freq; + if strcmpi(AR_order,'auto') + AR_order = 2; + EC = -1; + while 1 + [val_ar t_ar UI.FD{n}.val EC] = AR_estimate( t, val, freq, AR_order); + if (EC==11) + AR_order = AR_order*2; + else + break; + end + end + if (EC~=0) + warning('CSXCAD:ReadUI','AR estimation failed, skipping...') + UI.FD{n}.val = DFT_time2freq( t, val, freq, SignalType ); + end + elseif (AR_order<=0) + UI.FD{n}.val = DFT_time2freq( t, val, freq, SignalType ); + else + [val_ar t_ar UI.FD{n}.val EC] = AR_estimate( t, val, freq, AR_order); + if (EC~=0) + warning('CSXCAD:ReadUI','AR estimation failed, skipping...') + UI.FD{n}.val = DFT_time2freq( t, val, freq, SignalType ); + end + end + end +end diff --git a/openEMS/matlab/ResultsQueue.m b/openEMS/matlab/ResultsQueue.m new file mode 100644 index 0000000..cc9f745 --- /dev/null +++ b/openEMS/matlab/ResultsQueue.m @@ -0,0 +1,24 @@ +function [varargout] = ResultsQueue(queue, n) +% function [varargout] = ResultsQueue(queue, n) +% +% Use this function to retrieve the results from a finished queue. +% +% For more details see: InitQueue +% +% See also: InitQueue, FinishQueue, Add2Queue, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if n>numel(queue.jobs) + error 'ResultsQueue:job is missing' +end + +if (nargout>numel(queue.jobs{n}.outargs)) + error 'not enough job output arguments' +end + +for k=1:numel(queue.jobs{n}.outargs) + varargout{k} = queue.jobs{n}.outargs{k}; +end diff --git a/openEMS/matlab/RunOpenEMS.m b/openEMS/matlab/RunOpenEMS.m new file mode 100644 index 0000000..a4947e3 --- /dev/null +++ b/openEMS/matlab/RunOpenEMS.m @@ -0,0 +1,186 @@ +function RunOpenEMS(Sim_Path, Sim_File, opts, Settings) +% function RunOpenEMS(Sim_Path, Sim_File, <opts, Settings>) +% +% Run an openEMS simulation. +% +% arguments: +% Sim_Path: specifiy the simulation folder (folder must exist!) +% Sim_File: xml-filename to simulate, created by WriteOpenEMS +% +% optional arguments +% +% opts: list of openEMS options +% possible options: +% --disable-dumps Disable all field dumps for faster simulation +% --debug-material Dump material distribution to a vtk file for debugging +% --debug-PEC Dump metal distribution to a vtk file for debugging +% --debug-operator Dump operator to vtk file for debugging +% --debug-boxes Dump e.g. probe boxes to vtk file for debugging +% --debug-CSX Write CSX geometry file to debugCSX.xml +% --engine=<type> Choose engine type +% --engine=fastest fastest available engine (default) +% --engine=basic basic FDTD engine +% --engine=sse engine using sse vector extensions +% --engine=sse_compressed engine using compressed operator + sse vector extensions +% --engine=MPI engine using compressed operator + sse vector extensions + MPI parallel processing +% --engine=multithreaded engine using compressed operator + sse vector extensions + MPI + multithreading +% --numThreads=<n> Force use n threads for multithreaded engine +% --no-simulation only run preprocessing; do not simulate +% --dump-statistics dump simulation statistics to 'openEMS_run_stats.txt' and 'openEMS_stats.txt' +% +% Additional global arguments +% --showProbeDiscretization Show probe discretization information +% --nativeFieldDumps Dump all fields using the native field components +% -v,-vv,-vvv Set debug level: 1 to 3 +% +% +% settings: list of Matlab settings +% possible settings: +% Settings.LogFile = 'openEMS.log' +% Settings.Silent = 0 +% +% additional remote simulation settings +% Note: ssh only on unix with working ssh client or windows with putty client +% openEMS Linux server or Windows with cygwin necessary +% Settings.SSH.host = '<hostname or ip>' +% Settings.SSH.bin = '<path_to_openEMS>/openEMS.sh' +% ssh optional: +% Settings.SSH.host_list = {'list','of','hosts'}; %searches for a free host +% %on Windows needed additionally +% Settings.SSH.Putty.Path = '<path_to>\putty'; +% Settings.SSH.Putty.Key = '<path_to>\putty_private_key.ppk'; +% +% MPI settings: +% Settings.MPI.xxx --> help RunOpenEMS_MPI +% +% +% example: +% %create CSX and FDTD +% WriteOpenEMS('/tmp/path_to_run_in/myfile.xml', FDTD, CSX) +% RunOpenEMS('/tmp/path_to_run_in','myfile.xml','-v') +% +% See also WriteOpenEMS FindFreeSSH InitCSX InitFDTD RunOpenEMS_MPI +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if nargin < 2 + error 'specify the Sim_Path and Sim_file to simulate' +end + +if nargin < 3 + opts = ''; +end + +if (nargin<4) + Settings = []; +end + +if (isfield(Settings,'MPI') && isunix) + if (Settings.MPI.NrProc>1) + RunOpenEMS_MPI(Sim_Path, Sim_File, opts, Settings); + return; + end +end + +ssh_command = 'ssh'; +scp_command = 'scp'; +scp_options = ''; +ssh_options = ''; + +enable_ssh = 0; +enable_ssh = isfield(Settings,'SSH') && isunix; + +if ~isunix + enable_ssh = isfield(Settings,'SSH') && isfield(Settings.SSH,'Putty'); + if (enable_ssh) + ssh_command = [Settings.SSH.Putty.Path '/plink ']; + ssh_options = [ssh_options ' -i ' Settings.SSH.Putty.Key]; + + scp_command = [Settings.SSH.Putty.Path '/pscp ']; + scp_options = [scp_options ' -i ' Settings.SSH.Putty.Key]; + end +end + +savePath = pwd; +cd(Sim_Path); + +if (enable_ssh) + scp_options = [scp_options ' -C']; + ssh_options = [ssh_options ' -x -C']; + + % ssh options: no X forwarding; no password prompt (use pub keys!); no host checking + if (isunix) + ssh_options = [ssh_options ' -o "PasswordAuthentication no" -o "StrictHostKeyChecking no"']; + scp_options = [scp_options ' -o "PasswordAuthentication no" -o "StrictHostKeyChecking no"']; + end + + if isfield(Settings.SSH,'host_list') + host = FindFreeSSH(Settings.SSH.host_list, Settings); + if ~isempty(host) + Settings.SSH.host = host; + else + error('openEMS:RunOpenEMS', 'unable to find host, abort openEMS'); + end + end + + % create a tmp working dir + [status, result] = system([ssh_command ' ' ssh_options ' ' Settings.SSH.host ' "mktemp -d /tmp/openEMS_XXXXXXXXXXXX"']); + if (status~=0) + disp(result); + error('openEMS:RunOpenEMS','mktemp failed to create tmp directory!'); + end + ssh_work_path = strtrim(result); %remove tailing \n + + disp(['Running remote openEMS on ' Settings.SSH.host ' at working dir: ' ssh_work_path]); + + %copy openEMS all simulation files to the ssh host + [stat, res] = system([scp_command ' ' scp_options ' * ' Settings.SSH.host ':' ssh_work_path '/']); + if (stat~=0) + disp(res); + error('openEMS:RunOpenEMS','scp failed!'); + end + + %run openEMS (with log file if requested) + if isfield(Settings,'LogFile') && isunix + append_unix = [' 2>&1 | tee ' Settings.LogFile]; + else + append_unix = []; + end + status = system([ssh_command ' ' ssh_options ' ' Settings.SSH.host ' "cd ' ssh_work_path ' && ' Settings.SSH.bin ' ' Sim_File ' ' opts '"' append_unix]); + if (status~=0) + disp(result); + error('openEMS:RunOpenEMS','ssh openEMS failed!'); + end + + disp( 'Remote simulation done... copying back results and cleaning up...' ); + + %copy back all results + [stat, res] = system([scp_command ' -r ' scp_options ' ' Settings.SSH.host ':' ssh_work_path '/* ' pwd '/']); + if (stat~=0); + disp(res); + error('openEMS:RunOpenEMS','scp failed!'); + end + + %cleanup + [stat, res] = system([ssh_command ' ' ssh_options ' ' Settings.SSH.host ' rm -r ' ssh_work_path]); + if (stat~=0); + disp(res); + warning('openEMS:RunOpenEMS','remote cleanup failed!'); + end +else + args = [Sim_File ' ' opts]; + if isfield(Settings,'LogFile') && isfield(Settings,'Silent') + invoke_openEMS(args,Settings.LogFile,Settings.Silent); + elseif isfield(Settings,'LogFile') + invoke_openEMS(args,Settings.LogFile); + elseif isfield(Settings,'Silent') + invoke_openEMS(args,[],Settings.Silent); + else + invoke_openEMS(args); + end +end + +cd(savePath); +return diff --git a/openEMS/matlab/RunOpenEMS_MPI.m b/openEMS/matlab/RunOpenEMS_MPI.m new file mode 100644 index 0000000..495f7e5 --- /dev/null +++ b/openEMS/matlab/RunOpenEMS_MPI.m @@ -0,0 +1,125 @@ +function RunOpenEMS_MPI(Sim_Path, Sim_File, opts, Settings) +% function RunOpenEMS_MPI(Sim_Path, Sim_File, NrProc, opts, Settings) +% +% Run an openEMS simulation with MPI support +% +% % mpi binary path on all nodes needed +% Settings.MPI.Binary = '/opt/openEMS/openEMS'; +% % number of processes to run +% Settings.MPI.NrProc = 3; +% % define the mpi hosts : +% Settings.MPI.Hosts = {'host1','host2','host3'}; +% +% RunOpenEMS(Sim_Path, Sim_File, NrProc, opts, Settings) +% +% See also SetupMPI, WriteOpenEMS, RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if (isunix ~= 1) + error 'MPI version of openEMS currently only available using Linux' +end + +if nargin < 4 + error 'missing arguments: specify the Sim_Path, Sim_file, opts and Settings...' +end + +NrProc = Settings.MPI.NrProc; + +if (NrProc<2) + error('openEMS:RunOpenEMS_MPI','MPI number of processes to small...'); +end + +if ~isfield(Settings,'MPI') + error('openEMS:RunOpenEMS_MPI','MPI settings not found...'); +end + +savePath = pwd; +cd(Sim_Path); + +scp_options = '-C -o "PasswordAuthentication no" -o "StrictHostKeyChecking no"'; +ssh_options = [scp_options ' -x']; + +if isfield(Settings.MPI,'Hosts') + Remote_Nodes = Settings.MPI.Hosts; + HostList = ''; + for n=1:numel(Remote_Nodes) + remote_name = Remote_Nodes{n}; + + if (n==1) + [status, result] = unix(['ssh ' ssh_options ' ' remote_name ' "mktemp -d /tmp/openEMS_MPI_XXXXXXXXXXXX"']); + if (status~=0) + disp(result); + error('openEMS:RunOpenEMS','mktemp failed to create tmp directory!'); + end + work_path = strtrim(result); %remove tailing \n + HostList = remote_name; + else + [status, result] = unix(['ssh ' ssh_options ' ' remote_name ' "mkdir ' work_path '"']); + if (status~=0) + disp(result); + error('openEMS:RunOpenEMS',['mkdir failed to create tmp directory on remote ' remote_name ' !']); + end + HostList = [HostList ',' remote_name]; + end + + [stat, res] = unix(['scp ' scp_options ' * ' remote_name ':' work_path '/']); + if (stat~=0) + disp(res); + error('openEMS:RunOpenEMS',['scp to remote ' remote_name ' failed!']); + end + end +end + + +%run openEMS (with log file if requested) +if isfield(Settings,'LogFile') + append_unix = [' 2>&1 | tee ' Settings.LogFile]; +else + append_unix = []; +end + +if ~isfield(Settings.MPI,'GlobalArgs') + Settings.MPI.GlobalArgs = ''; +end + +if isfield(Settings.MPI,'Hosts') + disp(['Running remote openEMS_MPI in working dir: ' work_path]); + [status] = system(['mpiexec -host ' HostList ' -n ' int2str(NrProc) ' -wdir ' work_path ' ' Settings.MPI.Binary ' ' Sim_File ' ' opts ' ' append_unix]); +else + disp('Running local openEMS_MPI'); + [status] = system(['mpiexec ' Settings.MPI.GlobalArgs ' -n ' int2str(NrProc) ' ' Settings.MPI.Binary ' ' Sim_File ' ' opts ' ' append_unix]); +end + +if (status~=0) + error('openEMS:RunOpenEMS','mpirun openEMS failed!'); +end + +if isfield(Settings.MPI,'Hosts') + disp( 'Remote simulation done... copying back results and cleaning up...' ); + + if (strncmp(work_path,'/tmp/',5)~=1) % savety precaution... + error('openEMS:RunOpenEMS','working path invalid for deletion'); + end + + for n=1:numel(Remote_Nodes) + remote_name = Remote_Nodes{n}; + disp(['Copy data from remote node: ' remote_name]); + [stat, res] = unix(['scp -r ' scp_options ' ' remote_name ':' work_path '/* ''' pwd '''/']); + if (stat~=0); + disp(res); + error('openEMS:RunOpenEMS','remote scp failed!'); + end + + %cleanup + [stat, res] = unix(['ssh ' ssh_options ' ' remote_name ' rm -r ' work_path]); + if (stat~=0); + disp(res); + warning('openEMS:RunOpenEMS','remote cleanup failed!'); + end + end +end + +cd(savePath); diff --git a/openEMS/matlab/RunOpenEMS_Parallel.m b/openEMS/matlab/RunOpenEMS_Parallel.m new file mode 100644 index 0000000..cd5d4bd --- /dev/null +++ b/openEMS/matlab/RunOpenEMS_Parallel.m @@ -0,0 +1,92 @@ +function [stdout, stderr] = RunOpenEMS_Parallel(Sim_Paths, Sim_Files, opts, Settings, varargin) +% function [stdout, stderr] = RunOpenEMS_Parallel(Sim_Paths, Sim_Files, opts, Settings, varargin) +% +% Run multiple openEMS simulations in parallel, distributed on multiple +% machines using a ssh host_list! (currently on Linux only) +% +% This function relies on InitQueue etc. +% +% input: +% Sim_Paths: cell array of pathes to simulate by RunOpenEMS +% Sim_Files: filename or cell array of filenames to simulate +% opts: openEMS options. sa RunOpenEMS +% Settings: use the settings to define multiple host for simulation +% e.g.: Settings.SSH.bin ='<path_to_openEMS>/openEMS.sh'; +% Settings.SSH.host_list = {'list','of','hosts'}; +% +% Note: If no SSH host_list is defined, this function will skip the +% parallel run and switch back to a default RunOpenEMS! +% +% See also RunOpenEMS, FindFreeSSH, InitQueue +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig 2011 + +pause_queue = 5; %pause between consecutive runs (needed for FindFreeSSH) + +skip_parallel = 0; + +% currently only supporting linux, run conventional RunOpenEMS +if ~isunix + warning 'your OS is not supported (Unix only), running default RunOpenEMS'; + skip_parallel = 1; +end + +% in case only one path is given, run conventional RunOpenEMS +if ischar(Sim_Paths) + warning 'only a single path given, running default RunOpenEMS' + skip_parallel = 1; +end + +% in case SSH.host_list is not defined, run conventional RunOpenEMS +if ~isfield(Settings,'SSH') + warning 'SSH options missing, running default RunOpenEMS' + skip_parallel = 1; +elseif ~isfield(Settings.SSH,'host_list') + warning 'SSH.host_list option missing, running default RunOpenEMS' + skip_parallel = 1; +end + +if (skip_parallel) + for n=1:numel(Sim_Paths) + if iscell(Sim_Files) + Sim_File = Sim_Files{n}; + else + Sim_File = Sim_Files; + end + RunOpenEMS(Sim_Paths{n}, Sim_Files, opts, Settings) + end + stdout = []; + stderr = []; + return +end + +if ~iscell(Sim_Paths) + error('RunOpenEMS_Parallel:needs a cell array of Sim_Paths to simulate'); +end + +% get the path to this file +[dir] = fileparts( mfilename('fullpath') ); + +queue = InitQueue('DependPath',{dir}, varargin{:}); + +% spawn multiple simulations +for n=1:numel(Sim_Paths) + if iscell(Sim_Files) + Sim_File = Sim_Files{n}; + else + Sim_File = Sim_Files; + end + + queue = Add2Queue(queue,'RunOpenEMS',{Sim_Paths{n}, Sim_File, opts, Settings}); + disp(['openEMS simulation #' int2str(n) ' in directory: ' Sim_Paths{n} ' started!']); + pause(pause_queue); +end + +[queue] = FinishQueue(queue); + +for n=1:numel(Sim_Paths) + stdout{n} = queue.jobs{n}.stdout; + stderr{n} = queue.jobs{n}.stderr; +end diff --git a/openEMS/matlab/SetBoundaryCond.m b/openEMS/matlab/SetBoundaryCond.m new file mode 100644 index 0000000..fbfbd4c --- /dev/null +++ b/openEMS/matlab/SetBoundaryCond.m @@ -0,0 +1,68 @@ +function FDTD = SetBoundaryCond(FDTD, BC, varargin) +% FDTD = SetBoundaryCond(FDTD, BC, varargin) +% +% BC = [xmin xmax ymin ymax zmin zmax]; +% or BC = {xmin xmax ymin ymax zmin zmax}; +% ?min/?max: +% 0 = PEC or 'PEC' +% 1 = PMC or 'PMC' +% 2 = MUR-ABC or 'MUR' +% 3 = PML-ABC or 'PML_x' with pml size x => 4..50 +% +% example: +% BC = [ 1 1 0 0 2 3 ] %using numbers or +% BC = {'PMC' 'PMC' 'PEC' 'PEC' 'MUR' 'PML_8'} %usign equivalent strings +% +% mur-abc definitions +% define a phase-velocity to be used by the mur-abc +% useful e.g. for dispersive waveguides +% FDTD = SetBoundaryCond(FDTD,BC,'MUR_PhaseVelocity',299792457.93272); +% +% +% pml definitions +% arguments: 'PML_Grading','gradFunction' +% Define the pml grading grading function. +% Predefined variables in this grading function are: +% D = depth in the pml in meter +% dl = mesh delta inside the pml in meter +% W = width (length) of the pml in meter +% N = number of cells for the pml +% Z = wave impedance at the current depth and position +% +% example: +% FDTD = SetBoundaryCond(FDTD,BC); +% or +% FDTD = SetBoundaryCond(FDTD,BC,'PML_Grading','-log(1e-6)*log(2.5)/(2*dl*pow(2.5,W/dl)-1) * pow(2.5, D/dl) / Z'); +% +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +if (numel(BC)~=6) + error('openEMS:SetBoundaryCond','wrong number of boundary conditions'); +end + +if isnumeric(BC) + FDTD.BoundaryCond.ATTRIBUTE.xmin=BC(1); + FDTD.BoundaryCond.ATTRIBUTE.xmax=BC(2); + FDTD.BoundaryCond.ATTRIBUTE.ymin=BC(3); + FDTD.BoundaryCond.ATTRIBUTE.ymax=BC(4); + FDTD.BoundaryCond.ATTRIBUTE.zmin=BC(5); + FDTD.BoundaryCond.ATTRIBUTE.zmax=BC(6); +elseif iscell(BC) + FDTD.BoundaryCond.ATTRIBUTE.xmin=BC{1}; + FDTD.BoundaryCond.ATTRIBUTE.xmax=BC{2}; + FDTD.BoundaryCond.ATTRIBUTE.ymin=BC{3}; + FDTD.BoundaryCond.ATTRIBUTE.ymax=BC{4}; + FDTD.BoundaryCond.ATTRIBUTE.zmin=BC{5}; + FDTD.BoundaryCond.ATTRIBUTE.zmax=BC{6}; +else + error('openEMS:SetBoundaryCond','unknown boundary condition type'); +end + + +for n=1:(nargin-2)/2 + FDTD.BoundaryCond.ATTRIBUTE.(varargin{2*n-1}) = varargin{2*n}; +end +
\ No newline at end of file diff --git a/openEMS/matlab/SetCustomExcite.m b/openEMS/matlab/SetCustomExcite.m new file mode 100644 index 0000000..6f87a49 --- /dev/null +++ b/openEMS/matlab/SetCustomExcite.m @@ -0,0 +1,21 @@ +function FDTD = SetCustomExcite(FDTD,f0,funcStr) +% function FDTD = SetCustomExcite(FDTD,f0,funcStr) +% +% f0 : nyquist rate +% funcStr : string desribing the excitation function e(t) +% +% see also SetSinusExcite SetGaussExcite +% +% e.g for a ramped sinus excite... +% T = 1/f0; +% FDTD = SetCustomExcite(FDTD,1e9,.. +% [ '(1-exp(-1*(t/' num2str(T) ')^2) ) * sin(2*pi*' num2str(f0) '*t)' ]); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +FDTD.Excitation.ATTRIBUTE.Type=10; +FDTD.Excitation.ATTRIBUTE.f0=f0; +FDTD.Excitation.ATTRIBUTE.Function=funcStr; +FDTD.ATTRIBUTE.f_max=f0; diff --git a/openEMS/matlab/SetDiracExcite.m b/openEMS/matlab/SetDiracExcite.m new file mode 100644 index 0000000..18a342d --- /dev/null +++ b/openEMS/matlab/SetDiracExcite.m @@ -0,0 +1,3 @@ +function FDTD = SetDiracExcite(FDTD) + +FDTD.Excitation.ATTRIBUTE.Type=2; diff --git a/openEMS/matlab/SetGaussExcite.m b/openEMS/matlab/SetGaussExcite.m new file mode 100644 index 0000000..be650ba --- /dev/null +++ b/openEMS/matlab/SetGaussExcite.m @@ -0,0 +1,18 @@ +function FDTD = SetGaussExcite(FDTD,f0,fc) +% function FDTD = SetGaussExcite(FDTD,f0,fc); +% +% f0 : center frequency +% fc : 20dB cutoff frequency --> bandwidth is 2*fc +% +% see also SetSinusExcite SetCustomExcite +% +% e.g FDTD = SetGaussExcite(FDTD,1e9,1e8); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +FDTD.Excitation.ATTRIBUTE.Type=0; +FDTD.Excitation.ATTRIBUTE.f0=f0; +FDTD.Excitation.ATTRIBUTE.fc=fc; +FDTD.ATTRIBUTE.f_max=f0+fc; diff --git a/openEMS/matlab/SetSinusExcite.m b/openEMS/matlab/SetSinusExcite.m new file mode 100644 index 0000000..22aadde --- /dev/null +++ b/openEMS/matlab/SetSinusExcite.m @@ -0,0 +1,14 @@ +function FDTD = SetSinusExcite(FDTD,f0) +% function FDTD = SetSinusExcite(FDTD,f0) +% +% see also SetGaussExcite SetCustomExcite +% +% e.g FDTD = SetSinusExcite(FDTD,1e9); +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +FDTD.Excitation.ATTRIBUTE.Type=1; +FDTD.Excitation.ATTRIBUTE.f0=f0; +FDTD.ATTRIBUTE.f_max=f0; diff --git a/openEMS/matlab/SetStepExcite.m b/openEMS/matlab/SetStepExcite.m new file mode 100644 index 0000000..ff87e4f --- /dev/null +++ b/openEMS/matlab/SetStepExcite.m @@ -0,0 +1,3 @@ +function FDTD = SetStepExcite(FDTD) + +FDTD.Excitation.ATTRIBUTE.Type=3; diff --git a/openEMS/matlab/SetupMPI.m b/openEMS/matlab/SetupMPI.m new file mode 100644 index 0000000..51d6f65 --- /dev/null +++ b/openEMS/matlab/SetupMPI.m @@ -0,0 +1,17 @@ +function FDTD = SetupMPI(FDTD, varargin) +% function FDTD = SetupMPI(FDTD, varargin); +% +% % example, split the FDTD mesh in 2 equal parts in x-direction +% % and split the FDTD mesh in 3 parts in z-direction, split at z=-500 and z=500 +% % this will need a Settings.MPI.NrProc of 2*3=6 +% FDTD = SetupMPI(FDTD,'SplitN_X',2 ,'SplitPos_Z', '-500,500'); +% +% See also RunOpenEMS_MPI +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +for n=1:(nargin-1)/2 + FDTD.MPI.ATTRIBUTE.(varargin{2*n-1})=varargin{2*n}; +end diff --git a/openEMS/matlab/Tutorials/Bent_Patch_Antenna.m b/openEMS/matlab/Tutorials/Bent_Patch_Antenna.m new file mode 100644 index 0000000..33d5cac --- /dev/null +++ b/openEMS/matlab/Tutorials/Bent_Patch_Antenna.m @@ -0,0 +1,197 @@ +% +% Tutorials / bent patch antenna +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Bent_Patch_Antenna +% +% Tested with +% - Matlab 2011a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2013-2015 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% patch width in alpha-direction +patch.width = 32; % resonant length in alpha-direction +patch.radius = 50; % radius +patch.length = 40; % patch length in z-direction + +%substrate setup +substrate.epsR = 3.38; +substrate.kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate.epsR; +substrate.width = 80; +substrate.length = 90; +substrate.thickness = 1.524; +substrate.cells = 4; + +%setup feeding +feed.pos = -5.5; %feeding position in x-direction +feed.width = 2; %feeding port width +feed.R = 50; %feed resistance + +% size of the simulation box +SimBox.rad = 2*100; +SimBox.height = 1.5*200; + +%% setup FDTD parameter & excitation function +FDTD = InitFDTD('CoordSystem', 1); % init a cylindrical FDTD +f0 = 2e9; % center frequency +fc = 1e9; % 20 dB corner frequency +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +% init a cylindrical mesh +CSX = InitCSX('CoordSystem',1); + +% calculate some width as an angle in radiant +patch_ang_width = patch.width/(patch.radius+substrate.thickness); +substr_ang_width = substrate.width/patch.radius; +feed_angle = feed.pos/patch.radius; + +%% create patch +CSX = AddMetal( CSX, 'patch' ); % create a perfect electric conductor (PEC) +start = [patch.radius+substrate.thickness -patch_ang_width/2 -patch.length/2 ]; +stop = [patch.radius+substrate.thickness patch_ang_width/2 patch.length/2 ]; +CSX = AddBox(CSX,'patch',10,start,stop); % add a box-primitive to the metal property 'patch' + +%% create substrate +CSX = AddMaterial( CSX, 'substrate' ); +CSX = SetMaterialProperty( CSX, 'substrate', 'Epsilon', substrate.epsR, 'Kappa', substrate.kappa ); +start = [patch.radius -substr_ang_width/2 -substrate.length/2]; +stop = [patch.radius+substrate.thickness substr_ang_width/2 substrate.length/2]; +CSX = AddBox( CSX, 'substrate', 0, start, stop); + +%% save current density oon the patch +CSX = AddDump(CSX, 'Jt_patch','DumpType',3,'FileType',1); +start = [patch.radius+substrate.thickness -substr_ang_width/2 -substrate.length/2]; +stop = [patch.radius+substrate.thickness +substr_ang_width/2 substrate.length/2]; +CSX = AddBox( CSX, 'Jt_patch', 0, start, stop ); + +%% create ground (not really necessary, only for esthetic reasons) +CSX = AddMetal( CSX, 'gnd' ); % create a perfect electric conductor (PEC) +start = [patch.radius -substr_ang_width/2 -substrate.length/2]; +stop = [patch.radius +substr_ang_width/2 +substrate.length/2]; +CSX = AddBox(CSX,'gnd',10,start,stop); + +%% apply the excitation & resist as a current source +start = [patch.radius feed_angle 0]; +stop = [patch.radius+substrate.thickness feed_angle 0]; +[CSX port] = AddLumpedPort(CSX, 50 ,1 ,feed.R, start, stop, [1 0 0], true); + + +%% finalize the mesh +% detect all edges +mesh = DetectEdges(CSX); + +% add the simulation domain size +mesh.r = [mesh.r patch.radius+[-20 SimBox.rad]]; +mesh.a = [mesh.a -0.75*pi 0.75*pi]; +mesh.z = [mesh.z -SimBox.height/2 SimBox.height/2]; + +% add some lines for the substrate +mesh.r = [mesh.r patch.radius+linspace(0,substrate.thickness,substrate.cells)]; + +% generate a smooth mesh with max. cell size: lambda_min / 20 +max_res = c0 / (f0+fc) / unit / 20; +max_ang = max_res/(SimBox.rad+patch.radius); % max res in radiant +mesh = SmoothMesh(mesh, [max_res max_ang max_res], 1.4); + +disp(['Num of cells: ' num2str(numel(mesh.r)*numel(mesh.a)*numel(mesh.z))]); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create nf2ff, keep some distance to the boundary conditions, e.g. 8 cells pml +start = [mesh.r(4) mesh.a(8) mesh.z(8)]; +stop = [mesh.r(end-9) mesh.a(end-9) mesh.z(end-9)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop, 'Directions',[1 1 1 1 1 1]); + +%% prepare simulation folder & run +Sim_Path = ['tmp_' mfilename]; +Sim_CSX = [mfilename '.xml']; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +% show the structure +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + +% run openEMS +RunOpenEMS( Sim_Path, Sim_CSX); + +%% postprocessing & do the plots +freq = linspace( max([1e9,f0-fc]), f0+fc, 501 ); +port = calcPort(port, Sim_Path, freq); + +Zin = port.uf.tot ./ port.if.tot; +s11 = port.uf.ref ./ port.uf.inc; +P_in = 0.5*real(port.uf.tot .* conj(port.if.tot)); % antenna feed power + +% plot feed point impedance +figure +plot( freq/1e6, real(Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Zin), 'r--', 'Linewidth', 2 ); +title( 'feed point impedance' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'impedance Z_{in} / Ohm' ); +legend( 'real', 'imag' ); + +% plot reflection coefficient S11 +figure +plot( freq/1e6, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +drawnow + +%find resonance frequncy from s11 +f_res_ind = find(s11==min(s11)); +f_res = freq(f_res_ind); + +%% +disp('dumping resonant current distribution to vtk file, use Paraview to visualize'); +ConvertHDF5_VTK([Sim_Path '/Jt_patch.h5'],[Sim_Path '/Jf_patch'],'Frequency',f_res,'FieldName','J-Field'); + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% calculate the far field at phi=0 degree +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, [-180:2:180]*pi/180, 0,'Center',[patch.radius+substrate.thickness 0 0]*unit, 'Outfile','pattern_phi_0.h5'); +% normalized directivity as polar plot +figure +polarFF(nf2ff,'xaxis','theta','param',1,'normalize',1) + +% calculate the far field at phi=0 degree +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, pi/2, (-180:2:180)*pi/180,'Center',[patch.radius+substrate.thickness 0 0]*unit, 'Outfile','pattern_theta_90.h5'); +% normalized directivity as polar plot +figure +polarFF(nf2ff,'xaxis','phi','param',1,'normalize',1) + +% display power and directivity +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(nf2ff.Dmax) ' (' num2str(10*log10(nf2ff.Dmax)) ' dBi)'] ); +disp( ['efficiency: nu_rad = ' num2str(100*nf2ff.Prad./real(P_in(f_res_ind))) ' %']); + +drawnow + +%% +disp( 'calculating 3D far field pattern and dumping to vtk (use Paraview to visualize)...' ); +thetaRange = (0:2:180); +phiRange = (0:2:360) - 180; +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180,'Verbose',1,'Outfile','3D_Pattern.h5','Center',[patch.radius+substrate.thickness 0 0]*unit); + +figure +plotFF3D(nf2ff,'logscale',-20); + diff --git a/openEMS/matlab/Tutorials/CRLH_Extraction.m b/openEMS/matlab/Tutorials/CRLH_Extraction.m new file mode 100644 index 0000000..e0bc361 --- /dev/null +++ b/openEMS/matlab/Tutorials/CRLH_Extraction.m @@ -0,0 +1,155 @@ +% +% Tutorials / CRLH_Extraction +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_CRLH_Extraction +% +% Tested with +% - Matlab 2011a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2011-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um + +feed_length = 30000; + +substrate_thickness = [1524 101 254]; +substrate_epsr = [3.48 3.48 3.48]; + +CRLH.LL = 14e3; %CRLH totel (line) length +CRLH.LW = 4e3; %CRLH unit cell width (without the stubs) +CRLH.GLB = 1950; %CRLH gap width bottom layer +CRLH.GLT = 4700; %CRLH gap width top layer +CRLH.SL = 7800; %CRLH stub length (bottom layer, both sides) +CRLH.SW = 1000; %CRLH stub width (bottom layer, both sides) +CRLH.VR = 250; %CRLH via hole radius (stub -> ground) +CRLH.TopSig = sum(substrate_thickness); %top layer height +CRLH.BottomSig = CRLH.TopSig - substrate_thickness(end); %bottom layer height + +% frequency range of interest +f_start = 0.8e9; +f_stop = 6e9; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(); +FDTD = SetGaussExcite( FDTD, (f_start+f_stop)/2, (f_stop-f_start)/2 ); +BC = {'PML_8' 'PML_8' 'MUR' 'MUR' 'PEC' 'PML_8'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% Setup a basic mesh and create the CRLH unit cell +CSX = InitCSX(); +resolution = c0/(f_stop*sqrt(max(substrate_epsr)))/unit /30; % resolution of lambda/30 + +mesh.x = [-feed_length-CRLH.LL/2 0 feed_length+CRLH.LL/2]; +mesh.y = [-30000 0 30000]; +substratelines = cumsum(substrate_thickness); +mesh.z = [0 cumsum(substrate_thickness) linspace(substratelines(end-1),substratelines(end),4) 20000]; + +% create the CRLH unit cell (will define additional fixed mesh lines) +[CSX mesh] = CreateCRLH(CSX, mesh, CRLH, resolution/4); + +% Smooth the given mesh +mesh = SmoothMesh(mesh, resolution, 1.5, 'algorithm',[1 3]); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% Setup the substrate layer +substratelines = [0 substratelines]; +for n=1:numel(substrate_thickness) + CSX = AddMaterial( CSX, ['substrate' int2str(n)] ); + CSX = SetMaterialProperty( CSX, ['substrate' int2str(n)], 'Epsilon', substrate_epsr(n) ); + start = [mesh.x(1), mesh.y(1), substratelines(n)]; + stop = [mesh.x(end), mesh.y(end), substratelines(n+1)]; + CSX = AddBox( CSX, ['substrate' int2str(n)], 0, start, stop ); +end + +%% add the feeding MSL ports +CSX = AddMetal( CSX, 'PEC' ); +portstart = [ mesh.x(1) , -CRLH.LW/2, substratelines(end)]; +portstop = [ -CRLH.LL/2, CRLH.LW/2, 0]; +[CSX,port{1}] = AddMSLPort( CSX, 999, 1, 'PEC', portstart, portstop, 0, [0 0 -1], 'ExcitePort', true, 'FeedShift', 10*resolution(1), 'MeasPlaneShift', feed_length/2); + +portstart = [ mesh.x(end) , -CRLH.LW/2, substratelines(end)]; +portstop = [ +CRLH.LL/2, CRLH.LW/2, 0]; +[CSX,port{2}] = AddMSLPort( CSX, 999, 2, 'PEC', portstart, portstop, 0, [0 0 -1], 'MeasPlaneShift', feed_length/2 ); + +%% write/show/run the openEMS compatible xml-file +Sim_Path = 'tmp'; +Sim_CSX = 'CRLH.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +RunOpenEMS( Sim_Path, Sim_CSX ); + +%% post-processing +close all +f = linspace( f_start, f_stop, 1601 ); +port = calcPort( port, Sim_Path, f, 'RefPlaneShift', feed_length); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; + +plot(f/1e9,20*log10(abs(s11)),'k-','LineWidth',2); +hold on; +grid on; +plot(f/1e9,20*log10(abs(s21)),'r--','LineWidth',2); +l = legend('S_{11}','S_{21}','Location','Best'); +set(l,'FontSize',12); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (GHz) \rightarrow','FontSize',12); +ylim([-40 2]); + +%% extract parameter +A = ((1+s11).*(1-s11) + s21.*s21)./(2*s21); +C = ((1-s11).*(1-s11) - s21.*s21)./(2*s21) ./ port{2}.ZL; + +Y = C; +Z = 2*(A-1)./C; + +iZ = imag(Z); +iY = imag(Y); + +fse = interp1(iZ,f,0); +fsh = interp1(iY,f,0); + +df = f(2)-f(1); +fse_idx = find(f>fse,1); +fsh_idx = find(f>fsh,1); + +LR = 0.5*(iZ(fse_idx)-iZ(fse_idx-1))./(2*pi*df); +CL = 1/(2*pi*fse)^2/LR; + +CR = 0.5*(iY(fsh_idx)-iY(fsh_idx-1))./(2*pi*df); +LL = 1/(2*pi*fsh)^2/CR; + +disp([' Series tank: CL = ' num2str(CL*1e12,3) 'pF; LR = ' num2str(LR*1e9,3) 'nH -> f_se = ' num2str(fse*1e-9,3) 'GHz ']); +disp([' Shunt tank: CR = ' num2str(CR*1e12,3) 'pF; LL = ' num2str(LL*1e9,3) 'nH -> f_sh = ' num2str(fsh*1e-9,3) 'GHz ']); + +%% calculate analytical wave-number of an inf-array of cells +w = 2*pi*f; +wse = 2*pi*fse; +wsh = 2*pi*fsh; +beta_calc = real(acos(1-(w.^2-wse^2).*(w.^2-wsh^2)./(2*w.^2/CR/LR))); + +%% +figure +beta = -angle(s21)/CRLH.LL/unit; +plot(abs(beta)*CRLH.LL*unit/pi,f*1e-9,'k-','LineWidth',2) +grid on; +hold on; +plot(beta_calc/pi,f*1e-9,'c--','LineWidth',2) +plot(real(port{2}.beta)*CRLH.LL*unit/pi,f*1e-9,'g-','LineWidth',2) +ylim([1 6]) +xlabel('|\beta| p / \pi \rightarrow','FontSize',12) +ylabel('frequency (GHz) \rightarrow','FontSize',12) +l = legend('\beta_{CRLH, 1 cell}','\beta_{CRLH, \infty cells}','\beta_{MSL}','Location','East'); +set(l,'FontSize',12); diff --git a/openEMS/matlab/Tutorials/CRLH_LeakyWaveAnt.m b/openEMS/matlab/Tutorials/CRLH_LeakyWaveAnt.m new file mode 100644 index 0000000..a8359b7 --- /dev/null +++ b/openEMS/matlab/Tutorials/CRLH_LeakyWaveAnt.m @@ -0,0 +1,168 @@ +% +% Tutorials / CRLH_LeakyWaveAnt +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_CRLH_Leaky_Wave_Antenna +% +% Tested with +% - Matlab 2011a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2011-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um + +feed_length = 20000; + +substrate_thickness = [1524 101 254]; +substrate_epsr = [3.48 3.48 3.48]; +substrate_tanD = [1 1 1]*1e-3; + +N_Cells = 8; %number of CRLH unit cells + +CRLH.LL = 14e3; %CRLH totel (line) length +CRLH.LW = 4e3; %CRLH unit cell width (without the stubs) +CRLH.GLB = 1950; %CRLH gap width bottom layer +CRLH.GLT = 4700; %CRLH gap width top layer +CRLH.SL = 7800; %CRLH stub length (bottom layer, both sides) +CRLH.SW = 1000; %CRLH stub width (bottom layer, both sides) +CRLH.VR = 250; %CRLH via hole radius (stub -> ground) +CRLH.TopSig = sum(substrate_thickness); %top layer height +CRLH.BottomSig = CRLH.TopSig - substrate_thickness(end); %bottom layer height + +substrate_width = CRLH.LW + 2*CRLH.SL; +Air_Spacer = 30000; + +% frequency range of interest +f_start = 1e9; +f_stop = 6e9; + +% frequencies to calculate the 3D radiation pattern +f_rad = (1.9:0.05:4.2)*1e9; +nf2ff_resolution = c0/max(f_rad)/unit/15; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD('EndCriteria', 1e-3); +FDTD = SetGaussExcite( FDTD, (f_start+f_stop)/2, (f_stop-f_start)/2 ); +BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% Setup a basic mesh and create the CRLH unit cell +CSX = InitCSX(); +resolution = c0/(f_stop*sqrt(max(substrate_epsr)))/unit /30; % resolution of lambda/30 + +mesh.x = [-feed_length-(N_Cells*CRLH.LL)/2-Air_Spacer -feed_length-(N_Cells*CRLH.LL)/2 0 feed_length+(N_Cells*CRLH.LL)/2 feed_length+(N_Cells*CRLH.LL)/2+Air_Spacer]; +mesh.y = [-Air_Spacer-substrate_width/2 0 Air_Spacer+substrate_width/2]; +substratelines = cumsum(substrate_thickness); +mesh.z = [-0.5*Air_Spacer 0 cumsum(substrate_thickness) linspace(substratelines(end-1),substratelines(end),4) Air_Spacer]; + +% create the CRLH unit cells (will define additional fixed mesh lines) +pos_x = -(N_Cells*CRLH.LL)/2 + CRLH.LL/2; +for n=1:N_Cells + [CSX mesh] = CreateCRLH(CSX, mesh, CRLH, resolution/4, [pos_x 0 0]); + pos_x = pos_x + CRLH.LL; +end + +% Smooth the given mesh +mesh = SmoothMesh(mesh, resolution, 1.5, 'algorithm',[1 3]); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% Setup the substrate layer +substratelines = [0 substratelines]; +for n=1:numel(substrate_thickness) + CSX = AddMaterial( CSX, ['substrate' int2str(n)] ); + CSX = SetMaterialProperty( CSX, ['substrate' int2str(n)], 'Epsilon', substrate_epsr(n), 'Kappa', substrate_tanD(n)*substrate_epsr(n)*EPS0*2*pi*3e9 ); + start = [-feed_length-(N_Cells*CRLH.LL)/2, -substrate_width/2, substratelines(n)]; + stop = [+feed_length+(N_Cells*CRLH.LL)/2, substrate_width/2, substratelines(n+1)]; + CSX = AddBox( CSX, ['substrate' int2str(n)], 0, start, stop ); +end + +%% add the feeding MSL ports +%ground plane +CSX = AddMetal( CSX, 'ground' ); +start = [-feed_length-(N_Cells*CRLH.LL)/2, -substrate_width/2, 0]; +stop = [+feed_length+(N_Cells*CRLH.LL)/2, substrate_width/2, 0]; +CSX = AddBox( CSX, 'ground', 0, start, stop ); + +CSX = AddMetal( CSX, 'PEC' ); +portstart = [ -feed_length-(N_Cells*CRLH.LL)/2 , -CRLH.LW/2, substratelines(end)]; +portstop = [ -(N_Cells*CRLH.LL)/2, CRLH.LW/2, 0]; +[CSX,port{1}] = AddMSLPort( CSX, 999, 1, 'PEC', portstart, portstop, 0, [0 0 -1], 'ExcitePort', true, 'MeasPlaneShift', feed_length/2, 'Feed_R', 50); + +portstart = [ feed_length+(N_Cells*CRLH.LL)/2 , -CRLH.LW/2, substratelines(end)]; +portstop = [ +(N_Cells*CRLH.LL)/2, CRLH.LW/2, 0]; +[CSX,port{2}] = AddMSLPort( CSX, 999, 2, 'PEC', portstart, portstop, 0, [0 0 -1], 'MeasPlaneShift', feed_length/2, 'Feed_R', 50 ); + +%% nf2ff calc +start = [mesh.x(1) mesh.y(1) mesh.z(1) ] + 10*resolution; +stop = [mesh.x(end) mesh.y(end) mesh.z(end)] - 10*resolution; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop, 'OptResolution', nf2ff_resolution); + +%% write/show/run the openEMS compatible xml-file +Sim_Path = 'tmp_CRLH_LeakyWave'; +Sim_CSX = 'CRLH.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +RunOpenEMS( Sim_Path, Sim_CSX ); + +%% post-processing +close all +f = linspace( f_start, f_stop, 1601 ); +port = calcPort( port, Sim_Path, f, 'RefPlaneShift', feed_length*unit); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; + +plot(f/1e9,20*log10(abs(s11)),'k-','LineWidth',2); +hold on; +grid on; +plot(f/1e9,20*log10(abs(s21)),'r--','LineWidth',2); +l = legend('S_{11}','S_{21}','Location','Best'); +set(l,'FontSize',12); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (GHz) \rightarrow','FontSize',12); +ylim([-40 2]); + +drawnow + +%% calculate 3D pattern +phi = 0:2:360; +theta = 0:2:180; + +disp( 'calculating 3D far field pattern...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_rad, theta*pi/180, phi*pi/180, 'Outfile','3D_Pattern.h5', 'Mode', 0,'Verbose',1); + +%% +P_in = interp1(f, port{1}.P_acc, f_rad); + +figure() + +[AX,H1,H2] = plotyy(f_rad/1e9,nf2ff.Dmax',f_rad/1e9,100*nf2ff.Prad'./P_in,'plot'); +grid on +xlabel( 'frequency (GHz)' ); +set(get(AX(1),'Ylabel'),'String','directivity (dBi)') +set(get(AX(2),'Ylabel'),'String','radiation efficiency (%)') +set(H1,'Linewidth',2) +set(H2,'Linewidth',2) +set(H1,'Marker','*') +set(H2,'Marker','s') + +drawnow + +%% +disp( 'dumping 3D far field pattern to vtk, use Paraview to visualize...' ); +for n=1:numel(f_rad) + E_far_normalized_3D = nf2ff.E_norm{n} / max(max(nf2ff.E_norm{n})) * nf2ff.Dmax(n); + DumpFF2VTK( [Sim_Path '/FF_Pattern_' int2str(f_rad(n)/1e6) 'MHz.vtk'],E_far_normalized_3D,theta,phi,'scale',1e-3); +end + diff --git a/openEMS/matlab/Tutorials/Circ_Waveguide.m b/openEMS/matlab/Tutorials/Circ_Waveguide.m new file mode 100644 index 0000000..d61a713 --- /dev/null +++ b/openEMS/matlab/Tutorials/Circ_Waveguide.m @@ -0,0 +1,105 @@ +% +% Tutorials / Circ_Waveguide +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Circular_Waveguide +% +% Tested with +% - Matlab 2011a / Octave 3.4.3 +% - openEMS v0.0.31 +% +% (C) 2010-2013 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-3; %drawing unit in mm + +% waveguide dimensions +length = 2000; +rad = 350; %waveguide radius in mm + +% frequency range of interest +f_start = 300e6; +f_stop = 500e6; + +mesh_res = [10 2*pi/49.999 10]; %targeted mesh resolution + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD('EndCriteria',1e-4,'CoordSystem',1); +FDTD = SetGaussExcite(FDTD,0.5*(f_start+f_stop),0.5*(f_stop-f_start)); + +% boundary conditions +BC = [0 0 0 0 3 3]; %pml in pos. and neg. z-direction +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX('CoordSystem',1); % init a cylindrical mesh +mesh.r = SmoothMeshLines([0 rad], mesh_res(1)); %mesh in radial direction +mesh.a = SmoothMeshLines([0 2*pi], mesh_res(2)); % mesh in aziumthal dir. +mesh.z = SmoothMeshLines([0 length], mesh_res(3)); +CSX = DefineRectGrid(CSX, unit,mesh); + +%% apply the waveguide port %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start=[mesh.r(1) mesh.a(1) mesh.z(8)]; +stop =[mesh.r(end) mesh.a(end) mesh.z(15)]; +[CSX, port{1}] = AddCircWaveGuidePort( CSX, 0, 1, start, stop, rad*unit, 'TE11', 0, 1); + +start=[mesh.r(1) mesh.a(1) mesh.z(end-13)]; +stop =[mesh.r(end) mesh.a(end) mesh.z(end-14)]; +[CSX, port{2}] = AddCircWaveGuidePort( CSX, 0, 2, start, stop, rad*unit, 'TE11'); + +%% define dump box... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et','FileType',1,'SubSampling','4,4,4'); +start = [mesh.r(1) mesh.a(1) mesh.z(1)]; +stop = [mesh.r(end) mesh.a(end) mesh.z(end)]; +CSX = AddBox(CSX,'Et',0 , start,stop); + +%% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Sim_Path = 'tmp'; +Sim_CSX = 'circ_wg.xml'; + +[status, message, messageid] = rmdir(Sim_Path,'s'); +[status, message, messageid] = mkdir(Sim_Path); + +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +RunOpenEMS(Sim_Path, Sim_CSX) + +%% postproc %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +freq = linspace(f_start,f_stop,201); +port = calcPort( port, Sim_Path, freq); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; +ZL = port{1}.uf.tot./port{1}.if.tot; + + +%% plot s-parameter %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +figure +plot(freq*1e-6,20*log10(abs(s11)),'k-','Linewidth',2); +xlim([freq(1) freq(end)]*1e-6); +grid on; +hold on; +plot(freq*1e-6,20*log10(abs(s21)),'r--','Linewidth',2); +l = legend('S_{11}','S_{21}','Location','Best'); +set(l,'FontSize',12); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (MHz) \rightarrow','FontSize',12); + +%% compare analytic and numerical wave-impedance %%%%%%%%%%%%%%%%%%%%%%%%%% +figure +plot(freq*1e-6,real(ZL),'Linewidth',2); +hold on; +grid on; +plot(freq*1e-6,imag(ZL),'r--','Linewidth',2); +plot(freq*1e-6,port{1}.ZL,'g-.','Linewidth',2); +ylabel('ZL (\Omega)','FontSize',12); +xlabel('frequency (MHz) \rightarrow','FontSize',12); +xlim([freq(1) freq(end)]*1e-6); +l = legend('\Re(Z_L)','\Im(Z_L)','Z_L analytic','Location','Best'); +set(l,'FontSize',12); + diff --git a/openEMS/matlab/Tutorials/Conical_Horn_Antenna.m b/openEMS/matlab/Tutorials/Conical_Horn_Antenna.m new file mode 100644 index 0000000..f266993 --- /dev/null +++ b/openEMS/matlab/Tutorials/Conical_Horn_Antenna.m @@ -0,0 +1,180 @@ +% +% Tutorials / conical horn antenna +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Conical_Horn_Antenna +% +% Tested with +% - Matlab 2011a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2011-2015 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% horn radius +horn.radius = 20; +% horn length in z-direction +horn.length = 50; + +horn.feed_length = 50; + +horn.thickness = 2; + +% horn opening angle +horn.angle = 20*pi/180; + +% size of the simulation box +SimBox = [100 100 100]*2; + +% frequency range of interest +f_start = 10e9; +f_stop = 20e9; + +% frequency of interest +f0 = 15e9; + +%% setup FDTD parameter & excitation function +FDTD = InitFDTD( 'NrTS', 30000, 'EndCriteria', 1e-4 ); +FDTD = SetGaussExcite(FDTD,0.5*(f_start+f_stop),0.5*(f_stop-f_start)); +BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +% currently, openEMS cannot automatically generate a mesh +max_res = c0 / (f_stop) / unit / 15; % cell size: lambda/20 +CSX = InitCSX(); + +%create fixed lines for the simulation box, substrate and port +mesh.x = [-SimBox(1)/2 -horn.radius 0 horn.radius SimBox(1)/2]; +mesh.x = SmoothMeshLines( mesh.x, max_res, 1.4); % create a smooth mesh between specified fixed mesh lines + +mesh.y = mesh.x; + +%create fixed lines for the simulation box and given number of lines inside the substrate +mesh.z = [-horn.feed_length 0 SimBox(3) ]; +mesh.z = SmoothMeshLines( mesh.z, max_res, 1.4 ); + +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create horn +% horn + waveguide, defined by a rotational polygon +CSX = AddMetal(CSX, 'Conical_Horn'); +p(1,1) = horn.radius+horn.thickness; % x-coord point 1 +p(2,1) = -horn.feed_length; % z-coord point 1 +p(1,end+1) = horn.radius+horn.thickness; % x-coord point 1 +p(2,end) = 0; % z-coord point 1 +p(1,end+1) = horn.radius+horn.thickness + sin(horn.angle)*horn.length; % x-coord point 2 +p(2,end) = horn.length; % y-coord point 2 +p(1,end+1) = horn.radius + sin(horn.angle)*horn.length; % x-coord point 2 +p(2,end) = horn.length; % y-coord point 2 +p(1,end+1) = horn.radius; % x-coord point 1 +p(2,end) = 0; % z-coord point 1 +p(1,end+1) = horn.radius; % x-coord point 1 +p(2,end) = -horn.feed_length; % z-coord point 1 +CSX = AddRotPoly(CSX,'Conical_Horn',10,'x',p,'z'); + +% horn aperture +A = pi*((horn.radius + sin(horn.angle)*horn.length)*unit)^2; + +%% apply the excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start=[-horn.radius -horn.radius mesh.z(10) ]; +stop =[+horn.radius +horn.radius mesh.z(1)+horn.feed_length/2 ]; +[CSX, port] = AddCircWaveGuidePort( CSX, 0, 1, start, stop, horn.radius*unit, 'TE11', 0, 1); + +%% +CSX = AddDump(CSX,'Exc_dump'); +start=[-horn.radius -horn.radius mesh.z(8)]; +stop =[+horn.radius +horn.radius mesh.z(8)]; +CSX = AddBox(CSX,'Exc_dump',0,start,stop); + +%% nf2ff calc +start = [mesh.x(9) mesh.y(9) mesh.z(9)]; +stop = [mesh.x(end-8) mesh.y(end-8) mesh.z(end-8)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop, 'Directions', [1 1 1 1 0 1]); + +%% prepare simulation folder +Sim_Path = 'tmp'; +Sim_CSX = 'horn_ant.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +%% show the structure +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + +%% run openEMS +RunOpenEMS( Sim_Path, Sim_CSX); + +%% postprocessing & do the plots +freq = linspace(f_start,f_stop,201); + +port = calcPort(port, Sim_Path, freq); + +Zin = port.uf.tot ./ port.if.tot; +s11 = port.uf.ref ./ port.uf.inc; + +% plot reflection coefficient S11 +figure +plot( freq/1e9, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +ylim([-60 0]); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / GHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +drawnow + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% calculate the far field at phi=0 degrees and at phi=90 degrees +thetaRange = (0:2:359) - 180; +disp( 'calculating far field at phi=[0 90] deg...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f0, thetaRange*pi/180, [0 90]*pi/180); + +Dlog=10*log10(nf2ff.Dmax); +G_a = 4*pi*A/(c0/f0)^2; +e_a = nf2ff.Dmax/G_a; + +% display some antenna parameter +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(Dlog) ' dBi'] ); +disp( ['aperture efficiency: e_a = ' num2str(e_a*100) '%'] ); + +%% +% normalized directivity +figure +plotFFdB(nf2ff,'xaxis','theta','param',[1 2]); +drawnow +% D_log = 20*log10(nf2ff.E_norm{1}/max(max(nf2ff.E_norm{1}))); +% D_log = D_log + 10*log10(nf2ff.Dmax); +% plot( nf2ff.theta, D_log(:,1) ,'k-', nf2ff.theta, D_log(:,2) ,'r-' ); + +% polar plot +figure +polarFF(nf2ff,'xaxis','theta','param',[1 2],'logscale',[-40 20], 'xtics', 12); +drawnow +% polar( nf2ff.theta, nf2ff.E_norm{1}(:,1) ) + +%% calculate 3D pattern +phiRange = sort( unique( [-180:5:-100 -100:2.5:-50 -50:1:50 50:2.5:100 100:5:180] ) ); +thetaRange = sort( unique([ 0:1:50 50:2.:100 100:5:180 ])); + +disp( 'calculating 3D far field...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f0, thetaRange*pi/180, phiRange*pi/180, 'Verbose',2,'Outfile','nf2ff_3D.h5'); + +figure +plotFF3D(nf2ff); % plot liear 3D far field + +%% +E_far_normalized = nf2ff.E_norm{1}/max(nf2ff.E_norm{1}(:)); +DumpFF2VTK([Sim_Path '/Conical_Horn_Pattern.vtk'],E_far_normalized,thetaRange,phiRange,'scale',1e-3); diff --git a/openEMS/matlab/Tutorials/CreateCRLH.m b/openEMS/matlab/Tutorials/CreateCRLH.m new file mode 100644 index 0000000..6c10732 --- /dev/null +++ b/openEMS/matlab/Tutorials/CreateCRLH.m @@ -0,0 +1,55 @@ +function [CSX mesh] = CreateCRLH(CSX, mesh, CRLH, resolution, translate) +% function [CSX mesh] = CreateCRLH(CSX, mesh, CRLH, resolution, translate) +% +% support function to create a CRLH unit cell +% +% currently used by Tutorials/CRLH_Extraction +% +% Tested with +% - Matlab 2009b +% - openEMS v0.0.23 +% +% (C) 2011 Thorsten Liebig <thorsten.liebig@gmx.de> + +if (nargin<5) + translate = [0 0 0]; +end + +CSX = AddMetal(CSX, 'metal_top'); +one_two_third = [-resolution/3 2*resolution/3]; + +start = [-CRLH.LL/2 -CRLH.LW/2 CRLH.TopSig]+translate; +stop = [-CRLH.GLT/2 CRLH.LW/2 CRLH.TopSig]+translate; +CSX = AddBox(CSX, 'metal_top', 10, start, stop); +mesh.x = [mesh.x start(1) stop(1)+one_two_third]; +mesh.y = [mesh.y start(2)-one_two_third stop(2)+one_two_third]; + +start = [+CRLH.LL/2 -CRLH.LW/2 CRLH.TopSig]+translate; +stop = [+CRLH.GLT/2 CRLH.LW/2 CRLH.TopSig]+translate; +CSX = AddBox(CSX, 'metal_top', 10, start, stop); +mesh.x = [mesh.x start(1) stop(1)-one_two_third]; + +CSX = AddMetal(CSX, 'metal_bot'); +start = [-(CRLH.LL-CRLH.GLB)/2 -CRLH.LW/2 CRLH.BottomSig]+translate; +stop = [+(CRLH.LL-CRLH.GLB)/2 CRLH.LW/2 CRLH.BottomSig]+translate; +CSX = AddBox(CSX, 'metal_bot', 10, start, stop); +mesh.x = [mesh.x start(1)-one_two_third stop(1)+one_two_third]; + +start = [-CRLH.SW/2 -CRLH.LW/2-CRLH.SL CRLH.BottomSig]+translate; +stop = [+CRLH.SW/2 CRLH.LW/2+CRLH.SL CRLH.BottomSig]+translate; +CSX = AddBox(CSX, 'metal_bot', 10, start, stop); +mesh.x = [mesh.x start(1)-one_two_third stop(1)+one_two_third]; +mesh.y = [mesh.y start(2) stop(2)]; + +CSX = AddMetal(CSX, 'via'); +start = [0 -CRLH.LW/2-CRLH.SL+CRLH.SW/2 0]+translate; +stop = [0 -CRLH.LW/2-CRLH.SL+CRLH.SW/2 CRLH.BottomSig]+translate; +CSX = AddCylinder(CSX, 'via', 10, start, stop, CRLH.VR); +mesh.x = [mesh.x start(1)+[-1 0 1]*CRLH.VR]; +mesh.y = [mesh.y start(2)+[-1 0 1]*CRLH.VR]; + +start(2) = -start(2); +stop(2) = -stop(2); +CSX = AddCylinder(CSX, 'via', 10, start, stop, CRLH.VR); +mesh.y = [mesh.y start(2)+[-1 0 1]*CRLH.VR]; +end
\ No newline at end of file diff --git a/openEMS/matlab/Tutorials/CylindricalWave_CC.m b/openEMS/matlab/Tutorials/CylindricalWave_CC.m new file mode 100644 index 0000000..d55c470 --- /dev/null +++ b/openEMS/matlab/Tutorials/CylindricalWave_CC.m @@ -0,0 +1,104 @@ +% +% Tutorials / CylindricalWave_CC +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_2D_Cylindrical_Wave +% +% Tested with +% - Matlab 2011a/ Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2011-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants +mesh_res = 10; %desired mesh resolution +radius = 2560; %simulation domain radius +split = ['80,160,320,640,1280']; %radii to split the mesh into sub-grids +split_N = 5; %number of nested sub-grids +heigth = mesh_res*4; + +f0 = 1e9; + +exite_offset = 1300; +excite_angle = 45; + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(100000,1e-4,'CoordSystem',1,'MultiGrid',split); +FDTD = SetGaussExcite(FDTD,f0,f0/2); +BC = [0 3 0 0 0 0]; % pml in positive r-direction +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% 50 mesh lines for the inner most mesh +% increase the total number of meshlines in alpha direcion for all sub-grids +N_alpha = 50 * 2^split_N + 1; + +CSX = InitCSX('CoordSystem',1); +mesh.r = SmoothMeshLines([0 radius],mesh_res); +mesh.a = linspace(-pi,pi,N_alpha); +mesh.z = SmoothMeshLines([-heigth/2 0 heigth/2],mesh_res); +CSX = DefineRectGrid(CSX, 1e-3,mesh); + +%% add the dipol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start = [exite_offset excite_angle/180*pi-0.001 -20]; +stop = [exite_offset excite_angle/180*pi+0.001 20]; +if (exite_offset==0) + start(2) = mesh.a(1); + stop(2) = mesh.a(1); +end +CSX = AddExcitation(CSX,'excite',1,[0 0 1]); +CSX = AddBox(CSX,'excite',0 ,start,stop); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start = [mesh.r(1) mesh.a(1) 0]; +stop = [mesh.r(end-8) mesh.a(end) 0]; + +% time domain vtk dump +CSX = AddDump(CSX,'Et_ra','DumpType',0,'FileType',0,'SubSampling','4,10,1'); +CSX = AddBox(CSX,'Et_ra',0 , start,stop); + +% frequency domain hdf5 dump +CSX = AddDump(CSX,'Ef_ra','DumpType',10,'FileType',1,'SubSampling','2,2,2','Frequency',f0); +CSX = AddBox(CSX,'Ef_ra',0 , start,stop); + +%% write/run the openEMS compatible xml-file +Sim_Path = 'tmp'; +Sim_CSX = '2D_CC_Wave.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); +RunOpenEMS(Sim_Path, Sim_CSX); + +%% +disp('use Paraview to visualize the vtk field dump...'); + +%% +[field mesh_h5] = ReadHDF5Dump([Sim_Path '/Ef_ra.h5']); + +r = mesh_h5.lines{1}; +a = mesh_h5.lines{2}; +a(end+1) = a(1); %closeup mesh for visualization +[R A] = ndgrid(r,a); +X = R.*cos(A); +Y = R.*sin(A); + +Ez = squeeze(field.FD.values{1}(:,:,1,3)); +Ez(:,end+1) = Ez(:,1); %closeup mesh for visualization + +E_max = max(max(abs(Ez))); %get maximum E_z amplitude + +while 1 + for ph = linspace(0,360,41) %animate phase from 0..360 degree + surf(X,Y,real(Ez*exp(1j*ph*pi/180)),'EdgeColor','none') + caxis([-E_max E_max]/10) + zlim([-E_max E_max]) + pause(0.3) + end +end diff --git a/openEMS/matlab/Tutorials/Dipole_SAR.m b/openEMS/matlab/Tutorials/Dipole_SAR.m new file mode 100644 index 0000000..a172117 --- /dev/null +++ b/openEMS/matlab/Tutorials/Dipole_SAR.m @@ -0,0 +1,221 @@ +% +% Tutorials / Dipole SAR + Power budget +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Dipole_SAR +% +% Tested with +% - openEMS v0.0.33 +% +% (C) 2013-2015 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% switches & options... +postprocessing_only = 0; + +%% prepare simulation folder +Sim_Path = 'tmp_Dipole_SAR'; +Sim_CSX = 'Dipole_SAR.xml'; + +%% setup the simulation +physical_constants; +unit = 1e-3; % all lengths in mm + +feed.R = 50; % feed resistance + +%% define phantom +phantom{1}.name='skin'; +phantom{1}.epsR = 50; +phantom{1}.kappa = 0.65; % S/m +phantom{1}.density = 1100; % kg/m^3 +phantom{1}.radius = [80 100 100]; % ellipsoide +phantom{1}.center = [100 0 0]; + +phantom{2}.name='headbone'; +phantom{2}.epsR = 13; +phantom{2}.kappa = 0.1; % S/m +phantom{2}.density = 2000; % kg/m^3 +phantom{2}.radius = [75 95 95]; % ellipsoide +phantom{2}.center = [100 0 0]; + +phantom{3}.name='brain'; +phantom{3}.epsR = 60; +phantom{3}.kappa = 0.7; % S/m +phantom{3}.density = 1040; % kg/m^3 +phantom{3}.radius = [65 85 85]; % ellipsoide +phantom{3}.center = [100 0 0]; + +%% setup FDTD parameter & excitation function +f0 = 1e9; % center frequency +lambda0 = c0/f0; + +f_stop = 1.5e9; % 20 dB corner frequency +lambda_min = c0/f_stop; + +mesh_res_air = lambda_min/20/unit; +mesh_res_phantom = 2.5; + +dipole_length = 0.46*lambda0/unit; +disp(['Lambda-half dipole length: ' num2str(dipole_length) 'mm']) + +%% +FDTD = InitFDTD(); +FDTD = SetGaussExcite( FDTD, 0, f_stop ); +% apply PML-8 boundary conditions in all directions +BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +CSX = InitCSX(); + +%% Dipole +CSX = AddMetal( CSX, 'Dipole' ); % create a perfect electric conductor (PEC) +CSX = AddBox(CSX, 'Dipole', 1, [0 0 -dipole_length/2], [0 0 dipole_length/2]); + +% mesh lines for the dipole +mesh.x = 0; +mesh.y = 0; +mesh.z = [-dipole_length/2-[-1/3 2/3]*mesh_res_phantom dipole_length/2+[-1/3 2/3]*mesh_res_phantom]; + +%% add the dielectrics +for n=1:numel(phantom) + CSX = AddMaterial( CSX, phantom{n}.name ); + CSX = SetMaterialProperty( CSX, phantom{n}.name, 'Epsilon', phantom{n}.epsR, 'Kappa', phantom{n}.kappa, 'Density', phantom{n}.density); + CSX = AddSphere( CSX, phantom{n}.name, 10+n, [0 0 0], 1,'Transform',{'Scale',phantom{n}.radius, 'Translate', phantom{n}.center} ); + + %% mesh lines for the dielectrics + mesh.x = [mesh.x phantom{n}.radius(1)*[-1 1]+phantom{n}.center(1) ]; + mesh.y = [mesh.y phantom{n}.radius(2)*[-1 1]+phantom{n}.center(2) ]; + mesh.z = [mesh.z phantom{n}.radius(3)*[-1 1]+phantom{n}.center(3) ]; +end + +%% apply the excitation & resist as a current source +[CSX port] = AddLumpedPort(CSX, 100, 1, feed.R, [-0.1 -0.1 -mesh_res_phantom/2], [0.1 0.1 +mesh_res_phantom/2], [0 0 1], true); + +% mesh lines for the port +mesh.z = [mesh.z -mesh_res_phantom/2 +mesh_res_phantom/2]; + +%% smooth the mesh over the dipole and phantom +mesh = SmoothMesh(mesh, mesh_res_phantom); + +%% add lines for the air-box +mesh.x = [mesh.x -200 250+100]; +mesh.y = [mesh.y -250 250]; +mesh.z = [mesh.z -250 250]; + +% smooth the final mesh (incl. air box) +mesh = SmoothMesh(mesh, mesh_res_air, 1.2); + +%% dump SAR +start = [-10 -100 -100]; +stop = [180 100 100]; +CSX = AddDump( CSX, 'SAR', 'DumpType', 20, 'Frequency', f0,'FileType',1,'DumpMode',2); +CSX = AddBox( CSX, 'SAR', 0, start, stop); + +%% nf2ff calc +start = [mesh.x(1) mesh.y(1) mesh.z(1)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop, 'OptResolution', lambda_min/15/unit); + +%% +% add 10 equidistant cells (air) +% around the structure to keep the pml away from the nf2ff box +mesh = AddPML( mesh, 10 ); + +% Define the mesh +CSX = DefineRectGrid(CSX, unit, mesh); + +%% +if (postprocessing_only==0) + [status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory + [status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + + % write openEMS compatible xml-file + WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + + % show the structure + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + + % run openEMS + RunOpenEMS( Sim_Path, Sim_CSX ); +end + + +%% postprocessing & make the plots +freq = linspace(500e6, 1500e6, 501 ); +port = calcPort(port, Sim_Path, freq); + +s11 = port.uf.ref./port.uf.inc; +Zin = port.uf.tot./port.if.tot; + +Pin_f0 = interp1(freq, port.P_acc, f0); + +%% +% plot feed point impedance +figure +plot( freq/1e6, real(Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Zin), 'r--', 'Linewidth', 2 ); +title( 'feed point impedance' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'impedance Z_{in} / Ohm' ); +legend( 'real', 'imag' ); + +% plot reflection coefficient S11 +figure +plot( freq/1e9, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'S_{11} (dB)' ); + +%% read SAR and visualize +SAR_field = ReadHDF5Dump([Sim_Path '/SAR.h5']); + +SAR = SAR_field.FD.values{1}; +ptotal = ReadHDF5Attribute([Sim_Path '/SAR.h5'],'/FieldData/FD/f0','power'); + +%% calculate 3D pattern +phi = 0:3:360; +theta = 0:3:180; + +disp( 'calculating 3D far field pattern...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f0, theta*pi/180, phi*pi/180, 'Outfile','3D_Pattern.h5'); + +%% +disp(['max SAR: ' num2str(max(SAR(:))/Pin_f0) ' W/kg normalized to 1 W accepted power']); +disp(['accepted power: ' num2str(Pin_f0) ' W (100 %)']); +disp(['radiated power: ' num2str(nf2ff.Prad) ' W ( ' num2str(round(100*(nf2ff.Prad) / Pin_f0)) ' %)']); +disp(['absorbed power: ' num2str(ptotal) ' W ( ' num2str(round(100*(ptotal) / Pin_f0)) ' %)']); +disp(['power budget: ' num2str(100*(nf2ff.Prad + ptotal) / Pin_f0) ' %']); + +%% plot on a x/y-plane +[SAR_field SAR_mesh] = ReadHDF5Dump([Sim_Path '/SAR.h5'],'Range',{[],[],0}); +figure +[X Y] = ndgrid(SAR_mesh.lines{1},SAR_mesh.lines{2}); +h = pcolor(X,Y,log10(SAR_field.FD.values{1}/abs(Pin_f0))); +title( 'logarithmic SAR on an xy-plane' ); +xlabel('x -->') +ylabel('y -->') +axis equal tight +set(h,'EdgeColor','none'); + +%% plot on a x/z-plane +[SAR_field SAR_mesh] = ReadHDF5Dump([Sim_Path '/SAR.h5'],'Range',{[],0,[]}); +figure +[X Z] = ndgrid(SAR_mesh.lines{1},SAR_mesh.lines{3}); +h = pcolor(X,Z,log10(squeeze(SAR_field.FD.values{1}))/abs(Pin_f0)); +title( 'logarithmic SAR on an xz-plane' ); +xlabel('x -->') +ylabel('z -->') +axis equal tight +set(h,'EdgeColor','none'); + +%% dump SAR to vtk file +disp(['Full local/normalized SAR has been dumped to vtk file! Use Paraview to visualize']); +ConvertHDF5_VTK([Sim_Path '/SAR.h5'],[Sim_Path '/SAR'],'weight',1/abs(Pin_f0),'FieldName','SAR_local' ); + diff --git a/openEMS/matlab/Tutorials/Helical_Antenna.m b/openEMS/matlab/Tutorials/Helical_Antenna.m new file mode 100644 index 0000000..b94ebd6 --- /dev/null +++ b/openEMS/matlab/Tutorials/Helical_Antenna.m @@ -0,0 +1,202 @@ +% +% Tutorials / helical antenna +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Helical_Antenna +% +% Tested with +% - Matlab 2011a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2012-2015 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +post_proc_only = 0; + +close all + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +f0 = 2.4e9; % center frequency, frequency of interest! +lambda0 = round(c0/f0/unit); % wavelength in mm +fc = 0.5e9; % 20 dB corner frequency + +Helix.radius = 20; % --> diameter is ~ lambda/pi +Helix.turns = 10; % --> expected gain is G ~ 4 * 10 = 40 (16dBi) +Helix.pitch = 30; % --> pitch is ~ lambda/4 +Helix.mesh_res = 3; + +gnd.radius = lambda0/2; + +% feeding +feed.heigth = 3; +feed.R = 120; %feed impedance + +% size of the simulation box +SimBox = [1 1 1.5]*2*lambda0; + +%% setup FDTD parameter & excitation function +FDTD = InitFDTD( ); +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'PML_8'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +max_res = floor(c0 / (f0+fc) / unit / 20); % cell size: lambda/20 +CSX = InitCSX(); + +% create helix mesh +mesh.x = SmoothMeshLines([-Helix.radius 0 Helix.radius],Helix.mesh_res); +% add the air-box +mesh.x = [mesh.x -SimBox(1)/2-gnd.radius SimBox(1)/2+gnd.radius]; +% create a smooth mesh between specified fixed mesh lines +mesh.x = SmoothMeshLines( mesh.x, max_res, 1.4); + +% copy x-mesh to y-direction +mesh.y = mesh.x; + +% create helix mesh in z-direction +mesh.z = SmoothMeshLines([0 feed.heigth Helix.turns*Helix.pitch+feed.heigth],Helix.mesh_res); +% add the air-box +mesh.z = unique([mesh.z -SimBox(3)/2 max(mesh.z)+SimBox(3)/2 ]); +% create a smooth mesh between specified fixed mesh lines +mesh.z = SmoothMeshLines( mesh.z, max_res, 1.4 ); + +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create helix using the wire primitive +CSX = AddMetal( CSX, 'helix' ); % create a perfect electric conductor (PEC) + +ang = linspace(0,2*pi,21); +coil_x = Helix.radius*cos(ang); +coil_y = Helix.radius*sin(ang); +coil_z = ang/2/pi*Helix.pitch; + +helix.x=[]; +helix.y=[]; +helix.z=[]; +zpos = feed.heigth; +for n=0:Helix.turns-1 + helix.x = [helix.x coil_x]; + helix.y = [helix.y coil_y]; + helix.z = [helix.z coil_z+zpos]; + zpos = zpos + Helix.pitch; +end +clear p +p(1,:) = helix.x; +p(2,:) = helix.y; +p(3,:) = helix.z; +CSX = AddCurve(CSX, 'helix', 0, p); + +%% create ground circular ground +CSX = AddMetal( CSX, 'gnd' ); % create a perfect electric conductor (PEC) +% add a box using cylindrical coordinates +start = [0 0 0]; +stop = [gnd.radius 2*pi 0]; +CSX = AddBox(CSX,'gnd',10,start,stop,'CoordSystem',1); + +%% apply the excitation & resist as a current source +start = [Helix.radius 0 0]; +stop = [Helix.radius 0 feed.heigth]; +[CSX port] = AddLumpedPort(CSX, 5 ,1 ,feed.R, start, stop, [0 0 1], true); + +%%nf2ff calc +start = [mesh.x(11) mesh.y(11) mesh.z(11)]; +stop = [mesh.x(end-10) mesh.y(end-10) mesh.z(end-10)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop, 'OptResolution', lambda0/15); + +%% prepare simulation folder +Sim_Path = 'tmp_Helical_Ant'; +Sim_CSX = 'Helix_Ant.xml'; + +if (post_proc_only==0) + [status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory + [status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + + %% write openEMS compatible xml-file + WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + + %% show the structure + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + + %% run openEMS + RunOpenEMS( Sim_Path, Sim_CSX); +end + +%% postprocessing & do the plots +freq = linspace( f0-fc, f0+fc, 501 ); +port = calcPort(port, Sim_Path, freq); + +Zin = port.uf.tot ./ port.if.tot; +s11 = port.uf.ref ./ port.uf.inc; + +% plot feed point impedance +figure +plot( freq/1e6, real(Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Zin), 'r--', 'Linewidth', 2 ); +title( 'feed point impedance' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'impedance Z_{in} / Ohm' ); +legend( 'real', 'imag' ); + +% plot reflection coefficient S11 +figure +plot( freq/1e6, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +drawnow + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%find resonance frequncy from s11 +f_res = f0; + +% get accepted antenna power at frequency f0 +P_in_0 = interp1(freq, port.P_acc, f0); + +% calculate the far field at phi=0 degrees and at phi=90 degrees +thetaRange = unique([0:0.5:90 90:180]); +phiRange = (0:2:360) - 180; +disp( 'calculating the 3D far field...' ); + +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180,'Mode',0,'Outfile','3D_Pattern.h5','Verbose',1); + +theta_HPBW = interp1(nf2ff.E_norm{1}(:,1)/max(nf2ff.E_norm{1}(:,1)),thetaRange,1/sqrt(2))*2; + +% display power and directivity +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(nf2ff.Dmax) ' (' num2str(10*log10(nf2ff.Dmax)) ' dBi)'] ); +disp( ['efficiency: nu_rad = ' num2str(100*nf2ff.Prad./P_in_0) ' %']); +disp( ['theta_HPBW = ' num2str(theta_HPBW) ' °']); + + +%% +directivity = nf2ff.P_rad{1}/nf2ff.Prad*4*pi; +directivity_CPRH = abs(nf2ff.E_cprh{1}).^2./max(nf2ff.E_norm{1}(:)).^2*nf2ff.Dmax; +directivity_CPLH = abs(nf2ff.E_cplh{1}).^2./max(nf2ff.E_norm{1}(:)).^2*nf2ff.Dmax; + +%% +figure +plot(thetaRange, 10*log10(directivity(:,1)'),'k-','LineWidth',2); +hold on +grid on +xlabel('theta (deg)'); +ylabel('directivity (dBi)'); +plot(thetaRange, 10*log10(directivity_CPRH(:,1)'),'g--','LineWidth',2); +plot(thetaRange, 10*log10(directivity_CPLH(:,1)'),'r-.','LineWidth',2); +legend('norm','CPRH','CPLH'); + +%% dump to vtk +DumpFF2VTK([Sim_Path '/3D_Pattern.vtk'],directivity,thetaRange,phiRange,'scale',1e-3); +DumpFF2VTK([Sim_Path '/3D_Pattern_CPRH.vtk'],directivity_CPRH,thetaRange,phiRange,'scale',1e-3); +DumpFF2VTK([Sim_Path '/3D_Pattern_CPLH.vtk'],directivity_CPLH,thetaRange,phiRange,'scale',1e-3); + diff --git a/openEMS/matlab/Tutorials/Horn_Antenna.m b/openEMS/matlab/Tutorials/Horn_Antenna.m new file mode 100644 index 0000000..d463734 --- /dev/null +++ b/openEMS/matlab/Tutorials/Horn_Antenna.m @@ -0,0 +1,202 @@ +% +% Tutorials / horn antenna +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Horn_Antenna +% +% Tested with +% - Matlab 2011a / Octave 3.6.4 +% - openEMS v0.0.31 +% +% (C) 2011,2012,2013 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% horn width in x-direction +horn.width = 20; +% horn height in y-direction +horn.height = 30; +% horn length in z-direction +horn.length = 50; + +horn.feed_length = 50; + +horn.thickness = 2; + +% horn opening angle in x, y +horn.angle = [20 20]*pi/180; + +% size of the simulation box +SimBox = [200 200 200]; + +% frequency range of interest +f_start = 10e9; +f_stop = 20e9; + +% frequency of interest +f0 = 15e9; + +%waveguide TE-mode definition +TE_mode = 'TE10'; +a = horn.width; +b = horn.height; + +%% setup FDTD parameter & excitation function +FDTD = InitFDTD('EndCriteria', 1e-4); +FDTD = SetGaussExcite(FDTD,0.5*(f_start+f_stop),0.5*(f_stop-f_start)); +BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +% currently, openEMS cannot automatically generate a mesh +max_res = c0 / (f_stop) / unit / 15; % cell size: lambda/20 +CSX = InitCSX(); + +%create fixed lines for the simulation box, substrate and port +mesh.x = [-SimBox(1)/2 -a/2 a/2 SimBox(1)/2]; +mesh.x = SmoothMeshLines( mesh.x, max_res, 1.4); % create a smooth mesh between specified fixed mesh lines + +mesh.y = [-SimBox(2)/2 -b/2 b/2 SimBox(2)/2]; +mesh.y = SmoothMeshLines( mesh.y, max_res, 1.4 ); + +%create fixed lines for the simulation box and given number of lines inside the substrate +mesh.z = [-horn.feed_length 0 SimBox(3)-horn.feed_length ]; +mesh.z = SmoothMeshLines( mesh.z, max_res, 1.4 ); + +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create horn +% horn feed rect waveguide +CSX = AddMetal(CSX, 'horn'); +start = [-a/2-horn.thickness -b/2 mesh.z(1)]; +stop = [-a/2 b/2 0]; +CSX = AddBox(CSX,'horn',10,start,stop); +start = [a/2+horn.thickness -b/2 mesh.z(1)]; +stop = [a/2 b/2 0]; +CSX = AddBox(CSX,'horn',10,start,stop); +start = [-a/2-horn.thickness b/2+horn.thickness mesh.z(1)]; +stop = [ a/2+horn.thickness b/2 0]; +CSX = AddBox(CSX,'horn',10,start,stop); +start = [-a/2-horn.thickness -b/2-horn.thickness mesh.z(1)]; +stop = [ a/2+horn.thickness -b/2 0]; +CSX = AddBox(CSX,'horn',10,start,stop); + +% horn opening +p(2,1) = a/2; +p(1,1) = 0; +p(2,2) = a/2 + sin(horn.angle(1))*horn.length; +p(1,2) = horn.length; +p(2,3) = -a/2 - sin(horn.angle(1))*horn.length; +p(1,3) = horn.length; +p(2,4) = -a/2; +p(1,4) = 0; +CSX = AddLinPoly( CSX, 'horn', 10, 1, -horn.thickness/2, p, horn.thickness, 'Transform', {'Rotate_X',horn.angle(2),'Translate',['0,' num2str(-b/2-horn.thickness/2) ',0']}); +CSX = AddLinPoly( CSX, 'horn', 10, 1, -horn.thickness/2, p, horn.thickness, 'Transform', {'Rotate_X',-horn.angle(2),'Translate',['0,' num2str(b/2+horn.thickness/2) ',0']}); + +p(1,1) = b/2+horn.thickness; +p(2,1) = 0; +p(1,2) = b/2+horn.thickness + sin(horn.angle(2))*horn.length; +p(2,2) = horn.length; +p(1,3) = -b/2-horn.thickness - sin(horn.angle(2))*horn.length; +p(2,3) = horn.length; +p(1,4) = -b/2-horn.thickness; +p(2,4) = 0; +CSX = AddLinPoly( CSX, 'horn', 10, 0, -horn.thickness/2, p, horn.thickness, 'Transform', {'Rotate_Y',-horn.angle(2),'Translate',[ num2str(-a/2-horn.thickness/2) ',0,0']}); +CSX = AddLinPoly( CSX, 'horn', 10, 0, -horn.thickness/2, p, horn.thickness, 'Transform', {'Rotate_Y',+horn.angle(2),'Translate',[ num2str(a/2+horn.thickness/2) ',0,0']}); + +% horn aperture +A = (a + 2*sin(horn.angle(1))*horn.length)*unit * (b + 2*sin(horn.angle(2))*horn.length)*unit; + +%% apply the excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start=[-a/2 -b/2 mesh.z(8) ]; +stop =[ a/2 b/2 mesh.z(1)+horn.feed_length/2 ]; +[CSX, port] = AddRectWaveGuidePort( CSX, 0, 1, start, stop, 2, a*unit, b*unit, TE_mode, 1); + +%% nf2ff calc +start = [mesh.x(9) mesh.y(9) mesh.z(9)]; +stop = [mesh.x(end-8) mesh.y(end-8) mesh.z(end-8)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop, 'Directions', [1 1 1 1 0 1]); + +%% prepare simulation folder +Sim_Path = 'tmp_Horn_Antenna'; +Sim_CSX = 'horn_ant.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% write openEMS compatible xml-file +WriteOpenEMS([Sim_Path '/' Sim_CSX], FDTD, CSX); + +%% show the structure +CSXGeomPlot([Sim_Path '/' Sim_CSX]); + +%% run openEMS +RunOpenEMS(Sim_Path, Sim_CSX); + +%% postprocessing & do the plots +freq = linspace(f_start,f_stop,201); + +port = calcPort(port, Sim_Path, freq); + +Zin = port.uf.tot ./ port.if.tot; +s11 = port.uf.ref ./ port.uf.inc; + +plot( freq/1e9, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +ylim([-60 0]); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / GHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +drawnow + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% calculate the far field at phi=0 degrees and at phi=90 degrees +thetaRange = (0:2:359) - 180; +disp( 'calculating far field at phi=[0 90] deg...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f0, thetaRange*pi/180, [0 90]*pi/180); + +Dlog=10*log10(nf2ff.Dmax); +G_a = 4*pi*A/(c0/f0)^2; +e_a = nf2ff.Dmax/G_a; + +% display some antenna parameter +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(Dlog) ' dBi'] ); +disp( ['aperture efficiency: e_a = ' num2str(e_a*100) '%'] ); + +%% +% normalized directivity +figure +plotFFdB(nf2ff,'xaxis','theta','param',[1 2]); +drawnow +% D_log = 20*log10(nf2ff.E_norm{1}/max(max(nf2ff.E_norm{1}))); +% D_log = D_log + 10*log10(nf2ff.Dmax); +% plot( nf2ff.theta, D_log(:,1) ,'k-', nf2ff.theta, D_log(:,2) ,'r-' ); + +% polar plot +figure +polarFF(nf2ff,'xaxis','theta','param',[1 2],'logscale',[-40 20], 'xtics', 12); +drawnow +% polar( nf2ff.theta, nf2ff.E_norm{1}(:,1) ) + +%% calculate 3D pattern +phiRange = sort( unique( [-180:5:-100 -100:2.5:-50 -50:1:50 50:2.5:100 100:5:180] ) ); +thetaRange = sort( unique([ 0:1:50 50:2.:100 100:5:180 ])); + +disp( 'calculating 3D far field...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f0, thetaRange*pi/180, phiRange*pi/180, 'Verbose',2,'Outfile','nf2ff_3D.h5'); + +figure +plotFF3D(nf2ff); + +%% +E_far_normalized = nf2ff.E_norm{1}/max(nf2ff.E_norm{1}(:)); +DumpFF2VTK([Sim_Path '/Horn_Pattern.vtk'],E_far_normalized,thetaRange,phiRange,'scale',1e-3); diff --git a/openEMS/matlab/Tutorials/MRI_LP_Birdcage.m b/openEMS/matlab/Tutorials/MRI_LP_Birdcage.m new file mode 100644 index 0000000..2434f03 --- /dev/null +++ b/openEMS/matlab/Tutorials/MRI_LP_Birdcage.m @@ -0,0 +1,320 @@ +% +% Tutorials / 3T MRI Low Pass Birdcage coil +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_MRI_LP_Birdcage +% +% Estimated time to run: ~7h @ ~65MC/s +% Memory requirement (RAM): ~ 700MB +% +% Tested with +% - openEMS v0.0.33 +% - Matlab 7.12.0 (R2011a) +% +% (C) 2013-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + + +close all +clear +clc + +% simulation setup +f0 = 128e6; +excite.f_0 = 75e6; % excite gaussian pulse center frequency +excite.f_c = 75e6; % excite gaussian pulse cutoff frequency + +postproc_only = 0; % set to 1 to perform only post processing +GeomPlot = 1; % set to 0 to skip geometry viewer + +% bore setup +Bore.rad = 320; +Bore.length = 1600; + +% birdcage setup +BC.N_rungs = 8; +BC.rad = 120; +BC.stripwidth = 10; +BC.portwidth = BC.stripwidth/2; +BC.portlength = BC.stripwidth/2; +BC.length = 250; +BC.cap = 2.6e-12; + +% feed amplitude and phase at given rungs +BC.feed_pos = [1 3]; +BC.feed_amp = [1 -1j]; + +%% define the human body model (virtual family) +% set file name for human body model to create with "Convert_VF_DiscMaterial" +% the file name should contain a full path +body_model_file = [pwd '/Ella_centered_' num2str(f0/1e6) 'MHz.h5']; + +% convert only part of the model (head/shoulder section) +body_model_range = {[],[],[-0.85 0]}; + +body_mesh_res = 2.5; % should be something like: BC.stripwidth/4 + +% paths to virtual family voxel models (VFVM), adept to your install! +VF_raw_filesuffix = '/tmp/Ella_26y_V2_1mm'; +VF_mat_db_file = '/tmp/DB_h5_20120711_SEMCADv14.8.h5'; + +% delete(body_model_file); % uncomment to delete old model if something changed + +% convert model (if it does not exist) +Convert_VF_DiscMaterial(VF_raw_filesuffix, VF_mat_db_file, body_model_file, ... + 'Frequency', f0, 'Center', 1, ... + 'Range', body_model_range); + +% rotate model to face the nose in +y-dir, and translate +body_model_transform = {'Rotate_X',pi,'Rotate_Z',pi, ... + 'Translate',[0,5,-720]}; + +%% some internal parameter +physical_constants % load important physical constans +end_crit = 1e-5; %abort simulation at -50dB energy drop +unit = 1e-3; %drawing unit used + +%capacity footprint is 4mm x 4mm +lambda_min = c0/(excite.f_0+excite.f_c); + +% meshing options +% desired mesh resolution +mesh_res([1 3]) = min(15,lambda_min/20/unit); +mesh_res(2) = body_mesh_res / BC.rad; + +%% setup FDTD parameter & excitation function +FDTD = InitFDTD('CoordSystem', 1, ... %init a cylindrical FDTD setup + 'EndCriteria', 1e-4, ... % with an end criteria of -40dB (1e-4) + 'MultiGrid', '10,20',... % add two cylindrical sub-grids at a radius of 10 and 20 mm + 'CellConstantMaterial', 1); % assume a material is constant inside + % a cell (material probing in cell center) + +% define the excitation time-signal (unmodulated gaussian pulse) +FDTD = SetGaussExcite(FDTD,excite.f_0,excite.f_c); + +% define & set boundary conditions +% - pml in +/- z-direction +% - boundaries in -r and +/- alpha direction disabled (full cylindrical mesh) +% - PEC boundary in +r-direction to model bore RF shield +FDTD = SetBoundaryCond(FDTD, [0 0 0 0 3 3]); + + +%% setup CSXCAD geometry & mesh (cylindrical) +CSX = InitCSX('CoordSystem',1); + +% init empty mesh structure +mesh.r = []; +mesh.a = []; +mesh.z = []; + +%% Create metal bird cage and rung capacities +CSX = AddMetal(CSX,'metal'); +CSX = AddLumpedElement(CSX,'caps','z','C',BC.cap); + +da_Strip = BC.stripwidth/BC.rad; % width of a strip in radiant +da_Caps = BC.portwidth/BC.rad; % width of a cap/port in radiant +da_Segs = 2*pi/BC.N_rungs; % width of a rung in radiant + +a_start = -pi-da_Segs/2; % starting angle + +w0 = 2*pi*f0; +T0 = 1/f0; + +% port counter +port_Nr = 1; + +a0 = a_start; + +for n=1:BC.N_rungs + start = [BC.rad a0+da_Segs/2-da_Caps/2 -0.5*BC.portlength]; + stop = [BC.rad a0+da_Segs/2+da_Caps/2 +0.5*BC.portlength]; + CSX = AddBox(CSX,'caps',1, start, stop); + + start = [BC.rad a0+da_Segs/2-da_Caps/2 0.5*BC.length-BC.stripwidth/2-BC.portlength]; + stop = [BC.rad a0+da_Segs/2+da_Caps/2 0.5*BC.length-BC.stripwidth/2]; + if (~isempty(intersect(n, BC.feed_pos)) && (BC.feed_amp(port_Nr)~=0)) % active port + exc_amp = abs(BC.feed_amp(port_Nr)); + + % calculate time delay to achieve a given phase shift at f0 + T = -angle(BC.feed_amp(port_Nr)) / w0; + if T<0 + T = T + T0; + end + [CSX port{port_Nr}] = AddLumpedPort(CSX, 100, port_Nr, 50, start, stop, [0 0 1]*exc_amp, true,'Delay',T); + + %increase port count + port_Nr = port_Nr+1; + + start = [BC.rad a0+da_Segs/2-da_Strip/2 0.5*BC.length-BC.stripwidth/2-BC.portlength]; + elseif ~isempty(intersect(n, BC.feed_pos)) % passive port + [CSX port{port_Nr}] = AddLumpedPort(CSX, 100, port_Nr, 50, start, stop, [0 0 1], false); + + %increase port count + port_Nr = port_Nr+1; + + start = [BC.rad a0+da_Segs/2-da_Strip/2 0.5*BC.length-BC.stripwidth/2-BC.portlength]; + else + start = [BC.rad a0+da_Segs/2-da_Strip/2 0.5*BC.length]; + end + + % the start z-coordinate depends on the port (see above) + stop = [BC.rad a0+da_Segs/2+da_Strip/2 0.5*BC.portlength]; + CSX = AddBox(CSX,'metal',1, start, stop); + + start = [BC.rad a0+da_Segs/2-da_Strip/2 -0.5*BC.length]; + stop = [BC.rad a0+da_Segs/2+da_Strip/2 -0.5*BC.portlength]; + CSX = AddBox(CSX,'metal',1, start, stop); + + % some additonal mesh lines + mesh.a = [mesh.a a0+da_Segs/2]; + + a0 = a0 + da_Segs; +end + +% create metal top ring +start = [BC.rad a_start -(BC.length-BC.stripwidth)/2]; +stop = [BC.rad a_start+2*pi -(BC.length+BC.stripwidth)/2]; +CSX = AddBox(CSX,'metal',1, start, stop); + +% create metal bottom ring +start = [BC.rad a_start (BC.length-BC.stripwidth)/2]; +stop = [BC.rad a_start+2*pi (BC.length+BC.stripwidth)/2]; +CSX = AddBox(CSX,'metal',1, start, stop); + +%% create smooth mesh +mesh = DetectEdges(CSX, mesh); +mesh.r = [0 SmoothMeshLines([body_mesh_res*1.5 mesh.r], body_mesh_res)]; +mesh.z = SmoothMeshLines(mesh.z, body_mesh_res); + +mesh.r = [mesh.r Bore.rad]; %mesh lines in radial direction +mesh.z = [-Bore.length/2 mesh.z Bore.length/2]; %mesh lines in z-direction + +mesh = SmoothMesh(mesh, mesh_res, 1.5); + +%% check the cell limit +numCells = numel(mesh.r)*numel(mesh.a)*numel(mesh.z); + +%% define human body model +CSX = AddDiscMaterial(CSX, 'body_model', 'File', body_model_file, 'Scale', 1/unit, 'Transform', body_model_transform); +start = [mesh.r(1) mesh.a(1) mesh.z(1)]; +stop = [mesh.r(end) mesh.a(end) mesh.z(end)]; +CSX = AddBox(CSX, 'body_model', 0, start, stop); + + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start = [0 mesh.a(1) -BC.length/2]; +stop = [BC.rad mesh.a(end) +BC.length/2]; + +CSX = AddDump(CSX,'Ef','FileType',1,'DumpType',10,'DumpMode',2,'Frequency',f0); +CSX = AddBox(CSX,'Ef',0 , start,stop); + +CSX = AddDump(CSX,'Hf','FileType',1,'DumpType',11,'DumpMode',2,'Frequency',f0); +CSX = AddBox(CSX,'Hf',0 , start,stop); + +CSX = AddDump(CSX,'SAR','FileType',1,'DumpType',20,'DumpMode',2,'Frequency',f0); +CSX = AddBox(CSX,'SAR',0 , start,stop); + +start = [0 mesh.a(1) 0]; +stop = [BC.rad mesh.a(end) 0]; +CSX = AddDump(CSX,'Ht','FileType',1,'DumpType',1,'DumpMode',2); +CSX = AddBox(CSX,'Ht',0 , start,stop); + +%% finalize mesh +% add some lines for the pml in +/- z- direction +mesh = AddPML(mesh, [0 0 0 0 10 10], 1); + +% define the mesh +CSX = DefineRectGrid(CSX, unit, mesh); + +%% Write file & run openEMS +Sim_Path = ['tmp_' mfilename]; + +if (postproc_only==0) + [status, message, messageid] = rmdir(Sim_Path,'s'); %delete old results + [status, message, messageid] = mkdir(Sim_Path); %create folder + + WriteOpenEMS([Sim_Path '/BirdCage.xml'],FDTD,CSX); +end + +if (GeomPlot==1) + CSXGeomPlot( [Sim_Path '/BirdCage.xml'] , ['--export-polydata-vtk=' Sim_Path ' --RenderDiscMaterial -v']); +end + +if (postproc_only==0) + RunOpenEMS(Sim_Path, 'BirdCage.xml'); +end + +%% +freq = linspace(excite.f_0-excite.f_c,excite.f_0+excite.f_c,201); +port = calcPort(port, Sim_Path, freq); + +close all +s11 = port{1}.uf.ref./port{1}.uf.inc; +s22 = port{2}.uf.ref./port{2}.uf.inc; + +% the s-parameter may be larger than 1 (0dB) since all ports are excited +% and do not have a perfect port isolation +plot(freq*1e-6,20*log10(abs(s11)),'Linewidth',2) +hold on +grid on +plot(freq*1e-6,20*log10(abs(s22)),'r--','Linewidth',2) +legend('s11','s22'); + +%% read SAR values on a xy-plane (range) +[SAR SAR_mesh] = ReadHDF5Dump([Sim_Path '/SAR.h5'],'Range',{[],[],0},'CloseAlpha',1); +SAR = SAR.FD.values{1}; + +% SAR plot +figure() +[R A] = ndgrid(SAR_mesh.lines{1},SAR_mesh.lines{2}); +X = R.*cos(A);Y = R.*sin(A); +colormap('hot'); +h = pcolor(X,Y,(squeeze(SAR))); +% h = pcolor(X,Y,log10(squeeze(SAR))); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('y -->'); +title('local SAR'); +axis equal tight + +%% plot B1+/- on an xy-plane +[H_field H_mesh] = ReadHDF5Dump([Sim_Path '/Hf.h5'],'Range',{[0 0.1],[],0},'CloseAlpha',1); +% create a 2D grid to plot on +[R A] = ndgrid(H_mesh.lines{1},H_mesh.lines{2}); +X = R.*cos(A); +Y = R.*sin(A); + +% calc Bx,By (from Br and Ba), B1p, B1m +Bx = MUE0*(H_field.FD.values{1}(:,:,:,1).*cos(A) - H_field.FD.values{1}(:,:,:,2).*sin(A)); +By = MUE0*(H_field.FD.values{1}(:,:,:,1).*sin(A) + H_field.FD.values{1}(:,:,:,2).*cos(A)); +B1p = 0.5*(Bx+1j*By); +B1m = 0.5*(Bx-1j*By); + +Dump2VTK([Sim_Path '/B1p_xy.vtk'], abs(B1p), H_mesh, 'B-Field'); +Dump2VTK([Sim_Path '/B1m_xy.vtk'], abs(B1m), H_mesh, 'B-Field'); + +maxB1 = max([abs(B1p(:)); abs(B1m(:))]); + +% B1+ plot +figure() +subplot(1,2,1); +h = pcolor(X,Y,abs(B1p)); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('y -->'); +title('B_1^+ field (dB)'); +caxis([0 maxB1]); +axis equal tight + +% B1- plot +subplot(1,2,2); +h = pcolor(X,Y,abs(B1m)); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('y -->'); +title('B_1^- field (dB)'); +caxis([0 maxB1]); +axis equal tight + +%% +ConvertHDF5_VTK([Sim_Path '/Hf.h5'],[Sim_Path '/Hf_xy'],'Range',{[],[],0},'CloseAlpha',1) +ConvertHDF5_VTK([Sim_Path '/SAR.h5'],[Sim_Path '/SAR_xy'],'Range',{[],[],0},'CloseAlpha',1) diff --git a/openEMS/matlab/Tutorials/MRI_Loop_Coil.m b/openEMS/matlab/Tutorials/MRI_Loop_Coil.m new file mode 100644 index 0000000..088f8f3 --- /dev/null +++ b/openEMS/matlab/Tutorials/MRI_Loop_Coil.m @@ -0,0 +1,334 @@ +% +% Tutorials / 7T MRI Loop Coil +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_MRI_Loop_Coil +% +% Tested with +% - openEMS v0.0.33 +% - Matlab 7.12.0 (R2011a) +% +% (C) 2013-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; %get some physical constants like c0 and MUE0 +unit = 1e-3; % all length in mm + +% Loop-Coil parameter +loop.length = 80; % length of the loop (in z-direction) +loop.width = 60; % width of the loop (in y-direction) +loop.strip_width = 5; % metal strip width +loop.strip_N_cells = 3; % number of cells over the strip length +loop.air_gap = loop.strip_width/3; % air gap width for lumped capacitors +loop.pos_x = -130; % position of loop +loop.C_gap = 5.4e-12; % lumped cap value +loop.port_R = 10; % feeding port resistance + +%% define the human body model (virtual family) +% set file name for human body model to create with "Convert_VF_DiscMaterial" +% the file name should contain a full path +body_model_file = [pwd '/Ella_centered_298MHz.h5']; + +% convert only part of the model (head/shoulder section) +body_model_range = {[],[],[-0.85 -0.4]}; + +% paths to virtual family voxel models (VFVM), adept to your install! +VF_raw_filesuffix = '/tmp/Ella_26y_V2_1mm'; +VF_mat_db_file = '/tmp/DB_h5_20120711_SEMCADv14.8.h5'; + +% delete(body_model_file); % uncomment to delete old model if something changed + +% convert model (if it does not exist) +Convert_VF_DiscMaterial(VF_raw_filesuffix, VF_mat_db_file, body_model_file, ... + 'Frequency', 298e6, 'Center', 1, ... + 'Range', body_model_range); + +% rotate model to face the nose in x-dir, and translate +body_model_transform = {'Rotate_X',pi,'Rotate_Z',pi/2, ... + 'Translate',[0,5,-720]}; + +% the head should + part of shoulder should fit this box +body_box.start = [-120 -150 -200]; +body_box.stop = [+100 +150 +130]; + +% box with high res mesh +mesh_box.start = [-120 -80 -120]; +mesh_box.stop = [+100 +80 +120]; +mesh_box.resolution = 2; + +%% some mesh parameter +Air_Box = 150; % size of the surrounding air box (150mm) + +%% setup FDTD parameter & excitation function +% init FDTD structure +FDTD = InitFDTD( 'EndCriteria', 1e-4, 'CellConstantMaterial', 0); + +% define gaussian pulse excitation signal +f0 = 298e6; % center frequency +fc = 300e6; % 20 dB corner frequency +FDTD = SetGaussExcite( FDTD, f0, fc ); + +% setup boundary conditions +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +CSX = InitCSX(); + +%% create loop +% setup all properties needed +CSX = AddMetal( CSX, 'loop' ); +CSX = AddLumpedElement( CSX, 'caps_y', 1, 'C', loop.C_gap); +CSX = AddLumpedElement( CSX, 'caps_z', 2, 'C', loop.C_gap); + +% horizontal (y-direction) strips +start = [loop.pos_x -loop.width/2 -loop.length/2]; +stop = [loop.pos_x -loop.air_gap/2 -loop.length/2+loop.strip_width]; +CSX = AddBox(CSX,'loop',10,start,stop); + +start = [loop.pos_x -loop.width/2 loop.length/2 ]; +stop = [loop.pos_x -loop.air_gap/2 loop.length/2-loop.strip_width]; +CSX = AddBox(CSX,'loop',10,start,stop); + +start = [loop.pos_x loop.width/2 -loop.length/2]; +stop = [loop.pos_x loop.air_gap/2 -loop.length/2+loop.strip_width]; +CSX = AddBox(CSX,'loop',10,start,stop); + +start = [loop.pos_x loop.width/2 loop.length/2 ]; +stop = [loop.pos_x loop.air_gap/2 loop.length/2-loop.strip_width]; +CSX = AddBox(CSX,'loop',10,start,stop); + +% vertical (z-direction) strips +start = [loop.pos_x -loop.width/2 -loop.length/2+loop.strip_width]; +stop = [loop.pos_x -loop.width/2+loop.strip_width -loop.air_gap/2]; +CSX = AddBox(CSX,'loop',10,start,stop); + +start = [loop.pos_x -loop.width/2 loop.length/2-loop.strip_width]; +stop = [loop.pos_x -loop.width/2+loop.strip_width loop.air_gap/2]; +CSX = AddBox(CSX,'loop',10,start,stop); + +start = [loop.pos_x loop.width/2 -loop.length/2+loop.strip_width]; +stop = [loop.pos_x loop.width/2-loop.strip_width -loop.air_gap/2]; +CSX = AddBox(CSX,'loop',10,start,stop); + +start = [loop.pos_x loop.width/2 loop.length/2-loop.strip_width ]; +stop = [loop.pos_x loop.width/2-loop.strip_width loop.air_gap/2]; +CSX = AddBox(CSX,'loop',10,start,stop); + +% add the lumped capacities +start = [loop.pos_x -loop.width/2+loop.strip_width/2-loop.air_gap/2 -loop.air_gap/2]; +stop = [loop.pos_x -loop.width/2+loop.strip_width/2+loop.air_gap/2 +loop.air_gap/2]; +CSX = AddBox(CSX,'caps_z',10,start,stop); + +start = [loop.pos_x loop.width/2-loop.strip_width/2-loop.air_gap/2 -loop.air_gap/2]; +stop = [loop.pos_x loop.width/2-loop.strip_width/2+loop.air_gap/2 +loop.air_gap/2]; +CSX = AddBox(CSX,'caps_z',10,start,stop); + +start = [loop.pos_x -loop.air_gap/2 loop.length/2-loop.strip_width/2-loop.air_gap/2]; +stop = [loop.pos_x +loop.air_gap/2 loop.length/2-loop.strip_width/2+loop.air_gap/2]; +CSX = AddBox(CSX,'caps_y',10,start,stop); + +% add a lumped port as excitation +start = [loop.pos_x -loop.air_gap/2 -loop.length/2+loop.strip_width/2-loop.air_gap/2]; +stop = [loop.pos_x +loop.air_gap/2 -loop.length/2+loop.strip_width/2+loop.air_gap/2]; +[CSX port] = AddLumpedPort(CSX, 100, 1, loop.port_R, start, stop, [0 1 0], true); + +%% define human body model +CSX = AddDiscMaterial(CSX, 'body_model', 'File', body_model_file, 'Scale', 1/unit, 'Transform', body_model_transform); +CSX = AddBox(CSX, 'body_model', 0, body_box.start, body_box.stop); + +%% finalize mesh +% create loop mesh +mesh = DetectEdges(CSX); + +% add a dense homegeneous mesh inside the human body model +mesh.x = [mesh.x mesh_box.start(1) mesh_box.stop(1)]; +mesh.y = [mesh.y mesh_box.start(2) mesh_box.stop(2)]; +mesh.z = [mesh.z mesh_box.start(3) mesh_box.stop(3)]; + +% add lines in x-dir for the loop and a cell centered around 0 +mesh.x = [mesh.x loop.pos_x -mesh_box.resolution/2 mesh_box.resolution/2]; + +% smooth the mesh for the loop & body +mesh = SmoothMesh(mesh, mesh_box.resolution); + +% add air spacer +mesh.x = [-Air_Box+mesh.x(1) mesh.x mesh.x(end)+Air_Box]; +mesh.y = [-Air_Box+mesh.y(1) mesh.y mesh.y(end)+Air_Box]; +mesh.z = [-Air_Box+mesh.z(1) mesh.z mesh.z(end)+Air_Box]; + +mesh = SmoothMesh(mesh, c0 / (f0+fc) / unit / 10, 1.5, 'algorithm', 1); + +%% Add Dump boxes (2D boxes) for H and SAR on xy- and xz-plane +CSX = AddDump(CSX,'Hf_xy','DumpType',11,'FileType',1,'Frequency',f0); +CSX = AddBox(CSX,'Hf_xy',0, body_box.start.*[1 1 0], body_box.stop.*[1 1 0]); +CSX = AddDump(CSX,'SAR_xy','DumpType',20,'DumpMode',2,'FileType',1,'Frequency',f0); +CSX = AddBox(CSX,'SAR_xy',0, body_box.start.*[1 1 0], body_box.stop.*[1 1 0]); + +CSX = AddDump(CSX,'Hf_xz','DumpType',11,'FileType',1,'Frequency',f0); +CSX = AddBox(CSX,'Hf_xz',0, body_box.start.*[1 0 1], body_box.stop.*[1 0 1]); +CSX = AddDump(CSX,'SAR_xz','DumpType',20,'DumpMode',2,'FileType',1,'Frequency',f0); +CSX = AddBox(CSX,'SAR_xz',0, body_box.start.*[1 0 1], body_box.stop.*[1 0 1]); + +%% add 10 lines in all direction to make space for PML or MUR absorbing +%% boundary conditions +mesh = AddPML(mesh, 10); + +%% finaly define the FDTD mesh grid +disp(['number of cells: ' num2str(1e-6*numel(mesh.x)*numel(mesh.y)*numel(mesh.z)) ' Mcells']) +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% prepare simulation folder +Sim_Path = ['tmp_' mfilename]; +Sim_CSX = [mfilename '.xml']; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +%% show the structure and export as vtk data automatically +CSXGeomPlot( [Sim_Path '/' Sim_CSX] , ['--export-polydata-vtk=' Sim_Path ' --RenderDiscMaterial -v']); + +%% run openEMS +RunOpenEMS( Sim_Path, Sim_CSX); + +%% postprocessing & do the plots +freq = linspace( f0-fc, f0+fc, 501 ); +port = calcPort(port, Sim_Path, freq); + +Zin = port.uf.tot ./ port.if.tot; +s11 = port.uf.ref ./ port.uf.inc; + +% get the feeding power for frequency f0 +P0_in = interp1(freq, port.P_acc, f0); + +%% +% plot reflection coefficient S11 +figure +h = plot( freq/1e6, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'reflection coefficient |S_{11}| (dB)' ); + +% plot feed point admittance +figure +h = plot( freq/1e6, real(1./Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(1./Zin), 'r--', 'Linewidth', 2 ); +title( 'feed port admittance' ); +xlabel( 'frequency f (MHz)' ); +ylabel( 'admittance Y_{in} (S)' ); +legend( 'real', 'imag' ); + +%% read SAR values on a xy-plane (range) +[SAR SAR_mesh] = ReadHDF5Dump([Sim_Path '/SAR_xy.h5']); +SAR = SAR.FD.values{1}/P0_in; + +% SAR plot +figure() +subplot(1,2,1); +[X Y] = ndgrid(SAR_mesh.lines{1},SAR_mesh.lines{2}); +colormap('hot'); +h = pcolor(X,Y,(squeeze(SAR))); +% h = pcolor(X,Y,log10(squeeze(SAR))); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('y -->'); +title('local SAR'); +axis equal tight + +%% read SAR values on a xz-plane (range) +[SAR SAR_mesh] = ReadHDF5Dump([Sim_Path '/SAR_xz.h5']); +SAR = SAR.FD.values{1}/P0_in; + +% SAR plot +subplot(1,2,2); +[X Z] = ndgrid(SAR_mesh.lines{1},SAR_mesh.lines{3}); +colormap('hot'); +h = pcolor(X,Z,(squeeze(SAR))); +% h = pcolor(X,Y,log10(squeeze(SAR))); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('z -->'); +title('local SAR'); +axis equal tight + +%% plot B1+/- on an xy-plane +[H_field H_mesh] = ReadHDF5Dump([Sim_Path '/Hf_xy.h5']); +% calc Bx,By, B1p, B1m normalize to the input-power +Bx = MUE0*H_field.FD.values{1}(:,:,:,1)/sqrt(P0_in); +By = MUE0*H_field.FD.values{1}(:,:,:,2)/sqrt(P0_in); +B1p = 0.5*(Bx+1j*By); +B1m = 0.5*(Bx-1j*By); +% create a 2D grid to plot on +[X Y] = ndgrid(H_mesh.lines{1},H_mesh.lines{2}); + +Dump2VTK([Sim_Path '/B1p_xy.vtk'], abs(B1p), H_mesh, 'B-Field'); +Dump2VTK([Sim_Path '/B1m_xy.vtk'], abs(B1m), H_mesh, 'B-Field'); + +% B1+ plot +figure() +subplot(1,2,1); +h = pcolor(X,Y,log10(abs(B1p))); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('y -->'); +title('B_1^+ field (dB)'); +axis equal tight + +% B1- plot +subplot(1,2,2); +h = pcolor(X,Y,log10(abs(B1m))); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('y -->'); +title('B_1^- field (dB)'); +axis equal tight + +%% plot B1+/- on an xz-plane +[H_field H_mesh] = ReadHDF5Dump([Sim_Path '/Hf_xz.h5']); +% calc Bx,By, B1p, B1m normalize to the input-power +Bx = MUE0*H_field.FD.values{1}(:,:,:,1)/sqrt(P0_in); +By = MUE0*H_field.FD.values{1}(:,:,:,2)/sqrt(P0_in); +B1p = 0.5*(Bx+1j*By); +B1m = 0.5*(Bx-1j*By); +% create a 2D grid to plot on +[X Z] = ndgrid(H_mesh.lines{1},H_mesh.lines{3}); + +Dump2VTK([Sim_Path '/B1p_xz.vtk'], abs(B1p), H_mesh, 'B-Field'); +Dump2VTK([Sim_Path '/B1m_xz.vtk'], abs(B1m), H_mesh, 'B-Field'); + +% B1+ plot +figure() +subplot(1,2,1); +h = pcolor(X,Z,log10(squeeze(abs(B1p)))); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('z -->'); +title('B_1^+ field (dB)'); +axis equal tight + +% B1- plot +subplot(1,2,2); +h = pcolor(X,Z,log10(squeeze(abs(B1m)))); +set(h,'EdgeColor','none'); +xlabel('x -->'); +ylabel('z -->'); +title('B_1^- field (dB)'); +axis equal tight + +%% dump to vtk to view in Paraview +ConvertHDF5_VTK([Sim_Path '/SAR_xy.h5'],[Sim_Path '/SAR_xy'], 'weight', 1/P0_in, 'FieldName', 'SAR'); +ConvertHDF5_VTK([Sim_Path '/SAR_xz.h5'],[Sim_Path '/SAR_xz'], 'weight', 1/P0_in, 'FieldName', 'SAR'); + +%% +ConvertHDF5_VTK([Sim_Path '/Hf_xy.h5'],[Sim_Path '/B1_xy'], 'weight', MUE0/sqrt(P0_in), 'FieldName', 'B1-field'); +ConvertHDF5_VTK([Sim_Path '/Hf_xz.h5'],[Sim_Path '/B1_xz'], 'weight', MUE0/sqrt(P0_in), 'FieldName', 'B1-field'); diff --git a/openEMS/matlab/Tutorials/MSL_NotchFilter.m b/openEMS/matlab/Tutorials/MSL_NotchFilter.m new file mode 100644 index 0000000..612456c --- /dev/null +++ b/openEMS/matlab/Tutorials/MSL_NotchFilter.m @@ -0,0 +1,92 @@ +% +% Tutorials / MSL_NotchFilter +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Microstrip_Notch_Filter +% +% Tested with +% - Matlab 2011a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2011-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um +MSL_length = 50000; +MSL_width = 600; +substrate_thickness = 254; +substrate_epr = 3.66; +stub_length = 12e3; +f_max = 7e9; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(); +FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); +BC = {'PML_8' 'PML_8' 'MUR' 'MUR' 'PEC' 'MUR'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +resolution = c0/(f_max*sqrt(substrate_epr))/unit /50; % resolution of lambda/50 +mesh.x = SmoothMeshLines( [0 MSL_width/2+[2*resolution/3 -resolution/3]/4], resolution/4, 1.5 ,0 ); +mesh.x = SmoothMeshLines( [-MSL_length -mesh.x mesh.x MSL_length], resolution, 1.5 ,0 ); +mesh.y = SmoothMeshLines( [0 MSL_width/2+[-resolution/3 +resolution/3*2]/4], resolution/4 , 1.5 ,0); +mesh.y = SmoothMeshLines( [-15*MSL_width -mesh.y mesh.y stub_length+[-resolution/3 +resolution/3*2]/4 15*MSL_width+stub_length], resolution, 1.3 ,0); +mesh.z = SmoothMeshLines( [linspace(0,substrate_thickness,5) 10*substrate_thickness], resolution ); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% substrate +CSX = AddMaterial( CSX, 'RO4350B' ); +CSX = SetMaterialProperty( CSX, 'RO4350B', 'Epsilon', substrate_epr ); +start = [mesh.x(1), mesh.y(1), 0]; +stop = [mesh.x(end), mesh.y(end), substrate_thickness]; +CSX = AddBox( CSX, 'RO4350B', 0, start, stop ); + +%% MSL port +CSX = AddMetal( CSX, 'PEC' ); +portstart = [ mesh.x(1), -MSL_width/2, substrate_thickness]; +portstop = [ 0, MSL_width/2, 0]; +[CSX,port{1}] = AddMSLPort( CSX, 999, 1, 'PEC', portstart, portstop, 0, [0 0 -1], 'ExcitePort', true, 'FeedShift', 10*resolution, 'MeasPlaneShift', MSL_length/3); + +portstart = [mesh.x(end), -MSL_width/2, substrate_thickness]; +portstop = [0 , MSL_width/2, 0]; +[CSX,port{2}] = AddMSLPort( CSX, 999, 2, 'PEC', portstart, portstop, 0, [0 0 -1], 'MeasPlaneShift', MSL_length/3 ); + +%% Filter-stub +start = [-MSL_width/2, MSL_width/2, substrate_thickness]; +stop = [ MSL_width/2, MSL_width/2+stub_length, substrate_thickness]; +CSX = AddBox( CSX, 'PEC', 999, start, stop ); + +%% write/show/run the openEMS compatible xml-file +Sim_Path = 'tmp'; +Sim_CSX = 'msl.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +RunOpenEMS( Sim_Path, Sim_CSX ); + +%% post-processing +close all +f = linspace( 1e6, f_max, 1601 ); +port = calcPort( port, Sim_Path, f, 'RefImpedance', 50); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; + +plot(f/1e9,20*log10(abs(s11)),'k-','LineWidth',2); +hold on; +grid on; +plot(f/1e9,20*log10(abs(s21)),'r--','LineWidth',2); +legend('S_{11}','S_{21}'); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (GHz) \rightarrow','FontSize',12); +ylim([-40 2]); + diff --git a/openEMS/matlab/Tutorials/Parallel_Plate_Waveguide.m b/openEMS/matlab/Tutorials/Parallel_Plate_Waveguide.m new file mode 100644 index 0000000..cbf76c0 --- /dev/null +++ b/openEMS/matlab/Tutorials/Parallel_Plate_Waveguide.m @@ -0,0 +1,51 @@ +% +% Tutorials / Parallel_Plate_Waveguide +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Parallel_Plate_Waveguide +% +% Tested with +% - Matlab 2011a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2011,2012 Sebastian Held <sebastian.held@gmx.de> +% (C) 2011-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +% init and define FDTD parameter +FDTD = InitFDTD(100,0,'OverSampling',50); +FDTD = SetSinusExcite(FDTD,10e6); +BC = {'PMC' 'PMC' 'PEC' 'PEC' 'MUR' 'MUR'}; +FDTD = SetBoundaryCond(FDTD,BC); + +% init and define FDTD mesh +CSX = InitCSX(); +mesh.x = -10:10; +mesh.y = -10:10; +mesh.z = -10:30; +CSX = DefineRectGrid(CSX, 1, mesh); + +% define the excitation +CSX = AddExcitation(CSX,'excitation',0,[0 1 0]); +CSX = AddBox(CSX,'excitation',0,[-10 -10 0],[10 10 0]); + +% define a time domain e-field dump box +CSX = AddDump(CSX,'Et','DumpMode',0); +CSX = AddBox(CSX,'Et',0,[-10 0 -10],[10 0 30]); + +% remove old simulation results (if exist) +rmdir('tmp','s');mkdir('tmp'); + +% write openEMS xml data file +WriteOpenEMS('tmp/tmp.xml',FDTD,CSX); + +% view defined structure +CSXGeomPlot( 'tmp/tmp.xml' ); + +% run openEMS simulation +RunOpenEMS('tmp','tmp.xml',''); + +disp('use Paraview to visualize the FDTD result...'); diff --git a/openEMS/matlab/Tutorials/Patch_Antenna_Array.m b/openEMS/matlab/Tutorials/Patch_Antenna_Array.m new file mode 100644 index 0000000..ed12c67 --- /dev/null +++ b/openEMS/matlab/Tutorials/Patch_Antenna_Array.m @@ -0,0 +1,170 @@ +function [port nf2ff] = Patch_Antenna_Array(Sim_Path, postproc_only, show_structure, xpos, caps, resist, active ) +% [port nf2ff] = Patch_Antenna_Array(Sim_Path, postproc_only, show_structure, xpos, caps, resist, active ) +% +% Script to setup the patch array as described in [1]. +% Run main script in Patch_Antenna_Phased_Array.m instead! +% +% Sim_Path: Simulation path +% postproc_only: set to post process only 0/1 +% show_structure: show the strucuture in AppCSXCAD 0/1 +% xpos: the x-position for each antenna is defined +% caps: the port capacity (will override active port) +% resist: port resitance +% active: switch port active +% +% References: +% [1] Y. Yusuf and X. Gong, “A low-cost patch antenna phased array with +% analog beam steering using mutual coupling and reactive loading,” IEEE +% Antennas Wireless Propag. Lett., vol. 7, pp. 81–84, 2008. +% +% Tested with +% - Matlab 2011a +% - openEMS v0.0.31 +% +% (C) 2013 Thorsten Liebig <thorsten.liebig@gmx.de> + +% example +% xpos = [-41 0 41]; +% caps = [0.2e-12 0 0.2e-12]; +% active = [0 1 0]; +% resist = [50 50 50]; + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% patch geometry setup +patch.W = 35; % width +patch.L = 28.3; % length +patch.Ws = 3.8; % width of feeding stub +patch.Gs = 1; % width of feeding gab +patch.l = 6; % length of feeding stub +patch.y0 = 10; % depth of feeding stub into into patch + +% patch resonance frequency +f0 = 3e9; + +%substrate setup +substrate.name = 'Ro3003'; +substrate.epsR = 3; +substrate.kappa = 0.0013 * 2*pi*f0 * EPS0*substrate.epsR; +substrate.thickness = 1.524; +substrate.cells = 4; + +substrate.width = patch.W + max(xpos) - min(xpos) + 4*patch.l; +substrate.length = 3*patch.l + patch.L; + +% size of the simulation box +AirSpacer = [50 50 30]; + +edge_res = [-1/3 2/3]*1; + +%% setup FDTD parameter & excitation function +fc = 2e9; % 20 dB corner frequency +FDTD = InitFDTD( 'EndCriteria', 1e-4 ); +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = [1 1 1 1 1 1]*3; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +CSX = InitCSX(); + +mesh.x = []; +mesh.y = []; +mesh.z = []; + +%% create patch +CSX = AddMetal( CSX, 'patch' ); % create a perfect electric conductor (PEC) + +for port_nr=1:numel(xpos) + start = [xpos(port_nr)-patch.W/2 patch.l substrate.thickness]; + stop = [xpos(port_nr)-patch.Ws/2-patch.Gs patch.l+patch.L substrate.thickness]; + CSX = AddBox(CSX,'patch',10, start, stop); + mesh.x = [mesh.x xpos(port_nr)-patch.W/2-edge_res]; + + start = [xpos(port_nr)+patch.W/2 patch.l substrate.thickness]; + stop = [xpos(port_nr)+patch.Ws/2+patch.Gs patch.l+patch.L substrate.thickness]; + CSX = AddBox(CSX,'patch',10, start, stop); + mesh.x = [mesh.x xpos(port_nr)+patch.W/2+edge_res]; + + mesh.y = [mesh.y patch.l-edge_res patch.l+patch.L+edge_res]; + + start = [xpos(port_nr)-patch.Ws/2-patch.Gs patch.l+patch.y0 substrate.thickness]; + stop = [xpos(port_nr)+patch.Ws/2+patch.Gs patch.l+patch.L substrate.thickness]; + CSX = AddBox(CSX,'patch',10, start, stop); + + % feed line + start = [xpos(port_nr)-patch.Ws/2 patch.l+patch.y0 substrate.thickness]; + stop = [xpos(port_nr)+patch.Ws/2 0 substrate.thickness]; + CSX = AddBox(CSX,'patch',10, start, stop); + + mesh.x = [mesh.x xpos(port_nr)+linspace(-patch.Ws/2-patch.Gs,-patch.Ws/2,3) xpos(port_nr)+linspace(patch.Ws/2,patch.Ws/2+patch.Gs,3)]; + + start = [xpos(port_nr)-patch.Ws/2 0 0]; + stop = [xpos(port_nr)+patch.Ws/2 0 substrate.thickness]; + if (caps(port_nr)>0) + CSX = AddLumpedElement(CSX, ['C_' num2str(port_nr)], 2, 'C', caps(port_nr)); + CSX = AddBox(CSX,['C_' num2str(port_nr)],10, start, stop); + + [CSX port{port_nr}] = AddLumpedPort(CSX, 5 ,port_nr ,inf, start, stop, [0 0 1], 0); + else + % feed port + [CSX port{port_nr}] = AddLumpedPort(CSX, 5 ,port_nr, resist(port_nr), start, stop, [0 0 1], active(port_nr)); + end +end + +%% create substrate +CSX = AddMaterial( CSX, substrate.name ); +CSX = SetMaterialProperty( CSX, substrate.name, 'Epsilon', substrate.epsR, 'Kappa', substrate.kappa ); +start = [-substrate.width/2 0 0]; +stop = [ substrate.width/2 substrate.length substrate.thickness]; +CSX = AddBox( CSX, substrate.name, 0, start, stop ); + +mesh.x = [mesh.x start(1) stop(1)]; +mesh.y = [mesh.y start(2) stop(2)]; + +% add extra cells to discretize the substrate thickness +mesh.z = [linspace(0,substrate.thickness,substrate.cells+1) mesh.z]; + +%% create ground (same size as substrate) +CSX = AddMetal( CSX, 'gnd' ); % create a perfect electric conductor (PEC) +start(3)=0; +stop(3) =0; +CSX = AddBox(CSX,'gnd',10,start,stop); + +%% finalize the mesh +% generate a smooth mesh with max. cell size: lambda_min / 20 +mesh = SmoothMesh(mesh, 2, 1.3); +mesh.x = [mesh.x min(mesh.x)-AirSpacer(1) max(mesh.x)+AirSpacer(1)]; +mesh.y = [mesh.y min(mesh.y)-AirSpacer(2) max(mesh.y)+AirSpacer(2)]; +mesh.z = [mesh.z min(mesh.z)-AirSpacer(3) max(mesh.z)+2*AirSpacer(3)]; + +mesh = SmoothMesh(mesh, c0 / (f0+fc) / unit / 20, 1.3); + +%% add a nf2ff calc box; size is 3 cells away from MUR boundary condition +start = [mesh.x(4) mesh.y(4) mesh.z(4)]; +stop = [mesh.x(end-3) mesh.y(end-3) mesh.z(end-3)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop); + +mesh = AddPML(mesh,(BC==3)*8); +CSX = DefineRectGrid(CSX, unit, mesh); + +%% prepare simulation folder +Sim_CSX = 'patch_array.xml'; + +if (postproc_only==0) + [status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory + [status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + + %% write openEMS compatible xml-file + WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + + %% show the structure + if (show_structure>0) + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + end + + %% run openEMS + RunOpenEMS( Sim_Path, Sim_CSX); +end + diff --git a/openEMS/matlab/Tutorials/Patch_Antenna_Phased_Array.m b/openEMS/matlab/Tutorials/Patch_Antenna_Phased_Array.m new file mode 100644 index 0000000..873f345 --- /dev/null +++ b/openEMS/matlab/Tutorials/Patch_Antenna_Phased_Array.m @@ -0,0 +1,166 @@ +% +% Tutorials / Patch Antenna Phased Array +% +% Describtion at: +% +% Tested with +% - Matlab 2011a +% - Octave 4.0 +% - openEMS v0.0.33 +% +% References: +% [1] Y. Yusuf and X. Gong, “A low-cost patch antenna phased array with +% analog beam steering using mutual coupling and reactive loading,” IEEE +% Antennas Wireless Propag. Lett., vol. 7, pp. 81–84, 2008. +% [2] S. Otto, S. Held, A. Rennings, and K. Solbach, +% "Array and multiport antenna farfield simulation using +% EMPIRE, MATLAB and ADS," 39th European Microwave Conf. (EuMC 2009), +% Sept. 29 – Oct. 1, Rome, Italy, pp. 1547-1550, 2009. +% [3] K. Karlsson, J. Carlsson, I. Belov, G. Nilsson, and P.-S. Kildal, +% “Optimization of antenna diversity gain by combining full-wave and +% circuit simulations,” in Proc. Second European Conference on Antennas +% and Propagation EuCAP 2007, 11–16 Nov. 2007, pp. 1–5. +% +% (C) 2013-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + + +close all +clear +clc + +% we need the "Cuircuit Toolbox" +addpath('C:\CTB'); +% get the latest version from: +% using git: https://github.com/thliebig/CTB +% or zip: https://github.com/thliebig/CTB/archive/master.zip + +% set this to 0 to NOT run a reference simulation with the given C2 and C3 +% for comparison +do_reference_simulation = 1; + +% set to 1 if you want to run AppCSXCAD to see the simulated structure +show_structure = 1; + +% set this to 1, to force openEMS to run again even if the data already exist +force_rerun = 0; + +% frequency range of interest +f = linspace( 1e9, 5e9, 1601 ); + +% resonant frequency for far-field calculations +f0 = 3e9; + +% capacities for port 2 and 3 to shift the far-field pattern +C2 = 0.2e-12; +C3 = 0.2e-12; + +Sim_Path_Root = ['tmp_' mfilename]; + +%% calculate the full S-parameter set for all 3 patch antennas running 3 +% individual openEMS simulations in which one antenna is active and the +% other two a passive (50 Ohm load) respectively +xpos = [0 -41 41]; % x-center position of the 3 antennas +caps = [0 0 0]; +resist = [50 50 50]; + +spara = []; +color_code = {'k-','r--','m-.'}; + +for n=1:3 + active = [0 0 0]; + active(n) = 1; % activate antenna n + Sim_Path = [Sim_Path_Root '_' num2str(n)]; % create an individual path + [port{n} nf2ff{n}] = Patch_Antenna_Array(Sim_Path, ((exist(Sim_Path,'dir')>0) && (force_rerun==0)), show_structure, xpos, caps, resist, active); + port{n} = calcPort( port{n}, Sim_Path, f, 'RefImpedance', 50); + nf2ff{n} = CalcNF2FF(nf2ff{n}, Sim_Path, f0, [-180:2:180]*pi/180, 0); + + figure + hold on + grid on + for p=1:3 + I(p,n) = interp1(f, port{n}{p}.if.tot,f0); + P_in(p) = 0.5*interp1(f, port{n}{n}.uf.inc,f0)*conj(interp1(f, port{n}{n}.if.inc,f0)); + spara(p,n,:) = port{n}{p}.uf.ref./ port{n}{n}.uf.inc; + plot(f, squeeze(20*log10(abs(spara(p,n,:)))),color_code{p},'Linewidth',2); + end +end + +%% export sparameter to touchstone file +write_touchstone('s',f,spara,[Sim_Path_Root '.s3p']); + +% instructions for Qucs: +% load the written touchstone file +% attach C2 and C3 to port 2 and 3 +% attach a signal port to port 1 +% probe the currents going into port 1 to 3 + +% example currents for ports 1 to 3 for C2 = 0.2pF and C3=0.2pF +I_qucs(1,1) = 0.00398-0.000465j; +I_qucs(2,1) = 2.92e-5-0.000914j; +I_qucs(3,1) = 2.92e-5-0.000914j; + +disp(['I2/I1: Qucs: ' num2str(I_qucs(2)/I_qucs(1)) ' (defined manually)']) +disp(['I3/I1: Qucs: ' num2str(I_qucs(3)/I_qucs(1)) ' (defined manually)']) + +%% Calculate the currents of port 1 to 3 using Matlab [1] +z = s2z(spara); + +Z2 = 1/(1j*2*pi*f0*C2); +Z3 = 1/(1j*2*pi*f0*C3); + +z23(1,1) = interp1(f,squeeze(z(2,2,:)),f0) + Z2; +z23(1,2) = interp1(f,squeeze(z(2,3,:)),f0); +z23(2,1) = interp1(f,squeeze(z(3,2,:)),f0); +z23(2,2) = interp1(f,squeeze(z(3,3,:)),f0) + Z3; + +%set input/feeding current of port 1 to 1mA +I_out(1,1) = 1e-3; +% calc current for port 2 and 3 +I_out([2 3],1) = z23\[-interp1(f,squeeze(z(2,1,:)),f0);-interp1(f,squeeze(z(3,1,:)),f0)]*I_out(1); + +disp(['I2/I1: Matlab: ' num2str(I_out(2)/I_out(1))]) +disp(['I3/I1: Matlab: ' num2str(I_out(3)/I_out(1))]) + + +%% do a referenc simulation for the given C2/C3 values +if (do_reference_simulation) + active = [1 0 0]; + caps = [0 C2 C3]; + resist = [50 inf inf]; + Sim_Path = [Sim_Path_Root '_C2=' num2str(C2*1e12) '_C3=' num2str(C3*1e12)]; + [port_ref nf2ff_ref] = Patch_Antenna_Array(Sim_Path, ((exist(Sim_Path,'dir')>0) && (force_rerun==0)), show_structure, xpos, caps, resist, active); + port_ref = calcPort( port_ref, Sim_Path, f, 'RefImpedance', 50); + nf2ff_ref = CalcNF2FF(nf2ff_ref, Sim_Path, f0, [-180:2:180]*pi/180, 0); + + % extract currents from referenc simulation + for p=1:3 + I_ref(p,1) = interp1(f, port_ref{p}.if.tot,f0); + end + + disp(['I2/I1: openEMS: ' num2str(I_ref(2)/I_ref(1))]) + disp(['I3/I1: openEMS: ' num2str(I_ref(3)/I_ref(1))]) +end + +%% calculate and apply weighting cooefficients [3] +% calculate +coeff = I\I_out; + +% apply +E_ff_phi = 0*nf2ff{1}.E_phi{1}; +E_ff_theta = 0*nf2ff{1}.E_phi{1}; +for n=1:3 + E_ff_phi = E_ff_phi + coeff(n)*nf2ff{n}.E_phi{1}; + E_ff_theta = E_ff_theta + coeff(n)*nf2ff{n}.E_theta{1}; +end + +%% plot far-field patterns +figure +polar([-180:2:180]'*pi/180,abs(E_ff_phi(:))/max(abs(E_ff_phi(:)))); +hold on +if (do_reference_simulation) + polar([-180:2:180]'*pi/180,abs(nf2ff_ref.E_norm{1}(:,1))/max(abs(nf2ff_ref.E_norm{1}(:,1))),'r--'); +end +title('normalized far-field pattern','Interpreter', 'none') +legend('calculated','reference') + + diff --git a/openEMS/matlab/Tutorials/RCS_Sphere.m b/openEMS/matlab/Tutorials/RCS_Sphere.m new file mode 100644 index 0000000..b8bb5bc --- /dev/null +++ b/openEMS/matlab/Tutorials/RCS_Sphere.m @@ -0,0 +1,138 @@ +% +% Tutorials / radar cross section of a metal sphere +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_RCS_Sphere +% +% Tested with +% - Matlab 2013a / Octave 3.8.1 +% - openEMS v0.0.32 +% +% (C) 2012-2014 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +sphere.rad = 200; + +inc_angle = 0 /180*pi; %incident angle (to x-axis) in rad + +% size of the simulation box +SimBox = 1000; +PW_Box = 750; + +%% setup FDTD parameter & excitation function +f_start = 50e6; % start frequency +f_stop = 1000e6; % stop frequency +f0 = 500e6; + +FDTD = InitFDTD( ); +FDTD = SetGaussExcite( FDTD, 0.5*(f_start+f_stop), 0.5*(f_stop-f_start) ); +BC = [1 1 1 1 1 1]*3; % set boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +max_res = c0 / f_stop / unit / 20; % cell size: lambda/20 +CSX = InitCSX(); + +%create mesh +smooth_mesh = SmoothMeshLines([0 SimBox/2], max_res); +mesh.x = unique([-smooth_mesh smooth_mesh]); +mesh.y = mesh.x; +mesh.z = mesh.x; + +%% create metal sphere +CSX = AddMetal( CSX, 'sphere' ); % create a perfect electric conductor (PEC) +CSX = AddSphere(CSX,'sphere',10,[0 0 0],sphere.rad); + +%% plane wave excitation +k_dir = [cos(inc_angle) sin(inc_angle) 0]; % plane wave direction +E_dir = [0 0 1]; % plane wave polarization --> E_z + +CSX = AddPlaneWaveExcite(CSX, 'plane_wave', k_dir, E_dir, f0); +start = [-PW_Box/2 -PW_Box/2 -PW_Box/2]; +stop = -start; +CSX = AddBox(CSX, 'plane_wave', 0, start, stop); + +%% dump boxes +CSX = AddDump(CSX, 'Et'); +start = [mesh.x(1) mesh.y(1) 0]; +stop = [mesh.x(end) mesh.y(end) 0]; +CSX = AddBox(CSX, 'Et', 0, start, stop); + +%%nf2ff calc +start = [mesh.x(1) mesh.y(1) mesh.z(1)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop); + +% add 8 lines in all direction as pml spacing +mesh = AddPML(mesh,8); + +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% prepare simulation folder +Sim_Path = 'Sphere_RCS'; +Sim_CSX = 'Sphere_RCS.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +%% show the structure +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + +%% run openEMS +RunOpenEMS( Sim_Path, Sim_CSX); + +%% +disp('Use Paraview to display the elctric fields dumped by openEMS'); + +%% +EF = ReadUI( 'et', Sim_Path, f0 ); % time domain/freq domain voltage +Pin = 0.5*norm(E_dir)^2/Z0 .* abs(EF.FD{1}.val).^2; + +%% +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f0, pi/2, [-180:2:180]*pi/180, 'Mode',1); +RCS = 4*pi./Pin(1).*nf2ff.P_rad{1}(:); +polar(nf2ff.phi,RCS); +xlabel('x -->'); +ylabel('y -->'); +hold on +grid on + +drawnow + +%% +freq = linspace(f_start,f_stop,100); +EF = ReadUI( 'et', Sim_Path, freq ); % time domain/freq domain voltage +Pin = 0.5*norm(E_dir)^2/Z0 .* abs(EF.FD{1}.val).^2; + +nf2ff = CalcNF2FF(nf2ff, Sim_Path, freq, pi/2, pi+inc_angle, 'Mode',1); +for fn=1:numel(freq) + back_scat(fn) = 4*pi./Pin(fn).*nf2ff.P_rad{fn}(1); +end + +%% +figure +plot(freq/1e6,back_scat,'Linewidth',2); +grid on; +xlabel('frequency (MHz) \rightarrow'); +ylabel('RCS (m^2) \rightarrow'); +title('radar cross section'); + +%% +figure +lambda = c0./freq; +semilogy(sphere.rad*unit./lambda,back_scat/(pi*sphere.rad*unit*sphere.rad*unit),'Linewidth',2); +ylim([10^-2 10^1]) +grid on; +xlabel('sphere radius / wavelength \rightarrow'); +ylabel('RCS / (\pi a^2) \rightarrow'); +title('normalized radar cross section'); diff --git a/openEMS/matlab/Tutorials/Rect_Waveguide.m b/openEMS/matlab/Tutorials/Rect_Waveguide.m new file mode 100644 index 0000000..3b1a79a --- /dev/null +++ b/openEMS/matlab/Tutorials/Rect_Waveguide.m @@ -0,0 +1,121 @@ +% +% Tutorials / Rect_Waveguide +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Rectangular_Waveguide +% +% Tested with +% - Octave 4.0.0 +% - openEMS v0.0.33 +% +% (C) 2010-2015 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; %drawing unit in um + +% waveguide dimensions +% WR42 +a = 10700; %waveguide width +b = 4300; %waveguide heigth +length = 50000; + +% frequency range of interest +f_start = 20e9; +f_0 = 24e9; +f_stop = 26e9; +lambda0 = c0/f_0/unit; + +%waveguide TE-mode definition +TE_mode = 'TE10'; + +%targeted mesh resolution +mesh_res = lambda0./[30 30 30]; + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD('NrTS',1e4, 'OverSampling', 5); +FDTD = SetGaussExcite(FDTD,0.5*(f_start+f_stop),0.5*(f_stop-f_start)); + +% boundary conditions +BC = [0 0 0 0 3 3]; %pml in pos. and neg. z-direction +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = SmoothMeshLines([0 a], mesh_res(1)); +mesh.y = SmoothMeshLines([0 b], mesh_res(2)); +mesh.z = SmoothMeshLines([0 length], mesh_res(3)); +CSX = DefineRectGrid(CSX, unit,mesh); + +%% apply the waveguide port %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start=[mesh.x(1) mesh.y(1) mesh.z(11)]; +stop =[mesh.x(end) mesh.y(end) mesh.z(15)]; +[CSX, port{1}] = AddRectWaveGuidePort( CSX, 0, 1, start, stop, 'z', a*unit, b*unit, TE_mode, 1); + +start=[mesh.x(1) mesh.y(1) mesh.z(end-13)]; +stop =[mesh.x(end) mesh.y(end) mesh.z(end-14)]; +[CSX, port{2}] = AddRectWaveGuidePort( CSX, 0, 2, start, stop, 'z', a*unit, b*unit, TE_mode); + +%% define dump box... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et','FileType',1,'SubSampling','2,2,2'); +start = [mesh.x(1) mesh.y(1) mesh.z(1)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end)]; +CSX = AddBox(CSX,'Et',0 , start,stop); + +%% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Sim_Path = 'tmp_mod'; +Sim_CSX = 'rect_wg.xml'; + +[status, message, messageid] = rmdir(Sim_Path,'s'); +[status, message, messageid] = mkdir(Sim_Path); + +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +RunOpenEMS(Sim_Path, Sim_CSX) + +%% postproc %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +freq = linspace(f_start,f_stop,201); +port = calcPort(port, Sim_Path, freq); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; +ZL = port{1}.uf.tot./port{1}.if.tot; +ZL_a = port{1}.ZL; % analytic waveguide impedance + +%% plot s-parameter %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +figure +plot(freq*1e-6,20*log10(abs(s11)),'k-','Linewidth',2); +xlim([freq(1) freq(end)]*1e-6); +grid on; +hold on; +plot(freq*1e-6,20*log10(abs(s21)),'r--','Linewidth',2); +l = legend('S_{11}','S_{21}','Location','Best'); +set(l,'FontSize',12); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (MHz) \rightarrow','FontSize',12); + +%% compare analytic and numerical wave-impedance %%%%%%%%%%%%%%%%%%%%%%%%%% +figure +plot(freq*1e-6,real(ZL),'Linewidth',2); +hold on; +grid on; +plot(freq*1e-6,imag(ZL),'r--','Linewidth',2); +plot(freq*1e-6,ZL_a,'g-.','Linewidth',2); +ylabel('ZL (\Omega)','FontSize',12); +xlabel('frequency (MHz) \rightarrow','FontSize',12); +xlim([freq(1) freq(end)]*1e-6); +l = legend('\Re(Z_L)','\Im(Z_L)','Z_L analytic','Location','Best'); +set(l,'FontSize',12); + +%% Plot the field dumps %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +figure +dump_file = [Sim_Path '/Et.h5']; +PlotArgs.slice = {a/2*unit b/2*unit 0}; +PlotArgs.pauseTime=0.01; +PlotArgs.component=0; +PlotArgs.Limit = 'auto'; +PlotHDF5FieldData(dump_file, PlotArgs) diff --git a/openEMS/matlab/Tutorials/Simple_Patch_Antenna.m b/openEMS/matlab/Tutorials/Simple_Patch_Antenna.m new file mode 100644 index 0000000..ee2a8f0 --- /dev/null +++ b/openEMS/matlab/Tutorials/Simple_Patch_Antenna.m @@ -0,0 +1,180 @@ +% +% Tutorials / simple patch antenna +% +% Describtion at: +% http://openems.de/index.php/Tutorial:_Simple_Patch_Antenna +% +% Tested with +% - Matlab 2013a / Octave 4.0 +% - openEMS v0.0.33 +% +% (C) 2010-2015 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% patch width in x-direction +patch.width = 32; % resonant length +% patch length in y-direction +patch.length = 40; + +%substrate setup +substrate.epsR = 3.38; +substrate.kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate.epsR; +substrate.width = 60; +substrate.length = 60; +substrate.thickness = 1.524; +substrate.cells = 4; + +%setup feeding +feed.pos = -6; %feeding position in x-direction +feed.R = 50; %feed resistance + +% size of the simulation box +SimBox = [200 200 150]; + +%% setup FDTD parameter & excitation function +f0 = 2e9; % center frequency +fc = 1e9; % 20 dB corner frequency +FDTD = InitFDTD( 'NrTs', 30000 ); +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +CSX = InitCSX(); + +%initialize the mesh with the "air-box" dimensions +mesh.x = [-SimBox(1)/2 SimBox(1)/2]; +mesh.y = [-SimBox(2)/2 SimBox(2)/2]; +mesh.z = [-SimBox(3)/3 SimBox(3)*2/3]; + +%% create patch +CSX = AddMetal( CSX, 'patch' ); % create a perfect electric conductor (PEC) +start = [-patch.width/2 -patch.length/2 substrate.thickness]; +stop = [ patch.width/2 patch.length/2 substrate.thickness]; +CSX = AddBox(CSX,'patch',10,start,stop); % add a box-primitive to the metal property 'patch' + +%% create substrate +CSX = AddMaterial( CSX, 'substrate' ); +CSX = SetMaterialProperty( CSX, 'substrate', 'Epsilon', substrate.epsR, 'Kappa', substrate.kappa ); +start = [-substrate.width/2 -substrate.length/2 0]; +stop = [ substrate.width/2 substrate.length/2 substrate.thickness]; +CSX = AddBox( CSX, 'substrate', 0, start, stop ); + +% add extra cells to discretize the substrate thickness +mesh.z = [linspace(0,substrate.thickness,substrate.cells+1) mesh.z]; + +%% create ground (same size as substrate) +CSX = AddMetal( CSX, 'gnd' ); % create a perfect electric conductor (PEC) +start(3)=0; +stop(3) =0; +CSX = AddBox(CSX,'gnd',10,start,stop); + +%% apply the excitation & resist as a current source +start = [feed.pos 0 0]; +stop = [feed.pos 0 substrate.thickness]; +[CSX port] = AddLumpedPort(CSX, 5 ,1 ,feed.R, start, stop, [0 0 1], true); + +%% finalize the mesh +% detect all edges except of the patch +mesh = DetectEdges(CSX, mesh,'ExcludeProperty','patch'); +% detect and set a special 2D metal edge mesh for the patch +mesh = DetectEdges(CSX, mesh,'SetProperty','patch','2D_Metal_Edge_Res', c0/(f0+fc)/unit/50); +% generate a smooth mesh with max. cell size: lambda_min / 20 +mesh = SmoothMesh(mesh, c0/(f0+fc)/unit/20); +CSX = DefineRectGrid(CSX, unit, mesh); + +%% add a nf2ff calc box; size is 3 cells away from MUR boundary condition +start = [mesh.x(4) mesh.y(4) mesh.z(4)]; +stop = [mesh.x(end-3) mesh.y(end-3) mesh.z(end-3)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop); + +%% prepare simulation folder +Sim_Path = 'tmp_Patch_Ant'; +Sim_CSX = 'patch_ant.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +%% show the structure +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + +%% run openEMS +RunOpenEMS( Sim_Path, Sim_CSX); + +%% postprocessing & do the plots +freq = linspace( max([1e9,f0-fc]), f0+fc, 501 ); +port = calcPort(port, Sim_Path, freq); + +Zin = port.uf.tot ./ port.if.tot; +s11 = port.uf.ref ./ port.uf.inc; + +% plot feed point impedance +figure +plot( freq/1e6, real(Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Zin), 'r--', 'Linewidth', 2 ); +title( 'feed point impedance' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'impedance Z_{in} / Ohm' ); +legend( 'real', 'imag' ); + +% plot reflection coefficient S11 +figure +plot( freq/1e6, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +drawnow + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%find resonance frequncy from s11 +f_res_ind = find(s11==min(s11)); +f_res = freq(f_res_ind); + +% calculate the far field at phi=0 degrees and at phi=90 degrees +disp( 'calculating far field at phi=[0 90] deg...' ); + +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, [-180:2:180]*pi/180, [0 90]*pi/180); + +% display power and directivity +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(nf2ff.Dmax) ' (' num2str(10*log10(nf2ff.Dmax)) ' dBi)'] ); +disp( ['efficiency: nu_rad = ' num2str(100*nf2ff.Prad./port.P_inc(f_res_ind)) ' %']); + +% normalized directivity as polar plot +figure +polarFF(nf2ff,'xaxis','theta','param',[1 2],'normalize',1) + +% log-scale directivity plot +figure +plotFFdB(nf2ff,'xaxis','theta','param',[1 2]) +% conventional plot approach +% plot( nf2ff.theta*180/pi, 20*log10(nf2ff.E_norm{1}/max(nf2ff.E_norm{1}(:)))+10*log10(nf2ff.Dmax)); + +drawnow + +%% +disp( 'calculating 3D far field pattern and dumping to vtk (use Paraview to visualize)...' ); +thetaRange = (0:2:180); +phiRange = (0:2:360) - 180; +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180,'Verbose',1,'Outfile','3D_Pattern.h5'); + +figure +plotFF3D(nf2ff,'logscale',-20); + + +E_far_normalized = nf2ff.E_norm{1} / max(nf2ff.E_norm{1}(:)) * nf2ff.Dmax; +DumpFF2VTK([Sim_Path '/3D_Pattern.vtk'],E_far_normalized,thetaRange,phiRange,'scale',1e-3); diff --git a/openEMS/matlab/Tutorials/readme b/openEMS/matlab/Tutorials/readme new file mode 100644 index 0000000..a80dfa6 --- /dev/null +++ b/openEMS/matlab/Tutorials/readme @@ -0,0 +1 @@ +* Find the tutorial describtions at http://openems.de/index.php/Tutorials diff --git a/openEMS/matlab/WriteHDF5.m b/openEMS/matlab/WriteHDF5.m new file mode 100644 index 0000000..df6a8d7 --- /dev/null +++ b/openEMS/matlab/WriteHDF5.m @@ -0,0 +1,73 @@ +function WriteHDF5(filename,hdf_fielddata,hdf_mesh) +% function WriteHDF5(filename,hdf_fielddata,hdf_mesh) +% +% input: +% hdf_fielddata.time +% hdf_fielddata.names +% hdf_fielddata.values +% hdf_mesh.type +% hdf_mesh.names +% hdf_mesh.lines +% +% openEMS matlab interface +% ----------------------- +% (C) 2010 Sebastian Held <sebastian.held@uni-due.de> +% See also ReadHDF5FieldData ReadHDF5Mesh + +isOctave = exist('OCTAVE_VERSION','builtin') ~= 0; +if isOctave + WriteHDF5_octave(filename,hdf_fielddata,hdf_mesh); + return +end + +writemode = 'overwrite'; +if isfield( hdf_fielddata, 'TD' ) + % this is a time domain data set + time = hdf_fielddata.TD.time; + for n=1:numel(time) + name = ['/FieldData/TD/' int2str(n)]; + [details.Location, details.Name] = fileparts(name); + attribute_details.AttachedTo = name; + attribute_details.AttachType = 'dataset'; + attribute_details.Name = 'time'; + hdf5write( filename, details, hdf_fielddata.TD.values{n}, ... + attribute_details, time(n), ... + 'WriteMode', writemode ); + writemode = 'append'; + end +end +if isfield( hdf_fielddata, 'FD' ) + % this is a frequency domain data set + freq = hdf_fielddata.FD.frequency; + for n=1:numel(freq) + name = ['/FieldData/FD/f' int2str(n-1) '_real']; + [details.Location, details.Name] = fileparts(name); + hdf5write( filename, details, real(hdf_fielddata.FD.values{n}), ... + 'WriteMode', writemode ); + name = ['/FieldData/FD/f' int2str(n-1) '_imag']; + [details.Location, details.Name] = fileparts(name); + hdf5write( filename, details, imag(hdf_fielddata.FD.values{n}), ... + 'WriteMode', 'append' ); + writemode = 'append'; + end + name = '/FieldData/FD'; + [details.Location, details.Name] = fileparts(name); + attribute_details.AttachedTo = name; + attribute_details.AttachType = 'group'; + attribute_details.Name = 'frequency'; + hdf5write( filename, attribute_details, freq, ... + 'WriteMode', 'append' ); +end + +names = hdf_mesh.names; % names is a cell array +for n=1:numel(names) + [details.Location, details.Name, ext] = fileparts(names{n}); + details.Name = [details.Name ext]; + hdf5write( filename, details, hdf_mesh.lines{n}, ... + 'WriteMode', 'append' ); +end + + + +function WriteHDF5_octave(filename,hdf_fielddata,hdf_mesh) +error 'not yet implemented' diff --git a/openEMS/matlab/WriteOpenEMS.m b/openEMS/matlab/WriteOpenEMS.m new file mode 100644 index 0000000..d7c9cbd --- /dev/null +++ b/openEMS/matlab/WriteOpenEMS.m @@ -0,0 +1,19 @@ +function WriteOpenEMS(filename, FDTD, CSX) +% function WriteOpenEMS(filename, FDTD, CSX) +% +% Write the FDTD and CSX structures to a file. +% +% example: +% CSX = InitCSX(); +% FDTD = InitFDTD(); +% WriteOpenEMS('test.xml',FDTD,CSX) +% +% See also InitFDTD InitCSX CSXGeomPlot +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig + +openEMS.FDTD = FDTD; +openEMS.ContinuousStructure = CSX; +struct_2_xml(filename,openEMS,'openEMS');
\ No newline at end of file diff --git a/openEMS/matlab/calcLumpedPort.m b/openEMS/matlab/calcLumpedPort.m new file mode 100644 index 0000000..69dba69 --- /dev/null +++ b/openEMS/matlab/calcLumpedPort.m @@ -0,0 +1,107 @@ +function [port] = calcLumpedPort( port, SimDir, f, varargin) +% [port] = calcLumpedPort( port, SimDir, f, varargin) +% +% Calculate voltages and currents of given lumped port. +% +% The port has to be created by e.g. AddLumpedPort(). +% +% input: +% port: return value of e.g. AddMSLPort() +% SimDir: directory, where the simulation files are +% f: frequency vector for DFT +% +% variable input: +% 'RefImpedance': - use a given reference impedance to calculate inc and +% ref voltages and currents +% - default is given port or calculated line impedance +% 'SwitchDirection': 0/1, switch assumed direction of propagation +% +% output: +% % output signals/values in time domain (TD): +% port.ut.tot total voltage (time-domain) +% port.ut.time voltage time vector +% port.it.tot total current (time-domain) +% port.it.time current time vector +% +% % output signals/values in frequency domain (FD): +% port.f the given frequency fector +% port.uf.tot/inc/ref total, incoming and reflected voltage +% port.if.tot/inc/ref total, incoming and reflected current +% +% example: +% port{1} = calcLumpedPort( port{1}, Sim_Path, f, 'RefImpedance', 50); +% +% openEMS matlab interface +% ----------------------- +% (C) 2012 Thorsten Liebig <thorsten.liebig@gmx.de> +% +% See also AddLumpedPort, calcPort + +if (iscell(port)) + for n=1:numel(port) + port{n}=calcLumpedPort(port{n}, SimDir, f, varargin{:}); + end + return; +end + +if (strcmpi(port.type,'Lumped')~=1 && strcmpi(port.type,'Curve')~=1) + error('openEMS:calcLumpedPort','error, type is not a lumped port'); +end + + +%% read optional arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%set defaults +ref_ZL = port.Feed_R; +switch_dir = 1; + +UI_args = {}; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'RefImpedance')==1); + ref_ZL = varargin{n+1}; + elseif (strcmpi(varargin{n},'SwitchDirection')==1); + if (varargin{n+1}) + switch_dir = -1; + end + else + UI_args(end+1) = varargin(n); + UI_args(end+1) = varargin(n+1); + end +end + +port.ZL_ref = ref_ZL; + +% read time domain data +U = ReadUI( port.U_filename, SimDir, f, UI_args{:} ); +I = ReadUI( port.I_filename, SimDir, f, UI_args{:} ); + +% store the original frequency domain waveforms +u_f = U.FD{1}.val; +i_f = switch_dir*I.FD{1}.val; + +port.ut.time = U.TD{1}.t; +port.ut.tot = U.TD{1}.val; + +port.it.time = I.TD{1}.t; +port.it.tot = switch_dir*I.TD{1}.val; + +port.Zin = u_f./i_f; + +port.f = f; +uf_inc = 0.5 * ( u_f + i_f .* ref_ZL ); +if_inc = 0.5 * ( i_f + u_f ./ ref_ZL ); + +uf_ref = u_f - uf_inc; +if_ref = if_inc - i_f; + +port.uf.tot = u_f; +port.uf.inc = uf_inc; +port.uf.ref = uf_ref; + +port.if.tot = i_f; +port.if.inc = if_inc; +port.if.ref = if_ref; + +port.raw.U = U; +port.raw.I = I; diff --git a/openEMS/matlab/calcPort.m b/openEMS/matlab/calcPort.m new file mode 100644 index 0000000..9b56955 --- /dev/null +++ b/openEMS/matlab/calcPort.m @@ -0,0 +1,82 @@ +function [port] = calcPort( port, SimDir, f, varargin) +% [port] = calcPort( port, SimDir, f, varargin) +% +% Calculate: +% - voltages and currents +% - the propagation constant and the characteristic impedance (if applicable) +% +% The port has to be created by e.g. AddMSLPort(), AddLumpedPort() or AddCurvePort +% +% input: +% port: return value of AddMSLPort() +% SimDir: directory, where the simulation files are +% f: frequency vector for DFT +% +% variable input: +% 'RefImpedance': - use a given reference impedance to calculate inc and +% ref voltages and currents +% - default is given port or calculated line impedance +% 'RefPlaneShift': for transmission lines only, See also calcTLPort for +% more details +% 'SwitchDirection': 0/1, switch assumed direction of propagation +% 'SignalType': 'pulse' (default) or 'periodic' +% +% output: +% % output signals/values in time domain (TD): +% port.ut.tot total voltage (time-domain) +% port.ut.time voltage time vector +% port.it.tot total current (time-domain) +% port.it.time current time vector +% +% % output signals/values in frequency domain (FD): +% port.f the given frequency fector +% port.uf.tot/inc/ref total, incoming and reflected voltage +% port.if.tot/inc/ref total, incoming and reflected current +% port.ZL_ref used refernce impedance +% +% port.P_inc incoming power +% port.P_ref reflected power +% port.P_acc accepted power (incoming minus reflected, +% may be negative for passive ports) +% +% if port is a transmission line port: +% port.beta: propagation constant +% port.ZL: characteristic line impedance +% +% example: +% port = calcPort(port, Sim_Path, f, 'RefImpedance', 50); +% +% openEMS matlab interface +% ----------------------- +% (C) 2012 Thorsten Liebig <thorsten.liebig@gmx.de> +% +% See also AddMSLPort, AddLumpedPort, AddCurvePort, calcTLPort, calcLumpedPort + +if (iscell(port)) + for n=1:numel(port) + port{n}=calcPort(port{n}, SimDir, f, varargin{:}); + end + return; +end + +if isempty(port) + return; +end + +if (strcmpi(port.type,'MSL') || strcmpi(port.type,'Coaxial') || strcmpi(port.type,'StripLine') || strcmpi(port.type,'CPW')) + port = calcTLPort( port, SimDir, f, varargin{:}); +elseif strcmpi(port.type,'WaveGuide') + port = calcWGPort( port, SimDir, f, varargin{:}); +elseif (strcmpi(port.type,'Lumped') || strcmpi(port.type,'Curve')) + port = calcLumpedPort( port, SimDir, f, varargin{:}); +else + error 'unknown port type' +end + +% calc some more port parameter +% incoming power +port.P_inc = 0.5*real(port.uf.inc.*conj(port.if.inc)); +% reflected power +port.P_ref = 0.5*real(port.uf.ref.*conj(port.if.ref)); +% accepted power (incoming - reflected) +port.P_acc = 0.5*real(port.uf.tot.*conj(port.if.tot)); diff --git a/openEMS/matlab/calcTLPort.m b/openEMS/matlab/calcTLPort.m new file mode 100644 index 0000000..3a631ba --- /dev/null +++ b/openEMS/matlab/calcTLPort.m @@ -0,0 +1,173 @@ +function [port] = calcTLPort( port, SimDir, f, varargin) +% [port] = calcTLPort( port, SimDir, f, varargin) +% +% Calculate voltages and currents, the propagation constant beta +% and the characteristic impedance ZL of the given transmission line port. +% +% The port has to be created by e.g. AddMSLPort(). +% +% input: +% port: return value of e.g. AddMSLPort() +% SimDir: directory, where the simulation files are +% f: frequency vector for DFT +% +% variable input: +% 'RefImpedance': - use a given reference impedance to calculate inc and +% ref voltages and currents +% - default is given port or calculated line impedance +% 'RefPlaneShift': - use a given reference plane shift from port beginning +% for a desired phase correction +% - default is the measurement plane +% - the plane shift has to be given in drawing units! +% 'SwitchDirection': 0/1, switch assumed direction of propagation +% +% output: +% % output signals/values in time domain (TD): +% port.ut.tot total voltage (time-domain) +% port.ut.time voltage time vector +% port.it.tot total current (time-domain) +% port.it.time current time vector +% +% % output signals/values in frequency domain (FD): +% port.f the given frequency fector +% port.uf.tot/inc/ref total, incoming and reflected voltage +% port.if.tot/inc/ref total, incoming and reflected current +% port.beta: propagation constant +% port.ZL: characteristic line impedance +% port.ZL_ref used refernce impedance +% +% example: +% port{1} = calcTLPort( port{1}, Sim_Path, f, 'RefImpedance', 50); +% +% reference: W. K. Gwarek, "A Differential Method of Reflection Coefficient Extraction From FDTD Simulations", +% IEEE Microwave and Guided Wave Letters, Vol. 6, No. 5, May 1996 +% +% openEMS matlab interface +% ----------------------- +% (C) 2010 Sebastian Held <sebastian.held@uni-due.de> +% +% See also AddMSLPort, calcPort + +if (iscell(port)) + for n=1:numel(port) + port{n}=calcTLPort(port{n}, SimDir, f, varargin{:}); + end + return; +end + +if ((strcmpi(port.type,'MSL')~=1) && (strcmpi(port.type,'Coaxial')~=1) && (strcmpi(port.type,'StripLine')~=1) && (strcmpi(port.type,'CPW')~=1)) + error('openEMS:calcTLPort','error, type is not a transmission line port'); +end + +% check +if abs((port.v_delta(1) - port.v_delta(2)) / port.v_delta(1))>1e-6 + warning( 'openEMS:calcPort:mesh', 'mesh is not equidistant; expect degraded accuracy' ); +end + + +%% read optional arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%set defaults +ref_ZL = -1; +ref_shift = nan; +switch_dir = 1; + +UI_args = {}; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'RefPlaneShift')==1); + ref_shift = varargin{n+1}; + elseif (strcmp(varargin{n},'RefImpedance')==1); + ref_ZL = varargin{n+1}; + elseif (strcmpi(varargin{n},'SwitchDirection')==1); + if (varargin{n+1}) + switch_dir = -1; + end + else + UI_args(end+1) = varargin(n); + UI_args(end+1) = varargin(n+1); + end +end + +if ((strcmpi(port.type,'StripLine')==1) || (strcmpi(port.type,'CPW')==1)) + U1 = ReadUI( port.U_filename(:,1), SimDir, f, UI_args{:} ); + U2 = ReadUI( port.U_filename(:,1), SimDir, f, UI_args{:} ); + U = U1; + for n=1:3 + U.TD{n}.val = U1.TD{n}.val+U2.TD{n}.val; + U.FD{n}.val = U1.FD{n}.val+U2.FD{n}.val; + end +else + U = ReadUI( port.U_filename, SimDir, f, UI_args{:} ); +end +% read time domain data (multiples files) +I = ReadUI( port.I_filename, SimDir, f, UI_args{:} ); + +% time domain signals +port.ut.time = U.TD{2}.t; +port.ut.tot = U.TD{2}.val; + +port.it.time = I.TD{1}.t; +port.it.tot = switch_dir*(I.TD{1}.val + I.TD{2}.val) / 2; % interpolate to same position as v + +% store the original frequency domain waveforms +u_f = U.FD{2}.val; +i_f = switch_dir*(I.FD{1}.val + I.FD{2}.val) / 2; % shift to same position as v + +f = U.FD{2}.f; +Et = U.FD{2}.val; +dEt = (U.FD{3}.val - U.FD{1}.val) / (sum(abs(port.v_delta(1:2))) * port.drawingunit); +Ht = (I.FD{1}.val + I.FD{2}.val)/2; % space averaging: Ht is now defined at the same pos as Et +dHt = (I.FD{2}.val - I.FD{1}.val) / (abs(port.i_delta(1)) * port.drawingunit); + +beta = sqrt( - dEt .* dHt ./ (Ht .* Et) ); +beta(real(beta) < 0) = -beta(real(beta) < 0); % determine correct sign (unlike the paper) + +% determine ZL +ZL = sqrt(Et .* dEt ./ (Ht .* dHt)); + +% if (strcmpi(port.type,'Coaxial')) +% port.ZL = Z0/2/pi/ref_index*log(port.r_o/port.r_i); +% end + +% reference plane shift (lossless) +if ~isnan(ref_shift) + ref_shift = ref_shift * port.LengthScale; + % shift to the beginning of MSL + ref_shift = ref_shift - port.measplanepos; + ref_shift = ref_shift * port.drawingunit; + + % store the shifted frequency domain waveforms + phase = real(beta)*ref_shift; + U.FD{1}.val = u_f .* cos(-phase) + 1i * i_f.*ZL .* sin(-phase); + I.FD{1}.val = i_f .* cos(-phase) + 1i * u_f./ZL .* sin(-phase); + + u_f = U.FD{1}.val; + i_f = I.FD{1}.val; +end + +if (ref_ZL < 0) + ref_ZL = ZL; +end + +port.ZL = ZL; +port.beta = beta; +port.ZL_ref = ref_ZL; + +port.f = f; +uf_inc = 0.5 * ( u_f + i_f .* ref_ZL ); +if_inc = 0.5 * ( i_f + u_f ./ ref_ZL ); + +uf_ref = u_f - uf_inc; +if_ref = if_inc - i_f; + +port.uf.tot = u_f; +port.uf.inc = uf_inc; +port.uf.ref = uf_ref; + +port.if.tot = i_f; +port.if.inc = if_inc; +port.if.ref = if_ref; + +port.raw.U = U; +port.raw.I = I; diff --git a/openEMS/matlab/calcWGPort.m b/openEMS/matlab/calcWGPort.m new file mode 100644 index 0000000..ba845af --- /dev/null +++ b/openEMS/matlab/calcWGPort.m @@ -0,0 +1,145 @@ +function [port] = calcWGPort( port, SimDir, f, varargin) +% [port] = calcWGPort( port, SimDir, f, varargin) +% +% Calculate voltages and currents, the propagation constant beta +% and the characteristic impedance ZL of the given waveguide port. +% +% The port has to be created by e.g. AddWaveGuidePort(). +% +% input: +% port: return value of e.g. AddWaveGuidePort() +% SimDir: directory, where the simulation files are +% f: frequency vector for DFT +% +% variable input: +% 'RefImpedance': - use a given reference impedance to calculate inc and +% ref voltages and currents +% - default is given port or calculated line impedance +% 'RefPlaneShift': - use a given reference plane shift from port beginning +% for a desired phase correction +% - default is the measurement plane at the end of the +% port +% - the plane shift has to be given in drawing units! +% 'RefractiveIndex': set a material refractive index +% 'SwitchDirection': 0/1, switch assumed direction of propagation +% +% output: +% % output signals/values in time domain (TD): +% port.ut.tot total voltage (time-domain) +% port.ut.time voltage time vector +% port.it.tot total current (time-domain) +% port.it.time current time vector +% +% % output signals/values in frequency domain (FD): +% port.f the given frequency fector +% port.uf.tot/inc/ref total, incoming and reflected voltage +% port.if.tot/inc/ref total, incoming and reflected current +% port.beta: propagation constant +% port.ZL: characteristic line impedance +% port.ZL_ref used reference impedance +% +% example: +% port{1} = calcWGPort( port{1}, Sim_Path, f, 'RefImpedance', 50); +% +% openEMS matlab interface +% ----------------------- +% (C) 2013 Thorsten Liebig (thorsten.liebig@gmx.de) +% +% See also AddWaveGuidePort, calcPort + +if (iscell(port)) + for n=1:numel(port) + port{n}=calcWGPort(port{n}, SimDir, f, varargin{:}); + end + return; +end + +if (strcmpi(port.type,'WaveGuide')~=1) + error('openEMS:calcWGPort','error, type is not a waveguide port'); +end + +%set defaults +ref_ZL = -1; +ref_shift = nan; +ref_index = 1; +switch_dir = 1; + +UI_args = {}; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'RefPlaneShift')==1); + ref_shift = varargin{n+1}; + elseif (strcmp(varargin{n},'RefImpedance')==1); + ref_ZL = varargin{n+1}; + elseif (strcmp(varargin{n},'RefractiveIndex')==1); + ref_index = varargin{n+1}; + elseif (strcmpi(varargin{n},'SwitchDirection')==1); + if (varargin{n+1}) + switch_dir = -1; + end + else + UI_args(end+1) = varargin(n); + UI_args(end+1) = varargin(n+1); + end +end + +% read time domain data +U = ReadUI( port.U_filename, SimDir, f, UI_args{:} ); +I = ReadUI( port.I_filename, SimDir, f, UI_args{:} ); + +% store the original frequency domain waveforms +u_f = U.FD{1}.val; +i_f = I.FD{1}.val * switch_dir; + +% time domain signal +port.ut.time = U.TD{1}.t; +port.ut.tot = U.TD{1}.val; + +port.it.time = I.TD{1}.t; +port.it.tot = switch_dir*I.TD{1}.val; + + +physical_constants +k = 2*pi*f/C0*ref_index; +fc = C0*port.kc/2/pi/ref_index; +port.beta = sqrt(k.^2 - port.kc^2); +port.ZL = k * Z0 ./ port.beta; %analytic waveguide impedance + +% reference plane shift (lossless) +if ~isnan(ref_shift) + % shift relative to the beginning of the waveguide + ref_shift = ref_shift - port.measplanepos; + ref_shift = ref_shift * port.drawingunit; + + % store the shifted frequency domain waveforms + phase = real(beta)*ref_shift; + u_f_shift = u_f .* cos(-phase) + 1i * i_f.*port.ZL .* sin(-phase); + i_f_shift = i_f .* cos(-phase) + 1i * u_f./port.ZL .* sin(-phase); + + u_f = u_f_shift; + i_f = i_f_shift; +end + +if (ref_ZL < 0) + ref_ZL = port.ZL; +end + +port.ZL_ref = ref_ZL; + +port.f = f; +uf_inc = 0.5 * ( u_f + i_f .* ref_ZL ); +if_inc = 0.5 * ( i_f + u_f ./ ref_ZL ); + +uf_ref = u_f - uf_inc; +if_ref = if_inc - i_f; + +port.uf.tot = u_f; +port.uf.inc = uf_inc; +port.uf.ref = uf_ref; + +port.if.tot = i_f; +port.if.inc = if_inc; +port.if.ref = if_ref; + +port.raw.U = U; +port.raw.I = I; diff --git a/openEMS/matlab/calc_ypar.m b/openEMS/matlab/calc_ypar.m new file mode 100644 index 0000000..f6ae1bd --- /dev/null +++ b/openEMS/matlab/calc_ypar.m @@ -0,0 +1,69 @@ +function Y = calc_ypar( f, ports, Sim_Path_Prefix ) +% Y = calc_ypar( f, ports, Sim_Path_Prefix ) +% +% f: frequency vector (Hz) +% ports: cell array of ports (see AddMSLPort() and AddLumpedPort()) +% Sim_Path_Prefix: prefix of the simulation dirs (will be postfixed by +% excitation port number) +% +% This function calculates the Y-matrix representation of the ports +% +% It is assumed that each port (inside ports) is excited and the +% corresponding simulation was carried out at Sim_Path + portnr (e.g. for +% port 2: '/tmp/sim2') +% +% Sebastian Held <sebastian.held@uni-due.de> +% Jun 9 2010 +% +% See also AddMSLPort AddLumpedPort + +% sanitize input arguments +f = reshape(f,1,[]); % make it a row vector + +% prepare result matrix +maxportnr = max( cellfun(@(x) x.nr, ports) ); +Y = ones(maxportnr,maxportnr,numel(f)) * NaN; +U = ones(maxportnr,maxportnr,numel(f)) * NaN; +I = ones(maxportnr,maxportnr,numel(f)) * NaN; + +% read time domain simulation results +for runnr = 1:numel(ports) + Sim_Path = [Sim_Path_Prefix num2str(ports{runnr}.nr)]; + for pnr = 1:numel(ports) + if isfield( ports{pnr}, 'v_delta' ) + % this is an MSLPort + temp_U = ReadUI( ['port_ut' num2str(ports{pnr}.nr) 'B'], Sim_Path ); + temp = ReadUI( {['port_it' num2str(ports{pnr}.nr) 'A'],['port_it' num2str(ports{pnr}.nr) 'B']}, Sim_Path ); + temp_I.TD{1}.t = temp.TD{1}.t; + temp_I.TD{1}.val = (temp.TD{1}.val + temp.TD{2}.val) / 2; % space averaging + else + % this is a lumped port + temp_U = ReadUI( ['port_ut' num2str(ports{pnr}.nr)], Sim_Path ); + temp_I = ReadUI( ['port_it' num2str(ports{pnr}.nr)], Sim_Path ); + +% % correct the orientation of the probes (FIXME to be done inside +% % openEMS) +% temp_U.TD{1}.val = temp_U.TD{1}.val * (-ports{pnr}.direction); + end + +% % correct the orientation of the probes (FIXME to be done inside +% % openEMS) +% temp_I.TD{1}.val = temp_I.TD{1}.val * ports{pnr}.direction; +% if runnr == 5 % DEBUG +% temp_I.TD{1}.val = temp_I.TD{1}.val * -1; +% end + + % time domain -> frequency domain + U(ports{pnr}.nr,ports{runnr}.nr,:) = DFT_time2freq( temp_U.TD{1}.t, temp_U.TD{1}.val, f ); + I(ports{pnr}.nr,ports{runnr}.nr,:) = DFT_time2freq( temp_I.TD{1}.t, temp_I.TD{1}.val, f ); + + % compensate H-field time advance + delta_t_2 = temp_I.TD{1}.t(1) - temp_U.TD{1}.t(1); % half time-step (s) + I(ports{pnr}.nr,ports{runnr}.nr,:) = squeeze(I(ports{pnr}.nr,ports{runnr}.nr,:)).' .* exp(-1i*2*pi*f*delta_t_2); + end +end + +% calc Y-parameters +for a=1:numel(f) + Y(:,:,a) = I(:,:,a) / U(:,:,a); +end diff --git a/openEMS/matlab/examples/__deprecated__/MSL2.m b/openEMS/matlab/examples/__deprecated__/MSL2.m new file mode 100644 index 0000000..31a2600 --- /dev/null +++ b/openEMS/matlab/examples/__deprecated__/MSL2.m @@ -0,0 +1,254 @@ +% +% EXAMPLE / microstrip / MSL2 +% +% This example shows how to use the MSL-port. +% The MSL is excited at the center of the computational volume. The +% boundary at xmin is an absorbing boundary (Mur) and at xmax an electric +% wall. The reflection coefficient at this wall is S11 = -1. +% Direction of propagation is x. +% +% This example demonstrates: +% - simple microstrip geometry (made of PEC) +% - MSL port +% - MSL analysis +% +% You may modify the PEC boundary condition at xmax to become a MUR +% boundary. This resembles a matched microstrip line. +% +% Tested with +% - Matlab 2009b +% - Octave 3.3.52 +% - openEMS v0.0.14 +% +% (C) 2010 Sebastian Held <sebastian.held@uni-due.de> + +close all +clear +clc + +%% switches +postproc_only = 0; + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um +MSL_length = 10000; +MSL_width = 1000; +substrate_thickness = 254; +substrate_epr = 3.66; + +% mesh_res = [200 0 0]; + +%% prepare simulation folder +Sim_Path = 'tmp'; +Sim_CSX = 'msl2.xml'; +if ~postproc_only + [status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory + [status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder +end + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +max_timesteps = 20000; +min_decrement = 1e-6; +f_max = 7e9; +FDTD = InitFDTD( max_timesteps, min_decrement, 'OverSampling', 10 ); +FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); +BC = {'MUR' 'MUR' 'PEC' 'PEC' 'PEC' 'PMC'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +resolution = c0/(f_max*sqrt(substrate_epr))/unit /50; % resolution of lambda/50 +mesh.x = SmoothMeshLines( [-MSL_length MSL_length], resolution ); +mesh.y = SmoothMeshLines( [-4*MSL_width -MSL_width/2 MSL_width/2 4*MSL_width], resolution ); +mesh.z = SmoothMeshLines( [linspace(0,substrate_thickness,5) 10*substrate_thickness], resolution ); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% substrate +CSX = AddMaterial( CSX, 'RO4350B' ); +CSX = SetMaterialProperty( CSX, 'RO4350B', 'Epsilon', substrate_epr ); +start = [mesh.x(1), mesh.y(1), 0]; +stop = [mesh.x(end), mesh.y(end), substrate_thickness]; +CSX = AddBox( CSX, 'RO4350B', 0, start, stop ); + +%% MSL port +CSX = AddMetal( CSX, 'PEC' ); +portstart = [ 0, -MSL_width/2, substrate_thickness]; +portstop = [ MSL_length, MSL_width/2, 0]; +[CSX,portstruct] = AddMSLPort( CSX, 999, 1, 'PEC', portstart, portstop, [1 0 0], [0 0 1], [], 'excite' ); + +%% MSL +start = [-MSL_length, -MSL_width/2, substrate_thickness]; +stop = [ 0, MSL_width/2, substrate_thickness]; +CSX = AddBox( CSX, 'PEC', 999, start, stop ); % priority needs to be higher than + +%% define dump boxes +start = [mesh.x(1), mesh.y(1), substrate_thickness/2]; +stop = [mesh.x(end), mesh.y(end), substrate_thickness/2]; +CSX = AddDump( CSX, 'Et_', 'DumpType', 0,'DumpMode', 2 ); % cell interpolated +CSX = AddBox( CSX, 'Et_', 0, start, stop ); +CSX = AddDump( CSX, 'Ht_', 'DumpType', 1,'DumpMode', 2 ); % cell interpolated +CSX = AddBox( CSX, 'Ht_', 0, start, stop ); + +%% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +%% show the structure +if ~postproc_only + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +end + +%% run openEMS +openEMS_opts = ''; +openEMS_opts = [openEMS_opts ' --engine=fastest']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --debug-boxes']; +% openEMS_opts = [openEMS_opts ' --debug-PEC']; +if ~postproc_only + RunOpenEMS( Sim_Path, Sim_CSX, openEMS_opts ); +end + + +%% postprocess +f = linspace( 1e6, f_max, 1601 ); +U = ReadUI( {'port_ut1A','port_ut1B','port_ut1C','et'}, 'tmp/', f ); +I = ReadUI( {'port_it1A','port_it1B'}, 'tmp/', f ); + +% Z = (U.FD{1}.val+U.FD{2}.val)/2 ./ I.FD{1}.val; +% plot( f*1e-9, [real(Z);imag(Z)],'Linewidth',2); +% xlabel('frequency (GHz)'); +% ylabel('impedance (Ohm)'); +% grid on; +% legend( {'real','imaginary'}, 'location', 'northwest' ) +% title( 'line impedance (will fail in case of reflections!)' ); + +figure +ax = plotyy( U.TD{1}.t/1e-6, [U.TD{1}.val;U.TD{2}.val;U.TD{3}.val], U.TD{4}.t/1e-6, U.TD{4}.val ); +xlabel( 'time (us)' ); +ylabel( 'amplitude (V)' ); +grid on +title( 'Time domain voltage probes and excitation signal' ); +legend( {'ut1A','ut1B','ut1C','excitation'} ); +% now make the y-axis symmetric to y=0 (align zeros of y1 and y2) +y1 = ylim(ax(1)); +y2 = ylim(ax(2)); +ylim( ax(1), [-max(abs(y1)) max(abs(y1))] ); +ylim( ax(2), [-max(abs(y2)) max(abs(y2))] ); + +figure +plot( I.TD{1}.t/1e-6, [I.TD{1}.val;I.TD{2}.val] ); +xlabel( 'time (us)' ); +ylabel( 'amplitude (A)' ); +grid on +title( 'Time domain current probes' ); +legend( {'it1A','it1B'} ); + +figure +ax = plotyy( U.FD{1}.f/1e9, abs([U.FD{1}.val;U.FD{2}.val;U.FD{3}.val]), U.FD{1}.f/1e9, angle([U.FD{1}.val;U.FD{2}.val;U.FD{3}.val])/pi*180 ); +xlabel( 'frequency (GHz)' ); +ylabel( ax(1), 'amplitude (A)' ); +ylabel( ax(2), 'phase (deg)' ); +grid on +title( 'Frequency domain voltage probes' ); +legend( {'abs(uf1A)','abs(uf1B)','abs(uf1C)','angle(uf1A)','angle(uf1B)','angle(uf1C)'} ); + +figure +ax = plotyy( I.FD{1}.f/1e9, abs([I.FD{1}.val;I.FD{2}.val]), I.FD{1}.f/1e9, angle([I.FD{1}.val;I.FD{2}.val])/pi*180 ); +xlabel( 'frequency (GHz)' ); +ylabel( ax(1), 'amplitude (A)' ); +ylabel( ax(2), 'phase (deg)' ); +grid on +title( 'Frequency domain current probes' ); +legend( {'abs(if1A)','abs(if1B)','angle(if1A)','angle(if1B)'} ); + +%% port analysis +[U,I,beta,ZL] = calcPort( portstruct, Sim_Path, f ); +%% attention! the reflection coefficient S11 is normalized to ZL! + +figure +plot( sin(0:0.01:2*pi), cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +hold on +plot( 0.5+0.5*sin(0:0.01:2*pi), 0.5*cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +plot( [-1 1], [0 0], 'Color', [.7 .7 .7] ); +plot( S11, 'k' ); +plot( real(S11(1)), imag(S11(1)), '*r' ); +axis equal +title( 'Reflection coefficient S11 at the measurement plane' ); + +figure +plot( sin(0:0.01:2*pi), cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +hold on +plot( 0.5+0.5*sin(0:0.01:2*pi), 0.5*cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +plot( [-1 1], [0 0], 'Color', [.7 .7 .7] ); +Z = vi.FD.v.val ./ vi.FD.i.val; +S11_ = (Z-ZL) ./ (Z+ZL); +plot( S11_, 'k' ); +plot( real(S11_(1)), imag(S11_(1)), '*r' ); +axis equal +title( {'Reflection coefficient S11 at the measurement plane' 'calculated from voltages and currents'} ); + +figure +plot( f/1e9, [real(S11);imag(S11)], 'Linewidth',2 ); +legend( {'Re(S11)', 'Im(S11)'} ); +ylabel( 'amplitude' ); +xlabel( 'frequency (GHz)' ); +title( 'Reflection coefficient S11 at the measurement plane' ); + +figure +plotyy( f/1e9, 20*log10(abs(S11)), f/1e9, angle(S11)/pi*180 ); +legend( {'|S11|', 'angle(S11)'} ); +xlabel( 'frequency (GHz)' ); +ylabel( '|S11| (dB)' ); +title( 'Reflection coefficient S11 at the measurement plane' ); + +figure +plot( f/1e9, [real(beta);imag(beta)], 'Linewidth',2 ); +legend( 'Re(beta)', 'Im(beta)' ); +ylabel( 'propagation constant beta (1/m)' ); +xlabel( 'frequency (GHz)' ); +title( 'Propagation constant of the MSL' ); + +figure +plot( f/1e9, [real(ZL);imag(ZL)], 'Linewidth',2); +xlabel('frequency (GHz)'); +ylabel('impedance (Ohm)'); +grid on; +legend( {'real','imaginary'}, 'location', 'northeast' ) +title( 'Characteristic line impedance ZL' ); + +%% reference plane shift (to the end of the port) +ref_shift = abs(portstop(1) - portstart(1)); +[U, I,beta,ZL] = calcPort( portstruct, Sim_Path, f ); +%% + +figure +plotyy( f/1e9, 20*log10(abs(S11)), f/1e9, angle(S11)/pi*180 ); +legend( {'abs(S11)', 'angle(S11)'} ); +xlabel( 'frequency (GHz)' ); +title( 'Reflection coefficient S11 at the reference plane (at the electric wall)' ); + +figure +plot( sin(0:0.01:2*pi), cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +hold on +plot( 0.5+0.5*sin(0:0.01:2*pi), 0.5*cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +plot( [-1 1], [0 0], 'Color', [.7 .7 .7] ); +plot( S11, 'k' ); +plot( real(S11(1)), imag(S11(1)), '*r' ); +axis equal +title( 'Reflection coefficient S11 at the reference plane (at the electric wall)' ); + +figure +plot( sin(0:0.01:2*pi), cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +hold on +plot( 0.5+0.5*sin(0:0.01:2*pi), 0.5*cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +plot( [-1 1], [0 0], 'Color', [.7 .7 .7] ); +Z = vi.FD.v.val_shifted ./ vi.FD.i.val_shifted; +S11_ = (Z-ZL) ./ (Z+ZL); +plot( S11_, 'k' ); +plot( real(S11_(1)), imag(S11_(1)), '*r' ); +axis equal +title( {'Reflection coefficient S11 at the reference plane (at the electric wall)' 'calculated from shifted voltages and currents'} ); + +%% visualize electric and magnetic fields +% you will find vtk dump files in the simulation folder (tmp/) +% use paraview to visualize them diff --git a/openEMS/matlab/examples/antennas/Bi_Quad_Antenna.m b/openEMS/matlab/examples/antennas/Bi_Quad_Antenna.m new file mode 100644 index 0000000..80ae97f --- /dev/null +++ b/openEMS/matlab/examples/antennas/Bi_Quad_Antenna.m @@ -0,0 +1,139 @@ +% +% Tutorials / bi-quad antenna +% +% Tested with +% - Octave 3.8.1 +% - openEMS v0.0.32 +% +% (C) 2011-2014 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +quad_size = 110; +port_length = 10; +quad_mesh = 5; + +Feed_R = 75; + +% size of the simulation box +SimBox = [800 800 400]; + +% frequency range of interest +f_start = 400e6; +f_stop = 1000e6; + +% frequency of interest +f0 = 700e6; +freq = linspace(f_start,f_stop,201); + +%% setup FDTD parameter & excitation function +FDTD = InitFDTD( 'endCriteria', 1e-4 ); +FDTD = SetGaussExcite(FDTD,0.5*(f_start+f_stop),0.5*(f_stop-f_start)); +BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +CSX = InitCSX(); + +%create fixed lines for the antenna outline and port +mesh.x = [-quad_size*sqrt(2) -quad_size/sqrt(2) 0 quad_size/sqrt(2) quad_size*sqrt(2)]; +mesh.y = [-quad_size/sqrt(2) -port_length/2 0 port_length/2 quad_size/sqrt(2)]; +mesh.z = [0]; + +mesh = SmoothMesh(mesh, quad_mesh, 1.3); + +% add air box +mesh.x = [mesh.x -SimBox(1)/2 SimBox(1)/2]; +mesh.y = [mesh.y -SimBox(2)/2 SimBox(2)/2]; +mesh.z = [-SimBox(3)/2 0 SimBox(3)/2]; + +max_res = c0 / (f_stop) / unit / 20; % cell size: lambda/20 +mesh = SmoothMesh(mesh, max_res, 1.4); + +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create bi-quad +points(1,1) = 0; +points(2,1) = port_length/2; +points(3,1) = 0; +points(1,end+1) = quad_size/sqrt(2); +points(2,end) = quad_size/sqrt(2); +points(1,end+1) = quad_size*sqrt(2); +points(2,end) = 0; +points(1,end+1) = quad_size/sqrt(2); +points(2,end) = -quad_size/sqrt(2); +points(1,end+1) = 0; +points(2,end) = -port_length/2; +points(1,end+1) = -quad_size/sqrt(2); +points(2,end) = -quad_size/sqrt(2); +points(1,end+1) = -quad_size*sqrt(2); +points(2,end) = 0; +points(1,end+1) = -quad_size/sqrt(2); +points(2,end) = quad_size/sqrt(2); +points(1,end+1) = 0; +points(2,end) = port_length/2; + +% create a thin metal wire... +CSX = AddMetal(CSX,'metal'); %create PEC with propName 'metal' +CSX = AddCurve(CSX,'metal',10, points); + +%% apply the excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start = [0 -port_length/2 0]; +stop = [0 port_length/2 0]; +[CSX port] = AddLumpedPort(CSX,10,0,Feed_R,start,stop,[0 1 0], true); + +%% nf2ff calc +start = [mesh.x(9) mesh.y(9) mesh.z(9)]; +stop = [mesh.x(end-8) mesh.y(end-8) mesh.z(end-8)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop); + +%% prepare simulation folder +Sim_Path = 'tmp'; +Sim_CSX = 'bi_quad_ant.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% write openEMS compatible xml-file +WriteOpenEMS([Sim_Path '/' Sim_CSX], FDTD, CSX); + +%% show the structure +CSXGeomPlot([Sim_Path '/' Sim_CSX]); + +%% run openEMS +RunOpenEMS(Sim_Path, Sim_CSX); + +%% postprocessing & do the plots +port = calcPort(port, Sim_Path, freq); +s11 = port.uf.ref ./ port.uf.inc; + +% plot reflection coefficient S11 +figure +plot( freq/1e9, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +ylim([-30 0]); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / GHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +%% calculate 3D far field pattern +phiRange = -180:2.5:180; +thetaRange = 0:2.5:180; + +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f0, thetaRange*pi/180, phiRange*pi/180); + +disp( ['directivity: Dmax = ' num2str(10*log10(nf2ff.Dmax)) ' dBi'] ); + +% plot far-field pattern with Matlab +figure +plotFF3D(nf2ff, 'logscale', -20) + +%% +disp( 'Dumping far-field pattern to vtk (use Paraview to visualize)...' ); +DumpFF2VTK('Bi_Quad_Pattern.vtk', nf2ff.E_norm{1} / max(nf2ff.E_norm{1}(:)) * nf2ff.Dmax, thetaRange, phiRange, 'scale', 0.05); diff --git a/openEMS/matlab/examples/antennas/Patch_Antenna.m b/openEMS/matlab/examples/antennas/Patch_Antenna.m new file mode 100644 index 0000000..2011d6f --- /dev/null +++ b/openEMS/matlab/examples/antennas/Patch_Antenna.m @@ -0,0 +1,218 @@ +% +% EXAMPLE / antennas / patch antenna +% +% This example demonstrates how to: +% - calculate the reflection coefficient of a patch antenna +% +% +% Tested with +% - Matlab 2009b +% - Octave 3.3.52 +% - openEMS v0.0.23 +% +% (C) 2010,2011 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% switches & options... +postprocessing_only = 0; +draw_3d_pattern = 0; % this may take a while... +use_pml = 0; % use pml boundaries instead of mur +openEMS_opts = ''; + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% width in x-direction +% length in y-direction +% main radiation in z-direction +patch.width = 32.86; % resonant length +patch.length = 41.37; + +substrate.epsR = 3.38; +substrate.kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate.epsR; +substrate.width = 60; +substrate.length = 60; +substrate.thickness = 1.524; +substrate.cells = 4; + +feed.pos = -5.5; +feed.width = 2; +feed.R = 50; % feed resistance + +% size of the simulation box +SimBox = [100 100 25]; + +%% prepare simulation folder +Sim_Path = 'tmp'; +Sim_CSX = 'patch_ant.xml'; +if (postprocessing_only==0) + [status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory + [status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder +end + +%% setup FDTD parameter & excitation function +max_timesteps = 30000; +min_decrement = 1e-5; % equivalent to -50 dB +f0 = 0e9; % center frequency +fc = 3e9; % 20 dB corner frequency (in this case 0 Hz - 3e9 Hz) +FDTD = InitFDTD( 'NrTS', max_timesteps, 'EndCriteria', min_decrement ); +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; % boundary conditions +if (use_pml>0) + BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; % use pml instead of mur +end +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +% currently, openEMS cannot automatically generate a mesh +max_res = c0 / (f0+fc) / unit / 20; % cell size: lambda/20 +CSX = InitCSX(); +mesh.x = [-SimBox(1)/2 SimBox(1)/2 -substrate.width/2 substrate.width/2 feed.pos]; +% add patch mesh with 2/3 - 1/3 rule +mesh.x = [mesh.x -patch.width/2-max_res/2*0.66 -patch.width/2+max_res/2*0.33 patch.width/2+max_res/2*0.66 patch.width/2-max_res/2*0.33]; +mesh.x = SmoothMeshLines( mesh.x, max_res, 1.4); % create a smooth mesh between specified mesh lines +mesh.y = [-SimBox(2)/2 SimBox(2)/2 -substrate.length/2 substrate.length/2 -feed.width/2 feed.width/2]; +% add patch mesh with 2/3 - 1/3 rule +mesh.y = [mesh.y -patch.length/2-max_res/2*0.66 -patch.length/2+max_res/2*0.33 patch.length/2+max_res/2*0.66 patch.length/2-max_res/2*0.33]; +mesh.y = SmoothMeshLines( mesh.y, max_res, 1.4 ); +mesh.z = [-SimBox(3)/2 linspace(0,substrate.thickness,substrate.cells) SimBox(3) ]; +mesh.z = SmoothMeshLines( mesh.z, max_res, 1.4 ); +mesh = AddPML( mesh, [8 8 8 8 8 8] ); % add equidistant cells (air around the structure) +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create patch +CSX = AddMetal( CSX, 'patch' ); % create a perfect electric conductor (PEC) +start = [-patch.width/2 -patch.length/2 substrate.thickness]; +stop = [ patch.width/2 patch.length/2 substrate.thickness]; +CSX = AddBox(CSX,'patch',10,start,stop); + +%% create substrate +CSX = AddMaterial( CSX, 'substrate' ); +CSX = SetMaterialProperty( CSX, 'substrate', 'Epsilon', substrate.epsR, 'Kappa', substrate.kappa ); +start = [-substrate.width/2 -substrate.length/2 0]; +stop = [ substrate.width/2 substrate.length/2 substrate.thickness]; +CSX = AddBox( CSX, 'substrate', 0, start, stop ); + +%% create ground (same size as substrate) +CSX = AddMetal( CSX, 'gnd' ); % create a perfect electric conductor (PEC) +start(3)=0; +stop(3) =0; +CSX = AddBox(CSX,'gnd',10,start,stop); + +%% apply the excitation & resist as a current source +start = [feed.pos-.1 -feed.width/2 0]; +stop = [feed.pos+.1 +feed.width/2 substrate.thickness]; +[CSX] = AddLumpedPort(CSX, 5 ,1 ,feed.R, start, stop, [0 0 1], true); + +%% dump magnetic field over the patch antenna +CSX = AddDump( CSX, 'Ht_', 'DumpType', 1, 'DumpMode', 2); % cell interpolated +start = [-patch.width -patch.length substrate.thickness+1]; +stop = [ patch.width patch.length substrate.thickness+1]; +CSX = AddBox( CSX, 'Ht_', 0, start, stop ); + +%%nf2ff calc +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', -SimBox/2, SimBox/2); + +if (postprocessing_only==0) + %% write openEMS compatible xml-file + WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + + %% show the structure + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + + %% run openEMS + RunOpenEMS( Sim_Path, Sim_CSX, openEMS_opts ); +end + +%% postprocessing & do the plots +freq = linspace( max([1e9,f0-fc]), f0+fc, 501 ); +U = ReadUI( {'port_ut1','et'}, 'tmp/', freq ); % time domain/freq domain voltage +I = ReadUI( 'port_it1', 'tmp/', freq ); % time domain/freq domain current (half time step is corrected) + +% plot time domain voltage +figure +[ax,h1,h2] = plotyy( U.TD{1}.t/1e-9, U.TD{1}.val, U.TD{2}.t/1e-9, U.TD{2}.val ); +set( h1, 'Linewidth', 2 ); +set( h1, 'Color', [1 0 0] ); +set( h2, 'Linewidth', 2 ); +set( h2, 'Color', [0 0 0] ); +grid on +title( 'time domain voltage' ); +xlabel( 'time t / ns' ); +ylabel( ax(1), 'voltage ut1 / V' ); +ylabel( ax(2), 'voltage et / V' ); +% now make the y-axis symmetric to y=0 (align zeros of y1 and y2) +y1 = ylim(ax(1)); +y2 = ylim(ax(2)); +ylim( ax(1), [-max(abs(y1)) max(abs(y1))] ); +ylim( ax(2), [-max(abs(y2)) max(abs(y2))] ); + +% plot feed point impedance +figure +Zin = U.FD{1}.val ./ I.FD{1}.val; +plot( freq/1e6, real(Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Zin), 'r--', 'Linewidth', 2 ); +title( 'feed point impedance' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'impedance Z_{in} / Ohm' ); +legend( 'real', 'imag' ); + +% plot reflection coefficient S11 +figure +uf_inc = 0.5*(U.FD{1}.val + I.FD{1}.val * 50); +if_inc = 0.5*(I.FD{1}.val - U.FD{1}.val / 50); +uf_ref = U.FD{1}.val - uf_inc; +if_ref = I.FD{1}.val - if_inc; +s11 = uf_ref ./ uf_inc; +plot( freq/1e6, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +P_in = 0.5*U.FD{1}.val .* conj( I.FD{1}.val ); + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +f_res_ind = find(s11==min(s11)); +f_res = freq(f_res_ind); + +% calculate the far field at phi=0 degrees and at phi=90 degrees +thetaRange = (0:2:359) - 180; +phiRange = [0 90]; +disp( 'calculating far field at phi=[0 90] deg...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180); + +Dlog=10*log10(nf2ff.Dmax); + +% display power and directivity +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(Dlog) ' dBi'] ); +disp( ['efficiency: nu_rad = ' num2str(100*nf2ff.Prad./real(P_in(f_res_ind))) ' %']); + +% display phi +figure +plotFFdB(nf2ff,'xaxis','theta','param',[1 2]); +drawnow + +if (draw_3d_pattern==0) + return +end + +%% calculate 3D pattern +phiRange = 0:2:360; +thetaRange = 0:2:180; +disp( 'calculating 3D far field...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180, 'Verbose',2,'Outfile','nf2ff_3D.h5'); +figure +plotFF3D(nf2ff); + + +%% visualize magnetic fields +% you will find vtk dump files in the simulation folder (tmp/) +% use paraview to visulaize them diff --git a/openEMS/matlab/examples/antennas/Patch_Antenna_Array.m b/openEMS/matlab/examples/antennas/Patch_Antenna_Array.m new file mode 100644 index 0000000..40b3b46 --- /dev/null +++ b/openEMS/matlab/examples/antennas/Patch_Antenna_Array.m @@ -0,0 +1,256 @@ +% +% EXAMPLE / antennas / patch antenna array +% +% This example demonstrates how to: +% - calculate the reflection coefficient of a patch antenna array +% +% +% Tested with +% - Matlab 2009b +% - Octave 3.3.52 +% - openEMS v0.0.23 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% switches & options... +postprocessing_only = 0; +draw_3d_pattern = 0; % this may take a (very long) while... +use_pml = 0; % use pml boundaries instead of mur +openEMS_opts = ''; + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% width in x-direction +% length in y-direction +% main radiation in z-direction +patch.width = 32.86; % resonant length +patch.length = 41.37; + +% define array size and dimensions +array.xn = 4; +array.yn = 4; +array.x_spacing = patch.width * 3; +array.y_spacing = patch.length * 3; + +substrate.epsR = 3.38; +substrate.kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate.epsR; +substrate.width = 60 + (array.xn-1) * array.x_spacing; +substrate.length = 60 + (array.yn-1) * array.y_spacing; +substrate.thickness = 1.524; +substrate.cells = 4; + +feed.pos = -5.5; +feed.width = 2; +feed.R = 50; % feed resistance + +% size of the simulation box around the array +SimBox = [50+substrate.width 50+substrate.length 25]; + +%% prepare simulation folder +Sim_Path = 'tmp'; +Sim_CSX = 'patch_array.xml'; +if (postprocessing_only==0) + [status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory + [status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder +end + +%% setup FDTD parameter & excitation function +max_timesteps = 30000; +min_decrement = 1e-5; % equivalent to -50 dB +f0 = 0e9; % center frequency +fc = 3e9; % 10 dB corner frequency (in this case 0 Hz - 3e9 Hz) +FDTD = InitFDTD( 'NrTS', max_timesteps, 'EndCriteria', min_decrement ); +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; % boundary conditions +if (use_pml>0) + BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; % use pml instead of mur +end +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +% currently, openEMS cannot automatically generate a mesh +max_res = c0 / (f0+fc) / unit / 20; % cell size: lambda/20 +CSX = InitCSX(); +mesh.x = [-SimBox(1)/2 SimBox(1)/2 -substrate.width/2 substrate.width/2]; +mesh.y = [-SimBox(2)/2 SimBox(2)/2 -substrate.length/2 substrate.length/2]; + +mesh.z = [-SimBox(3)/2 linspace(0,substrate.thickness,substrate.cells) SimBox(3) ]; +mesh.z = SmoothMeshLines( mesh.z, max_res, 1.4 ); + +for xn=1:array.xn + for yn=1:array.yn + midX = (array.xn/2 - xn + 1/2) * array.x_spacing; + midY = (array.yn/2 - yn + 1/2) * array.y_spacing; + + % feeding mesh + mesh.x = [mesh.x midX+feed.pos]; + mesh.y = [mesh.y midY-feed.width/2 midY+feed.width/2]; + + % add patch mesh with 2/3 - 1/3 rule + mesh.x = [mesh.x midX-patch.width/2-max_res/2*0.66 midX-patch.width/2+max_res/2*0.33 midX+patch.width/2+max_res/2*0.66 midX+patch.width/2-max_res/2*0.33]; + % add patch mesh with 2/3 - 1/3 rule + mesh.y = [mesh.y midY-patch.length/2-max_res/2*0.66 midY-patch.length/2+max_res/2*0.33 midY+patch.length/2+max_res/2*0.66 midY+patch.length/2-max_res/2*0.33]; + end +end +mesh.x = SmoothMeshLines( mesh.x, max_res, 1.4); % create a smooth mesh between specified mesh lines +mesh.y = SmoothMeshLines( mesh.y, max_res, 1.4 ); + +mesh = AddPML( mesh, [8 8 8 8 8 8] ); % add equidistant cells (air around the structure) +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create substrate +CSX = AddMaterial( CSX, 'substrate' ); +CSX = SetMaterialProperty( CSX, 'substrate', 'Epsilon', substrate.epsR, 'Kappa', substrate.kappa); +start = [-substrate.width/2 -substrate.length/2 0]; +stop = [ substrate.width/2 substrate.length/2 substrate.thickness]; +CSX = AddBox( CSX, 'substrate', 0, start, stop ); + +%% create ground (same size as substrate) +CSX = AddMetal( CSX, 'gnd' ); % create a perfect electric conductor (PEC) +start(3)=0; +stop(3) =0; +CSX = AddBox(CSX,'gnd',10,start,stop); +%% +CSX = AddMetal( CSX, 'patch' ); % create a perfect electric conductor (PEC) +number = 1; +for xn=1:array.xn + for yn=1:array.yn + + midX = (array.xn/2 - xn + 1/2) * array.x_spacing; + midY = (array.yn/2 - yn + 1/2) * array.y_spacing; + + % create patch + start = [midX-patch.width/2 midY-patch.length/2 substrate.thickness]; + stop = [midX+patch.width/2 midY+patch.length/2 substrate.thickness]; + CSX = AddBox(CSX,'patch',10,start,stop); + + % apply the excitation & resist as a current source + start = [midX+feed.pos-feed.width/2 midY-feed.width/2 0]; + stop = [midX+feed.pos+feed.width/2 midY+feed.width/2 substrate.thickness]; + [CSX] = AddLumpedPort(CSX, 5, number,feed.R, start, stop,[0 0 1],true); + number=number+1; + end +end + +%%nf2ff calc +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', -SimBox/2, SimBox/2); + +if (postprocessing_only==0) + %% write openEMS compatible xml-file + WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + + %% show the structure + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + + %% run openEMS + RunOpenEMS( Sim_Path, Sim_CSX, openEMS_opts ); +end + +%% postprocessing & do the plots +freq = linspace( max([1e9,f0-fc]), f0+fc, 501 ); +U = ReadUI( {'port_ut1','et'}, 'tmp/', freq ); % time domain/freq domain voltage +I = ReadUI( 'port_it1', 'tmp/', freq ); % time domain/freq domain current (half time step is corrected) + +% plot time domain voltage +figure +[ax,h1,h2] = plotyy( U.TD{1}.t/1e-9, U.TD{1}.val, U.TD{2}.t/1e-9, U.TD{2}.val ); +set( h1, 'Linewidth', 2 ); +set( h1, 'Color', [1 0 0] ); +set( h2, 'Linewidth', 2 ); +set( h2, 'Color', [0 0 0] ); +grid on +title( 'time domain voltage' ); +xlabel( 'time t / ns' ); +ylabel( ax(1), 'voltage ut1 / V' ); +ylabel( ax(2), 'voltage et / V' ); +% now make the y-axis symmetric to y=0 (align zeros of y1 and y2) +y1 = ylim(ax(1)); +y2 = ylim(ax(2)); +ylim( ax(1), [-max(abs(y1)) max(abs(y1))] ); +ylim( ax(2), [-max(abs(y2)) max(abs(y2))] ); + +% plot feed point impedance +figure +Zin = U.FD{1}.val ./ I.FD{1}.val; +plot( freq/1e6, real(Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Zin), 'r--', 'Linewidth', 2 ); +title( 'feed point impedance' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'impedance Z_{in} / Ohm' ); +legend( 'real', 'imag' ); + +% plot reflection coefficient S11 +figure +uf_inc = 0.5*(U.FD{1}.val + I.FD{1}.val * 50); +if_inc = 0.5*(I.FD{1}.val - U.FD{1}.val / 50); +uf_ref = U.FD{1}.val - uf_inc; +if_ref = I.FD{1}.val - if_inc; +s11 = uf_ref ./ uf_inc; +plot( freq/1e6, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +%% +number = 1; +P_in = 0; +for xn=1:array.xn + for yn=1:array.yn + + U = ReadUI( ['port_ut' int2str(number)], 'tmp/', freq ); % time domain/freq domain voltage + I = ReadUI( ['port_it' int2str(number)], 'tmp/', freq ); % time domain/freq domain current (half time step is corrected) + + P_in = P_in + 0.5*U.FD{1}.val .* conj( I.FD{1}.val ); + number=number+1; + end +end + + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +f_res_ind = find(s11==min(s11)); +f_res = freq(f_res_ind); + +% calculate the far field at phi=0 degrees and at phi=90 degrees +thetaRange = (0:2:359) - 180; +phiRange = [0 90]; +r = 1; % evaluate fields at radius r +disp( 'calculating far field at phi=[0 90] deg...' ); + +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180); + +Dlog=10*log10(nf2ff.Dmax); + +% display power and directivity +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(Dlog) ' dBi'] ); +disp( ['efficiency: nu_rad = ' num2str(100*nf2ff.Prad./real(P_in(f_res_ind))) ' %']); + +% display phi +figure +plotFFdB(nf2ff,'xaxis','theta','param',[1 2]); +drawnow + +if (draw_3d_pattern==0) + return +end + +%% calculate 3D pattern +phiRange = 0:3:360; +thetaRange = unique([0:0.5:15 10:3:180]); +disp( 'calculating 3D far field...' ); +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180, 'Verbose',2,'Outfile','nf2ff_3D.h5'); +figure +plotFF3D(nf2ff); + +%% visualize magnetic fields +% you will find vtk dump files in the simulation folder (tmp/) +% use paraview to visulaize them diff --git a/openEMS/matlab/examples/antennas/infDipol.m b/openEMS/matlab/examples/antennas/infDipol.m new file mode 100644 index 0000000..0a43bc8 --- /dev/null +++ b/openEMS/matlab/examples/antennas/infDipol.m @@ -0,0 +1,121 @@ +% +% infinitesimal dipole example +% + +close all +clear +clc + +postprocessing_only = 0; + +physical_constants + +% setup the simulation +drawingunit = 1e-6; % specify everything in um +Sim_Path = 'tmp'; +Sim_CSX = 'tmp.xml'; + +f_max = 1e9; +lambda = c0/f_max; + +% setup geometry values +dipole_length = lambda/50 /drawingunit; + + +dipole_orientation = 3; % 1,2,3: x,y,z + + +CSX = InitCSX(); + +% create an equidistant mesh +mesh.x = -dipole_length*10:dipole_length/2:dipole_length*10; +mesh.y = -dipole_length*10:dipole_length/2:dipole_length*10; +mesh.z = -dipole_length*10:dipole_length/2:dipole_length*10; + +% excitation +ex_vector = [0 0 0]; +ex_vector(dipole_orientation) = 1; +start = ex_vector * -dipole_length/2; +stop = ex_vector * dipole_length/2; +CSX = AddExcitation( CSX, 'infDipole', 1, ex_vector ); +% enlarge the box to be sure that one mesh line is covered by it +start = start - [0.1 0.1 0.1] * dipole_length/2; +stop = stop + [0.1 0.1 0.1] * dipole_length/2; +CSX = AddBox( CSX, 'infDipole', 1, start, stop ); + +% NFFF contour +start = [mesh.x(1) mesh.y(1) mesh.z(1) ]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end) ]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop); + +% add space for PML +mesh = AddPML( mesh, [8 8 8 8 8 8] ); +% define the mesh +CSX = DefineRectGrid( CSX, drawingunit, mesh ); + +if ~postprocessing_only + % setup FDTD parameters & excitation function + max_timesteps = 2000; + min_decrement = 1e-6; + FDTD = InitFDTD( 'NrTS', max_timesteps, 'EndCriteria', min_decrement, 'OverSampling',10 ); + FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); + BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; + FDTD = SetBoundaryCond( FDTD, BC ); + + % Write openEMS compatible xml-file + [~,~,~] = rmdir(Sim_Path,'s'); + [~,~,~] = mkdir(Sim_Path); + WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + + % take a view at the "structure" + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + + % define openEMS options and start simulation + openEMS_opts = ''; + RunOpenEMS( Sim_Path, Sim_CSX, openEMS_opts ); +end + +%% post processing +disp( ' ' ); +disp( ' ********************************************************** ' ); +disp( ' ' ); + +% calculate the far field at phi=0 degrees and at phi=90 degrees +thetaRange = 0:0.5:359; +disp( 'calculating far field at phi=[0 90] deg..' ); +nf2ff = CalcNF2FF( nf2ff, Sim_Path, f_max, thetaRange/180*pi, [0 pi/2], 'Mode', 1 ); +Prad = nf2ff.Prad; +Dmax = nf2ff.Dmax; + +theta_HPBW = interp1(nf2ff.E_norm{1}(find(thetaRange<90),1)/max(nf2ff.E_norm{1}(find(thetaRange<90),1)),thetaRange(find(thetaRange<90)),1/sqrt(2))*2; + +% display power and directivity +disp( ['radiated power: Prad = ' num2str(Prad)] ); +disp( ['directivity: Dmax = ' num2str(Dmax)] ); +disp( ['theta_HPBW = ' num2str(theta_HPBW) ' °']); + +% display polar plot for the e-field magnitude for phi = 0 & 90 deg +figure +polarFF(nf2ff,'xaxis','theta','param',[1 2]); + +%% calculate the far field at theta=90 degrees +phiRange = 0:2:359; +disp( 'calculating far field at theta=90 deg..' ); +nf2ff = CalcNF2FF( nf2ff, Sim_Path, f_max, 90/180*pi, phiRange/180*pi, 'Mode', 1 ); + +% display polar plot +figure +polarFF(nf2ff,'xaxis','phi','param',1); + +%% calculate 3D pattern +phiRange = 0:5:360; +thetaRange = 0:5:180; +disp( 'calculating 3D far field...' ); +nf2ff = CalcNF2FF( nf2ff, Sim_Path, f_max, thetaRange/180*pi, phiRange/180*pi, 'Mode', 1 ); +figure +plotFF3D(nf2ff) + +%% +E_far_normalized = nf2ff.E_norm{1} / max(nf2ff.E_norm{1}(:)); +DumpFF2VTK([Sim_Path '/FF_pattern.vtk'],E_far_normalized, thetaRange, phiRange); +disp(['view the farfield pattern "' Sim_Path '/FF_pattern.vtk" using paraview' ]); diff --git a/openEMS/matlab/examples/antennas/inverted_f.m b/openEMS/matlab/examples/antennas/inverted_f.m new file mode 100644 index 0000000..175f94c --- /dev/null +++ b/openEMS/matlab/examples/antennas/inverted_f.m @@ -0,0 +1,205 @@ +% +% EXAMPLE / antennas / inverted-f antenna (ifa) 2.4GHz +% +% This example demonstrates how to: +% - calculate the reflection coefficient of an ifa +% - calculate farfield of an ifa +% +% Tested with +% - Octave 3.7.5 +% - openEMS v0.0.30+ (git 10.07.2013) +% +% (C) 2013 Stefan Mahr <dac922@gmx.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% substrate.width +% _______________________________________________ __ substrate. +% | A ifa.l |\ __ thickness +% | |ifa.e __________________________ | | +% | | | ___ _________________| w2 | | +% | | ifa.h | | || | | +% |_V_____________|___|___||______________________| | +% | .w1 .wf\ | | +% | |.fp| \ | | +% | | feed point | | +% | | | | substrate.length +% |<- substrate.width/2 ->| | | +% | | | +% |_______________________________________________| | +% \_______________________________________________\| +% +% Note: It's not checked whether your settings make sense, so check +% graphical output carefully. +% +substrate.width = 80; % width of substrate +substrate.length = 80; % length of substrate +substrate.thickness = 1.5; % thickness of substrate +substrate.cells = 4; % use 4 cells for meshing substrate + +ifa.h = 8; % height of short circuit stub +ifa.l = 22.5; % length of radiating element +ifa.w1 = 4; % width of short circuit stub +ifa.w2 = 2.5; % width of radiating element +ifa.wf = 1; % width of feed element +ifa.fp = 4; % position of feed element relative to short + % circuit stub +ifa.e = 10; % distance to edge + + +% substrate setup +substrate.epsR = 4.3; +substrate.kappa = 1e-3 * 2*pi*2.45e9 * EPS0*substrate.epsR; + +%setup feeding +feed.R = 50; %feed resistance + +%open AppCSXCAD and show ifa +show = 1; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% size of the simulation box +SimBox = [substrate.width*2 substrate.length*2 150]; + +%% setup FDTD parameter & excitation function +f0 = 2.5e9; % center frequency +fc = 1e9; % 20 dB corner frequency + +FDTD = InitFDTD('NrTS', 60000 ); +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; % boundary conditions +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +CSX = InitCSX(); + +%initialize the mesh with the "air-box" dimensions +mesh.x = [-SimBox(1)/2 SimBox(1)/2]; +mesh.y = [-SimBox(2)/2 SimBox(2)/2]; +mesh.z = [-SimBox(3)/2 SimBox(3)/2]; + +%% create substrate +CSX = AddMaterial( CSX, 'substrate'); +CSX = SetMaterialProperty( CSX, 'substrate', 'Epsilon',substrate.epsR, 'Kappa', substrate.kappa); +start = [-substrate.width/2 -substrate.length/2 0]; +stop = [ substrate.width/2 substrate.length/2 substrate.thickness]; +CSX = AddBox( CSX, 'substrate', 1, start, stop ); +% add extra cells to discretize the substrate thickness +mesh.z = [linspace(0,substrate.thickness,substrate.cells+1) mesh.z]; + +%% create ground plane +CSX = AddMetal( CSX, 'groundplane' ); % create a perfect electric conductor (PEC) +start = [-substrate.width/2 -substrate.length/2 substrate.thickness]; +stop = [ substrate.width/2 substrate.length/2-ifa.e substrate.thickness]; +CSX = AddBox(CSX, 'groundplane', 10, start,stop); + +%% create ifa +CSX = AddMetal( CSX, 'ifa' ); % create a perfect electric conductor (PEC) +tl = [0,substrate.length/2-ifa.e,substrate.thickness]; % translate +start = [0 0.5 0] + tl; +stop = start + [ifa.wf ifa.h-0.5 0]; +CSX = AddBox( CSX, 'ifa', 10, start, stop); % feed element +start = [-ifa.fp 0 0] + tl; +stop = start + [-ifa.w1 ifa.h 0]; +CSX = AddBox( CSX, 'ifa', 10, start, stop); % short circuit stub +start = [(-ifa.fp-ifa.w1) ifa.h 0] + tl; +stop = start + [ifa.l -ifa.w2 0]; +CSX = AddBox( CSX, 'ifa', 10, start, stop); % radiating element + +ifa_mesh = DetectEdges(CSX, [], 'SetProperty','ifa'); +mesh.x = [mesh.x SmoothMeshLines(ifa_mesh.x, 0.5)]; +mesh.y = [mesh.y SmoothMeshLines(ifa_mesh.y, 0.5)]; + +%% apply the excitation & resist as a current source +start = [0 0 0] + tl; +stop = start + [ifa.wf 0.5 0]; +[CSX port] = AddLumpedPort(CSX, 5 ,1 ,feed.R, start, stop, [0 1 0], true); + +%% finalize the mesh +% generate a smooth mesh with max. cell size: lambda_min / 20 +mesh = DetectEdges(CSX, mesh); +mesh = SmoothMesh(mesh, c0 / (f0+fc) / unit / 20); +CSX = DefineRectGrid(CSX, unit, mesh); + +%% add a nf2ff calc box; size is 3 cells away from MUR boundary condition +start = [mesh.x(4) mesh.y(4) mesh.z(4)]; +stop = [mesh.x(end-3) mesh.y(end-3) mesh.z(end-3)]; +[CSX nf2ff] = CreateNF2FFBox(CSX, 'nf2ff', start, stop); + +%% prepare simulation folder +Sim_Path = 'tmp_IFA'; +Sim_CSX = 'IFA.xml'; + +try confirm_recursive_rmdir(false,'local'); end + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +%% show the structure +if (show == 1) + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +end + + +%% run openEMS +RunOpenEMS( Sim_Path, Sim_CSX); %RunOpenEMS( Sim_Path, Sim_CSX, '--debug-PEC -v'); + +%% postprocessing & do the plots +freq = linspace( max([1e9,f0-fc]), f0+fc, 501 ); +port = calcPort(port, Sim_Path, freq); + +Zin = port.uf.tot ./ port.if.tot; +s11 = port.uf.ref ./ port.uf.inc; +P_in = real(0.5 * port.uf.tot .* conj( port.if.tot )); % antenna feed power + +% plot feed point impedance +figure +plot( freq/1e6, real(Zin), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Zin), 'r--', 'Linewidth', 2 ); +title( 'feed point impedance' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'impedance Z_{in} / Ohm' ); +legend( 'real', 'imag' ); + +% plot reflection coefficient S11 +figure +plot( freq/1e6, 20*log10(abs(s11)), 'k-', 'Linewidth', 2 ); +grid on +title( 'reflection coefficient S_{11}' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'reflection coefficient |S_{11}|' ); + +drawnow + +%% NFFF contour plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%find resonance frequncy from s11 +f_res_ind = find(s11==min(s11)); +f_res = freq(f_res_ind); + +%% +disp( 'calculating 3D far field pattern and dumping to vtk (use Paraview to visualize)...' ); +thetaRange = (0:2:180); +phiRange = (0:2:360) - 180; +nf2ff = CalcNF2FF(nf2ff, Sim_Path, f_res, thetaRange*pi/180, phiRange*pi/180,'Verbose',1,'Outfile','3D_Pattern.h5'); + +plotFF3D(nf2ff) + +% display power and directivity +disp( ['radiated power: Prad = ' num2str(nf2ff.Prad) ' Watt']); +disp( ['directivity: Dmax = ' num2str(nf2ff.Dmax) ' (' num2str(10*log10(nf2ff.Dmax)) ' dBi)'] ); +disp( ['efficiency: nu_rad = ' num2str(100*nf2ff.Prad./real(P_in(f_res_ind))) ' %']); + +E_far_normalized = nf2ff.E_norm{1} / max(nf2ff.E_norm{1}(:)) * nf2ff.Dmax; +DumpFF2VTK([Sim_Path '/3D_Pattern.vtk'],E_far_normalized,thetaRange,phiRange,1e-3); diff --git a/openEMS/matlab/examples/optimizer/optimizer_asco.m b/openEMS/matlab/examples/optimizer/optimizer_asco.m new file mode 100644 index 0000000..e405653 --- /dev/null +++ b/openEMS/matlab/examples/optimizer/optimizer_asco.m @@ -0,0 +1,36 @@ +% +% asco optimizer example -- optimize the turn number of a coil +% +% You need asco from http://asco.sf.net +% This is the main script. +% - optimizer_simfun.m starts the simulator with a parameter set from +% asco +% +% The goal is evaluated inside optimizer_simfun() to get as close to 2 uH. + +% clear +clear +close all +clc + +% setup the parameters +params = []; +params(end+1).name = 'turns'; +params(end).range = [1 30]; +params(end).value = 4; +params(end).step = 1; +params(end).active = 1; % this parameter is to be optimized + +% setup the simulation function +folder = fileparts( mfilename('fullpath') ); +options.simfun = [folder '/optimizer_simfun.m']; + +% additional options +% options.octave_exe = 'octave'; % must be newer than 3.2.4 (3.3.54 works) +options.clean = 1; + +% start the optimization +[params_opt,result] = optimize( 'opttmp', params, options, 'asco' ); + +% display best value +disp( ['ASCO found the optimum turn number: ' num2str(params_opt(1).value) ' result: ' num2str(result)] ); diff --git a/openEMS/matlab/examples/optimizer/optimizer_simfun.m b/openEMS/matlab/examples/optimizer/optimizer_simfun.m new file mode 100644 index 0000000..14152de --- /dev/null +++ b/openEMS/matlab/examples/optimizer/optimizer_simfun.m @@ -0,0 +1,134 @@ +function result = optimizer_simfun( folder, params ) +% +% simulation function +% +% the variable params contains the simulation parameters + +disp( [mfilename ': SIMULATING...'] ); + +if nargin == 0 + % visualize the structure if called without parameters + folder = 'tmp'; + params.turns = 10; +end + +oldpwd = pwd; +[a,a,a] = mkdir( folder ); +cd( folder ); + +% create the structure +f_max = 50e6; +structure( params, 'tmp.xml', f_max ); + +if nargin == 0 + % visualize the structure + CSXGeomPlot('tmp.xml'); + return; +end + +% start simulation +RunOpenEMS( '.', 'tmp.xml', '--engine=fastest' ); + +% postprocess the results +L = postproc( 'tmp.xml', f_max ); +disp( ['DONE. L = ' num2str(L(1)/1e-6) ' uH'] ); + +% calculate result +goal = 2e-6; % specify the goal: 2 uH +result = abs(goal - L(1)); % costs must not be negative + +% restore curent folder +cd( oldpwd ); + + + + +% ------------------------------------------------------------------------- +% ------------------------------------------------------------------------- + +function CSX = structure( params, filename, f_max ) +% CSX = structure( params, filename ) + +unit = 1e-3; % specify length in mm +lambda = 3e8/f_max; +resolution = lambda/15/unit; +mesh_size = 1.5; +radius = 10; +turns = params.turns; +height = 4 * turns; +feed_length = 10; + +CSX = InitCSX(); + +%% create coil +p1 = create_coil( radius, height, turns ); +p = p1(:,end) + [feed_length;0;0]; +p1 = [p1 p]; +p = p1(:,1) + [feed_length;0;0]; +p1 = [p p1]; +CSX = AddMetal(CSX,'PEC1'); +CSX = AddCurve(CSX, 'PEC1', 0, p1); + +%% create mesh +extraspace = 5*radius; +mesh.x = linspace(-radius,radius,ceil(2*radius/mesh_size)); +mesh.x = [mesh.x mesh.x(1)-extraspace mesh.x(end)+extraspace]; +mesh.x = [mesh.x p1(1,1) p1(1,1)-mesh_size p1(1,1)+mesh_size]; +mesh.x = SmoothMeshLines2( mesh.x, resolution ); +mesh.y = linspace(-radius,radius,ceil(2*radius/mesh_size)); +mesh.y = [mesh.y mesh.y(1)-extraspace mesh.y(end)+extraspace]; +mesh.y = SmoothMeshLines2( mesh.y, resolution ); +mesh.z = linspace(0,height,ceil(height/mesh_size)); +mesh.z = [mesh.z mesh.z(1)-extraspace mesh.z(end)+extraspace]; +% mesh.z = [mesh.z p1(3,1) p1(3,1)-mesh_size p1(3,1)+mesh_size]; +mesh.z = SmoothMeshLines2( mesh.z, resolution ); +CSX = DefineRectGrid(CSX, unit, mesh); + + +%% create port +[CSX,port] = AddCurvePort( CSX, 10, 1, 50, p1(:,1), p1(:,end), 'excite' ); + +if nargin > 1 + max_timesteps = 100000; + min_decrement = 1e-5; + FDTD = InitFDTD( max_timesteps, min_decrement ); + FDTD = SetGaussExcite( FDTD, 0, f_max ); + BC = {'PEC' 'PEC' 'PEC' 'PEC' 'PMC' 'PMC'}; + FDTD = SetBoundaryCond( FDTD, BC ); + + WriteOpenEMS( filename, FDTD, CSX ); +end + + +function p = create_coil(coil_rad,coil_length,coil_turns,coil_res,winding_direction,direction,offset,angle_offset) +if nargin < 8, angle_offset = 0; end +if nargin < 7, offset = [0; 0; 0]; end +if nargin < 6, direction = +1; end +if nargin < 5, winding_direction = +1; end +if nargin < 4, coil_res = 30; end +dt = 1/coil_res; +height = 0; + +p = []; +while abs(height) < coil_length + angle = height / (coil_length/coil_turns) * 2*pi; + p(1,end+1) = coil_rad * cos(angle*winding_direction+angle_offset); + p(2,end) = coil_rad * sin(angle*winding_direction+angle_offset); + p(3,end) = height * direction; + p(:,end) = p(:,end) + offset; + height = height + coil_length/coil_turns * dt; +end + + + +function L = postproc( filename, f_max ) +freq = linspace(0,f_max,201); +freq(1) = []; % delete DC component + +folder = fileparts(filename); +U = ReadUI( 'port_ut1', folder, freq ); +I = ReadUI( 'port_it1', folder, freq ); +Z = U.FD{1}.val ./ I.FD{1}.val; + +L = imag(Z) ./ (2*pi*freq); +L = reshape( L, 1, [] ); % row vector diff --git a/openEMS/matlab/examples/other/Helix.m b/openEMS/matlab/examples/other/Helix.m new file mode 100644 index 0000000..18e97c9 --- /dev/null +++ b/openEMS/matlab/examples/other/Helix.m @@ -0,0 +1,154 @@ +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +feed_length=10; +wire_rad = sqrt(1.4/pi); +mesh_size = wire_rad; +coil_rad = 10; +coil_length = 50; +coil_turns = 8; +coil_res = 10; +port_length = mesh_size; %coil_length/2; +port_resist = 1000; + +f_max = 100e6; +f_excite = 300e6; + +%% define openEMS options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --debug-boxes']; +% openEMS_opts = [openEMS_opts ' --debug-operator']; + +openEMS_opts = [openEMS_opts ' --disable-dumps --engine=fastest']; +% openEMS_opts = [openEMS_opts ' --engine=sse-compressed']; + +Sim_Path = 'tmp'; +Sim_CSX = 'helix.xml'; + +[status, message, messageid] = rmdir(Sim_Path,'s'); +[status,message,messageid] = mkdir(Sim_Path); + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(30000,1e-6); +FDTD = SetGaussExcite(FDTD,f_excite/2,f_excite/2); +BC = [1 1 1 1 1 1]; +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +add_Lines = mesh_size * 1.5.^(1:10); +add_Lines = add_Lines(find(add_Lines<(3e8/f_excite)/10*1e3)); + +CSX = InitCSX(); +mesh.x = -coil_rad-mesh_size : mesh_size : coil_rad+mesh_size+feed_length; +mesh.x = [mesh.x(1)-add_Lines mesh.x mesh.x(end)+add_Lines ]; +mesh.y = -coil_rad-mesh_size : mesh_size : coil_rad+mesh_size; +mesh.y = [mesh.y(1)-add_Lines mesh.y mesh.y(end)+add_Lines ]; +mesh.z = -mesh_size : mesh_size : coil_length+mesh_size; +mesh.z = [mesh.z(1)-add_Lines mesh.z mesh.z(end)+add_Lines ]; +CSX = DefineRectGrid(CSX, 1e-3,mesh); + +%% build/define helix %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddMaterial(CSX,'copper'); +CSX = SetMaterialProperty(CSX,'copper','Kappa',56e6); + +dt = 1.0/coil_res; +height=0; +wire.Vertex = {}; +p(1,1) = coil_rad + feed_length; +p(2,1) = 0; +p(3,1) = 0.5*(coil_length-port_length); +p(1,2) = coil_rad + feed_length; +p(2,2) = 0; +p(3,2) = 0; +count=2; +for n=0:coil_turns-1 + for m=0:coil_res + count = count + 1; + p(1,count) = coil_rad * cos(2*pi*dt*m); + p(2,count) = coil_rad * sin(2*pi*dt*m); + p(3,count) = height + coil_length/coil_turns * dt*m; + end + height = height + coil_length/coil_turns; +end +p(1,count+1) = coil_rad + feed_length; +p(2,count+1) = 0; +p(3,count+1) = coil_length; +p(1,count+2) = coil_rad + feed_length; +p(2,count+2) = 0; +p(3,count+2) = 0.5*(coil_length+port_length); +CSX = AddWire(CSX, 'copper', 0, p, wire_rad); + +%% apply the excitation & resist as a current source%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddMaterial(CSX,'resist'); +kappa = port_length/port_resist/wire_rad^2/pi/1e-3; +CSX = SetMaterialProperty(CSX,'resist','Kappa',kappa); + +start=[coil_rad+feed_length 0 (coil_length-port_length)/2]; +stop=[coil_rad+feed_length 0 (coil_length+port_length)/2]; +%start(3)=(coil_length-port_length)/2;stop(3)=(coil_length+port_length)/2; +CSX = AddCylinder(CSX,'resist',5 ,start,stop,wire_rad); + +CSX = AddExcitation(CSX,'excite',0,[0 0 1]); +CSX = AddCylinder(CSX,'excite', 0 ,start,stop,wire_rad); + +%% define voltage calc boxes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%voltage calc +CSX = AddProbe(CSX,'ut1',0); +CSX = AddBox(CSX,'ut1', 0 ,stop,start); + +%current calc +CSX = AddProbe(CSX,'it1',1); +start(3) = coil_length/2+mesh_size;stop(3) = coil_length/2+mesh_size; +start(1) = start(1)-2;start(2) = start(2)-2; +stop(1) = stop(1)+2;stop(2) = stop(2)+2; +CSX = AddBox(CSX,'it1', 0 ,start,stop); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et_'); +start = [mesh.x(1) , 0 , mesh.z(1)]; +stop = [mesh.x(end) , 0 , mesh.z(end)]; +CSX = AddBox(CSX,'Et_',0 , start,stop); + +CSX = AddDump(CSX,'Ht_','DumpType',1); +start = [mesh.x(1) , 0 , mesh.z(1)]; +stop = [mesh.x(end) , 0 , mesh.z(end)]; +CSX = AddBox(CSX,'Ht_',0 , start,stop); + +%% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +%% run openEMS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts); + +%% postproc & do the plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +U = ReadUI('ut1','tmp/'); +I = ReadUI('it1','tmp/'); + +Z = U.FD{1}.val./I.FD{1}.val; +f = U.FD{1}.f; +L = imag(Z)./(f*2*pi); +R = real(Z); +ind = find(f<f_max); + +subplot(2,1,1); +plot(f(ind)*1e-6,L(ind)*1e9,'Linewidth',2); +xlabel('frequency (MHz)'); +ylabel('coil inductance (nH)'); +grid on; +subplot(2,1,2); +plot(f(ind)*1e-6,R(ind),'Linewidth',2); +hold on +plot(f(ind)*1e-6,imag(Z(ind)),'r','Linewidth',2); +xlabel('frequency (MHz)'); +ylabel('resistance (Ohm)'); +grid on; +legend( {'real','imaginary'}, 'location', 'northwest' ) + +figure +plot(U.TD{1}.t/1e-6,U.TD{1}.val,'Linewidth',2); +xlabel('time (us)'); +ylabel('amplitude (V)'); +grid on; diff --git a/openEMS/matlab/examples/other/LumpedElement.m b/openEMS/matlab/examples/other/LumpedElement.m new file mode 100644 index 0000000..d44094c --- /dev/null +++ b/openEMS/matlab/examples/other/LumpedElement.m @@ -0,0 +1,158 @@ +% +% EXAMPLE / other / lumped elements +% +% This example demonstrates how to: +% - use lumped elements +% +% +% Tested with +% - Matlab 2009b +% - openEMS v0.0.21-3 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +f_max = 100e6; +f_excite = 300e6; +SimBox = 100; +mesh_size = 2; + +Lumped.R = 1000; +Lumped.C = 10e-12; + +% the parasitice inductance of the feeding has to be deduced with a R=0 +% simulation +parasitic_L = 63e-9; + +%% define openEMS options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --debug-boxes']; +% openEMS_opts = [openEMS_opts ' --debug-operator']; + +Sim_Path = 'tmp'; +Sim_CSX = 'lumped.xml'; + +[status, message, messageid] = rmdir(Sim_Path,'s'); +[status,message,messageid] = mkdir(Sim_Path); + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(30000,1e-6); +FDTD = SetGaussExcite(FDTD,f_excite/2,f_excite/2); +BC = [1 1 1 1 1 1]; +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = SmoothMeshLines([-SimBox/2,+SimBox/2],mesh_size); +mesh.y = SmoothMeshLines([-SimBox/2,+SimBox/2],mesh_size); +mesh.z = SmoothMeshLines([-SimBox/2,+SimBox/2],mesh_size); +CSX = DefineRectGrid(CSX, 1e-3,mesh); + + +%% create structure +% insert curve port +start = [ 10 -10 0]; +stop = [ 10 10 0]; +CSX = AddCurvePort(CSX,0,1,100,start,stop,'excite'); + +% insert lumped element +CSX = AddLumpedElement( CSX, 'Capacitor', 1, 'C', Lumped.C, 'R', Lumped.R); +start = [ -14 -4 -4]; +stop = [ -6 4 4]; +CSX = AddBox( CSX, 'Capacitor', 0, start, stop ); + +% insert feeding wire +CSX = AddMetal(CSX,'metal'); +%first point +points(1,1) = -10; +points(2,1) = 4; +points(3,1) = 0; +%second point +points(1,2) = -10; +points(2,2) = 15; +points(3,2) = 0; +%3 point +points(1,end+1) = 10; +points(2,end) = 15; +points(3,end) = 0; +%4 point +points(1,end+1) = 10; +points(2,end) = 10; +points(3,end) = 0; +CSX = AddCurve(CSX,'metal', 10, points); + +points(2,:) = -1*points(2,:); +CSX = AddCurve(CSX,'metal', 10, points); + +%% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +% CSXGeomPlot([Sim_Path '/' Sim_CSX]); + +%% run openEMS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts); + +%% postproc & do the plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +f = linspace(1e6,f_max,1001); +w = 2*pi*f; +% read currents and voltages +U = ReadUI('port_ut1','tmp/',f); +I = ReadUI('port_it1','tmp/',f); + +% calculate analytic impedance +if (Lumped.R>=0) + Z_a = Lumped.R*(1-1i*w*Lumped.C*Lumped.R)./(1+(w*Lumped.C*Lumped.R).^2); +else + Z_a = -1i./(w*Lumped.C); +end + +% calculate numerical impedance +Z = U.FD{1}.val./I.FD{1}.val; + +% remove parasitic feeding effects +Z = Z - 1i*w*parasitic_L; + +L = imag(Z)./w; +C = -1./(w.*imag(Z)); +C(find(C<0)) = nan; +L(find(L<0)) = nan; +R = real(Z); + +subplot(2,1,1); +plot(f*1e-6,C*1e12,'Linewidth',2); +xlabel('frequency (MHz)'); +ylabel('capacitance (pF)'); +grid on; +subplot(2,1,2); +plot(f*1e-6,L*1e9,'Linewidth',2); +xlabel('frequency (MHz)'); +ylabel('inductance (nH)'); +grid on; + +figure(); +plot(f*1e-6,R,'Linewidth',2); +hold on +plot(f*1e-6,imag(Z),'r--','Linewidth',2); + +plot(f*1e-6,real(Z_a),'g-.','Linewidth',1); +plot(f*1e-6,imag(Z_a),'m--','Linewidth',1); + +xlabel('frequency (MHz)'); +ylabel('resistance (Ohm)'); +grid on; +legend( '\Re\{Z\}','\Im\{Z\}','\Re\{Z_{analytisch}\}','\Im\{Z_{analytisch}\}', 'location', 'northeast' ) + +figure(); +errorR = (R-real(Z_a))./R*100; +errorX = (imag(Z)-imag(Z_a))./imag(Z)*100; +plot(f*1e-6,errorR,'Linewidth',2); +hold on +grid on; +plot(f*1e-6,errorX,'r--','Linewidth',2); +xlabel('frequency (MHz)'); +ylabel('error (%)'); diff --git a/openEMS/matlab/examples/other/Metamaterial_PlaneWave_Drude.m b/openEMS/matlab/examples/other/Metamaterial_PlaneWave_Drude.m new file mode 100644 index 0000000..db72b3e --- /dev/null +++ b/openEMS/matlab/examples/other/Metamaterial_PlaneWave_Drude.m @@ -0,0 +1,147 @@ +%%%%%%%%%%%%%%%%%%%%%%% +% example demonstrating double drude meta-material +% +% tested with openEMS v0.0.28 +% +% author: Thorsten Liebig @ 2010,2012 +%%%%%%%%%%%%%%%%%%%%%%% + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +postproc_only = 0; %set to 1 if the simulation is already done + +Settings = []; +Settings.LogFile = 'openEMS.log'; + +pic_size = round([1400 1400/4]); %define the animation picture size + +%simulation domain setup (in mm) +length = 500; +width = 10; +mesh_res = 0.5; % mesh resolution +height = 3*mesh_res; % hight is ony 3 lines with PEC (top/bottom) --> quasi 2D + +%FDTD setup +f0 = 5e9; %center frequency +f_BW = f0/sqrt(2); %bandwidth +MTM.eps_R = 1; +MTM.mue_R = 1; +MTM.f0 = f0; %plasma frequency of the drude material +MTM.relaxTime = 5e-9; %relaxation time (smaller number results in greater losses, set to 0 to disable) +MTM.length = 250; %length of the metamaterial +N_TS = 5e4; %number of timesteps +endCriteria = 1e-5; %stop simulation if signal is at -50dB + +%constants +physical_constants + +%% define openEMS options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +openEMS_opts = '-vvv'; + +Sim_Path = 'MTM_PW_Drude'; +Sim_CSX = 'MTM_PW_Drude.xml'; + +if (postproc_only==0) + + if (exist(Sim_Path,'dir')) + rmdir(Sim_Path,'s'); + end + mkdir(Sim_Path); + + %% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + FDTD = InitFDTD(N_TS,endCriteria,'OverSampling',10); + FDTD = SetGaussExcite(FDTD,0,2*f0); + BC = [1 1 0 0 2 2]; + FDTD = SetBoundaryCond(FDTD,BC); + + %% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + CSX = InitCSX(); + mesh.x = -width/2 : mesh_res : width/2; + mesh.y = -height/2 : mesh_res : height/2; + mesh.z = -length/2 : mesh_res : length/2; + CSX = DefineRectGrid(CSX, 1e-3,mesh); + + %% apply the plane wave excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + start=[-width/2 -height/2 ,mesh.z(3)]; + stop=[width/2 height/2 mesh.z(3)]; + CSX = AddExcitation(CSX,'excite',0,[0 1 0]); % excite E_y + CSX = AddBox(CSX,'excite',0 ,start,stop); + + %% apply drude material %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + CSX = AddLorentzMaterial(CSX,'drude'); + CSX = SetMaterialProperty(CSX,'drude','Epsilon',MTM.eps_R,'EpsilonPlasmaFrequency',MTM.f0,'EpsilonRelaxTime',MTM.relaxTime); + CSX = SetMaterialProperty(CSX,'drude','Mue',MTM.mue_R,'MuePlasmaFrequency',MTM.f0,'MueRelaxTime',MTM.relaxTime); + start=[mesh.x(1) mesh.y(1) -MTM.length/2]; + stop =[mesh.x(end) mesh.y(end) MTM.length/2]; + CSX = AddBox(CSX,'drude', 10 ,start,stop); + + %% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + CSX = AddDump(CSX,'Et','FileType',1,'SubSampling','10,10,1'); + start = [mesh.x(2) ,0 , mesh.z(1)]; + stop = [mesh.x(end-1) , 0 , mesh.z(end)]; + CSX = AddBox(CSX,'Et',0 , start,stop); + + %% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + + %% run openEMS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts, Settings); + +end + +%% plot the drude type material dependency +f = linspace(0.1*f0,2*f0,501); +w = 2*pi*f; +epsr = MTM.eps_R * (1 - (2*pi*MTM.f0)^2./( w.^2 - 1j*w./MTM.relaxTime )); +muer = MTM.mue_R * (1 - (2*pi*MTM.f0)^2./( w.^2 - 1j*w./MTM.relaxTime )); +plot(f,real(epsr),'Linewidth',2); +hold on +grid on +plot(f,imag(epsr),'r--','Linewidth',2); +plot(f,real(muer),'c-.','Linewidth',2); +plot(f,imag(muer),'m-.','Linewidth',2); +ylim([-10 MTM.eps_R]) +% l=legend('\Re \epsilon_r','\Im \epsilon_r','\Re \mue_r','\Im \mue_r'); +l=legend('$\Re\{\varepsilon_r\}$','$\Im\{\varepsilon_r\}$','$\Re\{\mu_r\}$','$\Im\{\mu_r\}$'); +set(l,'Interpreter','latex','Fontsize',12) + +%% plot E-fields +freq = [f0/sqrt(2) f0 f0*sqrt(2)]; +field = ReadHDF5FieldData([Sim_Path '/Et.h5']); +mesh_h5 = ReadHDF5Mesh([Sim_Path '/Et.h5']); + +ET = ReadUI('et',Sim_Path); +ef = DFT_time2freq(ET.TD{1}.t,ET.TD{1}.val,freq); + +field_FD = GetField_TD2FD(field, freq); + +mesh.x = linspace(-500,500,numel(mesh_h5.lines{1})); %make animation wider... +mesh.y = mesh_h5.lines{2}; +mesh.z = mesh_h5.lines{3}; + +[X Z] = meshgrid(mesh.x,mesh.z); +X = X'; +Z = Z'; + +for n=1:numel(field_FD.FD.values) + Ec{n} = squeeze(field_FD.FD.values{n}/ef(n)); +end + +%% +figure('Position',[10 100 pic_size(1) pic_size(2)]); +phase = linspace(0,2*pi,21); +disp('press CTRL+C to stop animation'); +while (1) + for ph = phase(1:end-1) + for n=1:numel(Ec) + subplot(1,numel(Ec),n) + E = real(Ec{n}.*exp(1j*ph)); + surf(X,Z,E(:,:,2)); + title(['f_0 = ' num2str(freq(n)*1e-9) ' GHz']) + end + pause(0.1); + end +end diff --git a/openEMS/matlab/examples/other/PML_reflection_analysis.m b/openEMS/matlab/examples/other/PML_reflection_analysis.m new file mode 100644 index 0000000..f243a83 --- /dev/null +++ b/openEMS/matlab/examples/other/PML_reflection_analysis.m @@ -0,0 +1,196 @@ +% +% fake-PML parallel plate waveguide example +% +% this example analyzes the reflection coefficient of a vacuum-pml +% interface +% + +% +% currently this example uses a normal material with a certain conductivity +% profile and not a pml +% + +close all +% clear +clc + +physical_constants + + +postprocessing_only = 0; + + + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +drawingunit = 1e-6; % specify everything in um + +length = 10000; +epr = 1; + +mesh_res = [200 200 200]; +max_timesteps = 100000; +min_decrement = 1e-6; +f_max = 8e9; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD( max_timesteps, min_decrement ); +FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); +BC = [0 0 1 1 0 0]; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% mesh grading +N_pml = 8; +pml_delta = cumsum(mesh_res(1) * 1.0 .^ (1:N_pml)); +% pml_delta = cumsum([200 200 200 200 200]); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = 0 : mesh_res(1) : length; +mesh.x = [mesh.x(1) - fliplr(pml_delta), mesh.x]; +mesh.y = -2*mesh_res(2) : mesh_res(2) : 2*mesh_res(2); +mesh.z = 0 : mesh_res(3) : 4*mesh_res(3); +CSX = DefineRectGrid( CSX, drawingunit, mesh ); + +%% fake pml %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g = 2; % 2..3 +R0 = 1e-6; % requested analytical reflection coefficient +Zm = sqrt(MUE0/(EPS0*epr)); % calculate reflection for substrate/pml interface +delta = pml_delta(end) * drawingunit; +deltal = mean(diff(pml_delta)) * drawingunit; +kappa0 = -log(R0)*log(g)/( 2*Zm*deltal*(g^(delta/deltal)-1) ); + +% kappa0 = 1.05; +CSX = AddMaterial( CSX, 'pml_xmin' ); +CSX = SetMaterialProperty( CSX, 'pml_xmin', 'Epsilon', epr ); +CSX = SetMaterialProperty( CSX, 'pml_xmin', 'Kappa', kappa0 ); +CSX = SetMaterialProperty( CSX, 'pml_xmin', 'Sigma', kappa0 * MUE0/(EPS0*epr) ); +CSX = SetMaterialWeight( CSX, 'pml_xmin', 'Kappa', [num2str(g) '^((abs(x-100)-' num2str(abs(mesh.x(N_pml+1))) ')/(' num2str(deltal) '/' num2str(drawingunit) '))'] ); % g^(rho/deltal)*kappa0 +CSX = SetMaterialWeight( CSX, 'pml_xmin', 'Sigma', [num2str(g) '^((abs(x-100)-' num2str(abs(mesh.x(N_pml+1))) ')/(' num2str(deltal) '/' num2str(drawingunit) '))'] ); +start = [mesh.x(1), mesh.y(1), mesh.z(1)]; +stop = [100, mesh.y(end), mesh.z(end)]; +CSX = AddBox( CSX, 'pml_xmin', 1, start, stop ); + +figure +x = [-fliplr(pml_delta) 50]; +plot( x, kappa0 * g.^((abs(x-50)-abs(mesh.x(N_pml+1)))./(deltal/drawingunit)) ,'x-'); +xlabel( 'x / m' ); +ylabel( 'kappa' ); +figure +title( 'conductivity profile inside the material' ); + +%% excitation +CSX = AddExcitation( CSX, 'excitation1', 0, [0 0 1]); +idx = interp1( mesh.x, 1:numel(mesh.x), length*2/3, 'nearest' ); +start = [mesh.x(idx), mesh.y(1), mesh.z(1)]; +stop = [mesh.x(idx), mesh.y(end), mesh.z(end)]; +CSX = AddBox( CSX, 'excitation1', 0, start, stop ); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump( CSX, 'Et_', 'DumpMode', 2 ); +start = [mesh.x(1), mesh.y(1), mesh.z(3)]; +stop = [mesh.x(end), mesh.y(end), mesh.z(3)]; +CSX = AddBox( CSX, 'Et_', 0, start, stop ); + +CSX = AddDump( CSX, 'Ht_', 'DumpType', 1, 'DumpMode', 2 ); +CSX = AddBox( CSX, 'Ht_', 0, start, stop ); + +% hdf5 file +CSX = AddDump( CSX, 'E', 'DumpType', 0, 'DumpMode', 2, 'FileType', 1 ); +idx = interp1( mesh.x, 1:numel(mesh.x), length*1/3, 'nearest' ); +start = [mesh.x(idx), mesh.y(3), mesh.z(1)]; +stop = [mesh.x(idx), mesh.y(3), mesh.z(end)]; +CSX = AddBox( CSX, 'E', 0, start, stop ); + +% hdf5 file +CSX = AddDump( CSX, 'H', 'DumpType', 1, 'DumpMode', 2, 'FileType', 1 ); +idx = interp1( mesh.x, 1:numel(mesh.x), length*1/3, 'nearest' ); +start = [mesh.x(idx), mesh.y(1), mesh.z(3)]; +stop = [mesh.x(idx), mesh.y(end), mesh.z(3)]; +CSX = AddBox( CSX, 'H', 0, start, stop ); + +%% define openEMS options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --debug-operator']; +% openEMS_opts = [openEMS_opts ' --debug-boxes']; +% openEMS_opts = [openEMS_opts ' --showProbeDiscretization']; +openEMS_opts = [openEMS_opts ' --engine=fastest']; + +Sim_Path = 'tmp'; +Sim_CSX = 'PML_reflection_analysis.xml'; + +if ~postprocessing_only + [~,~,~] = rmdir(Sim_Path,'s'); + [~,~,~] = mkdir(Sim_Path); +end + +%% Write openEMS compatible xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +%% cd to working dir and run openEMS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if ~postprocessing_only + savePath = pwd; + cd(Sim_Path); %cd to working dir + args = [Sim_CSX ' ' openEMS_opts]; + invoke_openEMS(args); + cd(savePath) +end + + +%% postproc & do the plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% E_coords = ReadHDF5Mesh( [Sim_Path '/E.h5'] ); +% H_coords = ReadHDF5Mesh( [Sim_Path '/H.h5'] ); +E = ReadHDF5FieldData( [Sim_Path '/E.h5'] ); +H = ReadHDF5FieldData( [Sim_Path '/H.h5'] ); +E_val = cellfun( @(x) squeeze(x(1,1,:,3)), E.values, 'UniformOutput', false ); +H_val = cellfun( @(x) squeeze(x(1,:,1,2)), H.values, 'UniformOutput', false ); +E_val = cell2mat(E_val); +H_val = cell2mat(H_val.'); + +% pick center point +Et = E_val(3,:); +Ht = H_val(:,3).'; + +delta_t_2 = H.time(1) - E.time(1); % half time-step (s) + +% create finer frequency resolution +f = linspace( 0, f_max, 1601 ); +Ef = DFT_time2freq( E.time, Et, f ); +Hf = DFT_time2freq( H.time, Ht, f ); +Hf = Hf .* exp(-1i*2*pi*f*delta_t_2); % compensate half time-step advance of H-field + +% H is now time interpolated, but the position is not corrected with +% respect to E + +% figure +% plot( E.time/1e-6, Et ); +% xlabel('time (us)'); +% ylabel('amplitude (V)'); +% grid on; +% title( 'Time domain voltage probe' ); +% +% figure +% plot( H.time/1e-6, Ht ); +% xlabel('time (us)'); +% ylabel('amplitude (A)'); +% grid on; +% title( 'Time domain current probe' ); + + +Z0 = sqrt(MUE0/EPS0); % line impedance +Z = Ef ./ Hf; % impedance at measurement plane +gamma = (Z - Z0) ./ (Z + Z0); + +plot( f/1e9, 20*log10(abs(gamma)),'Linewidth',2); +xlabel('frequency (GHz)'); +ylabel('reflection coefficient gamma (dB)'); +grid on; +title( 'Reflection Coefficient' ); + +if exist('ref_1','var') + hold on + plot( f/1e9, ref_1,'--','Linewidth',2, 'Color', [1 0 0]); + hold off +end +ref_1 = 20*log10(abs(gamma)); diff --git a/openEMS/matlab/examples/other/PlaneWave.m b/openEMS/matlab/examples/other/PlaneWave.m new file mode 100644 index 0000000..5e0d3e8 --- /dev/null +++ b/openEMS/matlab/examples/other/PlaneWave.m @@ -0,0 +1,69 @@ +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +length = 5000; +width = 300; +height = 200; +mesh_res = 15; +abs_length = mesh_res*10; + +EPS0 = 8.85418781762e-12; +MUE0 = 1.256637062e-6; + +%% define openEMS options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +openEMS_opts = [openEMS_opts ' --engine=fastest']; + +Sim_Path = 'tmp'; +Sim_CSX = 'plane_wave.xml'; + +if (exist(Sim_Path,'dir')) + rmdir(Sim_Path,'s'); +end +mkdir(Sim_Path); + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(5000,1e-5,'OverSampling',10); +FDTD = SetGaussExcite(FDTD,0.5e9,0.5e9); +BC = [1 1 0 0 2 2]; +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = -width/2 : mesh_res : width/2; +mesh.y = -height/2 : mesh_res : height/2; +mesh.z = 0 : mesh_res : length; +CSX = DefineRectGrid(CSX, 1e-3,mesh); + +%% apply the excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start=[-width/2 -height/2 mesh.z(3)]; +stop =[ width/2 height/2 mesh.z(3)]; +CSX = AddExcitation(CSX,'excite',0,[0 1 0]); +CSX = AddBox(CSX,'excite',0 ,start,stop); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et','FileType',1,'SubSampling','4,4,4'); +start = [mesh.x(1) , mesh.y(1) , mesh.z(1)]; +stop = [mesh.x(end) , mesh.y(end) , mesh.z(end)]; +CSX = AddBox(CSX,'Et',0 , start,stop); + +CSX = AddDump(CSX,'Ht','DumpType',1,'FileType',1,'SubSampling','4,4,4','DumpMode',2); +CSX = AddBox(CSX,'Ht',0,start,stop); + +%% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +%% run openEMS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts); + +%% do the plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +PlotArgs.slice = {mesh.x(round(end/2)) mesh.y(round(end/2)) mesh.z(round(end/2))}; +PlotArgs.pauseTime=0.01; +PlotArgs.component=1; +PlotArgs.Limit = 'auto'; + +PlotHDF5FieldData('tmp/Ht.h5',PlotArgs) diff --git a/openEMS/matlab/examples/other/gauss_excitation_test.m b/openEMS/matlab/examples/other/gauss_excitation_test.m new file mode 100644 index 0000000..a7e166e --- /dev/null +++ b/openEMS/matlab/examples/other/gauss_excitation_test.m @@ -0,0 +1,72 @@ +% +% this script evaluates the same gaussian excitation function, as openEMS does +% + +clear +close all +clc + +f0 = 0e9; +fc = 10e9; +dT = 1e-12; % sample time-step + + +sigma = 1/sqrt(8/9)/pi/fc; +t0 = sqrt(18)/sqrt(8/9)/pi/fc; + +len = 2 * 9/(2*pi*fc) / dT; % gauss length + +for n=1:len + t_(n) = (n-1)*dT; + ex(n) = cos(2*pi*f0*((n-1)*dT - 9/(2*pi*fc))) .* exp(-((t_(n)-t0)/sigma)^2/2); +end + +plot(t_/1e-9,ex) +xlabel( 'time (ns)' ); +ylabel( 'amplitude' ); + + +disp( ['Amplitude at t=0: ' num2str(20*log10(abs(ex(1))/1)) ' dB'] ); + +val = DFT_time2freq( t_, ex, [f0-fc f0 f0+fc] ); +disp( ['Amplitude at f=f0-fc: ' num2str(20*log10(abs(val(1))/abs(val(2)))) ' dB'] ); +disp( ['Amplitude at f=f0+fc: ' num2str(20*log10(abs(val(3))/abs(val(2)))) ' dB'] ); + +% calculate frequency domain via slow DFT +freq = linspace(f0-fc,f0+fc,1000); +val = DFT_time2freq( t_, ex, freq ); +figure +plot( freq/1e9, abs(val) ) + +% overlay the FFT result +[f,val_fft] = FFT_time2freq( t_, ex ); +val_fft = val_fft((f0-fc<=f) & (f<=f0+fc)); +f = f((f0-fc<=f) & (f<=f0+fc)); +hold on +plot( f/1e9, abs(val_fft), 'r' ) +hold on + +if (f0==0) + Fw = sigma*sqrt(2*pi)*exp(-0.5*(sigma*2*pi*f).^2); + plot( f/1e9, 2*abs(Fw), 'g--' ) + legend('dft','fft','analytic') +else + legend('dft','fft') +end + +xlim([0 max(f)/1e9]) + +xlabel( 'frequency (GHz)' ); +ylabel( 'amplitude' ); + + +% dB +figure +val = val(freq>=0); +freq = freq(freq>=0); +plot( freq/1e9, 20*log10(abs(val)/max(abs(val))), 'r' ) +xlabel( 'frequency (GHz)' ); +ylabel( 'amplitude (dB)' ); + + + diff --git a/openEMS/matlab/examples/other/resistance_sheet.m b/openEMS/matlab/examples/other/resistance_sheet.m new file mode 100644 index 0000000..b63c00e --- /dev/null +++ b/openEMS/matlab/examples/other/resistance_sheet.m @@ -0,0 +1,207 @@ +% +% resistance "sheet" example +% +% this example calculates the reflection coefficient of a sheet resistance +% at the end of a parallel plate wave guide +% +% play around with the R and epr values +% + +close all +clear +clc + +physical_constants + + +postprocessing_only = 0; + + + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +epr = 1; % relative permittivity of the material inside the parallel plate waveguide + +% define the resistance +R = sqrt(MUE0/(EPS0*epr)); % matched load (no reflections) (vacuum: approx. 377 Ohm) +% R = 1e-10; % short circuit (reflection coefficient = -1) +% R = 1e10; % open circuit (reflection coefficient = 1) + + +drawingunit = 1e-6; % specify everything in um +length = 10000; +mesh_res = [200 200 200]; +max_timesteps = 100000; +min_decrement = 1e-6; +f_max = 1e9; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD( max_timesteps, min_decrement ); +FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); +BC = [1 2 1 1 0 0]; % 0:PEC 1:PMC 2:MUR-ABC +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = 0 : mesh_res(1) : length; +mesh.y = -2*mesh_res(2) : mesh_res(2) : 2*mesh_res(2); +mesh.z = 0 : mesh_res(3) : 4*mesh_res(3); +CSX = DefineRectGrid( CSX, drawingunit, mesh ); + +%% measurement plane & reference plane +meas_plane_xidx = interp1( mesh.x, 1:numel(mesh.x), length*1/3, 'nearest' ); +ref_plane_xidx = 3; + +%% fill the parallel plate waveguide with material +CSX = AddMaterial( CSX, 'm1' ); +CSX = SetMaterialProperty( CSX, 'm1', 'Epsilon', epr ); +start = [mesh.x(1), mesh.y(1), mesh.z(1)]; +stop = [mesh.x(end), mesh.y(end), mesh.z(end)]; +CSX = AddBox( CSX, 'm1', -1, start, stop ); + +%% excitation +CSX = AddExcitation( CSX, 'excitation1', 0, [0 0 1]); +idx = interp1( mesh.x, 1:numel(mesh.x), length*2/3, 'nearest' ); +start = [mesh.x(idx), mesh.y(1), mesh.z(1)]; +stop = [mesh.x(idx), mesh.y(end), mesh.z(end)]; +CSX = AddBox( CSX, 'excitation1', 0, start, stop ); + +%% define the sheet resistance +start = [mesh.x(ref_plane_xidx-1), mesh.y(1), mesh.z(1)]; +stop = [mesh.x(ref_plane_xidx), mesh.y(end), mesh.z(end)]; +l = abs(mesh.z(end) - mesh.z(1)) * drawingunit; % length of the "sheet" +A = abs(start(1) - stop(1)) * abs(mesh.y(end) - mesh.y(1)) * drawingunit^2; % area of the "sheet" +kappa = l/A / R; % [kappa] = S/m +CSX = AddMaterial( CSX, 'sheet_resistance' ); +CSX = SetMaterialProperty( CSX, 'sheet_resistance', 'Kappa', kappa ); +CSX = AddBox( CSX, 'sheet_resistance', 0, start, stop ); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump( CSX, 'Et_', 'DumpMode', 2 ); +start = [mesh.x(1), mesh.y(1), mesh.z(3)]; +stop = [mesh.x(end), mesh.y(end), mesh.z(3)]; +CSX = AddBox( CSX, 'Et_', 0, start, stop ); + +CSX = AddDump( CSX, 'Ht_', 'DumpType', 1, 'DumpMode', 2 ); +CSX = AddBox( CSX, 'Ht_', 0, start, stop ); + +% hdf5 file +CSX = AddDump( CSX, 'E', 'DumpType', 0, 'DumpMode', 2, 'FileType', 1 ); +start = [mesh.x(meas_plane_xidx), mesh.y(3), mesh.z(1)]; +stop = [mesh.x(meas_plane_xidx), mesh.y(3), mesh.z(end)]; +CSX = AddBox( CSX, 'E', 0, start, stop ); + +% hdf5 file +CSX = AddDump( CSX, 'H', 'DumpType', 1, 'DumpMode', 2, 'FileType', 1 ); +start = [mesh.x(meas_plane_xidx), mesh.y(1), mesh.z(3)]; +stop = [mesh.x(meas_plane_xidx), mesh.y(end), mesh.z(3)]; +CSX = AddBox( CSX, 'H', 0, start, stop ); + +%% define openEMS options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --debug-operator']; +% openEMS_opts = [openEMS_opts ' --debug-boxes']; +% openEMS_opts = [openEMS_opts ' --showProbeDiscretization']; +openEMS_opts = [openEMS_opts ' --engine=fastest']; + +Sim_Path = 'tmp'; +Sim_CSX = 'tmp.xml'; + +if ~postprocessing_only + [~,~,~] = rmdir(Sim_Path,'s'); + [~,~,~] = mkdir(Sim_Path); +end + +%% Write openEMS compatible xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +%% cd to working dir and run openEMS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if ~postprocessing_only + savePath = pwd; + cd(Sim_Path); %cd to working dir + args = [Sim_CSX ' ' openEMS_opts]; + invoke_openEMS(args); + cd(savePath) +end + + +%% postproc & do the plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% E_coords = ReadHDF5Mesh( [Sim_Path '/E.h5'] ); +% H_coords = ReadHDF5Mesh( [Sim_Path '/H.h5'] ); +E = ReadHDF5FieldData( [Sim_Path '/E.h5'] ); +H = ReadHDF5FieldData( [Sim_Path '/H.h5'] ); +E_val = cellfun( @(x) squeeze(x(1,1,:,3)), E.values, 'UniformOutput', false ); +H_val = cellfun( @(x) squeeze(x(1,:,1,2)), H.values, 'UniformOutput', false ); +E_val = cell2mat(E_val); +H_val = cell2mat(H_val.'); + +% pick center point +Et = E_val(3,:); +Ht = H_val(:,3).'; + +delta_t_2 = H.time(1) - E.time(1); % half time-step (s) + +% create finer frequency resolution +f = linspace( 0, f_max, 201 ); +Ef = DFT_time2freq( E.time, Et, f ); +Hf = DFT_time2freq( H.time, Ht, f ); +Hf = Hf .* exp(-1i*2*pi*f*delta_t_2); % compensate half time-step advance of H-field + +% H is now time interpolated, but the position is not corrected with +% respect to E + +% figure +% plot( E.time/1e-6, Et ); +% xlabel('time (us)'); +% ylabel('amplitude (V)'); +% grid on; +% title( 'Time domain voltage probe' ); +% +% figure +% plot( H.time/1e-6, Ht ); +% xlabel('time (us)'); +% ylabel('amplitude (A)'); +% grid on; +% title( 'Time domain current probe' ); + + +Z0 = sqrt(MUE0/(EPS0*epr)); % line impedance +Z = Ef ./ Hf; % impedance at measurement plane +gamma = (Z - Z0) ./ (Z + Z0); + +% reference plane shift +beta = 2*pi*f * sqrt(MUE0*(EPS0*epr)); % TEM wave +meas_plane_x = mesh.x(meas_plane_xidx); +ref_plane_x = mesh.x(ref_plane_xidx); +gamma_refplane = gamma .* exp(2i*beta* (meas_plane_x-ref_plane_x)*drawingunit); +Z_refplane = Z0 * (1+gamma_refplane)./(1-gamma_refplane); + +% smith chart +figure +if exist( 'smith', 'file' ) + % smith chart + % www.ece.rutgers.edu/~orfanidi/ewa + % or cmt toolbox from git.ate.uni-duisburg.de + smith +else + % poor man smith chart + plot( sin(0:0.01:2*pi), cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); + hold on +% plot( 0.25+0.75*sin(0:0.01:2*pi), 0.75*cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); + plot( 0.5+0.5*sin(0:0.01:2*pi), 0.5*cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); +% plot( 0.75+0.25*sin(0:0.01:2*pi), 0.25*cos(0:0.01:2*pi), 'Color', [.7 .7 .7] ); + plot( [-1 1], [0 0], 'Color', [.7 .7 .7] ); + axis equal +end +plot( real(gamma_refplane), imag(gamma_refplane), 'r*' ); +% plot( real(gamma), imag(gamma), 'k*' ); +title( 'reflection coefficient S11 at reference plane' ) + +figure +plot( f/1e9, [real(Z_refplane);imag(Z_refplane)],'Linewidth',2); +xlabel('frequency (GHz)'); +ylabel('impedance (Ohm)'); +grid on; +title( 'Impedance at reference plane' ); +legend( {'real','imag'} ); diff --git a/openEMS/matlab/examples/transmission_lines/CPW_Line.m b/openEMS/matlab/examples/transmission_lines/CPW_Line.m new file mode 100644 index 0000000..6dac636 --- /dev/null +++ b/openEMS/matlab/examples/transmission_lines/CPW_Line.m @@ -0,0 +1,125 @@ +% +% Tutorials / CPW_Line +% +% Describtion at: +% +% Tested with +% - Octave 3.8.1 +% - openEMS v0.0.32 +% +% (C) 2014 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um +CPW_length = 40000; +CPW_port_length = 10000; +CPW_width = 1000; +CPW_gap = 140; +substrate_thickness = 512; +substrate_width = 5000 +substrate_epr = 3.66; +f_max = 10e9; +air_spacing = 7000 + +% use a finite line CPW waveguide +if 1 + feed_R = 50; + pml_add_cells = [8 8 8 8 8 8]; + feed_shift_cells = 0; + x_spacing = air_spacing; +else % or use a waveguide with start/end in a pml + feed_R = inf; % CPW ends in a pml --> disable termination resitance + feed_shift_cells = 10; % CPW ends in an 8 cells thick pml --> shift feed 10 cells + pml_add_cells = [0 0 8 8 8 8]; % do not add air-space in x-direction + x_spacing = 0; % do not add air-space in x-direction +end + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD('EndCriteria', 1e-4); +FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); +BC = [2 2 2 2 2 2]; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +resolution = c0/(f_max*sqrt(substrate_epr))/unit /30; % resolution of lambda/50 +edge_res = 40; +mesh.x = SmoothMeshLines( [0 CPW_length/2 CPW_length/2+x_spacing], resolution, 1.5 ,0 ); +mesh.x = unique(sort([-mesh.x mesh.x])); +mesh.y = SmoothMeshLines( [CPW_width/2+[-edge_res/3 +edge_res/3*2] CPW_gap+CPW_width/2+[-edge_res/3*2 +edge_res/3]], edge_res , 1.5 ,0); +mesh.y = SmoothMeshLines( [0 mesh.y], edge_res*2, 1.3 ,0); +mesh.y = SmoothMeshLines( [0 mesh.y substrate_width/2 substrate_width/2+air_spacing], resolution, 1.3 ,0); +mesh.y = unique(sort([-mesh.y mesh.y])); +mesh.z = SmoothMeshLines( [-air_spacing linspace(0,substrate_thickness,5) substrate_thickness+air_spacing], resolution ); + +mesh = AddPML(mesh, pml_add_cells); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% substrate +CSX = AddMaterial( CSX, 'RO4350B' ); +CSX = SetMaterialProperty( CSX, 'RO4350B', 'Epsilon', substrate_epr ); +start = [-CPW_length/2, -substrate_width/2, 0]; +stop = [+CPW_length/2, +substrate_width/2, substrate_thickness]; +CSX = AddBox( CSX, 'RO4350B', 0, start, stop ); + +%% +CSX = AddMetal( CSX, 'CPW_PORT' ); + +%% CPW port, with the measurement plane at the end of each port +portstart = [ -CPW_length/2 , -CPW_width/2, substrate_thickness]; +portstop = [ -CPW_length/2+CPW_port_length, CPW_width/2, substrate_thickness]; +[CSX,port{1}] = AddCPWPort( CSX, 999, 1, 'CPW_PORT', portstart, portstop, CPW_gap, 'x', [0 1 0], 'ExcitePort', true, 'FeedShift', feed_shift_cells*resolution, 'MeasPlaneShift', CPW_port_length, 'Feed_R', feed_R); + +portstart = [ CPW_length/2 , -CPW_width/2, substrate_thickness]; +portstop = [ CPW_length/2-CPW_port_length, CPW_width/2, substrate_thickness]; +[CSX,port{2}] = AddCPWPort( CSX, 999, 2, 'CPW_PORT', portstart, portstop, CPW_gap, 'x', [0 1 0], 'MeasPlaneShift', CPW_port_length, 'Feed_R', feed_R); + +%% CPW +CSX = AddMetal( CSX, 'CPW'); +start = [ -CPW_length/2+CPW_port_length, -CPW_width/2, substrate_thickness]; +stop = [ +CPW_length/2-CPW_port_length, CPW_width/2, substrate_thickness]; +CSX = AddBox(CSX, 'CPW', 999, start, stop); + +%% CPW grounds +CSX = AddMetal( CSX, 'GND' ); +start = [-CPW_length/2, -CPW_width/2-CPW_gap, substrate_thickness]; +stop = [+CPW_length/2, -substrate_width/2 , substrate_thickness]; +CSX = AddBox(CSX, 'GND', 999, start, stop); + +start = [-CPW_length/2, +CPW_width/2+CPW_gap, substrate_thickness]; +stop = [+CPW_length/2, +substrate_width/2 , substrate_thickness]; +CSX = AddBox(CSX, 'GND', 999, start, stop); + +%% write/show/run the openEMS compatible xml-file +Sim_Path = 'tmp'; +Sim_CSX = 'CPW.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +RunOpenEMS( Sim_Path, Sim_CSX ); + +%% post-processing +close all +f = linspace( 1e6, f_max, 1601 ); +port = calcPort( port, Sim_Path, f, 'RefImpedance', 50); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; + +plot(f/1e9,20*log10(abs(s11)),'k-','LineWidth',2); +hold on; +grid on; +plot(f/1e9,20*log10(abs(s21)),'r--','LineWidth',2); +legend('S_{11}','S_{21}'); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (GHz) \rightarrow','FontSize',12); + + diff --git a/openEMS/matlab/examples/transmission_lines/Finite_Stripline.m b/openEMS/matlab/examples/transmission_lines/Finite_Stripline.m new file mode 100644 index 0000000..da2b4a3 --- /dev/null +++ b/openEMS/matlab/examples/transmission_lines/Finite_Stripline.m @@ -0,0 +1,91 @@ +% example demonstrating the use of a stripline terminated by a resistance +% (c) 2013 Thorsten Liebig + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um +SL_length = 50000; +SL_width = 520; +SL_height = 500; +substrate_thickness = SL_height; +substrate_epr = 3.66; +f_max = 7e9; + +Air_Spacer = 20000; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(); +FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +resolution = c0/(f_max*sqrt(substrate_epr))/unit /50; % resolution of lambda/50 +mesh.x = SmoothMeshLines( [-SL_length/2 0 SL_length/2], resolution, 1.5 ,0 ); +mesh.y = SmoothMeshLines( [0 SL_width/2+[-resolution/3 +resolution/3*2]/4], resolution/4 , 1.5 ,0); +mesh.y = SmoothMeshLines( [-10*SL_width -mesh.y mesh.y 10*SL_width], resolution, 1.3 ,0); +mesh.z = linspace(0,substrate_thickness,5); +mesh.z = sort(unique([mesh.z -mesh.z])); + +%% substrate +CSX = AddMaterial( CSX, 'RO4350B' ); +CSX = SetMaterialProperty( CSX, 'RO4350B', 'Epsilon', substrate_epr ); +start = [mesh.x(1), mesh.y(1), mesh.z(1)]; +stop = [mesh.x(end), mesh.y(end), mesh.z(end)]; +CSX = AddBox( CSX, 'RO4350B', 0, start, stop ); + +%% add air spacer +mesh.x = [mesh.x mesh.x(1)-Air_Spacer mesh.x(end)+Air_Spacer]; +mesh.y = [mesh.y mesh.y(1)-Air_Spacer mesh.y(end)+Air_Spacer]; +mesh.z = [mesh.z mesh.z(1)-Air_Spacer mesh.z(end)+Air_Spacer]; +mesh = SmoothMesh(mesh, c0/f_max/unit/20); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% SL port +CSX = AddMetal( CSX, 'PEC' ); +portstart = [ -SL_length/2, -SL_width/2, 0]; +portstop = [ 0, SL_width/2, 0]; +[CSX,port{1}] = AddStripLinePort( CSX, 999, 1, 'PEC', portstart, portstop, SL_height, 'x', [0 0 -1], 'ExcitePort', true, 'Feed_R', 50, 'MeasPlaneShift', SL_length/3); + +portstart = [+SL_length/2, -SL_width/2, 0]; +portstop = [0 , SL_width/2, 0]; +[CSX,port{2}] = AddStripLinePort( CSX, 999, 2, 'PEC', portstart, portstop, SL_height, 'x', [0 0 -1], 'MeasPlaneShift', SL_length/3, 'Feed_R', 50); + +% bottom PEC plane +CSX = AddBox(CSX, 'PEC', 999, [-SL_length/2 -10*SL_width -SL_height],[+SL_length/2 +10*SL_width -SL_height]); +% top PEC plane +CSX = AddBox(CSX, 'PEC', 999, [-SL_length/2 -10*SL_width SL_height],[+SL_length/2 +10*SL_width SL_height]); + +%% write/show/run the openEMS compatible xml-file +Sim_Path = ['tmp_' mfilename]; +Sim_CSX = 'stripline.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +RunOpenEMS( Sim_Path, Sim_CSX ); + +%% post-processing +close all +f = linspace( 1e6, f_max, 1601 ); +port = calcPort( port, Sim_Path, f, 'RefImpedance', 50); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; + +plot(f/1e9,20*log10(abs(s11)),'k-','LineWidth',2); +hold on; +grid on; +plot(f/1e9,20*log10(abs(s21)),'r--','LineWidth',2); +legend('S_{11}','S_{21}'); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (GHz) \rightarrow','FontSize',12); +ylim([-50 2]); + diff --git a/openEMS/matlab/examples/transmission_lines/MSL.m b/openEMS/matlab/examples/transmission_lines/MSL.m new file mode 100644 index 0000000..b6ec0b3 --- /dev/null +++ b/openEMS/matlab/examples/transmission_lines/MSL.m @@ -0,0 +1,185 @@ +% +% EXAMPLE / microstrip / MSL +% +% Microstrip line on air "substrate" in z-direction. +% +% This example demonstrates: +% - simple microstrip geometry +% - characteristic impedance +% - material grading function +% - geometric priority concept +% +% +% Tested with +% - Matlab 2009b +% - Octave 3.3.52 +% - openEMS v0.0.14 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% setup the simulation +physical_constants; +unit = 1e-3; % all length in mm + +% geometry +abs_length = 100; % absorber length +length = 600; +width = 400; +height = 200; +MSL_width = 50; +MSL_height = 10; + +%% prepare simulation folder +Sim_Path = 'tmp'; +Sim_CSX = 'msl.xml'; +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +max_timesteps = 2000; +min_decrement = 1e-5; % equivalent to -50 dB +f0 = 2e9; % center frequency +fc = 1e9; % 10 dB corner frequency (in this case 1e9 Hz - 3e9 Hz) +FDTD = InitFDTD( max_timesteps, min_decrement ); +FDTD = SetGaussExcite( FDTD, f0, fc ); +BC = {'PMC' 'PMC' 'PEC' 'PMC' 'PEC' 'PEC'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh +% very simple mesh +CSX = InitCSX(); +resolution = c0/(f0+fc)/unit /15; % resolution of lambda/15 +mesh.x = SmoothMeshLines( [-width/2, width/2, -MSL_width/2, MSL_width/2], resolution ); % create smooth lines from fixed lines +mesh.y = SmoothMeshLines( [linspace(0,MSL_height,5) MSL_height+1 height], resolution ); +mesh.z = SmoothMeshLines( [0 length], resolution ); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% create MSL +% attention! the skin effect is not simulated, because the MSL is +% discretized with only one cell! +CSX = AddMaterial( CSX, 'copper' ); +CSX = SetMaterialProperty( CSX, 'copper', 'Kappa', 56e6 ); +start = [-MSL_width/2, MSL_height, 0]; +stop = [ MSL_width/2, MSL_height+1, length]; +priority = 100; % the geometric priority is set to 100 +CSX = AddBox( CSX, 'copper', priority, start, stop ); + +%% add excitation below the strip +start = [-MSL_width/2, 0 , mesh.z(1)]; +stop = [ MSL_width/2, MSL_height, mesh.z(1)]; +CSX = AddExcitation( CSX, 'excite', 0, [0 -1 0] ); +CSX = AddBox( CSX, 'excite', 0, start, stop ); + +%% fake pml +% this "pml" is a normal material with graded losses +% electric and magnetic losses are related to give low reflection +% for normally incident TEM waves +finalKappa = 1/abs_length^2; +finalSigma = finalKappa*MUE0/EPS0; +CSX = AddMaterial( CSX, 'fakepml' ); +CSX = SetMaterialProperty( CSX, 'fakepml', 'Kappa', finalKappa ); +CSX = SetMaterialProperty( CSX, 'fakepml', 'Sigma', finalSigma ); +CSX = SetMaterialWeight( CSX, 'fakepml', 'Kappa', ['pow(z-' num2str(length-abs_length) ',2)'] ); +CSX = SetMaterialWeight( CSX, 'fakepml', 'Sigma', ['pow(z-' num2str(length-abs_length) ',2)'] ); +start = [mesh.x(1) mesh.y(1) length-abs_length]; +stop = [mesh.x(end) mesh.y(end) length]; +% the geometric priority is set to 0, which is lower than the priority +% of the MSL, thus the MSL (copper) has precendence +priority = 0; +CSX = AddBox( CSX, 'fakepml', priority, start, stop ); + +%% define dump boxes +start = [mesh.x(1), MSL_height/2, mesh.z(1)]; +stop = [mesh.x(end), MSL_height/2, mesh.z(end)]; +CSX = AddDump( CSX, 'Et_', 'DumpMode', 2 ); % cell interpolated +CSX = AddBox( CSX, 'Et_', 0, start, stop ); +CSX = AddDump( CSX, 'Ht_', 'DumpType', 1, 'DumpMode', 2 ); % cell interpolated +CSX = AddBox( CSX, 'Ht_', 0, start, stop ); + +%% define voltage calc box +% voltage calc boxes will automatically snap to the next mesh-line +CSX = AddProbe( CSX, 'ut1', 0 ); +zidx = interp1( mesh.z, 1:numel(mesh.z), length/2, 'nearest' ); +start = [0 MSL_height mesh.z(zidx)]; +stop = [0 0 mesh.z(zidx)]; +CSX = AddBox( CSX, 'ut1', 0, start, stop ); +% add a second voltage probe to compensate space offset between voltage and +% current +CSX = AddProbe( CSX, 'ut2', 0 ); +start = [0 MSL_height mesh.z(zidx+1)]; +stop = [0 0 mesh.z(zidx+1)]; +CSX = AddBox( CSX, 'ut2', 0, start, stop ); + +%% define current calc box +% current calc boxes will automatically snap to the next dual mesh-line +CSX = AddProbe( CSX, 'it1', 1 ); +xidx1 = interp1( mesh.x, 1:numel(mesh.x), -MSL_width/2, 'nearest' ); +xidx2 = interp1( mesh.x, 1:numel(mesh.x), MSL_width/2, 'nearest' ); +xdelta = diff(mesh.x); +yidx1 = interp1( mesh.y, 1:numel(mesh.y), MSL_height, 'nearest' ); +yidx2 = interp1( mesh.y, 1:numel(mesh.y), MSL_height+1, 'nearest' ); +ydelta = diff(mesh.y); +zdelta = diff(mesh.z); +start = [mesh.x(xidx1)-xdelta(xidx1-1)/2, mesh.y(yidx1)-ydelta(yidx1-1)/2, mesh.z(zidx)+zdelta(zidx)/2]; +stop = [mesh.x(xidx2)+xdelta(xidx2)/2, mesh.y(yidx2)+ydelta(yidx2)/2, mesh.z(zidx)+zdelta(zidx)/2]; +CSX = AddBox( CSX, 'it1', 0, start, stop ); + +%% write openEMS compatible xml-file +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); + +%% show the structure +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); + +%% run openEMS +openEMS_opts = ''; +openEMS_opts = [openEMS_opts ' --engine=fastest']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --debug-boxes']; +RunOpenEMS( Sim_Path, Sim_CSX, openEMS_opts ); + +%% postprocess +freq = linspace( f0-fc, f0+fc, 501 ); +U = ReadUI( {'ut1','ut2','et'}, 'tmp/', freq ); % time domain/freq domain voltage +I = ReadUI( 'it1', 'tmp/', freq ); % time domain/freq domain current (half time step offset is corrected) + +% plot time domain voltage +figure +[ax,h1,h2] = plotyy( U.TD{1}.t/1e-9, U.TD{1}.val, U.TD{3}.t/1e-9, U.TD{3}.val ); +set( h1, 'Linewidth', 2 ); +set( h1, 'Color', [1 0 0] ); +set( h2, 'Linewidth', 2 ); +set( h2, 'Color', [0 0 0] ); +grid on +title( 'time domain voltage' ); +xlabel( 'time t / ns' ); +ylabel( ax(1), 'voltage ut1 / V' ); +ylabel( ax(2), 'voltage et / V' ); +% now make the y-axis symmetric to y=0 (align zeros of y1 and y2) +y1 = ylim(ax(1)); +y2 = ylim(ax(2)); +ylim( ax(1), [-max(abs(y1)) max(abs(y1))] ); +ylim( ax(2), [-max(abs(y2)) max(abs(y2))] ); + +% calculate characteristic impedance +% arithmetic mean of ut1 and ut2 -> voltage in the middle of ut1 and ut2 +U = (U.FD{1}.val + U.FD{2}.val) / 2; +Z = U ./ I.FD{1}.val; + +% plot characteristic impedance +figure +plot( freq/1e6, real(Z), 'k-', 'Linewidth', 2 ); +hold on +grid on +plot( freq/1e6, imag(Z), 'r--', 'Linewidth', 2 ); +title( 'characteristic impedance of MSL' ); +xlabel( 'frequency f / MHz' ); +ylabel( 'characteristic impedance Z / Ohm' ); +legend( 'real', 'imag' ); + +%% visualize electric and magnetic fields +% you will find vtk dump files in the simulation folder (tmp/) +% use paraview to visualize them diff --git a/openEMS/matlab/examples/transmission_lines/MSL_Losses.m b/openEMS/matlab/examples/transmission_lines/MSL_Losses.m new file mode 100644 index 0000000..95cad7b --- /dev/null +++ b/openEMS/matlab/examples/transmission_lines/MSL_Losses.m @@ -0,0 +1,102 @@ +% +% examples / microstrip / MSL_Losses +% +% This example demonstrates how to model sheet conductor losses +% +% Tested with +% - Matlab 2013a / Octave 3.8.1+ +% - openEMS v0.0.32 +% +% (C) 2012-2014 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um +MSL.length = 10000; +MSL.port_dist = 5000; +MSL.width = 225; +MSL.conductivity = 41e6; +MSL.thickness = 35e-6; + +substrate.thickness = 250; +substrate.epr = 9.8; + +f_start = 0e9; +f_stop = 25e9; + +lambda = c0/f_stop; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD('endCriteria',1e-4); +FDTD = SetGaussExcite(FDTD,0.5*(f_start+f_stop),0.5*(f_stop-f_start)); +BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PEC' 'PML_8'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +resolution = c0/(f_stop*sqrt(substrate.epr))/unit /20; +mesh.x = SmoothMeshLines( [-MSL.length*0.5-MSL.port_dist 0 MSL.length*0.5+MSL.port_dist], resolution, 1.3 ,0 ); +mesh.y = SmoothMeshLines2( [0 MSL.width/2], resolution/6 , 1.3); +mesh.y = SmoothMeshLines( [-0.5*lambda/unit -mesh.y mesh.y 0.5*lambda/unit], resolution, 1.4); +mesh.z = SmoothMeshLines( [linspace(0,substrate.thickness,10) 0.5*lambda/unit], resolution ); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% substrate +CSX = AddMaterial( CSX, 'RO4350B' ); +CSX = SetMaterialProperty( CSX, 'RO4350B', 'Epsilon', substrate.epr ); +start = [mesh.x(1), mesh.y(1), 0]; +stop = [mesh.x(end), mesh.y(end), substrate.thickness]; +CSX = AddBox( CSX, 'RO4350B', 0, start, stop ); + +%% MSL ports and lossy line +CSX = AddConductingSheet( CSX, 'gold', MSL.conductivity, MSL.thickness ); +portstart = [ mesh.x(1), -MSL.width/2, substrate.thickness]; +portstop = [ mesh.x(1)+MSL.port_dist, MSL.width/2, 0]; +[CSX, port{1}] = AddMSLPort( CSX, 999, 1, 'gold', portstart, portstop, 0, [0 0 -1], 'ExcitePort', true, 'FeedShift', 10*resolution, 'MeasPlaneShift', MSL.port_dist); + +portstart = [mesh.x(end), -MSL.width/2, substrate.thickness]; +portstop = [mesh.x(end)-MSL.port_dist, MSL.width/2, 0]; +[CSX, port{2}] = AddMSLPort( CSX, 999, 2, 'gold', portstart, portstop, 0, [0 0 -1], 'MeasPlaneShift', MSL.port_dist ); + +start = [mesh.x(1)+MSL.port_dist, -MSL.width/2, substrate.thickness]; +stop = [mesh.x(end)-MSL.port_dist, MSL.width/2, substrate.thickness]; +CSX = AddBox(CSX,'gold',500,start,stop); + +%% write/show/run the openEMS compatible xml-file +Sim_Path = 'tmp'; +Sim_CSX = 'msl.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +RunOpenEMS( Sim_Path, Sim_CSX ,''); + +%% post-processing +close all +f = linspace( f_start, f_stop, 1601 ); +port = calcPort(port, Sim_Path, f, 'RefImpedance', 50); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; + +plot(f/1e9,-20*log10(abs(s21)),'r--','LineWidth',2); +grid on; +hold on; +ylabel('-|S_21| (dB)','Interpreter','None'); +xlabel('frequency (GHz)'); + +%% plot 35um thickness loss model curve +% values extracted from http://wcalc.sourceforge.net/cgi-bin/microstrip.cgi +model.f = [1 2 2.5 3 4 5 7.5 10 12.5 15 17.5 20 25 ]; % frequency in GHz +model.loss = [3.0 4.2 4.7 5.2 5.9 6.6 8.1 9.38 10.5 11.5 12.4 13.2 14.65]; % loss in db/m + +plot(model.f, model.loss * MSL.length * unit ,'k-','LineWidth',1); +legend('FDTD simulated attenuation','t=35um, loss model by E. Hammerstad & F. Bekkadal','Location','NorthWest'); + + diff --git a/openEMS/matlab/examples/transmission_lines/Stripline.m b/openEMS/matlab/examples/transmission_lines/Stripline.m new file mode 100644 index 0000000..71fd774 --- /dev/null +++ b/openEMS/matlab/examples/transmission_lines/Stripline.m @@ -0,0 +1,78 @@ +% example demonstrating the use of a stripline terminated by the pml +% (c) 2013 Thorsten Liebig + +close all +clear +clc + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-6; % specify everything in um +SL_length = 50000; +SL_width = 520; +SL_height = 500; +substrate_thickness = SL_height; +substrate_epr = 3.66; +f_max = 7e9; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(); +FDTD = SetGaussExcite( FDTD, f_max/2, f_max/2 ); +BC = {'PML_8' 'PML_8' 'PMC' 'PMC' 'PEC' 'PEC'}; +FDTD = SetBoundaryCond( FDTD, BC ); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +resolution = c0/(f_max*sqrt(substrate_epr))/unit /50; % resolution of lambda/50 +mesh.x = SmoothMeshLines( [-SL_length/2 0 SL_length/2], resolution, 1.5 ,0 ); +mesh.y = SmoothMeshLines( [0 SL_width/2+[-resolution/3 +resolution/3*2]/4], resolution/4 , 1.5 ,0); +mesh.y = SmoothMeshLines( [-10*SL_width -mesh.y mesh.y 10*SL_width], resolution, 1.3 ,0); +mesh.z = linspace(0,substrate_thickness,5); +mesh.z = sort(unique([mesh.z -mesh.z])); +CSX = DefineRectGrid( CSX, unit, mesh ); + +%% substrate +CSX = AddMaterial( CSX, 'RO4350B' ); +CSX = SetMaterialProperty( CSX, 'RO4350B', 'Epsilon', substrate_epr ); +start = [mesh.x(1), mesh.y(1), mesh.z(1)]; +stop = [mesh.x(end), mesh.y(end), mesh.z(end)]; +CSX = AddBox( CSX, 'RO4350B', 0, start, stop ); + +%% SL port +CSX = AddMetal( CSX, 'PEC' ); +portstart = [ mesh.x(1), -SL_width/2, 0]; +portstop = [ 0, SL_width/2, 0]; +[CSX,port{1}] = AddStripLinePort( CSX, 999, 1, 'PEC', portstart, portstop, SL_height, 'x', [0 0 -1], 'ExcitePort', true, 'FeedShift', 10*resolution, 'MeasPlaneShift', SL_length/3); + +portstart = [mesh.x(end), -SL_width/2, 0]; +portstop = [0 , SL_width/2, 0]; +[CSX,port{2}] = AddStripLinePort( CSX, 999, 2, 'PEC', portstart, portstop, SL_height, 'x', [0 0 -1], 'MeasPlaneShift', SL_length/3 ); + +%% write/show/run the openEMS compatible xml-file +Sim_Path = ['tmp_' mfilename]; +Sim_CSX = 'stripline.xml'; + +[status, message, messageid] = rmdir( Sim_Path, 's' ); % clear previous directory +[status, message, messageid] = mkdir( Sim_Path ); % create empty simulation folder + +WriteOpenEMS( [Sim_Path '/' Sim_CSX], FDTD, CSX ); +CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +RunOpenEMS( Sim_Path, Sim_CSX ); + +%% post-processing +close all +f = linspace( 1e6, f_max, 1601 ); +port = calcPort( port, Sim_Path, f, 'RefImpedance', 50); + +s11 = port{1}.uf.ref./ port{1}.uf.inc; +s21 = port{2}.uf.ref./ port{1}.uf.inc; + +plot(f/1e9,20*log10(abs(s11)),'k-','LineWidth',2); +hold on; +grid on; +plot(f/1e9,20*log10(abs(s21)),'r--','LineWidth',2); +legend('S_{11}','S_{21}'); +ylabel('S-Parameter (dB)','FontSize',12); +xlabel('frequency (GHz) \rightarrow','FontSize',12); +ylim([-50 2]); + diff --git a/openEMS/matlab/examples/transmission_lines/directional_coupler.m b/openEMS/matlab/examples/transmission_lines/directional_coupler.m new file mode 100644 index 0000000..8ba86f2 --- /dev/null +++ b/openEMS/matlab/examples/transmission_lines/directional_coupler.m @@ -0,0 +1,261 @@ +function directional_coupler +% +% EXAMPLE / microstrip / directional_coupler +% +% Stacked directional coupler in microstrip technology. +% +% This example demonstrates: +% - simple microstrip geometry +% - S-parameter calculation using the ypar-method +% - display of coupler parameters +% - display of S11 (smith chart) +% +% +% Tested with +% - Matlab 2010b +% - Octave 3.2.4 +% - openEMS v0.0.17 +% +% (C) 2010 Sebastian Held <sebastian.held@gmx.de> + +clear +close all +clc + +% sim settings +showStructure = 1; +runSimulation = 1; + +for n=1:4 + if n > 1, showStructure = 0; end + ports{n} = sim( n, showStructure, runSimulation ); +end +postprocess( ports ); + + + + +function ports = sim( simnr, showStructure, runSimulation ) +physical_constants + +% setup the simulation +drawingunit = 1e-6; % specify everything in um +Sim_Path = ['tmp' int2str(simnr)]; +Sim_CSX = 'tmp.xml'; +f_max = 100e6; +lambda = c0/f_max; + +% specify the coupler +pcb1.w = 147000; +pcb1.h = 54500; +pcb1.t = 1524; +pcb1.epr = 3; +msl1.w = 135000; +msl1.h = 2800; +pcb2.w = 107000; +pcb2.h = 14000; +pcb2.t = 1524; +pcb2.epr = 3; +msl2.w = 95000; +msl2.h = 4000; + + +CSX = InitCSX(); + +% create the mesh +mesh.x = [-pcb1.w/2 pcb1.w/2 -pcb2.w/2 pcb2.w/2 -msl1.w/2 msl1.w/2 -msl2.w/2 msl2.w/2]; +mesh.x = [mesh.x linspace(-msl2.w/2,-msl2.w/2+msl2.h, 5) linspace(msl2.w/2,msl2.w/2-msl2.h, 5)]; +mesh.y = [-pcb1.h/2 pcb1.h/2 -pcb2.h/2 pcb2.h/2 -msl1.h/2 msl1.h/2 -msl2.h/2 msl2.h/2]; +mesh.z = [linspace(0,pcb1.t,5) linspace(pcb1.t,pcb1.t+pcb2.t,5)]; +mesh.z = [mesh.z mesh.z(end)+10*(mesh.z(end)-mesh.z(1))]; % add space above pcb +res = lambda/sqrt(max([pcb1.epr,pcb2.epr])) / 20 / drawingunit; +mesh.x = SmoothMeshLines2(mesh.x,res); +mesh.y = SmoothMeshLines2(mesh.y,res); +mesh.z = SmoothMeshLines2(mesh.z,res); +mesh = AddPML( mesh, [8 8 8 8 8 8] ); % add space for PML +CSX = DefineRectGrid( CSX, drawingunit, mesh ); + +%% create the structure + +% microstrip +CSX = AddMetal( CSX, 'PEC' ); +start = [-msl1.w/2, -msl1.h/2, pcb1.t]; +stop = [ msl1.w/2, msl1.h/2, pcb1.t]; +priority = 100; % the geometric priority is set to 100 +CSX = AddBox( CSX, 'PEC', priority, start, stop ); + +% ground plane +CSX = AddMetal( CSX, 'PEC_ground' ); +start = [-pcb1.w/2, -pcb1.h/2, 0]; +stop = [ pcb1.w/2, pcb1.h/2, 0]; +CSX = AddBox( CSX, 'PEC_ground', priority, start, stop ); + +% substrate 1 +start = [-pcb1.w/2, -pcb1.h/2, 0]; +stop = [ pcb1.w/2, pcb1.h/2, pcb1.t]; +priority = 10; +CSX = AddMaterial( CSX, 'substrate1' ); +CSX = SetMaterialProperty( CSX, 'substrate1', 'Epsilon', pcb1.epr ); +CSX = AddBox( CSX, 'substrate1', priority, start, stop ); + +% substrate 2 +start = [-pcb2.w/2, -pcb2.h/2, pcb1.t]; +stop = [ pcb2.w/2, pcb2.h/2, pcb1.t+pcb2.t]; +priority = 10; +CSX = AddMaterial( CSX, 'substrate2' ); +CSX = SetMaterialProperty( CSX, 'substrate2', 'Epsilon', pcb2.epr ); +CSX = AddBox( CSX, 'substrate2', priority, start, stop ); + +% stripline +start = [-msl2.w/2, -msl2.h/2, pcb1.t+pcb2.t]; +stop = [ msl2.w/2, msl2.h/2, pcb1.t+pcb2.t]; +priority = 100; +CSX = AddBox( CSX, 'PEC', priority, start, stop ); + +% connections +start = [-msl2.w/2, -msl2.h/2, pcb1.t+pcb2.t]; +stop = [-msl2.w/2+msl2.h, -pcb2.h/2, pcb1.t+pcb2.t]; +priority = 100; +CSX = AddBox( CSX, 'PEC', priority, start, stop ); +start = [ msl2.w/2, -msl2.h/2, pcb1.t+pcb2.t]; +stop = [ msl2.w/2-msl2.h, -pcb2.h/2, pcb1.t+pcb2.t]; +priority = 100; +CSX = AddBox( CSX, 'PEC', priority, start, stop ); + +%% ports +% this project needs 4 simulations +for n=1:4 + portexcite{n} = []; +end +portexcite{simnr} = 'excite'; + +% port 1: input port +start = [-msl1.w/2, 0, pcb1.t]; +stop = [-msl1.w/2, 0, 0]; +[CSX ports{1}] = AddCurvePort( CSX, 999, 1, 50, start, stop, portexcite{1} ); +% port 2: output port +start = [msl1.w/2, 0, pcb1.t]; +stop = [msl1.w/2, 0, 0]; +[CSX ports{2}] = AddCurvePort( CSX, 999, 2, 50, start, stop, portexcite{2} ); +% port 3: coupled port +start = [-msl2.w/2+msl2.h/2, -pcb2.h/2, pcb1.t+pcb2.t]; +stop = [-msl2.w/2+msl2.h/2, -pcb2.h/2, 0]; +[CSX ports{3}] = AddCurvePort( CSX, 999, 3, 50, start, stop, portexcite{3} ); +% port 4: isolated port +start = [msl2.w/2-msl2.h/2, -pcb2.h/2, pcb1.t+pcb2.t]; +stop = [msl2.w/2-msl2.h/2, -pcb2.h/2, 0]; +[CSX ports{4}] = AddCurvePort( CSX, 999, 4, 50, start, stop, portexcite{4} ); + +%% setup FDTD parameters & excitation function +max_timesteps = 50000; +min_decrement = 1e-6; +FDTD = InitFDTD( max_timesteps, min_decrement ); +FDTD = SetGaussExcite( FDTD, 0, f_max ); +BC = {'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8' 'PML_8'}; +BC = {'MUR' 'MUR' 'MUR' 'MUR' 'MUR' 'MUR'}; % faster +FDTD = SetBoundaryCond( FDTD, BC ); + +%% Write openEMS compatible xml-file +if runSimulation + [dummy,dummy,dummy] = rmdir(Sim_Path,'s'); +end +[dummy,dummy,dummy] = mkdir(Sim_Path); +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +if showStructure + CSXGeomPlot( [Sim_Path '/' Sim_CSX] ); +end + +%% run openEMS +openEMS_opts = ''; +openEMS_opts = [openEMS_opts ' --engine=fastest']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --debug-boxes']; +if runSimulation + RunOpenEMS( Sim_Path, Sim_CSX, openEMS_opts ); +end + + + + +function postprocess( ports ) +f = linspace( 0, 100e6, 201 ); +Y = calc_ypar( f, ports{1}, 'tmp' ); +R = 50; +S = y2s(Y,R); + +% insertion loss +IL_dB = -20 * log10(abs(squeeze(S(2,1,:)))); + +% coupling factor +CF_dB = -20 * log10(abs(squeeze(S(3,1,:)))); + +% isolation +I_dB = -20 * log10(abs(squeeze(S(4,1,:)))); + +% directivity +D_dB = -20 * log10(abs(squeeze(S(4,1,:) ./ S(3,1,:)))); + +figure +plot( f, [IL_dB CF_dB I_dB D_dB] ); +legend( {'insertion loss','coupling factor','isolation','directivity'} ); +title( ['performance of the coupler for a termination resistance of R=' num2str(R)] ); +grid on + +smithchart +S11 = squeeze(S(1,1,:)); +plot( real(S11), imag(S11) ); +legend( 'S_{11}' ); +title( ['performance of the coupler for a termination resistance of R=' num2str(R)] ); +axis( [-1 1 -1 1] ); + + + +function smithchart +% smith chart +figure +if exist( 'smith', 'file' ) + % smith chart + % www.ece.rutgers.edu/~orfanidi/ewa + % or cmt toolbox from git.ate.uni-duisburg.de + smith +else + % poor man smith chart + color = [.6 .6 .6]; + h = plot( sin(0:0.01:2*pi), cos(0:0.01:2*pi), 'Color', color ); + hg = hggroup; + set( h,'Parent',hg ); + hold on + plot( hg, 0.25+0.75*sin(0:0.01:2*pi), 0.75*cos(0:0.01:2*pi), 'Color', color ); + plot( hg, 0.5+0.5*sin(0:0.01:2*pi), 0.5*cos(0:0.01:2*pi), 'Color', color ); + plot( hg, 0.75+0.25*sin(0:0.01:2*pi), 0.25*cos(0:0.01:2*pi), 'Color', color ); + plot( hg, [-1 1], [0 0], 'Color', color ); + axis equal + axis off +end + + +function s = y2s(y, ZL) +% S = y2s(Y, ZL) +% +% Admittance to Scattering transformation +% for square matrices at multiple frequencies +% +% ZL defaults to 50 Ohm + +if nargin < 2 + ZL = 50; +end + +if size(size(y),2) > 2 + nF = size(y,3); +else + nF = 1; +end + +I = diag(ones(1, size(y,2)))/ZL; + +for i=1:nF + %s(:,:,i) = inv(I+y(:,:,i)) * (I-y(:,:,i)); + s(:,:,i) = (I+y(:,:,i)) \ (I-y(:,:,i)); +end diff --git a/openEMS/matlab/examples/waveguide/Circ_Waveguide.m b/openEMS/matlab/examples/waveguide/Circ_Waveguide.m new file mode 100644 index 0000000..9ee860e --- /dev/null +++ b/openEMS/matlab/examples/waveguide/Circ_Waveguide.m @@ -0,0 +1,207 @@ +% +% EXAMPLE / waveguide / circular waveguide +% +% This example demonstrates how to: +% - setup a circular waveguide +% - use analytic functions for waveguide excitations and voltage/current +% calculations +% +% +% Tested with +% - Matlab 2009b +% - openEMS v0.0.17 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% switches & options... +postprocessing_only = 0; +use_pml = 0; % use pml boundaries instead of mur +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +numTS = 1e5; %number of timesteps +length = 1000; %length of the waveguide +unit = 1e-3; %drawing unit used +rad = 300; %radius of the circular waveguide +mesh_res = [10 10 15]; %desired mesh resolution + +%excitation +f0 = 350e6; %center frequency +f0_BW = 25e6; %bandwidth: 10dB cut-off frequency + +physical_constants + +%% TE11 mode definitions (Pozar 3rd edition) +p11 = 1.841; +kc = p11 / rad /unit; +k = 2*pi*f0/C0; +fc = C0*kc/2/pi; +beta = sqrt(k^2 - kc^2); +n_eff = (beta/k); + +kc = kc*unit; %functions must be defined in drawing units +func_Er = [ num2str(-1/kc^2) '/rho*cos(a)*j1(' num2str(kc) '*rho)']; +func_Ea = [ num2str(1/kc) '*sin(a)*0.5*(j0(' num2str(kc) '*rho)-jn(2,' num2str(kc) '*rho))']; +func_Ex = ['(' func_Er '*cos(a) - ' func_Ea '*sin(a) )*(rho<' num2str(rad) ')']; +func_Ey = ['(' func_Er '*sin(a) + ' func_Ea '*cos(a) )*(rho<' num2str(rad) ')']; + +func_Ha = [ num2str(-1/kc^2,'%14.13f') '/rho*cos(a)*j1(' num2str(kc,'%14.13f') '*rho)']; +func_Hr = [ '-1*' num2str(1/kc,'%14.13f') '*sin(a)*0.5*(j0(' num2str(kc,'%14.13f') '*rho)-jn(2,' num2str(kc,'%14.13f') '*rho))']; +func_Hx = ['(' func_Hr '*cos(a) - ' func_Ha '*sin(a) )*(rho<' num2str(rad) ')']; +func_Hy = ['(' func_Hr '*sin(a) + ' func_Ha '*cos(a) )*(rho<' num2str(rad) ')']; + +%% define files and path %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Sim_Path = 'tmp'; +Sim_CSX = 'Circ_WG.xml'; + +if (postprocessing_only==0) + [status, message, messageid] = rmdir(Sim_Path,'s'); + [status, message, messageid] = mkdir(Sim_Path); +end + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(numTS,1e-6,'OverSampling',5); +FDTD = SetGaussExcite(FDTD,f0,f0_BW); +BC = {'PEC','PEC','PEC','PEC','PEC','MUR'}; +if (use_pml>0) + BC = {'PEC','PEC','PEC','PEC','PEC','PML_8'}; +end +FDTD = SetBoundaryCond(FDTD,BC,'MUR_PhaseVelocity',C0 / n_eff); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = -mesh_res(1)/2-rad:mesh_res(1):rad+mesh_res(1)/2; +mesh.y = -mesh_res(2)/2-rad:mesh_res(2):rad+mesh_res(2)/2; +mesh.z = 0 : mesh_res(3) : length; +CSX = DefineRectGrid(CSX, 1e-3,mesh); + +start = [0,0,0]; +stop = [0,0,length]; + +%%% fill everything with copper, priority 0 +CSX = AddMetal(CSX,'copper'); +% CSX = SetMaterialProperty(CSX,'copper','Kappa',56e6); +CSX = AddBox(CSX,'copper',0,[mesh.x(1) mesh.y(1) mesh.z(1)],[mesh.x(end) mesh.y(end) mesh.z(end)]); + +%%% cut out an air cylinder as circular waveguide... priority 5 +CSX = AddMaterial(CSX,'air'); +CSX = SetMaterialProperty(CSX,'air','Epsilon',1); +CSX = AddCylinder(CSX,'air', 5 ,start,stop,rad); + +CSX = AddExcitation(CSX,'excite',0,[1 1 0]); +weight{1} = func_Ex; +weight{2} = func_Ey; +weight{3} = 0; +CSX = SetExcitationWeight(CSX, 'excite', weight ); +CSX = AddCylinder(CSX,'excite', 5 ,[0 0 -0.1],[0 0 0.1],rad); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et_','SubSampling','2,2,2','FileType',0,'DumpMode',2); +start = [mesh.x(1) , 0 , mesh.z(1)]; +stop = [mesh.x(end), 0 , mesh.z(end)]; +CSX = AddBox(CSX,'Et_',0 , start,stop); + +CSX = AddDump(CSX,'Ht_','SubSampling','2,2,2','DumpType',1,'FileType',0,'DumpMode',2); +CSX = AddBox(CSX,'Ht_',0,start,stop); + +%% define voltage calc boxes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%voltage calc +start = [mesh.x(1) mesh.y(1) mesh.z(10)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(10)]; +CSX = AddProbe(CSX, 'ut1', 10, 1, [], 'ModeFunction',{func_Ex,func_Ey,0}); +CSX = AddBox(CSX, 'ut1', 0 ,start,stop); +CSX = AddProbe(CSX,'it1', 11, 1, [], 'ModeFunction',{func_Hx,func_Hy,0}); +CSX = AddBox(CSX,'it1', 0 ,start,stop); + +start = [mesh.x(1) mesh.y(1) mesh.z(end-10)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end-10)]; +CSX = AddProbe(CSX, 'ut2', 10, 1, [], 'ModeFunction',{func_Ex,func_Ey,0}); +CSX = AddBox(CSX, 'ut2', 0 ,start,stop); +CSX = AddProbe(CSX,'it2', 11, 1, [], 'ModeFunction',{func_Hx,func_Hy,0}); +CSX = AddBox(CSX,'it2', 0 ,start,stop); + +port_dist = mesh.z(end-10) - mesh.z(10); + +%% Write openEMS +if (postprocessing_only==0) + WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + + RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts); +end + +%% do the plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +freq = linspace(f0-f0_BW,f0+f0_BW,201); +U = ReadUI({'ut1','ut2'},[Sim_Path '/'],freq); +I = ReadUI({'it1','it2'},[Sim_Path '/'],freq); +Exc = ReadUI('et',Sim_Path,freq); + +k = 2*pi*freq/C0; +kc = p11 / rad /unit; +beta = sqrt(k.^2 - kc^2); + +ZL_a = Z0*k./beta ; + +uf1 = U.FD{1}.val./Exc.FD{1}.val; +uf2 = U.FD{2}.val./Exc.FD{1}.val; +if1 = I.FD{1}.val./Exc.FD{1}.val; +if2 = I.FD{2}.val./Exc.FD{1}.val; + +uf1_inc = 0.5 * ( uf1 + if1 .* ZL_a ); +if1_inc = 0.5 * ( if1 + uf1 ./ ZL_a ); +uf2_inc = 0.5 * ( uf2 + if2 .* ZL_a ); +if2_inc = 0.5 * ( if2 + uf2 ./ ZL_a ); + +uf1_ref = uf1 - uf1_inc; +if1_ref = if1 - if1_inc; +uf2_ref = uf2 - uf2_inc; +if2_ref = if2 - if2_inc; + +% plot s-parameter +figure +s11 = uf1_ref./uf1_inc; +s21 = uf2_inc./uf1_inc; +plot(freq,20*log10(abs(s11)),'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('s-para (dB)'); +% ylim([-40 5]); +grid on; +hold on; +plot(freq,20*log10(abs(s21)),'r','Linewidth',2); +legend('s11','s21','Location','SouthEast'); + +% plot line-impedance comparison +figure() +ZL = uf1./if1; +plot(freq,real(ZL),'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('line-impedance (\Omega)'); +grid on; +hold on; +plot(freq,imag(ZL),'r--','Linewidth',2); +plot(freq,ZL_a,'g-.','Linewidth',2); +legend('\Re\{ZL\}','\Im\{ZL\}','ZL-analytic','Location','Best'); + +% beta compare +figure() +da = angle(uf1_inc)-angle(uf2_inc); +da = mod(da,2*pi); +beta_12 = (da)/port_dist/unit; +plot(freq,beta_12,'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)'); +ylabel('\beta (m^{-1})'); +grid on; +hold on; +plot(freq,beta,'g--','Linewidth',2); +legend('\beta-FDTD','\beta-analytic','Location','Best'); + +%% visualize electric & magnetic fields +disp('you will find vtk dump files in the simulation folder (tmp/)') +disp('use paraview to visulaize them'); diff --git a/openEMS/matlab/examples/waveguide/Circ_Waveguide_CylinderCoords.m b/openEMS/matlab/examples/waveguide/Circ_Waveguide_CylinderCoords.m new file mode 100644 index 0000000..a767c0a --- /dev/null +++ b/openEMS/matlab/examples/waveguide/Circ_Waveguide_CylinderCoords.m @@ -0,0 +1,204 @@ +% +% EXAMPLE / waveguide / circular waveguide cylindrical coordinates +% +% This example demonstrates how to: +% - use cylindrical coordinates +% - setup a circular waveguide defined by the boundary conditions of the +% cylindrical coordinate system +% - use analytic functions for waveguide excitations and voltage/current +% calculations +% +% +% Tested with +% - Matlab 2009b +% - openEMS v0.0.17 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% switches & options... +postprocessing_only = 0; +use_pml = 0; % use pml boundaries instead of mur +use_MultiGrid = 1; % disable multi-grid for this example +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +numTS = 1e5; %number of timesteps +length = 1000; %length of the waveguide +unit = 1e-3; %drawing unit used +rad = 300; %radius of the circular waveguide +mesh_res = [10 nan 15]; %desired mesh resolution +N_alpha = 50; %mesh lines in azimuth direction + +MultiGrid_Level = [50]; % define multigrid radii (if enabled) + +%excitation +f0 = 350e6; %center frequency +f0_BW = 25e6; %bandwidth: 10dB cut-off frequency + +physical_constants + +%% TE11 mode definitions (Pozar 3rd edition) +p11 = 1.841; +kc = p11 / rad /unit; +k = 2*pi*f0/C0; +fc = C0*kc/2/pi; +beta = sqrt(k^2 - kc^2); +n_eff = (beta/k); + +kc = kc*unit; %functions must be defined in drawing units +func_Er = [ num2str(-1/kc^2,15) '/rho*cos(a)*j1(' num2str(kc,15) '*rho)']; +func_Ea = [ num2str(1/kc,15) '*sin(a)*0.5*(j0(' num2str(kc,15) '*rho)-jn(2,' num2str(kc,15) '*rho))']; +func_Ha = [ num2str(-1/kc^2,'%14.13f') '/rho*cos(a)*j1(' num2str(kc,'%14.13f') '*rho)']; +func_Hr = [ '-1*' num2str(1/kc,'%14.13f') '*sin(a)*0.5*(j0(' num2str(kc,'%14.13f') '*rho)-jn(2,' num2str(kc,'%14.13f') '*rho))']; + +%% define files and path %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Sim_Path = 'tmp'; +Sim_CSX = 'Circ_WG_CC.xml'; + +if (postprocessing_only==0) + [status, message, messageid] = rmdir(Sim_Path,'s'); + [status, message, messageid] = mkdir(Sim_Path); +end + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if (use_MultiGrid==0) + FDTD = InitCylindricalFDTD(numTS,1e-5,'OverSampling',10); +else + mg_str = num2str(MultiGrid_Level,'%d,'); %create comma-separated string + N_alpha = round(N_alpha * 2^numel(MultiGrid_Level)); + FDTD = InitCylindricalFDTD(numTS,1e-5,'OverSampling',10,'MultiGrid',mg_str(1:end-1)); +end +FDTD = SetGaussExcite(FDTD,f0,f0_BW); +BC = {'PEC','PEC','PEC','PEC','PEC','MUR'}; +if (use_pml>0) + BC = {'PEC','PEC','PEC','PEC','PEC','PML_8'}; +end +FDTD = SetBoundaryCond(FDTD,BC,'MUR_PhaseVelocity',C0 / n_eff); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX('CoordSystem',1); +mesh.x = 0:mesh_res(1):rad; +%define an odd number of lines in alpha-direction +mesh.y = linspace(-pi,pi,N_alpha+mod(N_alpha+1,2))+pi/2; +mesh.z = 0 : mesh_res(3) : length; +CSX = DefineRectGrid(CSX, unit,mesh); + +%% apply the excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddExcitation(CSX,'excite',0,[1 1 0]); +weight{1} = func_Er; +weight{2} = func_Ea; +weight{3} = 0; +CSX = SetExcitationWeight(CSX, 'excite', weight ); +start = [mesh.x(1) mesh.y(1) mesh.z(1)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(1)]; +CSX = AddBox(CSX,'excite', 5 ,start,stop); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et_','FileType',0,'DumpMode',2,'SubSampling','2,2,2'); +start = [mesh.x(1) , 0 , mesh.z(1)]; +stop = [mesh.x(end), 0 , mesh.z(end)]; +CSX = AddBox(CSX,'Et_',0 , start,stop); + +CSX = AddDump(CSX,'Ht','FileType',0,'DumpType',1,'DumpMode',2,'SubSampling','2,2,2'); +CSX = AddBox(CSX,'Ht',0 , start,stop); + +%% define voltage calc boxes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start = [mesh.x(1) mesh.y(1) mesh.z(10)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(10)]; +CSX = AddProbe(CSX, 'ut1', 10, 1, [], 'ModeFunction',{func_Er,func_Ea,0}); +CSX = AddBox(CSX, 'ut1', 0 ,start,stop); +CSX = AddProbe(CSX,'it1', 11, 1, [], 'ModeFunction',{func_Hr,func_Ha,0}); +CSX = AddBox(CSX,'it1', 0 ,start,stop); + +start = [mesh.x(1) mesh.y(1) mesh.z(end-10)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end-10)]; +CSX = AddProbe(CSX, 'ut2', 10, 1, [], 'ModeFunction',{func_Er,func_Ea,0}); +CSX = AddBox(CSX, 'ut2', 0 ,start,stop); +CSX = AddProbe(CSX,'it2', 11, 1, [], 'ModeFunction',{func_Hr,func_Ha,0}); +CSX = AddBox(CSX,'it2', 0 ,start,stop); + +port_dist = mesh.z(end-10) - mesh.z(10); + +%% Write openEMS +if (postprocessing_only==0) + WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + + RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts); +end + +%% do the plots %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +freq = linspace(f0-f0_BW,f0+f0_BW,201); +U = ReadUI({'ut1','ut2'},[Sim_Path '/'],freq); +I = ReadUI({'it1','it2'},[Sim_Path '/'],freq); +Exc = ReadUI('et',Sim_Path,freq); + +k = 2*pi*freq/C0; +kc = p11 / rad /unit; +beta = sqrt(k.^2 - kc^2); + +ZL_a = Z0*k./beta ; + +uf1 = U.FD{1}.val./Exc.FD{1}.val; +uf2 = U.FD{2}.val./Exc.FD{1}.val; +if1 = I.FD{1}.val./Exc.FD{1}.val; +if2 = I.FD{2}.val./Exc.FD{1}.val; + +uf1_inc = 0.5 * ( uf1 + if1 .* ZL_a ); +if1_inc = 0.5 * ( if1 + uf1 ./ ZL_a ); +uf2_inc = 0.5 * ( uf2 + if2 .* ZL_a ); +if2_inc = 0.5 * ( if2 + uf2 ./ ZL_a ); + +uf1_ref = uf1 - uf1_inc; +if1_ref = if1 - if1_inc; +uf2_ref = uf2 - uf2_inc; +if2_ref = if2 - if2_inc; + +% plot s-parameter +figure +s11 = uf1_ref./uf1_inc; +s21 = uf2_inc./uf1_inc; +plot(freq,20*log10(abs(s11)),'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('s-para (dB)'); +% ylim([-40 5]); +grid on; +hold on; +plot(freq,20*log10(abs(s21)),'r','Linewidth',2); +legend('s11','s21','Location','SouthEast'); + +% plot line-impedance comparison +figure() +ZL = uf1./if1; +plot(freq,real(ZL),'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('line-impedance (\Omega)'); +grid on; +hold on; +plot(freq,imag(ZL),'r--','Linewidth',2); +plot(freq,ZL_a,'g-.','Linewidth',2); +legend('\Re\{ZL\}','\Im\{ZL\}','ZL-analytic','Location','Best'); + +%% beta compare +figure() +da = angle(uf1_inc)-angle(uf2_inc); +da = mod(da,2*pi); +beta_12 = (da)/port_dist/unit; +plot(freq,beta_12,'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)'); +ylabel('\beta (m^{-1})'); +grid on; +hold on; +plot(freq,beta,'g--','Linewidth',2); +legend('\beta-FDTD','\beta-analytic','Location','Best'); + +%% visualize electric & magnetic fields +disp('you will find vtk dump files in the simulation folder (tmp/)') +disp('use paraview to visulaize them'); diff --git a/openEMS/matlab/examples/waveguide/Coax.m b/openEMS/matlab/examples/waveguide/Coax.m new file mode 100644 index 0000000..3db4d6a --- /dev/null +++ b/openEMS/matlab/examples/waveguide/Coax.m @@ -0,0 +1,120 @@ +% +% EXAMPLE / waveguide / coaxial cable +% +% This example demonstrates how to: +% - setup a coaxial waveguide +% - use analytic functions for waveguide excitations and voltage/current +% calculations +% +% +% Tested with +% - Matlab 2009b +% - openEMS v0.0.17 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% switches & options... +postprocessing_only = 0; +use_pml = 0; % use pml boundaries instead of mur +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +numTS = 5000; %number of timesteps +length = 1000; %length of the waveguide +unit = 1e-3; %drawing unit used +coax_rad_i = 100; %inner radius +coax_rad_ai = 230; %inner radius of outer cladding +coax_rad_aa = 240; %outer radius of outer cladding +mesh_res = [5 5 5]; %desired mesh resolution + +physical_constants; + +%excitation +f0 = 0.5e9; +epsR = 1; + +%% create sim path %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Sim_Path = 'tmp'; +Sim_CSX = 'coax.xml'; + +if (postprocessing_only==0) + [status, message, messageid] = rmdir(Sim_Path,'s'); + [status, message, messageid] = mkdir(Sim_Path); +end + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(numTS,1e-5); +FDTD = SetGaussExcite(FDTD,f0,f0); +BC = {'PEC','PEC','PEC','PEC','MUR','MUR'}; +if (use_pml>0) + BC = {'PEC','PEC','PEC','PEC','PML_8','PML_8'}; +end +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = -coax_rad_aa : mesh_res(1) : coax_rad_aa; +mesh.y = mesh.x; +mesh.z = SmoothMeshLines([0 length], mesh_res(3)); +CSX = DefineRectGrid(CSX, unit, mesh); + +%%% coax +CSX = AddMetal(CSX,'copper'); +start = [0,0,0]; +stop = [0,0,length/2]; +[CSX,port{1}] = AddCoaxialPort( CSX, 10, 1, 'copper', '', start, stop, 'z', coax_rad_i, coax_rad_ai, coax_rad_aa, 'ExciteAmp', 1,'FeedShift', 10*mesh_res(1) ); + +start = [0,0,length/2]; +stop = [0,0,length]; +[CSX,port{2}] = AddCoaxialPort( CSX, 10, 2, 'copper', '', start, stop, 'z', coax_rad_i, coax_rad_ai, coax_rad_aa ); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et_','DumpMode',2); +start = [mesh.x(1) , 0 , mesh.z(1)]; +stop = [mesh.x(end) , 0 , mesh.z(end)]; +CSX = AddBox(CSX,'Et_',0 , start,stop); + +CSX = AddDump(CSX,'Ht_','DumpType',1,'DumpMode',2); +CSX = AddBox(CSX,'Ht_',0,start,stop); + +%% Write openEMS +if (postprocessing_only==0) + WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + CSXGeomPlot([Sim_Path '/' Sim_CSX]); + RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts); +end + +%% +freq = linspace(0,2*f0,201); +port = calcPort(port, Sim_Path, freq); + +%% plot s-parameter +figure +s11 = port{1}.uf.ref./port{1}.uf.inc; +s21 = port{2}.uf.inc./port{1}.uf.inc; +plot(freq,20*log10(abs(s11)),'Linewidth',2); +hold on +grid on +plot(freq,20*log10(abs(s21)),'r--','Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('s-para (dB)'); + +%% plot line-impedance comparison +figure() +ZL_a = ones(size(freq))*Z0/2/pi/sqrt(epsR)*log(coax_rad_ai/coax_rad_i); %analytic line-impedance of a coax +ZL = port{2}.uf.tot./port{2}.if.tot; +plot(freq,real(port{1}.ZL),'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('line-impedance (\Omega)'); +grid on; +hold on; +plot(freq,imag(port{1}.ZL),'r--','Linewidth',2); +plot(freq,ZL_a,'g-.','Linewidth',2); +legend('\Re\{ZL\}','\Im\{ZL\}','ZL-analytic','Location','Best'); diff --git a/openEMS/matlab/examples/waveguide/Coax_CylinderCoords.m b/openEMS/matlab/examples/waveguide/Coax_CylinderCoords.m new file mode 100644 index 0000000..5e4606c --- /dev/null +++ b/openEMS/matlab/examples/waveguide/Coax_CylinderCoords.m @@ -0,0 +1,190 @@ +% +% EXAMPLE / waveguide / coaxial cable using cylindrical coordinates +% +% This example demonstrates how to: +% - use cylindrical coordinates +% - setup a coaxial waveguide +% - use analytic functions for waveguide excitations and voltage/current +% calculations +% +% +% Tested with +% - Matlab 2009b +% - openEMS v0.0.17 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@uni-due.de> + +close all +clear +clc + +%% switches & options... +postprocessing_only = 0; +use_pml = 0; % use pml boundaries instead of mur +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +numTS = 1e5; %number of timesteps +length = 1000; %length of the waveguide +unit = 1e-3; %drawing unit used +coax_rad_i = 100; %inner radius +coax_rad_a = 230; %outer radius +mesh_res = [10 nan 10]; %desired mesh resolution +N_alpha = 71; %mesh lines in azimuth direction + +physical_constants; + +%excitation +f0 = 0.5e9; +epsR = 1; + +%% create sim path %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Sim_Path = 'tmp'; +Sim_CSX = 'coax.xml'; + +if (postprocessing_only==0) + [status, message, messageid] = rmdir(Sim_Path,'s'); + [status, message, messageid] = mkdir(Sim_Path); +end + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitCylindricalFDTD(numTS,1e-5); +FDTD = SetGaussExcite(FDTD,f0,f0); +BC = {'PEC','PEC','PEC','PEC','PEC','MUR'}; +if (use_pml>0) + BC = {'PEC','PEC','PEC','PEC','PEC','PML_8'}; +end +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX('CoordSystem',1); +mesh.x = coax_rad_i : mesh_res(1) : coax_rad_a; +mesh.y = linspace(0,2*pi,N_alpha); +mesh.z = 0 : mesh_res(3) : length; +CSX = DefineRectGrid(CSX, unit, mesh); + +%% material +CSX = AddMaterial(CSX,'fill'); +CSX = SetMaterialProperty(CSX,'fill','Epsilon',epsR); +start = [mesh.x(1) mesh.y(1) 0]; +stop = [mesh.x(end) mesh.y(end) length]; +CSX = AddBox(CSX,'fill',0 ,start,stop); + +%% apply the excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddExcitation(CSX,'excite',0,[1 0 0]); +weight{1} = '1/rho'; +weight{2} = 0; +weight{3} = 0; +CSX = SetExcitationWeight(CSX, 'excite', weight ); +start = [coax_rad_i mesh.y(1) 0]; +stop = [coax_rad_a mesh.y(end) 0]; +CSX = AddBox(CSX,'excite',0 ,start,stop); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et_','DumpMode',0); +start = [mesh.x(1) , 0 , mesh.z(1)]; +stop = [mesh.x(end) , 0 , mesh.z(end)]; +CSX = AddBox(CSX,'Et_',0 , start,stop); + +CSX = AddDump(CSX,'Ht_','DumpType',1,'DumpMode',0); +CSX = AddBox(CSX,'Ht_',0,start,stop); + +%% define voltage calc boxes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%voltage calc +CSX = AddProbe(CSX,'ut1',0); +start = [ coax_rad_i 0 mesh.z(10) ]; +stop = [ coax_rad_a 0 mesh.z(10) ]; +CSX = AddBox(CSX,'ut1', 0 ,start,stop); +CSX = AddProbe(CSX,'ut2',0); +start = [ coax_rad_i 0 mesh.z(end-10)]; +stop = [ coax_rad_a 0 mesh.z(end-10)]; +CSX = AddBox(CSX,'ut2', 0 ,start,stop); + +%current calc, for each position there are two currents, which will get +%averaged to match the voltage position in between (!Yee grid!) +CSX = AddProbe(CSX,'it1a',1); +mid = 0.5*(coax_rad_i+coax_rad_a); +start = [ 0 mesh.z(1) mesh.z(9) ]; +stop = [ mid mesh.z(end) mesh.z(9) ]; +CSX = AddBox(CSX,'it1a', 0 ,start,stop); +CSX = AddProbe(CSX,'it1b',1); +start = [ 0 mesh.z(1) mesh.z(10) ]; +stop = [ mid mesh.z(end) mesh.z(10) ]; +CSX = AddBox(CSX,'it1b', 0 ,start,stop); + +CSX = AddProbe(CSX,'it2a',1); +start = [ 0 mesh.z(1) mesh.z(end-11) ]; +stop = [ mid mesh.z(end) mesh.z(end-11) ]; +CSX = AddBox(CSX,'it2a', 0 ,start,stop); +CSX = AddProbe(CSX,'it2b',1); +start = [ 0 mesh.z(1) mesh.z(end-10) ]; +stop = [ mid mesh.z(end) mesh.z(end-10) ]; +CSX = AddBox(CSX,'it2b', 0 ,start,stop); + +%% Write openEMS +if (postprocessing_only==0) + WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts); +end + +%% +freq = linspace(0,2*f0,201); +U = ReadUI({'ut1','ut2'},[Sim_Path '/'],freq); +I = ReadUI({'it1a','it1b','it2a','it2b'},[Sim_Path '/'],freq); +Exc = ReadUI('et',Sim_Path,freq); + +%% plot voltages +figure +plot(U.TD{1}.t, U.TD{1}.val,'Linewidth',2); +hold on; +grid on; +plot(U.TD{2}.t, U.TD{2}.val,'r--','Linewidth',2); +xlabel('time (s)') +ylabel('voltage (V)') +legend('u_1(t)','u_2(t)') + +%% calculate incoming and reflected voltages & currents +ZL_a = ones(size(freq))*Z0/2/pi/sqrt(epsR)*log(coax_rad_a/coax_rad_i); %analytic line-impedance of a coax + +uf1 = U.FD{1}.val./Exc.FD{1}.val; +uf2 = U.FD{2}.val./Exc.FD{1}.val; +if1 = 0.5*(I.FD{1}.val+I.FD{2}.val)./Exc.FD{1}.val; +if2 = 0.5*(I.FD{3}.val+I.FD{4}.val)./Exc.FD{1}.val; + +uf1_inc = 0.5 * ( uf1 + if1 .* ZL_a ); +if1_inc = 0.5 * ( if1 + uf1 ./ ZL_a ); +uf2_inc = 0.5 * ( uf2 + if2 .* ZL_a ); +if2_inc = 0.5 * ( if2 + uf2 ./ ZL_a ); + +uf1_ref = uf1 - uf1_inc; +if1_ref = if1 - if1_inc; +uf2_ref = uf2 - uf2_inc; +if2_ref = if2 - if2_inc; + +% plot s-parameter +figure +s11 = uf1_ref./uf1_inc; +s21 = uf2_inc./uf1_inc; +plot(freq,20*log10(abs(s11)),'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('s-para (dB)'); +% ylim([-40 5]); +grid on; +hold on; +plot(freq,20*log10(abs(s21)),'r','Linewidth',2); +legend('s11','s21','Location','SouthEast'); + +% plot line-impedance comparison +figure() +ZL = uf1./if1; +plot(freq,real(ZL),'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)') +ylabel('line-impedance (\Omega)'); +grid on; +hold on; +plot(freq,imag(ZL),'r--','Linewidth',2); +plot(freq,ZL_a,'g-.','Linewidth',2); +legend('\Re\{ZL\}','\Im\{ZL\}','ZL-analytic','Location','Best'); diff --git a/openEMS/matlab/examples/waveguide/Coax_Cylindrical_MG.m b/openEMS/matlab/examples/waveguide/Coax_Cylindrical_MG.m new file mode 100644 index 0000000..84a1668 --- /dev/null +++ b/openEMS/matlab/examples/waveguide/Coax_Cylindrical_MG.m @@ -0,0 +1,155 @@ +close all +clear +clc + +%example for an cylindrical mesh, modeling a coaxial cable +% this example is using a multi-grid approach + + +%% setup %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +Settings = []; +Settings.LogFile = 'openEMS.log'; + +physical_constants + +f0 = 0.5e9; +epsR = 1; %material filling + +length = 1000; +port_dist = length/2; +rad_i = 10; %inner radius +rad_a = 200; %outer radius +partial = 0.5; %e.g. 0.5 means only one half of a coax, should be <1 or change boundary cond. +max_mesh = 10 / sqrt(epsR); +max_alpha = max_mesh; +N_alpha = ceil(rad_a * 2*pi * partial / max_alpha); + +%make it even... +N_alpha = N_alpha + mod(N_alpha,2); +%make sure it is multiple of 4, needed for 2 multi-grid steps +N_alpha = ceil((N_alpha)/4) *4 + 1; + +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --numThreads=1']; + +def_refSimu = 0; % do a reference simulation without the multi-grid + +%% setup done %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +if (def_refSimu>0) + Sim_Path = 'tmp_ref'; +else + Sim_Path = 'tmp'; +end +Sim_CSX = 'coax.xml'; + +if (exist(Sim_Path,'dir')) + rmdir(Sim_Path,'s'); +end +mkdir(Sim_Path); + +%setup FDTD parameter +if (def_refSimu>0) + FDTD = InitCylindricalFDTD(1e5,1e-5,'OverSampling',5 ); +else + FDTD = InitCylindricalFDTD(1e5,1e-5,'OverSampling',5 ,'MultiGrid','60,120'); +end +FDTD = SetGaussExcite(FDTD,f0,f0); +BC = [0 0 1 1 2 2]; +FDTD = SetBoundaryCond(FDTD,BC); + +mesh_res = [max_mesh 2*pi*partial/N_alpha max_mesh]; + +%setup CSXCAD geometry +CSX = InitCSX(); +mesh.x = SmoothMeshLines([rad_i rad_a],mesh_res(1)); +mesh.y = linspace(-pi*partial,pi*partial,N_alpha); +mesh.z = SmoothMeshLines([0 port_dist length],mesh_res(3)); +CSX = DefineRectGrid(CSX, 1e-3,mesh); + +start = [rad_i mesh.y(1) mesh.z(3)]; +stop = [rad_a mesh.y(end) mesh.z(3)]; + +CSX = AddExcitation(CSX,'excite',0,[1 0 0]); +weight{1} = '1/rho'; +weight{2} = 0; +weight{3} = 0; +CSX = SetExcitationWeight(CSX, 'excite', weight ); +CSX = AddBox(CSX,'excite',0 ,start,stop); + + +start = [mesh.x(1) mesh.y(1) mesh.z(1)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end)]; +CSX = AddMaterial(CSX,'material'); +CSX = SetMaterialProperty(CSX,'material','Epsilon',epsR); +CSX = AddBox(CSX,'material',0 ,start,stop); + +%dump +CSX = AddDump(CSX,'Et_rz_','DumpMode',0); +start = [mesh.x(1) 0 mesh.z(1)]; +stop = [mesh.x(end) 0 mesh.z(end)]; +CSX = AddBox(CSX,'Et_rz_',0 , start,stop); + +CSX = AddDump(CSX,'Ht_rz_','DumpType',1,'DumpMode',0); +CSX = AddBox(CSX,'Ht_rz_',0 , start,stop); + +CSX = AddDump(CSX,'Et_','DumpType',0,'DumpMode',0); +start = [mesh.x(1) mesh.y(1) length/2]; +stop = [mesh.x(end) mesh.y(end) length/2]; +CSX = AddBox(CSX,'Et_',0,start,stop); + +CSX = AddDump(CSX,'Ht_','DumpType',1,'DumpMode',0); +start = [mesh.x(1) mesh.y(1) length/2]; +stop = [mesh.x(end) mesh.y(end) length/2]; +CSX = AddBox(CSX,'Ht_',0,start,stop); + +% voltage calc (take a voltage average to be at the same spot as the +% current calculation) +CSX = AddProbe(CSX,'ut1_1',0); +start = [ rad_i 0 port_dist ];stop = [ rad_a 0 port_dist ]; +CSX = AddBox(CSX,'ut1_1', 0 ,start,stop); +CSX = AddProbe(CSX,'ut1_2',0); +start = [ rad_i 0 port_dist+mesh_res(3) ];stop = [ rad_a 0 port_dist+mesh_res(3) ]; +CSX = AddBox(CSX,'ut1_2', 0 ,start,stop); + +% current calc +CSX = AddProbe(CSX,'it1',1); +mid = 75; +start = [ 0 mesh.y(1) port_dist+mesh_res(3)/2 ];stop = [ mid mesh.y(end) port_dist+mesh_res(3)/2 ]; +CSX = AddBox(CSX,'it1', 0 ,start,stop); + +%% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + +RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts, Settings) + +%% +close all +freq = linspace(0,2*f0,201); +UI = ReadUI({'ut1_1','ut1_2','it1'},Sim_Path,freq); +u_f = (UI.FD{1}.val + UI.FD{2}.val)/2; %averaging voltages to fit current +i_f = UI.FD{3}.val / partial; + +% plot(UI.TD{1}.t,UI.TD{1}.val); +% grid on; +% +% figure +% plot(UI.TD{3}.t,UI.TD{3}.val); +% grid on; + +%plot Z_L compare +figure +ZL = Z0/2/pi/sqrt(epsR)*log(rad_a/rad_i); %analytic line-impedance of a coax +plot(UI.FD{1}.f,ZL*ones(size(u_f)),'g','Linewidth',3); +hold on; +grid on; +Z = u_f./i_f; +plot(UI.FD{1}.f,real(Z),'k--','Linewidth',2); +plot(UI.FD{1}.f,imag(Z),'r-','Linewidth',2); +xlim([0 2*f0]); +legend('Z_L - analytic','\Re\{Z\} - FDTD','\Im\{Z\} - FDTD','Location','Best'); + + diff --git a/openEMS/matlab/examples/waveguide/Rect_Waveguide.m b/openEMS/matlab/examples/waveguide/Rect_Waveguide.m new file mode 100644 index 0000000..a9601ad --- /dev/null +++ b/openEMS/matlab/examples/waveguide/Rect_Waveguide.m @@ -0,0 +1,240 @@ +% +% EXAMPLE / waveguide / Rect_Waveguide +% +% This example demonstrates: +% - waveguide mode excitation +% - waveguide mode matching +% - pml absorbing boundaries +% +% +% Tested with +% - Matlab 2009b +% - openEMS v0.0.17 +% +% (C) 2010 Thorsten Liebig <thorsten.liebig@gmx.de> + +close all +clear +clc + +%% switches +postproc_only = 0; + +%% setup the simulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +physical_constants; +unit = 1e-3; %drawing unit in mm +numTS = 50000; %max. number of timesteps + +% waveguide dimensions +length = 1000; +a = 1000; %waveguide width +b = 600; %waveguide heigth + +%waveguide TE-mode definition +m = 1; +n = 0; + +mesh_res = [10 10 10]; + +%% setup FDTD parameters & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%% +f_start = 175e6; +f_stop = 500e6; + +% dump special frequencies to vtk, use paraview (www.paraview.org) to +% animate this dumps over phase +vtk_dump_freq = [200e6 300e6 500e6]; + +freq = linspace(f_start,f_stop,201); + +k = 2*pi*freq/c0; +kc = sqrt((m*pi/a/unit)^2 + (n*pi/b/unit)^2); +fc = c0*kc/2/pi; %cut-off frequency +beta = sqrt(k.^2 - kc^2); %waveguide phase-constant +ZL_a = k * Z0 ./ beta; %analytic waveguide impedance + +disp([' Cutoff frequencies for this mode and wavguide is: ' num2str(fc/1e6) ' MHz']); + +if (f_start<fc) + warning('openEMS:example','f_start is smaller than the cutoff-frequency, this may result in a long simulation... '); +end + +%% mode functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% by David M. Pozar, Microwave Engineering, third edition, page 113 +func_Ex = [num2str( n/b/unit) '*cos(' num2str(m*pi/a) '*x)*sin(' num2str(n*pi/b) '*y)']; +func_Ey = [num2str(-m/a/unit) '*sin(' num2str(m*pi/a) '*x)*cos(' num2str(n*pi/b) '*y)']; + +func_Hx = [num2str(m/a/unit) '*sin(' num2str(m*pi/a) '*x)*cos(' num2str(n*pi/b) '*y)']; +func_Hy = [num2str(n/b/unit) '*cos(' num2str(m*pi/a) '*x)*sin(' num2str(n*pi/b) '*y)']; + +%% define and openEMS options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +openEMS_opts = ''; +% openEMS_opts = [openEMS_opts ' --disable-dumps']; +% openEMS_opts = [openEMS_opts ' --debug-material']; +% openEMS_opts = [openEMS_opts ' --engine=basic']; + +Settings = []; +Settings.LogFile = 'openEMS.log'; + +Sim_Path = 'tmp'; +Sim_CSX = 'rect_wg.xml'; + +if (postproc_only==0) + [status, message, messageid] = rmdir(Sim_Path,'s'); + [status, message, messageid] = mkdir(Sim_Path); +end + +%% setup FDTD parameter & excitation function %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FDTD = InitFDTD(numTS,1e-5,'OverSampling',6); +FDTD = SetGaussExcite(FDTD,0.5*(f_start+f_stop),0.5*(f_stop-f_start)); +BC = [0 0 0 0 0 3]; +FDTD = SetBoundaryCond(FDTD,BC); + +%% setup CSXCAD geometry & mesh %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = InitCSX(); +mesh.x = SmoothMeshLines([0 a], mesh_res(1)); +mesh.y = SmoothMeshLines([0 b], mesh_res(2)); +mesh.z = SmoothMeshLines([0 length], mesh_res(3)); +CSX = DefineRectGrid(CSX, unit,mesh); + +%% apply the excitation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +start=[mesh.x(1) mesh.y(1) mesh.z(1) ]; +stop =[mesh.x(end) mesh.y(end) mesh.z(1) ]; +CSX = AddExcitation(CSX,'excite',0,[1 1 0]); +weight{1} = func_Ex; +weight{2} = func_Ey; +weight{3} = 0; +CSX = SetExcitationWeight(CSX,'excite',weight); +CSX = AddBox(CSX,'excite',0 ,start,stop); + +%% voltage and current definitions using the mode matching probes %%%%%%%%% +%port 1 +start = [mesh.x(1) mesh.y(1) mesh.z(15)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(15)]; +CSX = AddProbe(CSX, 'ut1', 10, 1, [], 'ModeFunction',{func_Ex,func_Ey,0}); +CSX = AddBox(CSX, 'ut1', 0 ,start,stop); +CSX = AddProbe(CSX,'it1', 11, 1, [], 'ModeFunction',{func_Hx,func_Hy,0}); +CSX = AddBox(CSX,'it1', 0 ,start,stop); + +%port 2 +start = [mesh.x(1) mesh.y(1) mesh.z(end-15)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end-15)]; +CSX = AddProbe(CSX, 'ut2', 10, 1, [], 'ModeFunction',{func_Ex,func_Ey,0}); +CSX = AddBox(CSX, 'ut2', 0 ,start,stop); +CSX = AddProbe(CSX,'it2', 11, 1, [], 'ModeFunction',{func_Hx,func_Hy,0}); +CSX = AddBox(CSX,'it2', 0 ,start,stop); + +port_dist = mesh.z(end-15) - mesh.z(15); + +%% define dump boxes... %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +CSX = AddDump(CSX,'Et','FileType',1,'SubSampling','4,4,2'); +start = [mesh.x(1) mesh.y(1) mesh.z(1)]; +stop = [mesh.x(end) mesh.y(end) mesh.z(end)]; +CSX = AddBox(CSX,'Et',0 , start,stop); + +CSX = AddDump(CSX,'Ht','DumpType',1,'FileType',1,'SubSampling','4,4,2'); +CSX = AddBox(CSX,'Ht',0,start,stop); + +%% Write openEMS compatoble xml-file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if (postproc_only==0) + WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX); + + RunOpenEMS(Sim_Path, Sim_CSX, openEMS_opts, Settings) +end + +%% postproc %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +U = ReadUI({'ut1','ut2'},[Sim_Path '/'],freq); +I = ReadUI({'it1','it2'},[Sim_Path '/'],freq); +Exc = ReadUI('et',Sim_Path,freq); + +uf1 = U.FD{1}.val./Exc.FD{1}.val; +uf2 = U.FD{2}.val./Exc.FD{1}.val; +if1 = I.FD{1}.val./Exc.FD{1}.val; +if2 = I.FD{2}.val./Exc.FD{1}.val; + +uf1_inc = 0.5 * ( uf1 + if1 .* ZL_a ); +if1_inc = 0.5 * ( if1 + uf1 ./ ZL_a ); +uf2_inc = 0.5 * ( uf2 + if2 .* ZL_a ); +if2_inc = 0.5 * ( if2 + uf2 ./ ZL_a ); + +uf1_ref = uf1 - uf1_inc; +if1_ref = if1 - if1_inc; +uf2_ref = uf2 - uf2_inc; +if2_ref = if2 - if2_inc; + +%% plot s-parameter %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +figure +s11 = uf1_ref./uf1_inc; +s21 = uf2_inc./uf1_inc; +plot(freq,20*log10(abs(s11)),'Linewidth',2); +xlim([freq(1) freq(end)]); +% ylim([-40 5]); +grid on; +hold on; +plot(freq,20*log10(abs(s21)),'r','Linewidth',2); +legend('s11','s21','Location','SouthEast'); +ylabel('s-para (dB)'); +xlabel('freq (Hz)'); + +%% compare analytic and numerical wave-impedance %%%%%%%%%%%%%%%%%%%%%%%%%% +ZL = uf1./if1; +figure() +plot(freq,real(ZL),'Linewidth',2); +hold on; +grid on; +plot(freq,imag(ZL),'r--','Linewidth',2); +plot(freq,ZL_a,'g-.','Linewidth',2); +ylabel('ZL (\Omega)'); +xlabel('freq (Hz)'); +xlim([freq(1) freq(end)]); +legend('\Re(Z_L)','\Im(Z_L)','Z_L analytic','Location','Best'); + +%% beta compare +figure() +da = unwrap(angle(uf1_inc./uf2_inc)) ; +% da = mod(da,2*pi); +beta_12 = (da)/port_dist/unit; +plot(freq,beta_12,'Linewidth',2); +xlim([freq(1) freq(end)]); +xlabel('frequency (Hz)'); +ylabel('\beta (m^{-1})'); +grid on; +hold on; +plot(freq,beta,'g--','Linewidth',2); +legend('\beta-FDTD','\beta-analytic','Location','Best'); + +%% Plot the field dumps %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +dump_file = [Sim_Path '/Et.h5']; +figure() +PlotArgs.slice = {a/2*unit b/2*unit 0}; +PlotArgs.pauseTime=0.01; +PlotArgs.component=0; +PlotArgs.Limit = 'auto'; +PlotHDF5FieldData(dump_file, PlotArgs) + +%% dump frequency to vtk %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% cleanup and create dump folder +vtk_path = [Sim_Path '/vtk']; +[status, message, messageid] = rmdir(vtk_path,'s'); +[status, message, messageid] = mkdir(vtk_path); + +disp('Dumping to vtk files... this may take a minute...') +% define interpolation mesh +mesh_interp{1}=mesh.x * unit; +mesh_interp{2}=b/2 * unit; +mesh_interp{3}=mesh.z * unit; +[field mesh_FD] = ReadHDF5Dump(dump_file,'Interpolation',mesh_interp,'Frequency',vtk_dump_freq); + +% dump animated phase to vtk +for n=1:numel(vtk_dump_freq) + phase = linspace(0,360,21); + phase = phase(1:end-1); + for ph = phase + filename = [vtk_path '/E_xz_f=' num2str(vtk_dump_freq(n)) '_p' num2str(ph) '.vtk']; + Dump2VTK(filename,real(field.FD.values{n}.*exp(1j*ph/180*pi)),mesh_FD,'E-Field'); + end + + filename = [vtk_path '/E_xz_f=' num2str(vtk_dump_freq(n)) '_mag.vtk']; + Dump2VTK(filename,abs(field.FD.values{n}),mesh_FD,'E-Field'); +end + +disp('done... you can open and visualize the vtk-files using Paraview (www.paraview.org)!') diff --git a/openEMS/matlab/h5readatt_octave.cc b/openEMS/matlab/h5readatt_octave.cc new file mode 100755 index 0000000..8bd58d0 --- /dev/null +++ b/openEMS/matlab/h5readatt_octave.cc @@ -0,0 +1,135 @@ +#include <octave/oct.h> +#include <octave/ov-struct.h> +#include "hdf5.h" + +// this special treatment is necessary because Win32-Octave ships with a very old hdf5 version (1.6.10) +void CloseH5Object(hid_t obj) +{ +#if ((H5_VERS_MAJOR == 1) && (H5_VERS_MINOR == 6)) + // try group close, than Dataset close + if (H5Gclose(obj)<0) + H5Dclose(obj); +#else + H5Oclose(obj); +#endif +} + +DEFUN_DLD (h5readatt_octave, args, nargout, "h5readatt_octave(<File_Name>,<DataSet_Name>,<Attribute_Name>)") +{ + octave_value retval; + int nargin = args.length(); + if (nargin != 3) + { + print_usage(); + return retval; + } + if ((args(0).is_string()==false) || (args(1).is_string()==false) || (args(2).is_string()==false)) + { + print_usage(); + return retval; + } + + //suppress hdf5 error output + H5Eset_auto1(NULL, NULL); + + hid_t file = H5Fopen( args(0).string_value().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT ); + if (file==-1) + { + error("h5readatt_octave: opening the given File failed"); + return retval; + } + +#if ((H5_VERS_MAJOR == 1) && (H5_VERS_MINOR == 6)) + // this special treatment is necessary because Win32-Octave ships with a very old hdf5 version (1.6.10) + hid_t obj = -1; + //try opening the group + obj = H5Gopen(file, args(1).string_value().c_str()); + //try opening the dataset if group failed + if (obj==-1) + obj = H5Dopen(file, args(1).string_value().c_str()); +#else + hid_t obj = H5Oopen(file, args(1).string_value().c_str(), H5P_DEFAULT); +#endif + + if (obj==-1) + { + CloseH5Object(obj); + H5Fclose(file); + error("h5readatt_octave: opening the given Object failed"); + return retval; + } + + hid_t attr = H5Aopen_name(obj, args(2).string_value().c_str()); + if (attr==-1) + { + CloseH5Object(obj); + H5Fclose(file); + error("h5readatt_octave: opening the given Attribute failed"); + return retval; + } + + hid_t type = H5Aget_type(attr); + if (type<0) + { + H5Aclose(attr); + CloseH5Object(obj); + H5Fclose(file); + error("h5readatt_octave: dataset type error"); + return retval; + } + + if (H5Tget_class(type)!=H5T_FLOAT) + { + H5Aclose(attr); + CloseH5Object(obj); + H5Fclose(file); + error("h5readatt_octave: attribute type not supported"); + return retval; + } + + size_t numVal = H5Aget_storage_size(attr)/H5Tget_size(type); + double value[numVal]; + if (H5Tget_size(type)==sizeof(float)) + { + float f_value[numVal]; + if (H5Aread(attr, H5T_NATIVE_FLOAT, f_value)<0) + { + H5Aclose(attr); + CloseH5Object(obj); + H5Fclose(file); + error("h5readatt_octave: reading the given Attribute failed"); + return retval; + } + for (size_t n=0;n<numVal;++n) + value[n] = f_value[n]; + } + else if (H5Tget_size(type)==sizeof(double)) + { + if (H5Aread(attr, H5T_NATIVE_DOUBLE, value)<0) + { + H5Aclose(attr); + CloseH5Object(obj); + H5Fclose(file); + error("h5readatt_octave: reading the given Attribute failed"); + return retval; + } + } + else + { + H5Aclose(attr); + CloseH5Object(obj); + H5Fclose(file); + error("h5readatt_octave: reading the given Attribute failed: unknown type"); + return retval; + } + + H5Aclose(attr); + CloseH5Object(obj); + H5Fclose(file); + Matrix mat(numVal,1); + for (size_t n=0;n<numVal;++n) + mat(n)=value[n]; + retval = octave_value(mat); + return retval; +} + diff --git a/openEMS/matlab/harminv.m b/openEMS/matlab/harminv.m new file mode 100644 index 0000000..a5d0c77 --- /dev/null +++ b/openEMS/matlab/harminv.m @@ -0,0 +1,77 @@ +function [f,decay,Q,amp,phase,err]=harminv( timeseries, timestep, start_freq, stop_freq ) +% [f,decay,Q,amp,phase,err]=harminv( timeseries, timestep, start_freq, stop_freq ) +% +% reconstruct time signal with: +% real(amp(n) * exp(-1i*(2*pi*freq(n)*t-phase(n))-decay(n)*t))); +% +% example: +% t = linspace(0,(1/0.3e9)*5,1000); +% u = 0.8 * sin(2*pi*0.7e9 * t + 0/180*pi); +% u = u + 0.3 * sin(2*pi*0.3e9 * t + 0/180*pi); +% [freq,decay,Q,amp,phase,err]=harminv( u, t(2)-t(1), 0, 1e9 ); +% +% ----------------------- +% Sebastian Held <sebastian.held@gmx.de> + +if isunix + harminv_exe = 'export LD_LIBRARY_PATH=; harminv'; +elseif ispc + m_filename = mfilename('fullpath'); + dir = fileparts( m_filename ); + harminv_exe = [ '"' dir '\..\harminv.exe"']; +else + error('openEMS:harminv','unknown/unsupported operating system...'); +end + +options = ['-t ' num2str(timestep)]; +tmpfile = tempname; + +% convert to column vector +if size(timeseries,2) > size(timeseries,1) + timeseries = timeseries.'; +end + +% harminv hangs if timeseries is zero only +if all(timeseries == 0) + disp( 'timeseries is 0' ); + return +end + +% write timeseries to temporary file +dlmwrite( tmpfile, timeseries ); + +command = [harminv_exe ' ' options ' '... + num2str(start_freq) '-' num2str(stop_freq) ' < "' tmpfile '"']; +[status,result] = system( command ); +if status ~= 0 + disp( 'error executing harminv:' ); + disp( command ); + disp( ['exit status: ' num2str(status)] ); + disp( ['output: ' result] ); + return +end + +%disp( command ) +%disp( result ) + +f = []; +decay = []; +Q = []; +amp = []; +phase = []; +err = []; + +lines = textscan( result, '%s', 'Delimiter', '\n'); + +for n=2:numel(lines{1}) + [C] = textscan( lines{1}{n}, '%f', 6, 'Delimiter', ','); + if isempty(C{1}), break; end + if C{1}(1) >= 0 + f = [f C{1}(1)]; + decay = [decay C{1}(2)]; + Q = [Q C{1}(3)]; + amp = [amp 2*C{1}(4)]; % neglecting negative frequencies => amplitude doubles + phase = [phase C{1}(5)]; + err = [err C{1}(6)]; + end +end diff --git a/openEMS/matlab/optimize.m b/openEMS/matlab/optimize.m new file mode 100644 index 0000000..78ef497 --- /dev/null +++ b/openEMS/matlab/optimize.m @@ -0,0 +1,203 @@ +function [params,result] = optimize( optimdir, params, options, algorithm ) +%params = optimize( optimdir, params, options, algorithm ) +% +% input: +% optimdir: folder where to optimize +% params: array of structures with parameters to optimize +% .name char string parameter name (e.g. 'length1') +% .value number +% .step number discretization of .value +% .range row vector range of .value (e.g. [1 10]) +% .active (0/1) 1=optimize this parameter +% options: structure +% .folder_matlabstart set the startup folder for matlab or octave +% .simfun char string with the simulation function name +% .octave_exe if this field is present, octave is used +% .clean clean the optimization folder before optimization start +% algorithm: 'asco' or 'simplex-downhill' +% +% output: +% params: optimal values +% result: optimization criterion for optimal values +% +% example: +% see openEMS/matlab/examples/optimizer +% +% notes: +% Create a file named 'STOP_OPTIMIZATION' in the optimdir folder to stop +% the optimization process (or press Ctrl+C). +% +% (C) 2010 Sebastian Held <sebastian.held@gmx.de> + +error( nargchk(3,4,nargin) ); + +% default to simplex-downhill +if nargin < 4 + algorithm = 'simplex-downhill'; +end +if ~strcmp( algorithm, 'asco' ) + algorithm = 'simplex-downhill'; +end + +optimdir = absolutepath( optimdir ); +if isfield(options,'clean') && (options.clean == 1) + [a,a,a] = rmdir( optimdir, 's' ); +end +[a,a,a] = mkdir( optimdir ); +oldfolder = cd( optimdir ); + +if strcmp( algorithm, 'asco' ) + % --------------------------------------------------------------------- + % selected algorithm: ASCO + % http://asco.sourceforge.net/ + +% if ~exist( 'asco', 'file' ) && ~exist( 'asco.exe', 'file' ) +% error 'asco was not found in PATH. Download from http://asco.sf.net/' +% end + + % create asco config file + fid = fopen( 'asco.cfg', 'wt' ); + fprintf( fid, '* asco configuration file\n' ); + fprintf( fid, '* http://asco.sourceforge.net/\n' ); + fprintf( fid, '* \n' ); + fprintf( fid, '* Optimization for openEMS\n\n' ); + fprintf( fid, '#Optimization Flow#\n' ); + fprintf( fid, 'Alter:no $do we want to do corner analysis?\n' ); + fprintf( fid, 'MonteCarlo:no $do we want to do MonteCarlo analysis?\n' ); + fprintf( fid, 'AlterMC cost:0.00 $point below which ALTER and/or MONTECARLO can start\n' ); + fprintf( fid, 'ExecuteRF:no $Execute or no the RF module to add RF parasitics?\n' ); + fprintf( fid, '#\n\n' ); + fprintf( fid, '#DE#\n' ); + fprintf( fid, 'choice of method:3\n' ); + fprintf( fid, 'maximum no. of iterations:50\n' ); + fprintf( fid, 'Output refresh cycle:2\n' ); + fprintf( fid, 'No. of parents NP:10\n' ); + fprintf( fid, 'Constant F:0.85\n' ); + fprintf( fid, 'Crossing Over factor CR:1\n' ); + fprintf( fid, 'Seed for pseudo random number generator:3\n' ); + fprintf( fid, 'Minimum Cost Variance:1e-6\n' ); + fprintf( fid, 'Cost objectives:10\n' ); + fprintf( fid, 'Cost constraints:100\n' ); + fprintf( fid, '#\n\n' ); + + fprintf( fid, '# Parameters #\n' ); + for n=1:numel(params) + if params(n).active == 1 + active = 'OPT'; + else + active = '---'; + end + value = params(n).value / params(n).step; + range = params(n).range / params(n).step; + fprintf( fid, 'description:#%s#:%i:%i:%i:LIN_INT:%s\n', params(n).name, value, range(1), range(2), active ); + end + fprintf( fid, '#\n\n' ); + + fprintf( fid, '# Measurements #\n' ); + fprintf( fid, 'value:---:MIN:0\n' ); + fprintf( fid, '#\n\n' ); + fclose(fid); + + % create extract file + [a,a,a]=mkdir( 'extract' ); + fid = fopen( 'extract/value', 'wt' ); + fprintf( fid, '# Info #\n' ); + fprintf( fid, 'Name:value\n' ); + fprintf( fid, 'Symbol:value\n' ); + fprintf( fid, '#\n\n' ); + fprintf( fid, '# Commands #\n' ); + fprintf( fid, '#\n\n' ); + fprintf( fid, '# Post Processing #\n' ); + fprintf( fid, 'MEASURE_VAR: #SYMBOL#: SEARCH_FOR:''value=''\n' ); + fprintf( fid, '#\n' ); + fclose(fid); + + % create matlab parameter file + fid = fopen( 'asco.txt', 'wt' ); + fprintf( fid, '%% this file is processed by asco and variables enclosed in ## are substituted\n' ); + fprintf( fid, 'params = [];\n' ); + for n=1:numel(params) + fprintf( fid, 'params.%s = #%s# * %i;\n', params(n).name, params(n).name, params(n).step ); + end + fclose(fid); + + % create shell script + folder_asco_helper = fileparts( mfilename('fullpath') ); + asco_sim_helper = 'optimizer_asco_sim'; + fid = fopen( 'general.sh', 'wt' ); + fprintf( fid, '#!/bin/sh\n' ); + fprintf( fid, 'rm "$2.out" 2> /dev/null\n' ); + fprintf( fid, 'mv "$1.txt" "$1.m"\n' ); + fprintf( fid, 'if [ -f STOP_OPTIMIZATION ]; then\n' ); + fprintf( fid, ' exit\n' ); + fprintf( fid, 'fi\n' ); + fprintf( fid, 'oldpwd=$PWD\n' ); + if isfield(options,'folder_matlabstart') + % this allows to start the new matlab process in a specific folder + % => startup.m is picked up here + fprintf( fid, 'cd "%s"\n', functions.folder_matlabstart ); + end + if ~isfield(options,'octave_exe') + % matlab + fprintf( fid, '%s/bin/matlab -nodesktop -nosplash -r "cd ''%s''; %s(''%s'',''$1'',''$2.out'',''%s''); exit"\n', matlabroot, folder_asco_helper, asco_sim_helper, optimdir, options.simfun ); + else + % octave + fprintf( fid, 'export LD_LIBRARY_PATH=\n' ); + fprintf( fid, '%s --silent --eval "cd ''%s''; %s(''%s'',''$1'',''$2.out'',''%s'');"\n', options.octave_exe, folder_asco_helper, asco_sim_helper, optimdir, options.simfun ); + end + fprintf( fid, 'cd "$oldpwd"\n' ); + fclose(fid); + fileattrib( 'general.sh', '+x' ); % make it executable + + % clean up old data + if exist( './best_result.mat', 'file' ), delete( 'best_result.mat' ); end + if exist( './STOP_OPTIMIZATION', 'file' ), delete( 'STOP_OPTIMIZATION' ); end + + % start asco + [status,result] = unix( 'asco -general asco.txt', '-echo' ); + + % get best result + best = load( 'best_result.mat' ); + best = best.best; + result = best.result; + for n=1:numel(params) + name = params(n).name; + if isfield(best.params,name) + params(n).value = best.params.(name); + end + end + +elseif strcmp( algorithm, 'simplex-downhill' ) + % --------------------------------------------------------------------- + % selected algorithm: simplex-downhill + % Thorsten Liebig <thorsten.liebig@uni-due.de> + + error( 'not implemented yet' ); +end + +cd( oldfolder ); + + + + + +function folder = absolutepath( folder ) +%folder = absolutepath( folder ) +% make the path absolute +if isunix + % Unix + if folder(1) == '/' + return + end + folder = fullfile( pwd, folder ); +else + % Windows + folder = strrep( folder, '\', '/' ); + if strcmp( folder(2:3), ':/' ) || strcmp( folder(1:2), '//' ) || (folder(1) == '/') + return + end + if (folder(2) == ':') && (folder(3) ~= '/') + error( 'relative paths with drive specifier are not supported' ); + end + folder = fullfile( pwd, folder ); +end diff --git a/openEMS/matlab/optimizer_asco_sim.m b/openEMS/matlab/optimizer_asco_sim.m new file mode 100644 index 0000000..6f62ebf --- /dev/null +++ b/openEMS/matlab/optimizer_asco_sim.m @@ -0,0 +1,88 @@ +function optimizer_asco_sim( optimdir, inputfile, outputfile, simfun ) +%optimizer_asco_sim( optimdir, inputfile, outputfile, simfun ) +% +% This function is called from general.sh. Do not call it yourself. +% +% tasks: +% - set correct matlab path +% - evaluate inputfile +% - start simulation or get result from cache +% - postprocess simulation results +% - create output file (important: needs single \n at the first line and double \n at the last line!) + +error( nargchk(4,4,nargin) ); + +% add CSXCAD and openEMS to the matlab path +folder = fileparts( mfilename('fullpath') ); +addpath( folder ); +addpath( [folder '/../../CSXCAD/matlab'] ); + +% change to optimdir +olddir = pwd; +cd( optimdir ); + +% read parameters set by asco +if ~isempty( strfind(inputfile,'-') ) + % matlab cannot execute a file with dashes... + inputfile2 = strrep( inputfile,'-','_' ); + movefile( [inputfile '.m'], [inputfile2 '.m'] ); + run( inputfile2 ); + movefile( [inputfile2 '.m'], [inputfile '.m'] ); +end +% now a structure named 'params' is available + +% check cache +folder = create_folder_name( params ); +if exist( ['./' folder], 'dir' ) && exist( ['./' folder '/result.mat'], 'file' ) + % read cache + disp( 'CACHE HIT' ); + result = load( [folder '/result.mat'], 'result' ); + result = result.result; +else + % start simulation in folder <folder> + disp( ['starting simulation function ' simfun] ); + disp( [' simulation folder ' folder] ); + [simfun_folder,simfun] = fileparts(simfun); + oldpath = path; + addpath( simfun_folder ); + fhandle = str2func(simfun); % does not work for octave-3.2.4! + path( oldpath ); + mkdir( folder ); + result = fhandle(folder,params); + save( [folder '/result.mat'], 'result', '-mat' ); +end + +% write results for asco +fid = fopen( outputfile, 'wt' ); +fprintf( fid, '\nvalue= %e\n\n', result ); +fclose( fid ); + +% update best result +best = []; +best.result = result; +best.params = params; +if exist( [pwd '/best_result.mat'], 'file' ) + old = load( 'best_result.mat', 'best' ); + if old.best.result > best.result + save( 'best_result.mat', 'best', '-mat' ); + end +else + save( 'best_result.mat', 'best', '-mat' ); +end + +% restore old folder +cd( olddir ); + + + + + + + +function folder = create_folder_name( params ) +params = orderfields( params ); +folder = 'opt'; +fnames = fieldnames(params); +for n=1:numel(fnames) + folder = [folder '_' fnames{n} '=' num2str(params.(fnames{n}))]; +end diff --git a/openEMS/matlab/physical_constants.m b/openEMS/matlab/physical_constants.m new file mode 100644 index 0000000..cbb333e --- /dev/null +++ b/openEMS/matlab/physical_constants.m @@ -0,0 +1,12 @@ +% +% physical constants +% + +% Bronstein 3rd ed., 1997, pp. 945-946 +C0 = 299792458; % m/s +c0 = C0; %constans in capital letters, c0 for legacy support +MUE0 = 4e-7*pi; % N/A^2 +EPS0 = 1/(MUE0*C0^2); % F/m + +% free space wave impedance +Z0 = sqrt(MUE0/EPS0); % Ohm diff --git a/openEMS/matlab/plotFF3D.m b/openEMS/matlab/plotFF3D.m new file mode 100644 index 0000000..45950c8 --- /dev/null +++ b/openEMS/matlab/plotFF3D.m @@ -0,0 +1,91 @@ +function h = plotFF3D(nf2ff,varargin) +% h = plotFF3D(nf2ff,varargin) +% +% plot normalized 3D far field pattern +% +% input: +% nf2ff: output of CalcNF2FF +% +% variable input: +% 'freq_index': - use the given frequency index, see nf2ff.freq +% - default is 1 +% 'logscale': - if set, show farfield with logarithmic scale +% - set the dB value for point of origin +% - values below will be clamped +% 'normalize': - true/false, normalize linear plot +% - default is false, log-plot is always normalized! +% +% example: +% plotFF3D(nf2ff, 'freq_index', 2, 'logscale', -20) +% +% see examples/antennas/infDipol.m +% +% See also CalcNF2FF, plotFFdB, polarFF +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig, Stefan Mahr + +% defaults +logscale = []; +freq_index = 1; +normalize = 0; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'logscale')==1); + logscale = varargin{n+1}; + elseif (strcmp(varargin{n},'freq_index')==1); + freq_index = varargin{n+1}; + elseif (strcmp(varargin{n},'normalize')==1); + normalize = varargin{n+1}; + else + warning('openEMS:plotFF3D',['unknown argument key: ''' varargin{n} '''']); + end +end + +if ((normalize~=0) || ~isempty(logscale)) + E_far = nf2ff.E_norm{freq_index} / max(nf2ff.E_norm{freq_index}(:)); +else + E_far = nf2ff.E_norm{freq_index}; +end; + +if ~isempty(logscale) + E_far = 20*log10(E_far)/-logscale + 1; + E_far = E_far .* ( E_far > 0 ); + titletext = sprintf('electrical far field [dB] @ f = %e Hz',nf2ff.freq(freq_index)); +elseif (normalize==0) + titletext = sprintf('electrical far field [V/m] @ f = %e Hz',nf2ff.freq(freq_index)); +else + titletext = sprintf('normalized electrical far field @ f = %e Hz',nf2ff.freq(freq_index)); +end + +[theta,phi] = ndgrid(nf2ff.theta,nf2ff.phi); +x = E_far .* sin(theta) .* cos(phi); +y = E_far .* sin(theta) .* sin(phi); +z = E_far .* cos(theta); +%figure +h = surf( x,y,z, E_far ); +set(h,'EdgeColor','none'); +axis equal +axis off + +try + if (isOctave && (strcmp(graphics_toolkit,'gnuplot')==1)) + warning('openEMS:plotFF3D','Colorbar doesn''t work properly with octave and gnuplot. On problems, try ''colorbar off'''); + end +end + +if ~isempty(logscale) + colorbar('YTick', linspace(0,max(E_far(:)),9), ... + 'YTickLabel',num2str(linspace(logscale, 10*log10(nf2ff.Dmax(freq_index)),9)')); +else + colorbar; +end + +title( titletext ); + +if (nargout == 0) + clear h; +end + +end diff --git a/openEMS/matlab/plotFFdB.m b/openEMS/matlab/plotFFdB.m new file mode 100644 index 0000000..07e96e3 --- /dev/null +++ b/openEMS/matlab/plotFFdB.m @@ -0,0 +1,78 @@ +function h = plotFFdB(nf2ff,varargin) +% h = plotFFdB(nf2ff,varargin) +% +% plot far field pattern in dBi +% +% input: +% nf2ff: output of CalcNF2FF +% +% variable input: +% 'freq_index': - use the given frequency index, see nf2ff.freq +% - default is 1 +% 'xaxis': - 'phi' (default) or 'theta' +% 'param': - array positions of parametric plot +% - if xaxis='phi', theta is parameter, and vice versa +% - default is 1 +% +% example: +% plotFFdB(nf2ff, 'freq_index', 2, ... +% 'xaxis', 'phi', 'param', [1 46 91]) +% +% see examples/NF2FF/infDipol.m +% +% See also CalcNF2FF, plotFF3D, polarFF +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig, Stefan Mahr + +% defaults +freq_index = 1; +xaxis = 'phi'; +param = 1; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'freq_index')==1); + freq_index = varargin{n+1}; + elseif (strcmp(varargin{n},'xaxis')==1); + xaxis = varargin{n+1}; + elseif (strcmp(varargin{n},'param')==1); + param = varargin{n+1}; + else + warning('openEMS:plotFFdB',['unknown argument key: ''' varargin{n} '''']); + end +end + +D_log = nf2ff.E_norm{freq_index} / max(nf2ff.E_norm{freq_index}(:)); +D_log = 20*log10(D_log) + 10*log10(nf2ff.Dmax(freq_index)); + +if (strcmp(xaxis,'theta')==1); + xax = nf2ff.theta; + yax = D_log(:,param); + parval = nf2ff.phi(param); + param = 'phi'; +elseif (strcmp(xaxis,'phi')==1); + xax = nf2ff.phi; + yax = D_log(param,:); + parval = nf2ff.theta(param); + param = 'theta'; +else + error('openEMS:plotFFdB','unknown parameter to ''xaxis'''); +end + +%figure +h = plot( xax / pi * 180 , yax ); +xlabel( sprintf('%s (deg)',xaxis )); +ylabel( 'directivity (dBi)'); + +createlegend = @(d)sprintf('%s = %3.1f',param,d / pi * 180); +legendtext = arrayfun(createlegend,parval,'UniformOutput',0); +legend( legendtext ); +title( sprintf('far field pattern @ f = %e Hz',nf2ff.freq(freq_index)) ); +grid on; + +if (nargout == 0) + clear h; +end + +end diff --git a/openEMS/matlab/polarFF.m b/openEMS/matlab/polarFF.m new file mode 100644 index 0000000..96b97da --- /dev/null +++ b/openEMS/matlab/polarFF.m @@ -0,0 +1,172 @@ +function h = polarFF(nf2ff,varargin) +% h = polarFF(nf2ff,varargin) +% +% plot polar far field pattern +% +% input: +% nf2ff: output of CalcNF2FF +% +% variable input: +% 'freq_index': - use the given frequency index, see nf2ff.freq +% - default is 1 +% 'xaxis': - 'phi' (default) or 'theta' +% 'param': - array positions of parametric plot +% - if xaxis='phi', theta is parameter, and vice versa +% - default is 1 +% 'normalize': - true/false, normalize linear plot +% - default is false, log-plot is always normalized! +% 'logscale': - if set, plot logarithmic polar +% - set the dB value for point of origin if scalar +% - set point of origin and maximum if 2-element array +% - values below minimum will be clamped +% - default is -20 +% 'xtics': - set the number of tics for polar grid +% - default is 5 +% +% example: +% polarFF(nf2ff, 'freq_index', 2, ... +% 'xaxis', 'phi', 'param', [1 46 91] ); +% +% polarFF(..., 'normalize', true ); +% polarFF(..., 'logscale', -30 ); +% polarFF(..., 'logscale', [-30 10]); +% +% polarFF(..., 'xtics', 10); +% +% see examples/antenna/infDipol.m +% +% See also CalcNF2FF, plotFFdB, plotFF3D +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig, Stefan Mahr + +% defaults +freq_index = 1; +xaxis = 'phi'; +param = 1; +logscale = []; +xtics = 5; +normalize = 0; + +for n=1:2:numel(varargin) + if (strcmp(varargin{n},'freq_index')==1); + freq_index = varargin{n+1}; + elseif (strcmp(varargin{n},'xaxis')==1); + xaxis = varargin{n+1}; + elseif (strcmp(varargin{n},'param')==1); + param = varargin{n+1}; + elseif (strcmp(varargin{n},'normalize')==1); + normalize = varargin{n+1}; + elseif (strcmp(varargin{n},'logscale')==1); + logscale = varargin{n+1}; + elseif (strcmp(varargin{n},'xtics')==1); + xtics = varargin{n+1}; + else + warning('openEMS:polarFF',['unknown argument key: ''' varargin{n} '''']); + end +end + +E_far_max = max(nf2ff.E_norm{freq_index}(:)); +if ~isempty(logscale) + gridmin = logscale(1); + + Dmax = 10*log10(nf2ff.Dmax(freq_index)); + E_far_scale = Dmax - gridmin; + E_far = 20*log10(nf2ff.E_norm{freq_index}) - 20*log10(E_far_max) + E_far_scale; + E_far = E_far .* ( E_far > 0 ); + E_far = E_far ./ E_far_scale; + + titletext = sprintf('electrical far field [dBi] @ f = %e Hz',nf2ff.freq(freq_index)); + + if numel(logscale) == 2 % normalize to maximum grid + gridmax = logscale(2); + E_far = E_far .* E_far_scale/(gridmax-gridmin); + else + gridmax = Dmax; + end +elseif (normalize==0) + E_far = nf2ff.E_norm{freq_index}; + + titletext = sprintf('electrical far field [V/m] @ f = %e Hz',nf2ff.freq(freq_index)); + + gridmin = 0; + gridmax = E_far_max; +else % normalize == 1 + E_far = nf2ff.E_norm{freq_index} / E_far_max; + + titletext = sprintf('normalized electrical far field @ f = %e Hz',nf2ff.freq(freq_index)); + + gridmin = 0; + gridmax = 1; +end + + +if (strcmp(xaxis,'theta')==1); + xax = nf2ff.theta(:); + yax = E_far(:,param); + parval = nf2ff.phi(param); + param = 'phi'; +elseif (strcmp(xaxis,'phi')==1); + xax = nf2ff.phi(:); + yax = E_far(param,:)'; + parval = nf2ff.theta(param); + param = 'theta'; +else + error('openEMS:polarFF','unknown parameter to ''xaxis'''); +end + +if ~isempty(logscale) + scalegrid = 1; +else + scalegrid = gridmax; +end + +% workaround for polar plot +gridcolor = [0.85 0.85 0.85]; +% plot xtics circles +a=linspace(0,2*pi,60); +b=linspace(0,scalegrid,xtics+1); +b=repmat(b(2:end),numel(a),1)'; +a=repmat(a,size(b,1),1); +[x,y] = pol2cart(a,b); +h = plot(x',y'); +%h=polar(a,b,'-k'); +set(h,'Color',gridcolor); +set(h(end),'Color',gridcolor*0.8); +hold on; +% plot degree lines +a=bsxfun(@plus,[0:pi/6:pi-pi/6],[0 pi]'); +b=scalegrid.*ones(size(a)); +h=polar(a,b,'-k'); +set(h,'Color',gridcolor); +set(h([1 4]),'Color',gridcolor*0.8); +text(scalegrid*0.05,scalegrid*0.05,num2str(gridmin)) +text(scalegrid*1.05,scalegrid*0.05,num2str(gridmax)) + + +% draw far field +xax = repmat(xax,1,size(yax,2)); +[x,y] = pol2cart(xax,yax); +h = plot(x,y); +%h = polar( xax, yax ); + +% legend +ylabel( sprintf('%s / deg', xaxis) ); +title( titletext ); +createlegend = @(d)sprintf('%s = %3.1f',param,d / pi * 180); +legendtext = arrayfun(createlegend,parval,'UniformOutput',0); +legend( h, legendtext ,'location','southeast'); + +% workaround for polar plot +axis equal tight +axis ([-scalegrid scalegrid -scalegrid scalegrid]); +axis off +hold off +set(gcf,'Color','white'); + +if (nargout == 0) + clear h; +end + +end diff --git a/openEMS/matlab/private/ReadNF2FF.m b/openEMS/matlab/private/ReadNF2FF.m new file mode 100644 index 0000000..55f67f9 --- /dev/null +++ b/openEMS/matlab/private/ReadNF2FF.m @@ -0,0 +1,82 @@ +function nf2ff = ReadNF2FF(nf2ff) +% function nf2ff = ReadNF2FF(nf2ff) +% +% internal function to read calculated nf2ff data, use CalcNF2FF to read +% existing nf2ff data +% +% See also: CalcNF2FF, CreateNF2FFBox +% +% openEMS matlab interface +% ----------------------- +% author: Thorsten Liebig, 2012 + +file = nf2ff.hdf5; + +hdf_mesh = ReadHDF5Mesh(file); + +nf2ff.r = double(hdf_mesh.lines{1}); +nf2ff.theta = double(hdf_mesh.lines{2}); +nf2ff.phi = double(hdf_mesh.lines{3}); + +% read attributes +nf2ff.freq = ReadHDF5Attribute(file,'/nf2ff','Frequency'); +nf2ff.Prad = ReadHDF5Attribute(file,'/nf2ff','Prad'); +nf2ff.Dmax = ReadHDF5Attribute(file,'/nf2ff','Dmax'); + +try + nf2ff.Eps_r = ReadHDF5Attribute(file,'/nf2ff','Eps_r'); +catch + nf2ff.Eps_r = ones(size(nf2ff.freq)); +end +try + nf2ff.Mue_r = ReadHDF5Attribute(file,'/nf2ff','Mue_r'); +catch + nf2ff.Mue_r = ones(size(nf2ff.freq)); +end + +if isOctave + hdf = load( '-hdf5', file ); + for n=1:numel(nf2ff.freq) + nf2ff.E_theta{n} = double(hdf.nf2ff.E_theta.FD.(['f' int2str(n-1) '_real']) +1i*hdf.nf2ff.E_theta.FD.(['f' int2str(n-1) '_imag']) ); + nf2ff.E_phi{n} = double(hdf.nf2ff.E_phi.FD.(['f' int2str(n-1) '_real']) +1i*hdf.nf2ff.E_phi.FD.(['f' int2str(n-1) '_imag']) ); + nf2ff.E_norm{n} = double(sqrt(abs(nf2ff.E_theta{n}).^2+abs(nf2ff.E_phi{n}).^2)); + nf2ff.P_rad{n} = double(hdf.nf2ff.P_rad.FD.(['f' int2str(n-1)])); + end +else + % matlab compatibility to older versions + if verLessThan('matlab','7.12') + % read data + for n=1:numel(nf2ff.freq) + nf2ff.E_theta{n} = double(hdf5read(file,['/nf2ff/E_theta/FD/f' int2str(n-1) '_real']) + 1i*hdf5read(file,['/nf2ff/E_theta/FD/f' int2str(n-1) '_imag'])); + nf2ff.E_phi{n} = double(hdf5read(file,['/nf2ff/E_phi/FD/f' int2str(n-1) '_real']) + 1i*hdf5read(file,['/nf2ff/E_phi/FD/f' int2str(n-1) '_imag'])); + nf2ff.E_norm{n} = double(sqrt(abs(nf2ff.E_theta{n}).^2+abs(nf2ff.E_phi{n}).^2)); + nf2ff.P_rad{n} = double(hdf5read(file,['/nf2ff/P_rad/FD/f' int2str(n-1)])); + end + else + % read data + for n=1:numel(nf2ff.freq) + nf2ff.E_theta{n} = double(h5read(file,['/nf2ff/E_theta/FD/f' int2str(n-1) '_real']) + 1i*h5read(file,['/nf2ff/E_theta/FD/f' int2str(n-1) '_imag'])); + nf2ff.E_phi{n} = double(h5read(file,['/nf2ff/E_phi/FD/f' int2str(n-1) '_real']) + 1i*h5read(file,['/nf2ff/E_phi/FD/f' int2str(n-1) '_imag'])); + nf2ff.E_norm{n} = double(sqrt(abs(nf2ff.E_theta{n}).^2+abs(nf2ff.E_phi{n}).^2)); + nf2ff.P_rad{n} = double(h5read(file,['/nf2ff/P_rad/FD/f' int2str(n-1)])); + end + end +end + +% Calculation of right- and left-handed circular polarization +% adopted from +% 2012, Tim Pegg <teepegg@gmail.com> + +% cleanup (if exist) +nf2ff.E_cprh = []; +nf2ff.E_cplh = []; + +% Setup vectors for converting to LHCP and RHCP polarization senses +[THETHA PHI] = ndgrid(nf2ff.theta,nf2ff.phi); +cosphi = cos(PHI); +sinphi = sin(PHI); + +for f=1:numel(nf2ff.freq) + nf2ff.E_cprh{f} = (cosphi+1i*sinphi) .* (nf2ff.E_theta{f}+1i*nf2ff.E_phi{f})/sqrt(2); + nf2ff.E_cplh{f} = (cosphi-1i*sinphi) .* (nf2ff.E_theta{f}-1i*nf2ff.E_phi{f})/sqrt(2); +end diff --git a/openEMS/matlab/private/invoke_openEMS.m b/openEMS/matlab/private/invoke_openEMS.m new file mode 100644 index 0000000..afb2b46 --- /dev/null +++ b/openEMS/matlab/private/invoke_openEMS.m @@ -0,0 +1,47 @@ +function invoke_openEMS( opts, logfile, silent ) +% function invoke_openEMS( opts, logfile, silent ) +% +% internal method to invoke openEMS, use RunOpenEMS instead +% +% See also RunOpenEMS +% +% openEMS matlab interface +% ----------------------- +% author: Sebastian Held, Thorsten Liebig + +if nargin < 1 + error 'specify the xml file to simulate' +end +if nargin < 3 + silent = 0; +end +if (nargin < 2) || isempty(logfile) + if isunix + logfile = '/dev/null'; + else + logfile = 'nul:'; + end +end + +filename = mfilename('fullpath'); +dir = fileparts( filename ); + +if isunix + openEMS_bin = searchBinary('openEMS.sh', ... + {[dir filesep '..' filesep '..' filesep], ... % try devel path + [dir filesep '..' filesep '..' filesep '..' filesep '..' filesep 'bin' filesep]}); % try (default) install path +else % assume windows + openEMS_bin = searchBinary('openEMS.exe', [dir filesep '..' filesep '..' filesep]); +end + +command = [openEMS_bin ' ' opts]; + +if ~silent + if (isunix && nargin>1) + command = [command ' 2>&1 | tee ' logfile]; + end +else + command = [command ' > ' logfile ' 2>&1']; +end + +system(command); diff --git a/openEMS/matlab/queue_addProcess.m b/openEMS/matlab/queue_addProcess.m new file mode 100644 index 0000000..73d4e14 --- /dev/null +++ b/openEMS/matlab/queue_addProcess.m @@ -0,0 +1,22 @@ +function [pid,filenames] = queue_addProcess( command ) +% [pid,filenames] = queue_addProcess( command ) +% +% Sebastian Held <sebastian.held@uni-due.de> +% 12.5.2010 + +if ~isunix + error 'your OS is not supported (Unix only)' +end + +if nargout > 1 + filenames.stdout = tempname; + filenames.stderr = tempname; +else + filenames.stdout = '/dev/null'; + filenames.stderr = '/dev/null'; +end + +cmd = ['(' command ') >' filenames.stdout ' 2>' filenames.stderr ' & echo $!' ]; +[~,result] = unix( cmd ); + +pid = str2double(result); diff --git a/openEMS/matlab/queue_checkProcess.m b/openEMS/matlab/queue_checkProcess.m new file mode 100644 index 0000000..878165a --- /dev/null +++ b/openEMS/matlab/queue_checkProcess.m @@ -0,0 +1,25 @@ +function [alive,stdout,stderr] = queue_checkProcess( pid, filenames ) +% [alive,stdout,stderr] = queue_checkProcess( pid ) +% +% Sebastian Held <sebastian.held@uni-due.de> +% 12.5.2010 + +if ~isunix + error 'your OS is not supported (Unix only)' +end + +if nargout > 1 + fid = fopen( filenames.stdout ); + stdout = fread(fid, '*char')'; + fclose(fid); +end +if nargout > 2 + fid = fopen( filenames.stderr ); + stderr = fread(fid, '*char')'; + fclose(fid); +end + +cmd = ['ps --no-headers -p' num2str(pid) ]; +[status,~] = unix( cmd ); + +alive = (status == 0); diff --git a/openEMS/matlab/queue_delProcess.m b/openEMS/matlab/queue_delProcess.m new file mode 100644 index 0000000..39ab45c --- /dev/null +++ b/openEMS/matlab/queue_delProcess.m @@ -0,0 +1,40 @@ +function [stdout,stderr] = queue_delProcess( pid, filenames ) +% [stdout,stderr] = queue_delProcess( pid, filenames ) +% +% if pid == 0, do not kill a process, but clean up files +% +% Sebastian Held <sebastian.held@uni-due.de> +% 12.5.2010 + +if ~isunix + error 'your OS is not supported (Unix only)' +end + +if pid ~= 0 + alive = queue_checkProcess( pid ); + + if alive + unix( ['kill ' num2str(pid)] ); + alive = queue_checkProcess( pid ); + end + if alive + pause(1) + unix( ['kill ' num2str(pid)] ); + alive = queue_checkProcess( pid ); + end + if alive + unix( ['kill -KILL ' num2str(pid)] ); + end +end + +if nargin > 1 + if nargout == 1 + [~,stdout] = queue_checkProcess( pid, filenames ); + end + if nargout == 2 + [~,stdout,stderr] = queue_checkProcess( pid, filenames ); + end + + delete( filenames.stdout ); + delete( filenames.stderr ); +end diff --git a/openEMS/matlab/setup.m b/openEMS/matlab/setup.m new file mode 100644 index 0000000..df56b25 --- /dev/null +++ b/openEMS/matlab/setup.m @@ -0,0 +1,36 @@ +function setup() +% function setup() +% +% setup openEMS Matlab/octave interface +% +% openEMS matlab/octave interface +% ----------------------- +% author: Thorsten Liebig (2011) + +disp('setting up openEMS matlab/octave interface') + +% cd to directory of this file and restore current path at the end +current_path = pwd; +dir = fileparts( mfilename('fullpath') ); +cd(dir); + +if isOctave() + disp('compiling oct files') + fflush(stdout) + if isunix + [res, fn] = unix('find /usr/lib -name libhdf5.so'); + if length(fn)>0 + [hdf5lib_dir, hdf5lib_fn] = fileparts(fn); + disp(["HDF5 library path found at: " hdf5lib_dir]) + mkoctfile(["-L" hdf5lib_dir ],"-lhdf5 -DH5_USE_16_API", "h5readatt_octave.cc") + else + mkoctfile -lhdf5 -DH5_USE_16_API h5readatt_octave.cc + end + else + mkoctfile -lhdf5 -DH5_USE_16_API h5readatt_octave.cc + end +else + disp('Matlab does not need this function. It is Octave only.') +end + +cd(current_path); |