I just figured out that workspace:GetPartsInPart() probably also uses spatial partitioning (not sure tho). But even if there is spatial partitioning on it, i think it would be better to make your own Spatial Partitioning. And that’s because GetPartsInPart() uses collisions to check if a Part is in part, and because droplets are circle shaped there is no need to calculate collisions with mesh, you can just check for distance between two particles to see for collisions.
Also, maybe i confused you a little, the spatial partitioning method still uses distance checks but only does them on adjacent grid cells so instead for checking every droplet for collision, you just check collisions on few particles that are in adjacent grid cells. Also when using Spatial Partitioning, it is easier to implement Multithreading which can boost performance alot. Many Physics engines use Spatial Partitioning, so it would be a good idea for it to be implemented also in your work.
Ive made a fairly fast real-time fluid particle simulation: