1function [CSX,port] = AddMSLPort( CSX, prio, portnr, materialname, start, stop, dir, evec, varargin )
2% [CSX,port] = AddMSLPort( CSX, prio, portnr, materialname, start, stop, dir, evec, varargin )
3%
4% CSX:          CSX-object created by InitCSX()
5% prio:         priority for excitation and probe boxes
6% portnr:       (integer) number of the port
7% materialname: property for the MSL (created by AddMetal())
8% start:        3D start rowvector for port definition
9% stop:         3D end rowvector for port definition
10% dir:          direction of wave propagation (choices: 0, 1, 2 or 'x','y','z')
11% evec:         excitation vector, which defines the direction of the e-field (must be the same as used in AddExcitation())
12%
13% variable input:
14%  varargin:    optional additional excitations options, see also AddExcitation
15% 'ExcitePort'  true/false to make the port an active feeding port (default
16%               is false)
17% 'FeedShift'   shift to port from start by a given distance in drawing
18%               units. Default is 0. Only active if 'ExcitePort' is set!
19% 'Feed_R'      Specify a lumped port resistance. Default is no lumped
20%               port resistance --> port has to end in an ABC.
21% 'MeasPlaneShift'  Shift the measurement plane from start t a given distance
22%               in drawing units. Default is the middle of start/stop.
23% 'PortNamePrefix' a prefix to the port name
24%
25% Important: The mesh has to be already set and defined by DefineRectGrid!
26%
27% example:
28%   CSX = AddMetal( CSX, 'metal' ); %create a PEC called 'metal'
29%   start = [0       -width/2  height];
30%   stop  = [length  +width/2  0     ];
31%   [CSX,port] = AddMSLPort( CSX, 0, 1, 'metal', start, stop, 'x', [0 0 -1], ...
32%                           'ExcitePort', true, 'Feed_R', 50 )
33% Explanation:
34%   - this defines a MSL in x-direction (dir='x')
35%     --> the wave travels along the x-direction
36%   - with an e-field excitation in -z-direction (evec=[0 0 -1])
37%   - the excitation is active and placed at x=start(1) ('ExcitePort', true)
38%   - a 50 Ohm lumped port resistance is placed at x=start(1) ('Feed_R', 50)
39%   - the width-direction is determined by the cross product of the
40%       direction of propagation (dir='x') and the excitation vector
41%       (evec=[0 0 -1]), in this case it is the y-direction
42%   - the MSL-metal is created in a xy-plane at a height at z=start(3)
43%     --> It is important to define the MSL height in the start coordinate!
44%   - the ground (xy-plane, not defined by the port) is assumed at z=stop(3)
45%     --> The reference plane (ground) is defined in the stop coordinate!
46%
47% Sebastian Held <sebastian.held@gmx.de> May 13 2010
48% Thorsten Liebig <thorsten.liebig@gmx.de> (c) 2011-2013
49%
50% See also InitCSX DefineRectGrid AddMetal AddMaterial AddExcitation calcPort
51
52%% validate arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53%check mesh
54if ~isfield(CSX,'RectilinearGrid')
55    error 'mesh needs to be defined! Use DefineRectGrid() first!';
56end
57if (~isfield(CSX.RectilinearGrid,'XLines') || ~isfield(CSX.RectilinearGrid,'YLines') || ~isfield(CSX.RectilinearGrid,'ZLines'))
58    error 'mesh needs to be defined! Use DefineRectGrid() first!';
59end
60
61% check dir
62dir = DirChar2Int(dir);
63
64% check evec
65if ~(evec(1) == evec(2) == 0) && ~(evec(1) == evec(3) == 0) && ~(evec(2) == evec(3) == 0) || (sum(evec) == 0)
66	error 'evec must have exactly one component ~= 0'
67end
68evec0 = evec ./ sum(evec); % evec0 is a unit vector
69
70%set defaults
71feed_shift = 0;
72feed_R = inf; %(default is open, no resitance)
73excite = false;
74measplanepos = nan;
75PortNamePrefix = '';
76
77excite_args = {};
78
79%% read optional arguments %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80for n=1:2:numel(varargin)
81    if (strcmp(varargin{n},'FeedShift')==1);
82        feed_shift = varargin{n+1};
83        if (numel(feed_shift)>1)
84            error 'FeedShift must be a scalar value'
85        end
86    elseif (strcmp(varargin{n},'Feed_R')==1);
87        feed_R = varargin{n+1};
88        if (numel(feed_R)>1)
89            error 'Feed_R must be a scalar value'
90        end
91    elseif (strcmp(varargin{n},'MeasPlaneShift')==1);
92        measplanepos = varargin{n+1};
93        if (numel(measplanepos)>1)
94            error 'MeasPlaneShift must be a scalar value'
95        end
96    elseif (strcmp(varargin{n},'ExcitePort')==1);
97        if ischar(varargin{n+1})
98            warning('CSXCAD:AddMSLPort','deprecated: a string as excite option is no longer supported and will be removed in the future, please use true or false');
99            if ~isempty(excite)
100                excite = true;
101            else
102                excite = false;
103            end
104        else
105            excite = varargin{n+1};
106        end
107    elseif (strcmpi(varargin{n},'PortNamePrefix'))
108        PortNamePrefix = varargin{n+1};
109    else
110        excite_args{end+1} = varargin{n};
111        excite_args{end+1} = varargin{n+1};
112    end
113end
114
115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116
117% normalize start and stop
118nstart = min( [start;stop] );
119nstop  = max( [start;stop] );
120
121% determine index (1, 2 or 3) of propagation (length of MSL)
122idx_prop = dir + 1;
123
124% determine index (1, 2 or 3) of width of MSL
125dir = [0 0 0];
126dir(idx_prop) = 1;
127idx_width = abs(cross(dir,evec0)) * [1;2;3];
128
129% determine index (1, 2 or 3) of height
130idx_height = abs(evec0) * [1;2;3];
131
132% direction of propagation
133if stop(idx_prop)-start(idx_prop) > 0
134    direction = +1;
135else
136    direction = -1;
137end
138
139% direction of propagation
140if stop(idx_height)-start(idx_height) > 0
141    upsidedown = +1;
142else
143    upsidedown = -1;
144end
145
146% create the metal/material for the MSL
147MSL_start = start;
148MSL_stop = stop;
149MSL_stop(idx_height) = MSL_start(idx_height);
150CSX = AddBox( CSX, materialname, prio, MSL_start, MSL_stop );
151
152if isnan(measplanepos)
153    measplanepos = (nstart(idx_prop)+nstop(idx_prop))/2;
154else
155    measplanepos = start(idx_prop)+direction*measplanepos;
156end
157
158% calculate position of the voltage probes
159try
160	mesh{1} = sort(CSX.RectilinearGrid.XLines);
161	mesh{2} = sort(CSX.RectilinearGrid.YLines);
162	mesh{3} = sort(CSX.RectilinearGrid.ZLines);
163	meshlines = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), measplanepos, 'nearest' );
164	meshlines = mesh{idx_prop}(meshlines-1:meshlines+1); % get three lines (approx. at center)
165	if direction == -1
166		meshlines = fliplr(meshlines);
167	end
168	MSL_w2 = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), (nstart(idx_width)+nstop(idx_width))/2, 'nearest' );
169	MSL_w2 = mesh{idx_width}(MSL_w2); % get e-line at center of MSL (MSL_width/2)
170	v1_start(idx_prop)   = meshlines(1);
171	v1_start(idx_width)  = MSL_w2;
172	v1_start(idx_height) = start(idx_height);
173	v1_stop  = v1_start;
174	v1_stop(idx_height)  = stop(idx_height);
175	v2_start = v1_start;
176	v2_stop  = v1_stop;
177	v2_start(idx_prop)   = meshlines(2);
178	v2_stop(idx_prop)    = meshlines(2);
179	v3_start = v2_start;
180	v3_stop  = v2_stop;
181	v3_start(idx_prop)   = meshlines(3);
182	v3_stop(idx_prop)    = meshlines(3);
183catch
184	error('Unable to place voltage probe on mesh; check the location of the MSL and the probe (MeasPlaneShift), and make sure that the mesh is large enough');
185end
186
187% calculate position of the current probes
188try
189	idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstart(idx_width), 'nearest' );
190	i1_start(idx_width)  = mesh{idx_width}(idx) - diff(mesh{idx_width}(idx-1:idx))/2;
191	idx = interp1( mesh{idx_height}, 1:numel(mesh{idx_height}), start(idx_height), 'nearest' );
192	i1_start(idx_height) = mesh{idx_height}(idx-1) - diff(mesh{idx_height}(idx-2:idx-1))/2;
193	i1_stop(idx_height)  = mesh{idx_height}(idx+1) + diff(mesh{idx_height}(idx+1:idx+2))/2;
194	i1_start(idx_prop)   = sum(meshlines(1:2))/2;
195	i1_stop(idx_prop)    = i1_start(idx_prop);
196	idx = interp1( mesh{idx_width}, 1:numel(mesh{idx_width}), nstop(idx_width), 'nearest' );
197	i1_stop(idx_width)   = mesh{idx_width}(idx) + diff(mesh{idx_width}(idx:idx+1))/2;
198	i2_start = i1_start;
199	i2_stop  = i1_stop;
200	i2_start(idx_prop)   = sum(meshlines(2:3))/2;
201	i2_stop(idx_prop)    = i2_start(idx_prop);
202catch
203  error('Unable to place current probe on mesh; check the location of the MSL, and make sure that the mesh is large enough');
204end
205
206% create the probes
207port.U_filename{1} = [PortNamePrefix 'port_ut' num2str(portnr) 'A'];
208% weight = sign(stop(idx_height)-start(idx_height))
209weight = upsidedown;
210CSX = AddProbe( CSX, port.U_filename{1}, 0, 'weight', weight );
211CSX = AddBox( CSX, port.U_filename{1}, prio, v1_start, v1_stop );
212port.U_filename{2} = [PortNamePrefix 'port_ut' num2str(portnr) 'B'];
213CSX = AddProbe( CSX, port.U_filename{2}, 0, 'weight', weight );
214CSX = AddBox( CSX, port.U_filename{2}, prio, v2_start, v2_stop );
215port.U_filename{3} = [PortNamePrefix 'port_ut' num2str(portnr) 'C'];
216CSX = AddProbe( CSX, port.U_filename{3}, 0, 'weight', weight );
217CSX = AddBox( CSX, port.U_filename{3}, prio, v3_start, v3_stop );
218
219weight = direction;
220port.I_filename{1} = [PortNamePrefix 'port_it' num2str(portnr) 'A'];
221CSX = AddProbe( CSX, port.I_filename{1}, 1, 'weight', weight );
222CSX = AddBox( CSX, port.I_filename{1}, prio, i1_start, i1_stop );
223port.I_filename{2} = [PortNamePrefix 'port_it' num2str(portnr) 'B'];
224CSX = AddProbe( CSX, port.I_filename{2}, 1,'weight', weight );
225CSX = AddBox( CSX, port.I_filename{2}, prio, i2_start, i2_stop );
226
227% create port structure
228port.LengthScale = 1;
229if ((CSX.ATTRIBUTE.CoordSystem==1) && (idx_prop==2))
230    port.LengthScale = MSL_stop(idx_height);
231end
232port.nr = portnr;
233port.type = 'MSL';
234port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit;
235port.v_delta = diff(meshlines)*port.LengthScale;
236port.i_delta = diff( meshlines(1:end-1) + diff(meshlines)/2 )*port.LengthScale;
237port.direction = direction;
238port.excite = 0;
239port.measplanepos = abs(v2_start(idx_prop) - start(idx_prop))*port.LengthScale;
240% port
241
242% create excitation (if enabled) and port resistance
243try
244	meshline = interp1( mesh{idx_prop}, 1:numel(mesh{idx_prop}), start(idx_prop) + feed_shift*direction, 'nearest' );
245	ex_start(idx_prop)   = mesh{idx_prop}(meshline) ;
246	ex_start(idx_width)  = nstart(idx_width);
247	ex_start(idx_height) = nstart(idx_height);
248	ex_stop(idx_prop)    = ex_start(idx_prop);
249	ex_stop(idx_width)   = nstop(idx_width);
250	ex_stop(idx_height)  = nstop(idx_height);
251catch
252  error('Unable to place excitation on mesh; check the location of the MSL and the excitation (FeedShift), and make sure that the mesh is large enough');
253end
254
255port.excite = 0;
256if excite
257    port.excite = 1;
258    CSX = AddExcitation( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], 0, evec, excite_args{:} );
259    CSX = AddBox( CSX, [PortNamePrefix 'port_excite_' num2str(portnr)], prio, ex_start, ex_stop );
260end
261
262%% MSL resistance at start of MSL line
263ex_start(idx_prop) = start(idx_prop);
264ex_stop(idx_prop) = ex_start(idx_prop);
265
266if (feed_R > 0) && ~isinf(feed_R)
267    CSX = AddLumpedElement( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], idx_height-1, 'R', feed_R );
268    CSX = AddBox( CSX, [PortNamePrefix 'port_resist_' int2str(portnr)], prio, ex_start, ex_stop );
269elseif isinf(feed_R)
270    % do nothing --> open port
271elseif feed_R == 0
272    %port "resistance" as metal
273    CSX = AddBox( CSX, materialname, prio, ex_start, ex_stop );
274else
275    error('openEMS:AddMSLPort','MSL port with resistance <= 0 it not possible');
276end
277end
278