def show_L0_eigfuns(G, n=12, **kwargs): ''' Show eigenfunctions of 0-Laplacian ''' obs = gds.node_gds(G) # L = np.array(nx.laplacian_matrix(G).todense()) L = -obs.laplacian(np.eye(obs.ndim)) # vals, vecs = sp.sparse.linalg.eigs(-L, k=n, which='SM') vals, vecs = np.linalg.eigh(L) vals, vecs = np.real(vals), np.real(vecs) # pdb.set_trace() sys = dict() sys['Surface'] = gds.face_gds(G) sys['Surface'].set_evolution(nil=True) canvas = dict() canvas['Surface'] = [[[sys['Surface']]]] for i, (ev, vec) in enumerate(sorted(zip(vals, vecs.T), key=lambda x: np.abs(x[0]))): ev = np.round(ev, 5) obs = gds.node_gds(G) obs.set_evolution(nil=True) obs.set_initial(y0=lambda x: vec[obs.X[x]]) if ev in canvas: sys[f'eigval_{ev} eigfun_{len(canvas[ev])}'] = obs canvas[ev].append([[obs]]) else: sys[f'eigval_{ev} eigfun_{0}'] = obs canvas[ev] = [[[obs]]] if i == n: break # canvas = sorted(list(canvas.values()), key=len) canvas = list(canvas.values()) sys = gds.couple(sys) gds.render(sys, canvas=canvas, n_spring_iters=1200, title=f'L0-eigenfunctions', **kwargs)
def heat_test(): G = gds.grid_graph(10, 10) temp1 = gds.node_gds(G) x = (5, 5) i = temp1.X[x] B = temp1.incidence.copy().todense() B[i][B[i] < 0] = 0 temp1.set_evolution(dydt=lambda t, y: -B @ B.T @ y) temp1.set_initial(y0=lambda n: 1.0 if n == x else 0.0) temp2 = gds.node_gds(G) temp2.set_evolution(dydt=lambda t, y: temp2.laplacian(y)) temp2.set_constraints(dirichlet={x: 1.0}) return temp1, temp2
def schrodinger(G: nx.Graph, V: Callable, psi0: Callable, **kwargs): re, im = gds.node_gds(G), gds.node_gds(G) V_arr = np.array([V(x) for x in re.X]) def f(t, y): psi = re.y + 1j*im.y return 1j * (re.laplacian(psi) - V_arr*psi) re.set_evolution(dydt=lambda t, y: np.real(f(t, y))) re.set_initial(y0=lambda x: np.real(psi0(x))) im.set_evolution(dydt=lambda t, y: np.imag(f(t, y))) im.set_initial(y0=lambda x: np.imag(psi0(x))) sys = gds.couple({ 'real': re, 'imag': im, }) gds.render(sys, dynamic_ranges=True, node_palette=cc.bmy, n_spring_iters=1000, **kwargs)
def lagrangian_tracer(velocity: gds.edge_gds, alpha=1.0) -> gds.node_gds: ''' Passive tracer ''' tracer = gds.node_gds(velocity.G) tracer.set_evolution(dydt=lambda t, y: -alpha * tracer.advect(velocity)) start_x = random.choice(list(velocity.G.nodes())) tracer.set_initial(y0=lambda x: 1. if x == start_x else 0.) return tracer
def wave(G: nx.Graph, c: float=5.0): n = 20 G = gds.triangular_lattice(n, n*2, periodic=True) amplitude = gds.node_gds(G) amplitude.set_evolution(dydt=lambda t, y: (c**2)*amplitude.laplacian(), order=2) amplitude.set_initial(y0=lambda x: x[0]*x[1]*(n-x[0])*(n-x[1]) / (n**3)) gds.render(amplitude, dynamic_ranges=True, node_palette=cc.bmy, n_spring_iters=1000, title='Waves on a simplicial torus')
def navier_stokes(G: nx.Graph, viscosity=1e-3, density=1.0, v_free=[], body_force=None, advect=None, integrator=Integrators.lsoda, **kwargs) -> (gds.node_gds, gds.edge_gds): if body_force is None: body_force = lambda t, y: 0 if advect is None: advect = lambda v: v.advect() pressure = gds.node_gds(G, **kwargs) pressure.set_evolution(lhs=lambda t, p: pressure.laplacian(p) / density, refresh_cvx=False) v_free = np.array([pressure.X[x] for x in set(v_free)], dtype=np.intp) velocity = gds.edge_gds(G, v_free=v_free, **kwargs) velocity.set_evolution( dydt=lambda t, u: velocity. leray_project(-advect(velocity) + body_force(t, u) + (0 if viscosity == 0 else velocity.laplacian() * viscosity / density)) - pressure.grad() / density, max_step=1e-3, integrator=integrator, ) return velocity, pressure
def grid_tv_boundary(): G = gds.square_lattice(10, 10) temperature = gds.node_gds(G) temperature.set_evolution(dydt=lambda t, y: temperature.laplacian()) temperature.set_constraints(dirichlet=gds.combine_bcs( lambda x: 0 if x[0] == 9 else None, lambda t, x: np.sin(t + x[1] / 4)**2 if x[0] == 0 else None)) gds.render(temperature, title='Heat equation with time-varying boundary')
def SIR_model( G, dS=0.1, dI=0.5, dR=0.0, # Diffusive terms muS=0.1, muI=0.3, muR=0.1, # Death rates Lambda=0.5, # Birth rate beta=0.2, # Rate of contact gamma=0.2, # Rate of recovery initial_population=100, patient_zero=None, **kwargs): ''' Reaction-Diffusion SIR model Based on Huang et al, https://www.researchgate.net/publication/281739911_The_reaction-diffusion_system_for_an_SIR_epidemic_model_with_a_free_boundary ''' susceptible = gds.node_gds(G, **kwargs) infected = gds.node_gds(G, **kwargs) recovered = gds.node_gds(G, **kwargs) susceptible.set_evolution(dydt=lambda t, y: dS * susceptible.laplacian( ) - muS * susceptible.y - beta * susceptible.y * infected.y + Lambda) infected.set_evolution( dydt=lambda t, y: dI * infected.laplacian() + beta * susceptible.y * infected.y - muI * infected.y - gamma * infected.y) recovered.set_evolution(dydt=lambda t, y: dR * recovered.laplacian() + gamma * infected.y - muR * recovered.y) if patient_zero is None: patient_zero = random.choice(list(G.nodes())) print(patient_zero) susceptible.set_initial(y0=lambda x: initial_population) infected.set_initial(y0=lambda x: 1 if x == patient_zero else 0.) sys = gds.couple({ 'Susceptible': susceptible, 'Infected': infected, 'Recovered': recovered }) return sys
def swift_hohenberg(G: nx.graph, a: float, b: float, c: float, gam0: float, gam2: float) -> gds.node_gds: assert c > 0, 'Unstable' amplitude = gds.node_gds(G) amplitude.set_evolution( dydt=lambda t, y: -a*y - b*(y**2) - c*(y**3) + gam0*amplitude.laplacian(y) - gam2*amplitude.bilaplacian(y) ) # amplitude.set_initial(y0=lambda _: np.random.uniform()) amplitude.set_initial(y0=lambda x: x[0]+x[1]) return amplitude
def heat_grid(n=10, steady_state=False) -> gds.node_gds: G = gds.square_lattice(n, n) temp = gds.node_gds(G) if steady_state: ''' Steady-state version (for comparison) ''' temp.set_evolution(cost=lambda t, y: temp.laplacian(y)) else: temp.set_evolution(dydt=lambda t, y: temp.laplacian(y)) return temp
def advection(G, v_field, kind=None): flow_diff = np.zeros(len(G.edges())) flow = gds.edge_gds(G) flow.set_evolution(dydt=lambda t, y: flow_diff) flow.set_initial(y0 = v_field) conc = gds.node_gds(G) if kind == None: conc.set_evolution(dydt=lambda t, y: -conc.advect(flow)) elif kind == 'lie': conc.set_evolution(dydt=lambda t, y: -conc.lie_advect(flow)) else: raise Exception('unrecognized kind') return conc, flow
def test_orientation(): G = gds.hexagonal_lattice(3, 4) # G = gds.random_planar_graph(100, 0.2) eq1 = gds.face_gds(G) eq1.set_evolution(nil=True) eq1.set_initial(y0=lambda x: 0.5) eq2 = gds.node_gds(G) eq2.set_evolution(nil=True) sys = gds.couple({ 'eq1': eq1, 'eq2': eq2, }) gds.render(sys, face_orientations=True)
def navier_stokes(G: nx.Graph, viscosity=1e-3, density=1.0, v_free=[], e_free=[], e_normal=[], advect=None, integrator=Integrators.lsoda, **kwargs) -> (gds.node_gds, gds.edge_gds): if advect is None: advect = lambda v: v.advect() pressure = gds.node_gds(G, **kwargs) velocity = gds.edge_gds(G, **kwargs) v_free = np.array( [pressure.X[x] for x in set(v_free)], dtype=np.intp) # Inlet/outlet nodes (typically, pressure boundaries) e_free = np.array([velocity.X[x] for x in set(e_free)], dtype=np.intp) # Free-slip surface e_normal = np.array([velocity.X[x] for x in set(e_normal)], dtype=np.intp) # Inlets/outlet edges normal to surface min_step = 1e-3 def pressure_f(t, y): dt = max(min_step, velocity.dt) lhs = velocity.div(velocity.y / dt - advect(velocity) + velocity.laplacian(free=e_free, normal=e_normal) * viscosity / density) lhs[v_free] = 0. lhs -= pressure.laplacian(y) / density return lhs def velocity_f(t, y): return -advect( velocity) - pressure.grad() / density + velocity.laplacian( free=e_free, normal=e_normal) * viscosity / density pressure.set_evolution(lhs=pressure_f) velocity.set_evolution(dydt=velocity_f, integrator=integrator) return velocity, pressure