コード例 #1
0
ファイル: von_karman.py プロジェクト: asrvsn/gds
def von_karman_projected():
    m = 20
    n = 40
    gradP = 100.0
    inlet_v = 5.0
    outlet_p = 0.0

    G, (l, r, t, b) = gds.triangular_lattice(m, n, with_boundaries=True)
    # G, (l, r, t, b) = gds.square_lattice(m, n, with_boundaries=True)
    # G, (l, r, t, b) = gds.hexagonal_lattice(m, n, with_boundaries=True)
    # G = gds.triangular_cylinder(m, n)
    # G = gds.square_cylinder(m, n)
    # l, r = G.l_boundary, G.r_boundary

    j, k = 3, m // 2
    # Introduce occlusion
    obstacle = [
        (j, k),
        # (j+1, k),
        # (j+1, k+1),
        # (j+1, k-1),
        # (j-1, k+1),
        # (j, k+2),
    ]
    G.remove_nodes_from(obstacle)
    velocity, pressure = fluid_projected.navier_stokes(G, viscosity=0.0001)

    pressure.set_constraints(dirichlet=gds.combine_bcs(
        {n: gradP / 2
         for n in l.nodes}, {n: -gradP / 2
                             for n in r.nodes}))

    # velocity.set_constraints(dirichlet=gds.combine_bcs(
    # 	{((0, i), (1, i)): inlet_v for i in range(1, m)},
    # 	{((n//2, i), (n//2+1, i)): inlet_v for i in range(1, m)},
    # 	{((n//2-1, 2*i+1), (n//2, 2*i+1)): inlet_v for i in range(0, m//2)},
    # 	# gds.utils.bidict({e: 0 for e in obstacle_boundary}),
    # 	gds.utils.bidict({e: 0 for e in t.edges}),
    # 	gds.utils.bidict({e: 0 for e in b.edges})
    # ))

    sys = gds.couple({
        'velocity':
        velocity,
        # 'divergence': velocity.project(gds.GraphDomain.nodes, lambda v: v.div()),
        'vorticity':
        velocity.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'pressure':
        pressure,
        # 'tracer': lagrangian_tracer(velocity),
        # 'advective': velocity.project(gds.GraphDomain.edges, lambda v: -advector(v)),
        # 'L2': velocity.project(PointObservable, lambda v: np.sqrt(np.dot(v.y, v.y))),
        # 'dK/dt': velocity.project(PointObservable, lambda v: np.dot(v.y, v.leray_project(-advector(v)))),
    })
    gds.render(sys,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               edge_max=0.6,
               dynamic_ranges=True,
               edge_colors=True,
               edge_palette=cc.bgy)
コード例 #2
0
def render():
	p1, v1 = sq_couette_ivp()
	# p1, v1 = tri_couette_ivp()
	# p1, v1 = hex_couette_ivp()
	# p1, v1 = sq_couette()
	# p2, v2 = tri_couette()
	# p3, v3 = hex_couette()

	sys = gds.couple({
		'velocity': v1,
		# 'velocity_tri': v2,
		# 'velocity_hex': v3,
		'pressure': p1,
		# 'pressure_tri': p2,
		# 'pressure_hex': p3,
		'vorticity': v1.project(gds.GraphDomain.faces, lambda v: v.curl()),
		# 'vorticity_tri': v2.project(gds.GraphDomain.faces, lambda v: v.curl()),
		# 'vorticity_hex': v3.project(gds.GraphDomain.faces, lambda v: v.curl()),
		# 'divergence_square': v1.project(gds.GraphDomain.nodes, lambda v: v.div()),
		'kinetic energy': v1.project(PointObservable, lambda v: (v1.y ** 2).sum(), min_rng=0.1),
		'momentum': v1.project(PointObservable, lambda v: np.abs(v1.y).sum(), min_rng=0.1),
		'rotational energy': v1.project(PointObservable, lambda v: (v1.curl() ** 2).sum(), min_rng=0.1),
		'angular momentum': v1.project(PointObservable, lambda v: np.abs(v1.curl()).sum(), min_rng=0.1),
	})
	gds.render(sys, canvas=gds.grid_canvas(sys.observables.values(), 3), edge_max=0.6, dynamic_ranges=True, min_rng_size=1e-2)
コード例 #3
0
ファイル: poiseuille.py プロジェクト: asrvsn/gds
def render_all():
	G, dG = gds.square_lattice(14, 28, with_boundaries=True)
	v1, p1 = poiseuille(G, dG)
	G, dG = gds.triangular_lattice(14, 58, with_boundaries=True)
	v2, p2 = poiseuille(G, dG)
	G, dG = gds.hexagonal_lattice(14, 29, with_boundaries=True)
	v3, p3 = poiseuille(G, dG)
	G, dG = gds.voronoi_lattice(10, 100, with_boundaries=True, eps=0.07)
	v4, p4 = poiseuille(G, dG)

	sys = gds.couple({
		'velocity (sq)': v1,
		'velocity (tri)': v2,
		'velocity (hex)': v3,
		'velocity (voronoi)': v4,
		'pressure (sq)': p1,
		'pressure (tri)': p2,
		'pressure (hex)': p3,
		'pressure (voronoi)': p4,
		'vorticity (sq)': v1.project(gds.GraphDomain.faces, lambda v: v.curl()),
		'vorticity (tri)': v2.project(gds.GraphDomain.faces, lambda v: v.curl()),
		'vorticity (hex)': v3.project(gds.GraphDomain.faces, lambda v: v.curl()),
		'vorticity (voronoi)': v4.project(gds.GraphDomain.faces, lambda v: v.curl()),
	})
	gds.render(sys, canvas=gds.grid_canvas(sys.observables.values(), 4), edge_max=0.6, dynamic_ranges=True, plot_width=900, node_size=0.04)
コード例 #4
0
ファイル: laplace.py プロジェクト: asrvsn/gds
def test_curl():
    G1 = nx.Graph()
    G1.add_nodes_from([1, 2, 3, 4])
    G1.add_edges_from([(1, 2), (2, 3), (3, 4), (4, 1)])
    v1 = gds.edge_gds(G1)
    v1.set_evolution(nil=True)
    v1.set_initial(y0=lambda e: 1 if e == (1, 2) else 0)
    G2 = nx.Graph()
    G2.add_nodes_from([1, 2, 3, 4, 5, 6])
    G2.add_edges_from([(1, 2), (2, 3), (3, 4), (4, 1), (1, 5), (5, 6), (6, 2)])
    v2 = gds.edge_gds(G2)
    v2.set_evolution(nil=True)
    v2.set_initial(y0=lambda e: 1 if e == (1, 2) else 0)
    sys = gds.couple({
        'velocity1':
        v1,
        'curl1':
        v1.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'curl*curl1':
        v1.project(gds.GraphDomain.edges,
                   lambda v: v.curl_face.T @ v.curl_face @ v.y),
        'velocity2':
        v2,
        'curl2':
        v2.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'curl*curl2':
        v2.project(gds.GraphDomain.edges,
                   lambda v: v.curl_face.T @ v.curl_face @ v.y)
    })
    gds.render(sys,
               edge_max=0.5,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               dynamic_ranges=True)
コード例 #5
0
ファイル: von_karman.py プロジェクト: asrvsn/gds
def euler_vortex_street():
    '''
	Vortex street translation in the inviscid model.
	x-periodic hexagonal lattice.
	'''
    # m, n = 4, 20
    # G = gds.hexagonal_lattice(m, n)
    # speed = 1
    # # G = gds.contract_pairs(G, [((0, j), (n, j)) for j in range(1, 2*m)])
    # # G = gds.remove_pos(G)

    # velocity, pressure = fluid_projected.euler(G)
    # y0 = np.zeros(velocity.ndim)
    # for j in range(n-1):
    # 	if j == 5:
    # 		e = ((j, m), (j, m+1))
    # 		y_e = np.zeros(velocity.ndim)
    # 		y_e[velocity.X[e]] = 1
    # 		y_f = speed * velocity.curl_face@y_e
    # 		y_e = velocity.curl_face.T@y_f
    # 		y0 += y_e

    m, n = 3, 20
    G = gds.square_lattice(m, n)
    speed = 1

    velocity, pressure = fluid_projected.euler(G)
    y0 = np.zeros(velocity.ndim)

    for j in [m // 2]:
        i = 5
        e = ((i, j), (i + 1, j))
        y_e = np.zeros(velocity.ndim)
        y_e[velocity.X[e]] = 1
        y_f = speed * velocity.curl_face @ y_e
        y0 += velocity.curl_face.T @ y_f

    velocity.set_initial(y0=lambda x: y0[velocity.X[x]])
    velocity.advect()

    sys = gds.couple({
        'velocity':
        velocity,
        'vorticity':
        velocity.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'advective':
        velocity.project(gds.GraphDomain.edges, lambda v: -v.advect()),
        'pressure':
        pressure,
        # 'divergence': velocity.project(gds.GraphDomain.nodes, lambda v: v.div()),
    })
    gds.render(sys,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               edge_max=0.6,
               dynamic_ranges=True,
               edge_colors=True,
               edge_palette=cc.bgy,
               n_spring_iters=2000)
コード例 #6
0
def scalar_advection_kinds_test():
	conc1, flow1 = advection_on_triangles(periodic=True)
	# conc1, flow1 = advection_on_circle()
	conc2, flow2 = advection_on_triangles(periodic=True, kind='lie')
	# conc2, flow2 = advection_on_circle()
	sys = gds.couple({
		'conc1': conc1,
		'flow1': flow1,
		'div1': flow1.project(GraphDomain.nodes, lambda v: v.div()),
		'conc2': conc2,
		'flow2': flow2,
		'div2': flow2.project(GraphDomain.nodes, lambda v: v.div()),
	})
	gds.render(sys, canvas=gds.grid_canvas(sys.observables.values(), 3), dynamic_ranges=True, node_size=.05, title='Advection of a Gaussian concentration')
コード例 #7
0
ファイル: fluid_projected.py プロジェクト: asrvsn/gds
def ns_cycle_test():
    n = 30
    G = gds.directed_cycle_graph(n)
    velocity = gds.edge_gds(G)
    mu = 0  # viscosity
    print(velocity.X.keys())
    D = np.zeros((velocity.ndim, velocity.ndim))
    L = -D.T @ D
    IV = np.zeros((velocity.ndim, velocity.ndim))
    edge_pairs = zip(
        zip(chain([n - 2, n - 1], range(n - 2)), chain([n - 1], range(n - 1))),
        zip(chain([n - 1], range(n - 1)), range(n)))
    for idx, (e_i, e_j) in enumerate(edge_pairs):
        print(e_i, e_j)
        i, j = velocity.X[e_i], velocity.X[e_j]
        D[i, i] = -1
        D[i, j] = 1
        IV[j, idx] = 1
    # print(D)
    # D = -velocity.incidence # Either incidence or dual derivative seems to work
    Dm = relu(-D)
    Dp = relu(D)
    F = Dm.T @ Dp - Dp.T @ Dm

    # pdb.set_trace()
    def dvdt(t, v):
        A = np.multiply(F.T, v).T + np.multiply(F, v)
        return -A @ v + mu * L @ v

    velocity.set_evolution(dydt=dvdt)
    bump = 2 * stats.norm().pdf(np.linspace(-4, 4, n))
    # v0 = -IV @ bump
    # v0 = IV @ rotate(bump, 10)
    v0 = IV @ (bump - rotate(bump, n // 2))
    velocity.set_initial(y0=lambda e: v0[velocity.X[e]])

    sys = gds.couple({
        'velocity': velocity,
        # 'gradient': velocity.project(gds.GraphDomain.edges, lambda v: D @ v.y),
        # 'laplacian': velocity.project(gds.GraphDomain.edges, lambda v: -D.T @ D @ v.y),
        # 'dual': velocity.project(gds.GraphDomain.nodes, lambda v: v.y),
        # 'L1': velocity.project(PointObservable, lambda v: np.abs(v.y).sum()),
        # 'L2': velocity.project(PointObservable, lambda v: np.linalg.norm(v.y)),
        # 'min': velocity.project(PointObservable, lambda v: v.y.min()),
    })
    gds.render(sys,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               edge_max=3,
               dynamic_ranges=False)
コード例 #8
0
ファイル: ns_symmetries.py プロジェクト: asrvsn/gds
def render2():
	viscosity, density = 1., 1e-2
	p1, v1 = sq_couette_ivp(viscosity, density)
	p2, v2 = tri_couette_ivp(viscosity, density)
	p3, v3 = hex_couette_ivp(viscosity, density)


	sys = gds.couple({
		'velocity_sq': v1,
		'velocity_tri': v2,
		'velocity_hex': v3,
		'flow_material_derivative_sq': v1.project(PointObservable, lambda v: np.abs(viscosity * v.laplacian() - p1.grad()).sum()),
		'flow_material_derivative_tri': v2.project(PointObservable, lambda v: np.abs(viscosity * v.laplacian() - p2.grad()).sum()),
		'flow_material_derivative_hex': v3.project(PointObservable, lambda v: np.abs(viscosity * v.laplacian() - p3.grad()).sum()),
	})
	gds.render(sys, canvas=gds.grid_canvas(sys.observables.values(), 3), edge_max=0.6, dynamic_ranges=True, min_rng_size=1e-2)
コード例 #9
0
ファイル: telegrapher.py プロジェクト: asrvsn/gds
def render():
    # p, v = voronoi_poiseuille()
    p, v = sq_poiseuille()
    # p1, v1 = sq_poiseuille()
    # p2, v2 = tri_poiseuille()
    # p3, v3 = hex_poiseuille()

    # v = v3
    sys = gds.couple({
        'velocity':
        v,
        # 'velocity_square': v1,
        # 'velocity_tri': v2,
        # 'velocity_hex': v3,
        'pressure':
        p,
        # 'pressure_square': p1,
        # 'pressure_tri': p2,
        # 'pressure_hex': p3,
        'vorticity':
        v.project(gds.GraphDomain.faces, lambda v: v.curl()),
        # 'vorticity_square': v1.project(gds.GraphDomain.faces, lambda v: v.curl()),
        # 'vorticity_tri': v2.project(gds.GraphDomain.faces, lambda v: v.curl()),
        # 'vorticity_hex': v3.project(gds.GraphDomain.faces, lambda v: v.curl()),
        # 'div_square': v1.project(gds.GraphDomain.nodes, lambda v: v.div()),
        # 'div_tri': v2.project(gds.GraphDomain.nodes, lambda v: v.div()),
        # 'div_hex': v3.project(gds.GraphDomain.nodes, lambda v: v.div()),
        # 'divergence': v.project(gds.GraphDomain.nodes, lambda v: v.div()),
        # 'laplacian_square': v1.project(gds.GraphDomain.edges, lambda v: v.laplacian()),
        # 'laplacian_tri': v2.project(gds.GraphDomain.edges, lambda v: v.laplacian()),
        # 'laplacian_hex': v3.project(gds.GraphDomain.edges, lambda v: v.laplacian()),
        # 'dd*': v.project(gds.GraphDomain.edges, lambda v: v.dd_()),
        # 'd*d': v.project(gds.GraphDomain.edges, lambda v: v.d_d()),
        'energy':
        v.project(PointObservable, lambda v: (v.y**2).sum()),
        'momentum':
        v.project(PointObservable, lambda v: np.abs(v.y).sum()),
    })
    gds.render(sys,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               edge_max=0.6,
               dynamic_ranges=True,
               plot_width=900,
               node_size=0.04)
コード例 #10
0
def show_leray(G, v: Callable = None, **kwargs):
    '''
	Show Leray decomposition of a vector field.
	'''
    if v is None:
        v = lambda x: np.random.uniform(1, 2)
    orig = gds.edge_gds(G)
    orig.set_evolution(nil=True)
    orig.set_initial(y0=v)

    div_free = orig.project(GraphDomain.edges, lambda u: u.leray_project())
    curl_free = orig.project(GraphDomain.edges,
                             lambda u: u.y - u.leray_project())

    sys = gds.couple({
        'original':
        orig,
        'original (div)':
        orig.project(GraphDomain.nodes, lambda u: u.div()),
        'original (curl)':
        orig.project(GraphDomain.faces, lambda u: u.curl()),
        'div-free':
        div_free,
        'div-free (div)':
        div_free.project(
            GraphDomain.nodes,
            lambda u: orig.div(u.y)),  # TODO: chaining projections?
        'div-free (curl)':
        div_free.project(GraphDomain.faces, lambda u: orig.curl(u.y)),
        'curl-free':
        curl_free,
        'curl-free (div)':
        curl_free.project(GraphDomain.nodes, lambda u: orig.div(u.y)),
        'curl-free (curl)':
        curl_free.project(GraphDomain.faces, lambda u: orig.curl(u.y)),
    })

    gds.render(sys,
               n_spring_iters=1000,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               title='Leray decomposition',
               min_rng_size=1e-3,
               **kwargs)
コード例 #11
0
ファイル: beltrami_fields.py プロジェクト: asrvsn/gds
def plot_beltrami():
    if not os.path.exists(save_path):
        raise Exception('no data')

    sys = dict()
    fig_N, fig_M = 0, 0
    with open(save_path, 'rb') as f:
        data = cloudpickle.load(f)
        fig_N, fig_M = len(data), len(data[next(iter(data))])
        for N, subdata in sorted(data.items(), key=lambda x: x[0]):
            for i in subdata:
                print((N, i))
                u = subdata[i]
                G = gds.triangular_lattice(m=1, n=N)
                flow = gds.edge_gds(G)
                flow.set_evolution(nil=True)
                flow.set_initial(y0=lambda x: u[flow.X[x]])
                sys[f'{N}_{i}'] = flow
    sys = gds.couple(sys)
    canvas = gds.grid_canvas(sys.observables.values(), fig_M)
    gds.render(sys, canvas=canvas, edge_max=0.6, dynamic_ranges=True)
コード例 #12
0
ファイル: laplace.py プロジェクト: asrvsn/gds
def render_edge_diffusion(v):
    sys = gds.couple({
        'velocity':
        v,
        'divergence':
        v.project(gds.GraphDomain.nodes, lambda v: v.div()),
        'curl':
        v.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'd*d':
        v.project(gds.GraphDomain.edges,
                  lambda v: -v.curl_face.T @ v.curl_face @ v.y),
        'dd*':
        v.project(gds.GraphDomain.edges,
                  lambda v: v.dirichlet_laplacian @ v.y),
        'L1':
        v.project(gds.GraphDomain.edges, lambda v: v.laplacian()),
    })
    gds.render(sys,
               edge_max=0.6,
               dynamic_ranges=True,
               canvas=gds.grid_canvas(sys.observables.values(), 3))
コード例 #13
0
ファイル: ns_symmetries.py プロジェクト: asrvsn/gds
def render1():
	viscosity, density = 1., 1e-2
	p, v = sq_couette_ivp(viscosity, density)
	# p, v = tri_couette_ivp(viscosity, density)
	# p, v = hex_couette_ivp(viscosity, density)


	# components = dict()
	# for (name, G) in p.G.lattice_components.items():
	# 	components[name] = v.project(G, lambda v: v)
	# 	components[f'{name}_energy'] = components[name].project(PointObservable, lambda v: (v.y ** 2).sum(), min_rng=0.1)
	# 	components[f'{name}_momentum'] = components[name].project(PointObservable, lambda v: v.y.sum(), min_rng=0.1)

	sys = gds.couple({
		'velocity': v,
		'pressure': p,
		'vorticity': v.project(GraphDomain.faces, lambda v: v.curl()),
		'mass flux': v.project(GraphDomain.edges, lambda v: viscosity * v.laplacian() - p.grad()), 
		'total mass flux': v.project(PointObservable, lambda v: np.abs(viscosity * v.laplacian() - p.grad()).sum()),
		'divergence': v.project(GraphDomain.nodes, lambda v: v.div()),
	})
	gds.render(sys, canvas=gds.grid_canvas(sys.observables.values(), 3), edge_max=0.6, dynamic_ranges=True, min_rng_size=1e-2)
コード例 #14
0
ファイル: laplace.py プロジェクト: asrvsn/gds
def edge_diffusion_comp():
    v1 = square_edge_diffusion()
    v2 = tri_edge_diffusion()
    v3 = hex_edge_diffusion()
    sys = gds.couple({
        'flow_square':
        v1,
        'flow_tri':
        v2,
        'flow_hex':
        v3,
        'curl_square':
        v1.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'curl_tri':
        v2.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'curl_hex':
        v3.project(gds.GraphDomain.faces, lambda v: v.curl()),
    })
    gds.render(sys,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               edge_max=0.6,
               dynamic_ranges=True)
コード例 #15
0
ファイル: fluid.py プロジェクト: asrvsn/gds
def fluid_test(velocity, pressure=None, columns=3, **kwargs):
    if hasattr(velocity, 'advector'):
        advector = velocity.advector  # TODO: hacky
    else:
        advector = lambda v: v.advect()
    freqs, spec_fun = edge_power_spectrum(velocity.G)
    obs = {
        'velocity':
        velocity,
        'divergence':
        velocity.project(gds.GraphDomain.nodes, lambda v: v.div()),
        # 'vorticity': velocity.project(gds.GraphDomain.faces, lambda v: v.curl()),
        # 'diffusion': velocity.project(gds.GraphDomain.edges, lambda v: v.laplacian()),
        # 'tracer': lagrangian_tracer(velocity),
        # 'advective': velocity.project(gds.GraphDomain.edges, lambda v: -advector(v)),
        # 'leray projection': velocity.project(gds.GraphDomain.edges, lambda v: v.leray_project()),
        'L1':
        velocity.project(PointObservable, lambda v: np.abs(v.y).sum()),
        'power spectrum':
        velocity.project(VectorObservable, lambda v: spec_fun(v.y),
                         freqs.tolist()),
        # 'power L2': velocity.project(PointObservable, lambda v: np.sqrt(spec_fun(v.y).sum())),
        'L2':
        velocity.project(PointObservable, lambda v: np.sqrt(np.dot(v.y, v.y))),
        # 'dK/dt': velocity.project(PointObservable, lambda v: np.dot(v.y, -advector(v))),
        # 'dK/dt': velocity.project(PointObservable, lambda v: np.dot(v.y, -advector(velocity) - pressure.grad())),
    }
    if pressure != None:
        obs['pressure'] = pressure
        # obs['pressure_grad'] = pressure.project(gds.GraphDomain.edges, lambda p: -p.grad())
    sys = gds.couple(obs)
    gds.render(sys,
               canvas=gds.grid_canvas(sys.observables.values(), columns),
               edge_max=0.6,
               dynamic_ranges=True,
               **kwargs)
コード例 #16
0
ファイル: von_karman.py プロジェクト: asrvsn/gds
def von_karman():
    m = 20
    n = 50
    gradP = 80.0
    inlet_v = 20.0
    outlet_p = 0.0
    G, (l, r, t, b) = gds.triangular_lattice(m, n, with_boundaries=True)

    tb = set(t.nodes()) | set(b.nodes())
    lr = set(l.nodes()) | set(r.nodes())
    v_free = lr - tb
    e_free = set(gds.edge_domain(G, lr))
    e_normal = set(nx.edge_boundary(G, lr))

    j, k = 8, m // 2
    # Introduce occlusion
    obstacle = [
        (j, k),
        (j + 1, k),
        (j + 2, k),
        # (j, k+1),
        # (j, k-1),
        # (j-1, k),
        # (j+1, k+1),
        # (j+1, k-1),
        # (j, k+2),
        # (j, k-2),
    ]
    obstacle_boundary = gds.utils.flatten([G.neighbors(n) for n in obstacle])
    obstacle_boundary = gds.edge_domain(G, obstacle_boundary)
    G.remove_nodes_from(obstacle)
    # G.remove_edges_from(list(nx.edge_boundary(G, l, l)))
    # G.remove_edges_from(list(nx.edge_boundary(G, [(0, 2*i+1) for i in range(m//2)], [(1, 2*i) for i in range(m//2+1)])))
    # G.remove_edges_from(list(nx.edge_boundary(G, r, r)))
    # G.remove_edges_from(list(nx.edge_boundary(G, [(n//2, 2*i+1) for i in range(m//2)], [(n//2, 2*i) for i in range(m//2+1)])))
    velocity, pressure = fluid.navier_stokes(G,
                                             viscosity=10,
                                             v_free=v_free,
                                             e_free=e_free,
                                             e_normal=e_normal)
    pressure.set_constraints(dirichlet=gds.combine_bcs(
        {n: gradP / 2
         for n in l.nodes}, {n: -gradP / 2
                             for n in r.nodes}
        # {n: 0 for n in l.nodes}
        # {(n//2+1, j): outlet_p for j in range(n)}
    ))
    gradation = np.linspace(-0.1, 0.1, m + 1)
    velocity.set_constraints(dirichlet=gds.combine_bcs(
        # {((0, i), (1, i)): inlet_v + gradation[i] for i in range(1, m)},
        # {((n//2, i), (n//2+1, i)): inlet_v - gradation[i] for i in range(1, m)},
        # {((n//2-1, 2*i+1), (n//2, 2*i+1)): inlet_v - gradation[2*i+1] for i in range(0, m//2)},
        {e: 0
         for e in obstacle_boundary},
        gds.zero_edge_bc(t),
        gds.zero_edge_bc(b),
    ))

    sys = gds.couple({
        'velocity': velocity,
        # 'divergence': velocity.project(gds.GraphDomain.nodes, lambda v: v.div()),
        # 'vorticity': velocity.project(gds.GraphDomain.faces, lambda v: v.curl()),
        'pressure': pressure,
    })
    gds.render(sys,
               canvas=gds.grid_canvas(sys.observables.values(), 3),
               edge_max=0.6,
               dynamic_ranges=True,
               edge_palette=cc.bgy)