def test_domain_grid_from_constant(self): domain = Domain(x=4, y=3) # int / float grid = domain.scalar_grid(1) math.assert_close(grid.values, 1) # complex grid = domain.grid(1 + 1j) math.assert_close(grid.values, 1 + 1j) # NumPy grid = domain.grid(numpy.array(1)) math.assert_close(grid.values, 1) # PyTorch grid = domain.grid(torch.tensor(1, dtype=torch.float32)) math.assert_close(grid.values, 1) # TensorFlow grid = domain.grid(tf.constant(1, tf.float32)) math.assert_close(grid.values, 1)
def test_demo_vs_numpy(self): dt = 0.1 # Physical parameters L = 4 * 2 * np.pi # 2 * np.pi / 0.15 # 4*2*np.pi#/0.15 c1 = 5 # 0.01 # Numerical Parameters grid_pts = 64 nu = 0 # 1e-8 N = 0 # 3 arakawa_coeff = 0 kappa_coeff = 0 # Derived Parameters dx = L / grid_pts k0 = 2 * np.pi / L # Get input data rnd_noise = np.random.rand(grid_pts * grid_pts).reshape( grid_pts, grid_pts) sine = get_2d_sine((grid_pts, grid_pts), L) init_values = sine / 1000 density_coeff = 1 omega_coeff = -1 / 2 phi_coeff = -1 / 2 x = grid_pts y = grid_pts params = dict(c1=c1, nu=nu, N=N, arakawa_coeff=arakawa_coeff, kappa_coeff=kappa_coeff) # NumPy reference hw = HW( c1=c1, nu=nu, N=N, arakawa_coeff=arakawa_coeff, kappa_coeff=kappa_coeff, debug=False, ) hw_state_numpy = Namespace( density=init_values * density_coeff, omega=init_values * omega_coeff, phi=init_values * phi_coeff, age=0, dx=dx, ) # Phi version domain = Domain(x=x, y=y, boundaries=PERIODIC, bounds=Box[0:L, 0:L]) hw_state_phi = Namespace( density=domain.grid( math.tensor(init_values * density_coeff, names=["x", "y"])), omega=domain.grid( math.tensor(init_values * omega_coeff, names=["x", "y"])), phi=domain.grid( math.tensor(init_values * phi_coeff, names=["x", "y"])), # domain=domain, age=0, dx=dx, ) from functools import partial get_phi = partial(get_domain_phi, domain=domain) rk4_step2 = partial(rk4_step, params=params, get_phi=get_phi) app = App("Hasegawa-Wakatani", dt=dt) app.set_state(hw_state_phi, step_function=rk4_step2, show=["density", "omega", "phi"]) app.prepare() # Run def compare(iterable): for k in iterable[0].keys(): compare = [] for state in iterable: if isinstance(state[k], field._grid.Grid): val = state[k].values.numpy(order="x,y,z")[0] else: val = state[k] compare.append(val) assert len(compare) == 2 print(f" {k:<7}:" + f" {np.max(np.abs(compare[0]-compare[1])):.7f}" + f" {np.array_equal(*compare)}" + f" {np.max(np.abs(compare[0]-compare[1])):.2g}") return True np_times = [] phi_times = [] for i in range(0, STEPS + 1): print(f"step {i:>3} {i*dt:>12.7f}") # Numpy t0 = time.time() hw_state_numpy = hw.rk4_step(hw_state_numpy, dt=dt) np_time = time.time() - t0 np_times.append(np_time) # Phi t0 = time.time() app.step() phi_time = time.time() - t0 phi_times.append(phi_time) hw_state_phi = app.state compare([hw_state_numpy, hw_state_phi]) # if i % 100 == 0: # plot({'numpy': hw_state_numpy.density, # 'phi': hw_state_phi.density.values.numpy(order='zyx')[0], # 'diff': hw_state_numpy.density - hw_state_phi.density.values.numpy(order='zyx')[0]}, # title=f"step: {i}") # assert np.allclose(hw_state_numpy.density, hw_state_phi.density.values.numpy(order='zyx')[0]) print(f"Comparison | NumPy | PhiFlow") def get_str(name, func, vals): return " | ".join([ f"{name:<10}", f"{func(vals[0]):<10.4f}", f"{func(vals[1]):<10.4f}", ]) print(get_str("Mean (s)", np.mean, (np_times, phi_times))) print(get_str("Median (s)", np.median, (np_times, phi_times))) print(get_str("Min (s)", np.min, (np_times, phi_times))) print(get_str("Max (s)", np.max, (np_times, phi_times)))
def test_gradient(self): domain = Domain(x=4, y=3) phi = domain.grid() * (1, 2) grad = field.gradient(phi, stack_dim='gradient') self.assertEqual(('spatial', 'spatial', 'channel', 'channel'), grad.shape.types)
def test_domain_grid_memory_allocation(self): domain = Domain(x=10000, y=10000, z=10000, w=10000) grid = domain.grid() self.assertEqual((10000, ) * 4, grid.shape.sizes) sgrid = domain.staggered_grid() self.assertEqual((10000, 10000, 10000, 10000, 4), sgrid.shape.sizes)
def test_domain_grid_from_tensor(self): domain = Domain(x=4, y=3) grid = domain.grid(Noise(vector=2)) grid2 = domain.grid(grid.values) math.assert_close(grid.values, grid2.values)
def make_incompressible( velocity: StaggeredGrid, domain: Domain, obstacles: tuple or list or StaggeredGrid = (), particles: PointCloud or None = None, solve_params: math.LinearSolve = math.LinearSolve(), pressure_guess: CenteredGrid = None ) -> Tuple[StaggeredGrid, CenteredGrid, math.Tensor, CenteredGrid, StaggeredGrid]: """ Projects the given velocity field by solving for the pressure and subtracting its spatial_gradient. Args: velocity: Current velocity field as StaggeredGrid obstacles: Sequence of `phi.physics.Obstacle` objects or binary StaggeredGrid marking through-flow cell faces particles (Optional if occupation masks are provided): Pointcloud holding the current positions of the particles domain (Optional if occupation masks are provided): Domain object pressure_guess (Optional): Initial pressure guess as CenteredGrid solve_params: Parameters for the pressure solve Returns: velocity: divergence-free velocity of type `type(velocity)` pressure: solved pressure field, `CenteredGrid` iterations: Number of iterations required to solve for the pressure divergence: divergence field of input velocity, `CenteredGrid` occupation_mask: StaggeredGrid """ points = particles.with_(values=math.wrap(1)) occupied_centered = points >> domain.grid() occupied_staggered = points >> domain.staggered_grid() if isinstance(obstacles, StaggeredGrid): accessible = obstacles else: accessible = domain.accessible_mask( union(*[obstacle.geometry for obstacle in obstacles]), type=StaggeredGrid) # --- Extrapolation is needed to exclude border divergence from the `occupied_centered` mask and thus # from the pressure solve. If particles are randomly distributed, the `occupied_centered` mask # could sometimes include the divergence at the borders (due to single particles right at the edge # which temporarily deform the `occupied_centered` mask when moving into a new cell) which would then # get compensated by the pressure. This is unwanted for falling liquids and therefore prevented by this # extrapolation. --- velocity_field, _ = extrapolate_valid(velocity * occupied_staggered, occupied_staggered, 1) velocity_field *= accessible # Enforces boundary conditions after extrapolation div = field.divergence( velocity_field ) * occupied_centered # Multiplication with `occupied_centered` excludes border divergence from pressure solve def matrix_eq(p): return field.where( occupied_centered, field.divergence( field.spatial_gradient(p, type=StaggeredGrid) * accessible), p) converged, pressure, iterations = field.solve(matrix_eq, div, pressure_guess or domain.grid(), solve_params=solve_params) gradp = field.spatial_gradient(pressure, type=type(velocity_field)) * accessible return velocity_field - gradp, pressure, iterations, div, occupied_staggered