def points(field: PointCloud, velocity: Field, dt: float, integrator=euler): """ Advects the sample points of a point cloud using a simple Euler step. Each point moves by an amount equal to the local velocity times `dt`. Args: field: point cloud to be advected velocity: velocity sampled at the same points as the point cloud dt: Euler step time increment integrator: ODE integrator for solving the movement. Returns: Advected point cloud """ new_elements = integrator(field.elements, velocity, dt) return field.with_elements(new_elements)
def respect_boundaries(particles: PointCloud, domain: Domain, not_accessible: list, offset: float = 0.5) -> PointCloud: """ Enforces boundary conditions by correcting possible errors of the advection step and shifting particles out of obstacles or back into the domain. Args: particles: PointCloud holding particle positions as elements domain: Domain for which any particles outside should get shifted inwards not_accessible: List of Obstacle or Geometry objects where any particles inside should get shifted outwards offset: Minimum distance between particles and domain boundary / obstacle surface after particles have been shifted. Returns: PointCloud where all particles are inside the domain / outside of obstacles. """ new_positions = particles.elements.center for obj in not_accessible: if isinstance(obj, Obstacle): obj = obj.geometry new_positions = obj.push(new_positions, shift_amount=offset) new_positions = (~domain.bounds).push(new_positions, shift_amount=offset) return particles.with_elements(Sphere(new_positions, math.mean(particles.bounds.size) * 0.005))