1 |
function [xx,yy,j]=drog2ddt(fem_grid_struct,t1,t2,dt,xi,yi,V) |
---|
2 |
%DROG2DDT - Track drogues in a 2-D flow-field |
---|
3 |
% DROG2DDT tracks passive particles in a 2-D flow-field defined |
---|
4 |
% on a FEM domain. The integrator is 4th-order Runge-Kutta. |
---|
5 |
% |
---|
6 |
% Input: fem_grid_struct - fem grid structure (see LOADGRID) |
---|
7 |
% t1,t2 - integration interval (in hours) |
---|
8 |
% dt - integration timestep (in hours) |
---|
9 |
% xi,yi - arrays of drogue starting positions |
---|
10 |
% V - array of structures containing velocity slices |
---|
11 |
% Type |
---|
12 |
% |
---|
13 |
% Output: xx,yy - arrays of particle trajectories |
---|
14 |
% j - elements containing final drogue positions |
---|
15 |
% |
---|
16 |
% Note: unless the first (and only) argument is a help string ('mainhelp', |
---|
17 |
% 'velhelp',...), all arguments are required. |
---|
18 |
% |
---|
19 |
% Call as:[xx,yy,j]=drog2ddt(fem_grid_struct,t1,t2,dt,xi,yi,V) |
---|
20 |
|
---|
21 |
% Check first argument |
---|
22 |
if nargin==1 |
---|
23 |
drog2ddthelp(fem_grid_struct); |
---|
24 |
return |
---|
25 |
elseif nargin>7 |
---|
26 |
error('Too many arguments to DROG2DDT') |
---|
27 |
elseif nargin==0 |
---|
28 |
error('DROG2DDT must be called with 1|7 input arguments') |
---|
29 |
end |
---|
30 |
|
---|
31 |
if ~isstruct(fem_grid_struct) |
---|
32 |
disp(' First argument to DROG2DDT must be a structure.');return |
---|
33 |
elseif ~is_valid_struct(fem_grid_struct) |
---|
34 |
error(' fem_grid_struct to DROG2DDT invalid.') |
---|
35 |
end |
---|
36 |
|
---|
37 |
|
---|
38 |
% Error-check the input times relative to the timestamps |
---|
39 |
% in the velocity cell array V. It is ASSUMED that the |
---|
40 |
% velocity slices are in temporal order within the cells |
---|
41 |
timemin=V(1).time; |
---|
42 |
timemax=V(length(V)).time; |
---|
43 |
if t1 < timemin |
---|
44 |
error('T1 less than minimum time in velocity cells.') |
---|
45 |
elseif t2 > timemax |
---|
46 |
error('T2 exceeds maximum time in velocity cells.') |
---|
47 |
end |
---|
48 |
|
---|
49 |
xi=xi(:);yi=yi(:); |
---|
50 |
if length(xi)~=length(yi) |
---|
51 |
error('Lengths of xi,yi must be equal') |
---|
52 |
end |
---|
53 |
|
---|
54 |
% Extract a time vector for the times at which the velocity |
---|
55 |
% slices are available. |
---|
56 |
timevec=[V.time]; |
---|
57 |
|
---|
58 |
% Attach element finding arrays to fem_grid_struct |
---|
59 |
if ~isfield(fem_grid_struct,'A')|~isfield(fem_grid_struct,'B')|... |
---|
60 |
~isfield(fem_grid_struct,'A0')| ~isfield(fem_grid_struct,'T') |
---|
61 |
fem_grid_struct=belint(fem_grid_struct); |
---|
62 |
disp(' BELINT info added to fem_grid_struct') |
---|
63 |
end |
---|
64 |
if ~isfield(fem_grid_struct,'ar') |
---|
65 |
disp(' EL_AREAS info added to fem_grid_struct') |
---|
66 |
fem_grid_struct=el_areas(fem_grid_struct); |
---|
67 |
end |
---|
68 |
|
---|
69 |
% Locate initial positions in grid |
---|
70 |
% j will be the array that keeps track of the |
---|
71 |
% current element number of each drog. A NaN will |
---|
72 |
% be used to indicate drog in-activity, either because |
---|
73 |
% the drog is initially out-of-bounds, or because the |
---|
74 |
% drogue has left the domain during tracking. |
---|
75 |
j=findelem(fem_grid_struct,[xi yi]); |
---|
76 |
|
---|
77 |
% Allocate space for time history of positions |
---|
78 |
xx=NaN*(ones(size(t1:dt:t2))'*ones(size(xi')))'; |
---|
79 |
yy=xx; |
---|
80 |
|
---|
81 |
% |
---|
82 |
xx(:,1)=xi; |
---|
83 |
yy(:,1)=yi; |
---|
84 |
|
---|
85 |
% Loop over time; for development purposes, the integration will |
---|
86 |
% be forward Euler. |
---|
87 |
time=t1; |
---|
88 |
iter=1; |
---|
89 |
dtsecs=dt*3600; |
---|
90 |
disp(['Starting: [' int2str(iter) ' ' num2str(time) ']']) |
---|
91 |
|
---|
92 |
while time<t2 |
---|
93 |
% |
---|
94 |
% if iter==3,keyboard,end; |
---|
95 |
xnow=xx(:,iter); |
---|
96 |
ynow=yy(:,iter); |
---|
97 |
xnew=xnow; % Propagate previously known positions, in case a |
---|
98 |
ynew=ynow; % drogue is eliminated. |
---|
99 |
% line(xnow,ynow,'Color','r','Marker','+') |
---|
100 |
|
---|
101 |
igood=find(~isnan(j)); |
---|
102 |
% If j contains NaN's, these drogues are have exited the |
---|
103 |
% domain at some previous timestep. |
---|
104 |
|
---|
105 |
if isempty(igood) |
---|
106 |
disp('All drogues eliminated') |
---|
107 |
disp(['Ending: [' int2str(iter) ' ' num2str(time) ']']) |
---|
108 |
return |
---|
109 |
end |
---|
110 |
|
---|
111 |
% Extract drogues currently in domain |
---|
112 |
jgood=j(igood);xgood=xnow(igood);ygood=ynow(igood); |
---|
113 |
|
---|
114 |
[xnext,ynext,jnext]=track(fem_grid_struct,jgood,xgood,ygood,V,timevec,time,dtsecs); |
---|
115 |
j(igood)=jnext; |
---|
116 |
xnew(igood)=xnext; |
---|
117 |
ynew(igood)=ynext; |
---|
118 |
|
---|
119 |
time=time+dt; |
---|
120 |
iter=iter+1; |
---|
121 |
|
---|
122 |
xx(:,iter)=xnew; |
---|
123 |
yy(:,iter)=ynew; |
---|
124 |
|
---|
125 |
end |
---|
126 |
|
---|
127 |
% Prepare for return |
---|
128 |
disp(['Ending: [' int2str(iter) ' ' num2str(time) ']']) |
---|
129 |
return |
---|
130 |
|
---|
131 |
|
---|
132 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
133 |
% PRIVATE FUNCTIONS % |
---|
134 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
135 |
|
---|
136 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
137 |
% locate_drog % |
---|
138 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
139 |
function jnew=locate_drog(fem_grid_struct,x,y,j) |
---|
140 |
|
---|
141 |
jnew=j; |
---|
142 |
|
---|
143 |
% See if drogs are still in Previously known element |
---|
144 |
idx=belel(fem_grid_struct,j,[x y]); |
---|
145 |
inotfound=find(idx==0); |
---|
146 |
|
---|
147 |
% Get new elements, if not in previously known element |
---|
148 |
if ~isempty(inotfound) |
---|
149 |
idx=inotfound; |
---|
150 |
jnew(idx)=findelem(fem_grid_struct,[x(idx) y(idx)]); |
---|
151 |
end |
---|
152 |
|
---|
153 |
function [xnew,ynew,jnew]=track(fem_grid_struct,j,x,y,V,timevec,t,dt) |
---|
154 |
|
---|
155 |
jnew=j; |
---|
156 |
|
---|
157 |
% Runge-Kutta 4 |
---|
158 |
tt1=t+.5*dt/3600; |
---|
159 |
|
---|
160 |
[u,v]=vel_interp(fem_grid_struct,x,y,j,V,timevec,t); |
---|
161 |
k1u=dt*u; |
---|
162 |
k1v=dt*v; |
---|
163 |
xinter1=x+.5*k1u; |
---|
164 |
yinter1=y+.5*k1v; |
---|
165 |
jinter=locate_drog(fem_grid_struct,xinter1,yinter1,j); |
---|
166 |
if all(isnan(jinter)) |
---|
167 |
xnew=x; |
---|
168 |
ynew=y; |
---|
169 |
jnew=jinter; |
---|
170 |
return |
---|
171 |
end |
---|
172 |
|
---|
173 |
[uinter,vinter]=vel_interp(fem_grid_struct,xinter1,yinter1,jinter,V,timevec,tt1); |
---|
174 |
k2u=dt*uinter; |
---|
175 |
k2v=dt*vinter; |
---|
176 |
xinter2=x+.5*k2u; |
---|
177 |
yinter2=y+.5*k2v; |
---|
178 |
jinter=locate_drog(fem_grid_struct,xinter2,yinter2,j); |
---|
179 |
if all(isnan(jinter)) |
---|
180 |
xnew=xinter1; |
---|
181 |
ynew=yinter1; |
---|
182 |
jnew=jinter; |
---|
183 |
return |
---|
184 |
end |
---|
185 |
|
---|
186 |
[uinter,vinter]=vel_interp(fem_grid_struct,xinter2,yinter2,jinter,V,timevec,tt1); |
---|
187 |
k3u=dt*uinter; |
---|
188 |
k3v=dt*vinter; |
---|
189 |
xinter3=x+k3u; |
---|
190 |
yinter3=y+k3v; |
---|
191 |
jinter=locate_drog(fem_grid_struct,xinter3,yinter3,j); |
---|
192 |
if all(isnan(jinter)) |
---|
193 |
xnew=xinter2; |
---|
194 |
ynew=yinter2; |
---|
195 |
jnew=jinter; |
---|
196 |
return |
---|
197 |
end |
---|
198 |
|
---|
199 |
[uinter,vinter]=vel_interp(fem_grid_struct,xinter3,yinter3,jinter,V,timevec,t+dt/3600); |
---|
200 |
k4u=dt*uinter; |
---|
201 |
k4v=dt*vinter; |
---|
202 |
|
---|
203 |
|
---|
204 |
xnew=x+k1u/6+k2u/3+k3u/3+k4u/6; |
---|
205 |
ynew=y+k1v/6+k2v/3+k3v/3+k4v/6; |
---|
206 |
|
---|
207 |
jnew=locate_drog(fem_grid_struct,xnew,ynew,j); |
---|
208 |
|
---|
209 |
% If NaN is in j, then drog has left domain. |
---|
210 |
% insert last known location into arrays |
---|
211 |
inan=find(isnan(jnew));; |
---|
212 |
if ~isempty(inan) |
---|
213 |
xnew(inan)=x(inan); |
---|
214 |
ynew(inan)=y(inan); |
---|
215 |
end |
---|
216 |
|
---|
217 |
|
---|
218 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
219 |
% belel % |
---|
220 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
221 |
function retval=belel(fem_grid_struct,j,xylist) |
---|
222 |
%BELEL - determine if points are in elements |
---|
223 |
% BELEL |
---|
224 |
tol=eps*10000000; |
---|
225 |
phi=basis2d(fem_grid_struct,xylist,j); |
---|
226 |
test=phi>=-tol & phi<=1+tol; |
---|
227 |
retval=all(test'==1); |
---|
228 |
|
---|
229 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
230 |
% vel_interp % |
---|
231 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
232 |
function [u,v]=vel_interp(fem_grid_struct,x,y,j,V,timevec,t) |
---|
233 |
% Get the velocities at this time |
---|
234 |
% Temporal interpolation of velocity slices to this time. |
---|
235 |
it1=find(t<=timevec);it1=it1(1); |
---|
236 |
it2=find(t>=timevec);it2=it2(length(it2)); |
---|
237 |
if it1==it2 |
---|
238 |
tfac1=1;tfac2=0; |
---|
239 |
else |
---|
240 |
tfac1=(timevec(it1)-t)/(timevec(it1)-timevec(it2)); |
---|
241 |
tfac2=1-tfac1; |
---|
242 |
end |
---|
243 |
u1=V(it1).u; |
---|
244 |
u2=V(it2).u; |
---|
245 |
v1=V(it1).v; |
---|
246 |
v2=V(it2).v; |
---|
247 |
|
---|
248 |
% Depending on the number of particles to track, the |
---|
249 |
% interpolation to (x,y,t) is done one of two ways. |
---|
250 |
% it is not ovvious, but the flop savings can be huge. |
---|
251 |
if length(j)>150 |
---|
252 |
% Interpolate in time first,... |
---|
253 |
uu=tfac1*u2 + tfac2*u1; |
---|
254 |
vv=tfac1*v2 + tfac2*v1; |
---|
255 |
% ... then, space |
---|
256 |
u=interp_scalar(fem_grid_struct,uu,x,y,j); |
---|
257 |
v=interp_scalar(fem_grid_struct,vv,x,y,j); |
---|
258 |
else |
---|
259 |
% Interpolate in space first, at the 2 time levels |
---|
260 |
uu1=interp_scalar(fem_grid_struct,u1(:),x,y,j); |
---|
261 |
vv1=interp_scalar(fem_grid_struct,v1(:),x,y,j); |
---|
262 |
uu2=interp_scalar(fem_grid_struct,u2(:),x,y,j); |
---|
263 |
vv2=interp_scalar(fem_grid_struct,v2(:),x,y,j); |
---|
264 |
% Then, interpolate BETWEEN time levels |
---|
265 |
u=tfac1*uu2 + tfac2*uu1; |
---|
266 |
v=tfac1*vv2 + tfac2*vv1; |
---|
267 |
end |
---|
268 |
|
---|
269 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
270 |
% drog2ddthelp % |
---|
271 |
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
272 |
function drog2ddthelp(option) |
---|
273 |
|
---|
274 |
if ~isstr(option) |
---|
275 |
error('Invalid help string to DROG2DDT') |
---|
276 |
end |
---|
277 |
|
---|
278 |
switch lower(option) |
---|
279 |
case 'mainhelp' |
---|
280 |
str=[sprintf('DROG2DDT HELP: mainhelp\n')]; |
---|
281 |
case 'velhelp' |
---|
282 |
str=[sprintf('\nDROG2DDT HELP: velhelp\n')]; |
---|
283 |
str=[str sprintf('\nDROG2DDT needs a series of 2-D velocity fields. This\n')]; |
---|
284 |
str=[str sprintf('information is passed into DROG2DDT in an array of structures. \n')]; |
---|
285 |
str=[str sprintf('Each structure has fields named ''u'',''v'', and ''time''. The first\n')]; |
---|
286 |
str=[str sprintf('two structures thus look like:\n')]; |
---|
287 |
str=[str sprintf('\n V(1).u; %% u-velocity for time 1\n')]; |
---|
288 |
str=[str sprintf(' V(1).v; %% v-velocity for time 1\n')]; |
---|
289 |
str=[str sprintf(' V(1).time; %% actual time in hours!! for time 1\n')]; |
---|
290 |
str=[str sprintf(' V(2).u; %% u-velocity for time 2\n')]; |
---|
291 |
str=[str sprintf(' V(2).v; %% u-velocity for time 2\n')]; |
---|
292 |
str=[str sprintf(' V(2).time; %% actual time in hours!! for time 2\n')]; |
---|
293 |
str=[str sprintf('\nand so on...\n')]; |
---|
294 |
str=[str sprintf('DROG2DDT makes sure that each structure is identical in fields, \n')]; |
---|
295 |
str=[str sprintf('and that the integration times are within the initial and final\n')]; |
---|
296 |
str=[str sprintf('times specified in the velocity array V. The sizes of the ''u'' and \n')]; |
---|
297 |
str=[str sprintf('''v'' fields must match the nuber of nodes in the fem_grid_struct.\n')]; |
---|
298 |
|
---|
299 |
|
---|
300 |
|
---|
301 |
otherwise |
---|
302 |
disp('DROG2DDT must be called with a valid help string.') |
---|
303 |
disp('Try: ''mainhelp'',''velhelp''') |
---|
304 |
end |
---|
305 |
|
---|
306 |
if ~isempty(str) |
---|
307 |
disp(str) |
---|
308 |
end |
---|
309 |
return |
---|