Hello,
I am looking for the most efficient way to discharge spheres and reinsert them within the geometry with DEM-Engine.
My ideal goal would be to take the lowest spheres in a sphere stack one by one and reinsert them (one by one) from the top of the geometry. It can be relaxed in reinserting the spheres from a pipe that can contain a few spheres. My application also requires that I track the number of discharged/reinserted spheres, and to be as fast as possible in solving each iteration. However, I have not been able so far to do it.
My first attempt was to make it as simple as possible, but I sacrificed too many requirements. In that attempt, I make a discharge plane, and spheres that are below the plane have their positions reset at the top of the geometry:
[...]
recirculation_vmax = 100 # "Explosion" safety when colliding -> reset
vel_reinsertion = [0,0,0] # How fast we reinsert the sphere
z_discharge = sphere['equivalent_radius'] # How high we want the spheres to be discharged
pipe_height = world_height - 4*sphere['equivalent_radius'] # How high do we want the pipe in which the sphere is reinserted
pipe_radius = 2*sphere['equivalent_radius'] # How wide the "pipe" that reinserts the spheres is
# Make the position block: for a given sphere, precalculate the x,y,z of the re-insertion when the test is satisfied
# Note: the test is satisfied if the sphere is below the recirculation plane, or if the velocity is too high
pos_block = f'''curandState state;
float Zmin, Zmax, Rmax, x, y, z, radius, radius_squared, angle;
curand_init(1234, ownerID, 0, &state);
float random_number = curand_uniform(&state);
float max_v = max(max(vX, vY), vZ);;
bool need_reset = ((Z > {z_discharge}) || (max_v > {recirculation_vmax}));
if (need_reset)
{{
Zmin = {world_height - pipe_height + sphere['equivalent_radius']};
Zmax = {world_height - sphere['equivalent_radius']};
Rmax = {pipe_radius - sphere['equivalent_radius']};
radius_squared = curand_uniform(&state) * std::pow(Rmax, 2.0f);
radius = std::pow(radius_squared, 0.5f);
angle = curand_uniform(&state) * 3.14 * 2;
x = radius * sin(angle);
y = radius * cos(angle);
z = Zmin + curand_uniform(&state)*(Zmax-Zmin);
}}'''
# Make the velocity block. No calculation is needed, just the same check
vel_block = f'''float max_v = max(max(vX, vY), vZ);;
bool need_reset = ((Z > {z_discharge}) || (max_v > {recirculation_vmax}));'''
# Apply rules to the spheres' family (1)
DEMSim.CorrectFamilyPosition(1, '(need_reset) ? x : X', '(need_reset) ? y : Y', '(need_reset) ? z : Z', pos_block)
DEMSim.CorrectFamilyLinVel(1, f'(need_reset) ? {vel_reinsertion[0]} : vX', f'(need_reset) ? {vel_reinsertion[1]} : vY', f'(need_reset) ? {vel_reinsertion[2]} : vZ', vel_block)
[...]
A few issues arise here: I cannot control how many spheres are discharged, so there are way too many of these spheres that are trying to be reinserted at the same time, without the possibility of avoiding collisions when sampling the new positions, and the reset condition is not enough.
Another idea would be to play with families. I could use ChangeClumpFamily for a box with the same discharge plane (DEMSim.ChangeClumpFamily(100, Z=[-1e99, z_discharge], orig_fam=1)), but that does not resolve my issue much further. It allows me to change z_discharge dynamically but requires me to extract and sort the elevation of each sphere which seems highly inefficient.
What could be good would be to "force" a discharge rate, but that seems impossible as is. The last idea would be to delete the spheres at the bottom and create new ones at the top. That would still require tracking in some way that I am not aware of, but also would require calling UpdateClumps() regularly (as discussed in one of my previous posts) and would keep increasing memory/time as the calculation goes.
Therefore, my question is: is there a way to select the lowest spheres and re-circulate them from the top at a given rate with collision prevention, while being as efficient as possible?
Thank you!