Search_VerletList class
Contents
Description
This is a sub-class of the Search class for the implementation of the search algorithm Verlet List.
This algorithm performs an outer loop over all particles and searches for their interactions through an inner loop over the particles that are contained in their Verlet lists, and all walls.
The Verlet list of a particle contains other particles that are whithin a threshold distance, called Verlet distance, and have a higher ID number. This list is updated in a given frequency, which is smaller (less often) than the search frequency.
The reference element of each interaction (element 1) is the particle with smaller ID number.
For each interaction found, a binary Interaction object is created to manage the mechanical and / or thermal interaction between both elements.
classdef Search_VerletList < Search
Public properties
properties (SetAccess = public, GetAccess = public) % Verlet properties verlet_dist double = double.empty; % threshold surface separation distance to include particles in the verlet list verlet_freq uint32 = uint32.empty; % verlet list update frequency (in steps) auto_freq logical = logical.empty; % flag for automatic setting the update frequency last_search double = double.empty; % last step when search was executed over all elements (double to allow inf value) % Base objects for kinematics kinpp_sph BinKinematics = BinKinematics.empty; % sphere particle - sphere particle kinpw_sph_line BinKinematics = BinKinematics.empty; % sphere particle - line wall kinpw_sph_circ BinKinematics = BinKinematics.empty; % sphere particle - circle wall kinpp_cyl BinKinematics = BinKinematics.empty; % cylinder particle - sphere particle kinpw_cyl_line BinKinematics = BinKinematics.empty; % cylinder particle - line wall kinpw_cyl_circ BinKinematics = BinKinematics.empty; % cylinder particle - circle wall end
Constructor method
methods function this = Search_VerletList() this = this@Search(Search.VERLET_LIST); this.setDefaultProps(); end end
Public methods: implementation of super-class declarations
methods %------------------------------------------------------------------ function setDefaultProps(this) this.freq = 1; this.done = false; this.cutoff = 0; this.verlet_dist = inf; this.verlet_freq = 100; this.auto_freq = true; this.kinpp_sph = BinKinematics_SphereSphere(); this.kinpw_sph_line = BinKinematics_SphereWlin(); this.kinpw_sph_circ = BinKinematics_SphereWcirc(); this.kinpp_cyl = BinKinematics_CylinderCylinder(); this.kinpw_cyl_line = BinKinematics_CylinderWlin(); this.kinpw_cyl_circ = BinKinematics_CylinderWcirc(); end %------------------------------------------------------------------ function initialize(this,drv) % Initialize last search step % (large negative value to call searchAll in the 1st step) this.last_search = -inf; % Set update frequency if (this.auto_freq) vmax = max(vecnorm([drv.particles.veloc_trl])); rmax = max([drv.particles.radius]); if (vmax == 0) this.verlet_freq = 10 * this.freq; else this.verlet_freq = (this.verlet_dist - this.cutoff * rmax) / (2 * vmax * drv.time_step); end end end %------------------------------------------------------------------ function execute(this,drv) % Set flags this.done = true; % Check if it is time to search through all particles and % update verlet list if (drv.step-this.last_search > this.verlet_freq) this.searchAll(drv); else this.searchVerlet(drv); end end end
Public methods: sub-class specifics
methods %------------------------------------------------------------------ function searchAll(this,drv) % Set flags rmv = false; % Initialize maximum velocity and radius vmax = 0; rmax = 0; % Save search step this.last_search = drv.step; % Outer loop over reference particles for i = 1:drv.n_particles p1 = drv.particles(i); % Update maximum velocity if (this.auto_freq) vparticle = vecnorm(p1.veloc_trl); if (vparticle > vmax) vmax = vparticle; end if (p1.radius > rmax) rmax = p1.radius; end end % Reset verlet lists p1.verlet_p = Particle.empty; p1.verlet_w = Wall.empty; % Inner loop over all other particles with higher ID for j = 1:drv.n_particles p2 = drv.particles(j); if (p1.id >= p2.id) continue; end % Check for existing interaction if (any(p1.neigh_pid == p2.id)) % Get interaction object int = findobj(p1.interacts,'elem2',p2); % Compute separation between elements int.kinemat = int.kinemat.setRelPos(p1,p2); % Check if particle is within verlet distance % Assumption: particle do not go directly from % interaction to outside the verlet distance p1.verlet_p(end+1) = p2; % Check if separation is greater than cutoff distance % Assumption: cutoff ratio applies to maximum radius if (int.kinemat.separ >= this.cutoff * max(p1.radius,p2.radius)) % Remove interaction references from elements p1.interacts(p1.interacts==int) = []; p1.neigh_p(p1.neigh_p==p2) = []; p1.neigh_pid(p1.neigh_pid==p2.id) = []; p2.interacts(p2.interacts==int) = []; p2.neigh_p(p2.neigh_p==p1) = []; p2.neigh_pid(p2.neigh_pid==p1.id) = []; % Delete interaction object delete(int); rmv = true; end else % Create new particle-particle interaction if needed this.createInteractPP(drv,p1,p2,true); end end % Inner loop over all walls for j = 1:drv.n_walls w = drv.walls(j); % Check for existing interaction if (any(p1.neigh_wid == w.id)) % Get interaction object int = findobj(p1.interacts,'elem2',w); % Compute separation between elements int.kinemat = int.kinemat.setRelPos(p1,w); % Check if wall is within verlet distance % Assumption: particle do not go directly from % interaction to outside the verlet distance p1.verlet_w(end+1) = w; % Check if separation is greater than cutoff distance % Assumption: cutoff ratio applies to particle radius if (int.kinemat.separ >= this.cutoff * p1.radius) % Remove interaction references from particle % (neigh_w is cleaned by searching the id % to avoid error when it is heterogeneous: % i.e. different wall types) p1.interacts(p1.interacts==int) = []; p1.neigh_w([p1.neigh_w.id]==w.id) = []; p1.neigh_wid(p1.neigh_wid==w.id) = []; % Delete interaction object delete(int); rmv = true; end else % Create new particle-wall interaction if needed this.createInteractPW(drv,p1,w,true); end end end % Erase handles to removed interactions from global list if (rmv) drv.interacts(~isvalid(drv.interacts)) = []; end % Update total number of interactions drv.n_interacts = length(drv.interacts); % Set new verlet update frequency if (this.auto_freq) if (vmax == 0) this.verlet_freq = 10 * this.freq; else this.verlet_freq = (this.verlet_dist - this.cutoff * rmax) / (2 * vmax * drv.time_step); end end end %------------------------------------------------------------------ function searchVerlet(this,drv) % Set flags rmv = false; % Initialize maximum velocity and radius vmax = 0; rmax = 0; % Outer loop over reference particles for i = 1:drv.n_particles p1 = drv.particles(i); % Update maximum velocity if (this.auto_freq) vparticle = vecnorm(p1.veloc_trl); if (vparticle > vmax) vmax = vparticle; end if (p1.radius > rmax) rmax = p1.radius; end end % Inner loop over particles in the verlet list for j = 1:length(p1.verlet_p) p2 = p1.verlet_p(j); % Check if particle has been deleted if (~isvalid(p2)) p1.neigh_p(~isvalid(p1.neigh_p)) = []; continue; end % Check for existing interaction if (any(p1.neigh_pid == p2.id)) % Get interaction object int = findobj(p1.interacts,'elem2',p2); % Compute separation between elements int.kinemat = int.kinemat.setRelPos(p1,p2); % Check if separation is greater than cutoff distance % Assumption: cutoff ratio applies to maximum radius if (int.kinemat.separ >= this.cutoff * max(p1.radius,p2.radius)) % Remove interaction references from elements p1.interacts(p1.interacts==int) = []; p1.neigh_p(p1.neigh_p==p2) = []; p1.neigh_pid(p1.neigh_pid==p2.id) = []; p2.interacts(p2.interacts==int) = []; p2.neigh_p(p2.neigh_p==p1) = []; p2.neigh_pid(p2.neigh_pid==p1.id) = []; % Delete interaction object delete(int); rmv = true; end else % Create new particle-particle interaction if needed this.createInteractPP(drv,p1,p2,false); end end % Inner loop over all walls for j = 1:length(p1.verlet_w) w = p1.verlet_w(j); % Check for existing interaction if (any(p1.neigh_wid == w.id)) % Get interaction object int = findobj(p1.interacts,'elem2',w); % Compute separation between elements int.kinemat = int.kinemat.setRelPos(p1,w); % Check if separation is greater than cutoff distance % Assumption: cutoff ratio applies to particle radius if (int.kinemat.separ >= this.cutoff * p1.radius) % Remove interaction references from particle % (neigh_w is cleaned by searching the id % to avoid error when it is heterogeneous: % i.e. different wall types) p1.interacts(p1.interacts==int) = []; p1.neigh_w([p1.neigh_w.id]==w.id) = []; p1.neigh_wid(p1.neigh_wid==w.id) = []; % Delete interaction object delete(int); rmv = true; end else % Create new particle-wall interaction if needed this.createInteractPW(drv,p1,w,false); end end end % Erase handles to removed interactions from global list if (rmv) drv.interacts(~isvalid(drv.interacts)) = []; end % Update total number of interactions drv.n_interacts = length(drv.interacts); % Set new verlet update frequency if (this.auto_freq) if (vmax == 0) this.verlet_freq = 10 * this.freq; else this.verlet_freq = (this.verlet_dist - this.cutoff * rmax) / (2 * vmax * drv.time_step); end end end %------------------------------------------------------------------ function createInteractPP(this,drv,p1,p2,addVL) % Compute separation between particles surfaces % PS: This is exclusive for round particles to avoid calling the % base kinematic object (a bit slower). % For other shapes, the base kinematic object will be used. dir = p2.coord - p1.coord; dist = norm(dir); separ = dist - p1.radius - p2.radius; % Check if particle is within verlet distance if (addVL && separ < this.verlet_dist) p1.verlet_p(end+1) = p2; end % Check if interaction exists % Assumption: cutoff ratio applies to maximum radius if (separ >= this.cutoff * max(p1.radius,p2.radius)) return; end % Create new interaction object by copying base object int = copy(this.b_interact); % Set handles to interecting elements int.elem1 = p1; int.elem2 = p2; % Create binary kinematic object kin = this.createPPKinematic(p1,dir,dist,separ); kin.setEffParams(int); int.kinemat = kin; % Add references of new interaction to both elements and global list p1.interacts(end+1) = int; p1.neigh_p(end+1) = p2; p1.neigh_pid(end+1) = p2.id; p2.interacts(end+1) = int; p2.neigh_p(end+1) = p1; p2.neigh_pid(end+1) = p1.id; drv.interacts(end+1) = int; end %------------------------------------------------------------------ function createInteractPW(this,drv,p,w,addVL) % Check elements distance and separation and copy kinematics % object from base object % Assumption: cutoff ratio applies to particle radius switch (this.pwInteractionType(p,w)) case 1 this.kinpw_sph_line.setRelPos(p,w); if (addVL && this.kinpw_sph_line.separ < this.verlet_dist) p.verlet_w(end+1) = w; end if (this.kinpw_sph_line.separ >= this.cutoff * p.radius) return; end kin = copy(this.kinpw_sph_line); case 2 this.kinpw_sph_circ.setRelPos(p,w); if (addVL && this.kinpw_sph_circ.separ < this.verlet_dist) p.verlet_w(end+1) = w; end if (this.kinpw_sph_circ.separ >= this.cutoff * p.radius) return; end kin = copy(this.kinpw_sph_circ); case 3 this.kinpw_cyl_line.setRelPos(p,w); if (addVL && this.kinpw_cyl_line.separ < this.verlet_dist) p.verlet_w(end+1) = w; end if (this.kinpw_cyl_line.separ >= this.cutoff * p.radius) return; end kin = copy(this.kinpw_cyl_line); case 4 this.kinpw_cyl_circ.setRelPos(p,w); if (addVL && this.kinpw_cyl_circ.separ < this.verlet_dist) p.verlet_w(end+1) = w; end if (this.kinpw_cyl_circ.separ >= this.cutoff * p.radius) return; end kin = copy(this.kinpw_cyl_circ); end % Create new interaction object by copying base object int = copy(this.b_interact); % Set handles to interecting elements int.elem1 = p; int.elem2 = w; % Set binary kinematic object kin.setEffParams(int); int.kinemat = kin; % Set flag for insulated interaction int.insulated = w.insulated; % Add references of new interaction to particle and global list p.interacts(end+1) = int; p.neigh_w(end+1) = w; p.neigh_wid(end+1) = w.id; drv.interacts(end+1) = int; end end
end