def test03_dJdq(): """ Compute dJdq for a simple problem, check that it works """ # # Mesh # mesh = Mesh1D(resolution=(20, )) mesh.mark_region('left', lambda x: np.abs(x) < 1e-10) mesh.mark_region('right', lambda x: np.abs(x - 1) < 1e-10) # # Element # Q = QuadFE(mesh.dim(), 'Q3') dh = DofHandler(mesh, Q) dh.distribute_dofs() nx = dh.n_dofs() x = dh.get_dof_vertices() # # Basis # phi = Basis(dh, 'v') phi_x = Basis(dh, 'vx') # # Parameters # # Reference q q_ref = Nodal(data=np.zeros(nx), basis=phi) # Perturbation dq = Nodal(data=np.ones(nx), basis=phi) # # Sample Reference QoI # J, u_ref = sample_qoi(q_ref.data(), dh, return_state=True) u_ref = Nodal(data=u_ref, basis=phi) # # Compute dJdq # # Perturbation method Jp_per = dJdq_per(q_ref, dq, dh) # Sensitivity method Jp_sen = dJdq_sen(q_ref, u_ref, dq) # Adjoint method Jp_adj = dJdq_adj(q_ref, u_ref, dq) # Check that the answers are close to -1 assert np.allclose(Jp_per, -1) assert np.allclose(Jp_sen, -1) assert np.allclose(Jp_adj, -1)
def test09_1d_inverse(self): """ Compute the inverse of a matrix and apply it to a vector/matrix. """ # # Mesh # mesh = Mesh1D(resolution=(1, )) mesh.mark_region('left', lambda x: np.abs(x) < 1e-9, on_boundary=True) mesh.mark_region('right', lambda x: np.abs(1 - x) < 1e-9, on_boundary=True) # # Elements # Q3 = QuadFE(1, 'Q3') dofhandler = DofHandler(mesh, Q3) dofhandler.distribute_dofs() # # Basis # u = Basis(dofhandler, 'u') ux = Basis(dofhandler, 'ux') # # Define sampled right hand side and exact solution # xv = dofhandler.get_dof_vertices() n_points = dofhandler.n_dofs() n_samples = 6 a = np.arange(n_samples) ffn = lambda x, a: a * x ufn = lambda x, a: a / 6 * (x - x**3) + x fdata = np.zeros((n_points, n_samples)) udata = np.zeros((n_points, n_samples)) for i in range(n_samples): fdata[:, i] = ffn(xv, a[i]).ravel() udata[:, i] = ufn(xv, a[i]).ravel() # Define sampled function fn = Nodal(data=fdata, basis=u) ue = Nodal(data=udata, basis=u) # # Forms # one = Constant(1) a = Form(Kernel(one), test=ux, trial=ux) L = Form(Kernel(fn), test=u) problem = [[a], [L]] # # Assembler # assembler = Assembler(problem, mesh) assembler.assemble() A = assembler.get_matrix() b = assembler.get_vector(i_problem=1) # # Linear System # system = LinearSystem(u, A=A) # Set constraints system.add_dirichlet_constraint('left', 0) system.add_dirichlet_constraint('right', 1) system.solve_system(b) # Extract finite element solution ua = system.get_solution(as_function=True) system2 = LinearSystem(u, A=A, b=b) # Set constraints system2.add_dirichlet_constraint('left', 0) system2.add_dirichlet_constraint('right', 1) system2.solve_system() u2 = system2.get_solution(as_function=True) # Check that the solution is close self.assertTrue(np.allclose(ue.data()[:, 0], ua.data()[:, 0])) self.assertTrue(np.allclose(ue.data()[:, [0]], u2.data()))
def test08_1d_sampled_rhs(self): # # Mesh # mesh = Mesh1D(resolution=(1, )) mesh.mark_region('left', lambda x: np.abs(x) < 1e-9, on_boundary=True) mesh.mark_region('right', lambda x: np.abs(1 - x) < 1e-9, on_boundary=True) # # Elements # Q3 = QuadFE(1, 'Q3') dofhandler = DofHandler(mesh, Q3) dofhandler.distribute_dofs() # # Basis # v = Basis(dofhandler, 'u') vx = Basis(dofhandler, 'ux') # # Define sampled right hand side and exact solution # xv = dofhandler.get_dof_vertices() n_points = dofhandler.n_dofs() n_samples = 6 a = np.arange(n_samples) f = lambda x, a: a * x u = lambda x, a: a / 6 * (x - x**3) + x fdata = np.zeros((n_points, n_samples)) udata = np.zeros((n_points, n_samples)) for i in range(n_samples): fdata[:, i] = f(xv, a[i]).ravel() udata[:, i] = u(xv, a[i]).ravel() # Define sampled function fn = Nodal(data=fdata, basis=v) ue = Nodal(data=udata, basis=v) # # Forms # one = Constant(1) a = Form(Kernel(one), test=vx, trial=vx) L = Form(Kernel(fn), test=v) problem = [a, L] # # Assembler # assembler = Assembler(problem, mesh) assembler.assemble() A = assembler.get_matrix() b = assembler.get_vector() # # Linear System # system = LinearSystem(v, A=A, b=b) # Set constraints system.add_dirichlet_constraint('left', 0) system.add_dirichlet_constraint('right', 1) #system.set_constraint_relation() #system.incorporate_constraints() # Solve and resolve constraints system.solve_system() #system.resolve_constraints() # Extract finite element solution ua = system.get_solution(as_function=True) # Check that the solution is close print(ue.data()[:, [0]]) print(ua.data()) self.assertTrue(np.allclose(ue.data()[:, [0]], ua.data()))
def test_set_data(self): meshes = {1: Mesh1D(), 2: QuadMesh()} elements = {1: QuadFE(1, 'Q2'), 2: QuadFE(2, 'Q2')} # # Use function to set data # fns = { 1: { 1: lambda x: 2 * x[:, 0]**2, 2: lambda x, y: 2 * x[:, 0] + 2 * y[:, 0] }, 2: { 1: lambda x: x[:, 0]**2 + x[:, 1], 2: lambda x, y: x[:, 0] * y[:, 0] + x[:, 1] * y[:, 1] } } parms = {1: {1: [{}, {}], 2: [{}, {}]}, 2: {1: [{}, {}], 2: [{}, {}]}} for dim in [1, 2]: # Get mesh and element mesh = meshes[dim] element = elements[dim] # Set dofhandler dofhandler = DofHandler(mesh, element) dofhandler.distribute_dofs() n_dofs = dofhandler.n_dofs() # Set basis basis = Basis(dofhandler) # Determine the shapes of the data det_shapes = {1: (n_dofs, 1), 2: (n_dofs, n_dofs, 1)} smp_shapes = {1: (n_dofs, 2), 2: (n_dofs, n_dofs, 2)} # Get a vertex i = np.random.randint(n_dofs) j = np.random.randint(n_dofs) dofhandler.set_dof_vertices() x = dofhandler.get_dof_vertices() x1 = np.array([x[i, :]]) x2 = np.array([x[j, :]]) for n_variables in [1, 2]: fn = fns[dim][n_variables] parm = parms[dim][n_variables] # # Deterministic # f = Nodal(f=fn, basis=basis, n_variables=n_variables) # Check shape self.assertEqual(f.data().shape, det_shapes[n_variables]) # Check value if n_variables == 1: val = fn(x1)[0] self.assertEqual(val, f.data()[i]) else: val = fn(x1, x2) self.assertEqual(val[0], f.data()[i, j]) # # Sampled # f = Nodal(f=fn, parameters=parm, basis=basis, n_variables=n_variables) # Check shape self.assertEqual(f.data().shape, smp_shapes[n_variables]) # Check that samples are stored in the right place if n_variables == 1: self.assertTrue(np.allclose(f.data()[:, 0], f.data()[:, 1])) elif n_variables == 2: self.assertTrue( np.allclose(f.data()[:, :, 0], f.data()[:, :, 1]))
# mesh = Mesh1D(resolution=(20,)) # Mark Dirichlet Vertices mesh.mark_region('left', lambda x: np.abs(x)<1e-9) mesh.mark_region('right', lambda x: np.abs(x-1)<1e-9) # # Element # Q1 = QuadFE(mesh.dim(), 'Q1') # Dofhandler dofhandler = DofHandler(mesh, Q1) dofhandler.distribute_dofs() x = dofhandler.get_dof_vertices() n = dofhandler.n_dofs() # ----------------------------------------------------------------------------- # Sample full dimensional parameter space # ----------------------------------------------------------------------------- # # Set up sparse grid on coarse parameter space # tasmanian_library="/home/hans-werner/bin/TASMANIAN-6.0/libtasmaniansparsegrid.so" grid = TasmanianSG.TasmanianSparseGrid(tasmanian_library=tasmanian_library) dimensions = 4 outputs = 1 max_depth = 8 type = 'level' rule = 'gauss-legendre'
def test_constructor(self): # ===================================================================== # Test 1D # ===================================================================== # # Kernel consists of a single explicit Function: # f1 = lambda x: x+2 f = Explicit(f1, dim=1) k = Kernel(f) x = np.linspace(0,1,100) n_points = len(x) # Check that it evaluates correctly. self.assertTrue(np.allclose(f1(x), k.eval(x).ravel())) # Check shape of kernel self.assertEqual(k.eval(x).shape, (n_points,1)) # # Kernel consists of a combination of two explicit functions # f1 = Explicit(lambda x: x+2, dim=1) f2 = Explicit(lambda x: x**2 + 1, dim=1) F = lambda f1, f2: f1**2 + f2 f_t = lambda x: (x+2)**2 + x**2 + 1 k = Kernel([f1,f2], F=F) # Check evaluation self.assertTrue(np.allclose(f_t(x), k.eval(x).ravel())) # Check shape self.assertEqual(k.eval(x).shape, (n_points,1)) # # Same thing as above, but with nodal functions # mesh = Mesh1D(resolution=(1,)) Q1 = QuadFE(1,'Q1') Q2 = QuadFE(1,'Q2') dQ1 = DofHandler(mesh,Q1) dQ2 = DofHandler(mesh,Q2) # Distribute dofs [dQ.distribute_dofs() for dQ in [dQ1,dQ2]] # Basis functions phi1 = Basis(dQ1,'u') phi2 = Basis(dQ2,'u') f1 = Nodal(lambda x: x+2, basis=phi1) f2 = Nodal(lambda x: x**2 + 1, basis=phi2) k = Kernel([f1,f2], F=F) # Check evaluation self.assertTrue(np.allclose(f_t(x), k.eval(x).ravel())) # # Replace f2 above with its derivative # k = Kernel([f1,f2], derivatives=['f', 'fx'], F=F) f_t = lambda x: (x+2)**2 + 2*x # Check derivative evaluation F = F(f1, df2_dx) self.assertTrue(np.allclose(f_t(x), k.eval(x).ravel())) # # Sampling # one = Constant(1) f1 = Explicit(lambda x: x**2 + 1, dim=1) # Sampled function a = np.linspace(0,1,11) n_samples = len(a) # Define Dofhandler dh = DofHandler(mesh, Q2) dh.distribute_dofs() dh.set_dof_vertices() xv = dh.get_dof_vertices() n_dofs = dh.n_dofs() phi = Basis(dh, 'u') # Evaluate parameterized function at mesh dof vertices f2_m = np.empty((n_dofs, n_samples)) for i in range(n_samples): f2_m[:,i] = xv.ravel() + a[i]*xv.ravel()**2 f2 = Nodal(data=f2_m, basis=phi) # Define kernel F = lambda f1, f2, one: f1 + f2 + one k = Kernel([f1,f2,one], F=F) # Evaluate on a fine mesh x = np.linspace(0,1,100) n_points = len(x) self.assertEqual(k.eval(x).shape, (n_points, n_samples)) for i in range(n_samples): # Check evaluation self.assertTrue(np.allclose(k.eval(x)[:,i], f1.eval(x)[:,i] + x + a[i]*x**2+ 1)) # # Sample multiple constant functions # f1 = Constant(data=a) f2 = Explicit(lambda x: 1 + x**2, dim=1) f3 = Nodal(data=f2_m[:,-1], basis=phi) F = lambda f1, f2, f3: f1 + f2 + f3 k = Kernel([f1,f2,f3], F=F) x = np.linspace(0,1,100) for i in range(n_samples): self.assertTrue(np.allclose(k.eval(x)[:,i], \ a[i] + f2.eval(x)[:,i] + f3.eval(x)[:,i])) # # Submeshes # mesh = Mesh1D(resolution=(1,)) mesh_labels = Tree(regular=False) mesh = Mesh1D(resolution=(1,)) Q1 = QuadFE(1,'Q1') Q2 = QuadFE(1,'Q2') dQ1 = DofHandler(mesh,Q1) dQ2 = DofHandler(mesh,Q2) # Distribute dofs [dQ.distribute_dofs() for dQ in [dQ1,dQ2]] # Basis p1 = Basis(dQ1) p2 = Basis(dQ2) f1 = Nodal(lambda x: x, basis=p1) f2 = Nodal(lambda x: -2+2*x**2, basis=p2) one = Constant(np.array([1,2])) F = lambda f1, f2, one: 2*f1**2 + f2 + one I = mesh.cells.get_child(0) kernel = Kernel([f1,f2, one], F=F) rule1D = GaussRule(5,shape='interval') x = I.reference_map(rule1D.nodes())
def reference_solution(): """ Use sparse grid method to compute a benchmark solution """ mesh = QuadMesh(resolution=(10,10)) mesh.mark_region('boundary', lambda x,y:True, entity_type='half_edge', on_boundary=True) element = QuadFE(mesh.dim(), 'Q1') dofhandler = DofHandler(mesh, element) dofhandler.distribute_dofs() n = dofhandler.n_dofs() phi = Basis(dofhandler, 'u') phi_x = Basis(dofhandler, 'ux') phi_y = Basis(dofhandler, 'uy') yd_fn = Explicit(lambda x: np.sin(2*np.pi*x[:,0])*np.sin(2*np.pi*x[:,1]),dim=2) g = np.ones((n,1)) # # Random diffusion coefficient # # Sparse grid tasmanian_library="/home/hans-werner/bin/TASMANIAN-6.0/libtasmaniansparsegrid.so" grid = TasmanianSG.TasmanianSparseGrid(tasmanian_library=tasmanian_library) dimensions = 4 outputs = 1 depth = 4 type = 'tensor' rule = 'gauss-legendre' grid.makeGlobalGrid(dimensions, outputs, depth, type, rule) Y = grid.getPoints() w = grid.getQuadratureWeights() n_samples = grid.getNumPoints() x = dofhandler.get_dof_vertices() a_nodal = 1 + 0.1*(np.outer(np.cos(np.pi*x[:,1]),Y[:,0])+\ np.outer(np.cos(np.pi*x[:,0]),Y[:,1])+\ np.outer(np.sin(2*np.pi*x[:,1]),Y[:,2])+\ np.outer(np.sin(2*np.pi*x[:,0]),Y[:,3])) a = Nodal(data=a_nodal, dofhandler=dofhandler) yd_vec = yd_fn.eval(x) problems = [[Form(a, test=phi_x, trial=phi_x), Form(a, test=phi_y, trial=phi_y)], [Form(1, test=phi, trial=phi)]] assembler = Assembler(problems, mesh) assembler.assemble() A = assembler.af[0]['bilinear'].get_matrix() M = assembler.af[1]['bilinear'].get_matrix() state = LS(phi) state.add_dirichlet_constraint('boundary') adjoint = LS(phi) adjoint.add_dirichlet_constraint('boundary') tau = 10 k_max = 20 alpha = 0.1 u = np.zeros((n,1)) norm_dJ_iter = [] J_iter = [] u_iter = [] for k in range(k_max): print('iteration', k) # # Compute average cost and gradient # dJ = np.zeros((n,1)) J = 0 print('sampling') for n in range(n_samples): print(n) yn, pn, Jn, dJn = cost_gradient(state,adjoint,A[n],M, g,u,yd_vec,alpha) J += w[n]*Jn dJ += w[n]*dJn print('') norm_dJ = np.sqrt(dJ.T.dot(M.dot(dJ))) # # Store current iterates # norm_dJ_iter.append(norm_dJ) J_iter.append(J) u_iter.append(u) # # Check for convergence # if norm_dJ<1e-8: break # # Update iterate # u -= tau*dJ
phi_0 = Basis(dhQ1) print(phi_0.subforest_flag()) phi_1 = Basis(dhQ1, subforest_flag='coarse') cell = mesh.cells.get_leaves(subforest_flag='coarse')[0] cell.info() print(mesh.cells.is_contained_in('fine', 'coarse')) # Evaluate phi_1 on cell sub_dofs = [] for child in cell.get_leaves(flag='fine'): sub_dofs.extend(dhQ1.get_cell_dofs(child)) sub_dofs = list(set(sub_dofs)) print(sub_dofs) x = dhQ1.get_dof_vertices(dofs=sub_dofs) x_ref = cell.reference_map(x, mapsto='reference') print(x_ref) data = phi_1.eval(x_ref, cell) fig = plt.figure() ax = plt.axes(projection='3d') print(data.shape, x.shape) ax.scatter3D(x[:, 0], x[:, 1], data[:, 0]) plt.show() print(data) phi_2 = Basis(dhQ1, subforest_flag='fine') phi_0 = Basis(dhQ0)
flag = tuple(mtags.get_node_address()) mesh.cells.record(flag) mtags.add_child() new_flag = tuple(mtags.get_child(0).get_node_address()) mesh.cells.refine(subforest_flag=flag, \ new_label=new_flag) for leaf in mesh.cells.get_leaves(subforest_flag=flag): leaf.info() print('==' * 20) for leaf in mesh.cells.get_leaves(subforest_flag=new_flag): leaf.info() element = QuadFE(1, 'Q2') dofhandler = DofHandler(mesh, element) dofhandler.distribute_dofs() dofhandler.set_dof_vertices() dofs = dofhandler.get_global_dofs(subforest_flag=flag) print(dofs) dofs = dofhandler.get_global_dofs(subforest_flag=new_flag) print(dofs) dv = dofhandler.get_dof_vertices(dofs) print(dv)
n_production = (4, 3) # resolution x_production = np.linspace(0.5, 1.5, n_production[0]) y_production = np.linspace(0.2, 0.8, n_production[1]) X, Y = np.meshgrid(x_production, y_production) xy = np.array([X.ravel(), Y.ravel()]).T cells_production = mesh.bin_points(xy) # Mark vertices for cell, dummy in cells_production: cell.get_vertex(2).mark('production') # Extract degrees of freedom production_dofs = dh_Q1.get_region_dofs(entity_type='vertex', entity_flag='production') v_production = dh_Q1.get_dof_vertices(dofs=production_dofs) # Target pressure at production wells z_fn = Explicit(f=lambda x: 3 - 4 * (x[:, 0] - 1)**2 - 8 * (x[:, 1] - 0.5)**2, dim=2, mesh=mesh) y_target = z_fn.eval(v_production) # # Locations of injection wells # n_injection = (5, 4) # resolution x_injection = np.linspace(0.5, 1.5, n_injection[0]) y_injection = np.linspace(0.25, 0.75, n_injection[1]) X, Y = np.meshgrid(x_injection, y_injection) xy = np.array([X.ravel(), Y.ravel()]).T
def test02_sensitivity(): """ Check that the sensitivity calculation works. Compare J(q+eps*dq) - J(q) ~= eps*dJ^T dq """ # # Mesh # mesh = Mesh1D(resolution=(20, )) mesh.mark_region('left', lambda x: np.abs(x) < 1e-10) mesh.mark_region('right', lambda x: np.abs(x - 1) < 1e-10) # # Element # Q = QuadFE(mesh.dim(), 'Q3') dQ = DofHandler(mesh, Q) dQ.distribute_dofs() nx = dQ.n_dofs() x = dQ.get_dof_vertices() # # Basis # phi = Basis(dQ, 'v') phi_x = Basis(dQ, 'vx') # # Parameters # # Reference q q_ref = np.zeros(nx) # Perturbation dq = np.ones(nx) # Perturbed q n_eps = 10 # Number of refinements epsilons = [10**(-l) for l in range(n_eps)] q_per = np.empty((nx, n_eps)) for i in range(n_eps): q_per[:, i] = q_ref + epsilons[i] * dq # Define finite element function exp_qref = Nodal(data=np.exp(q_ref), basis=phi) exp_qper = Nodal(data=np.exp(q_per), basis=phi) # # PDEs # # 1. State Equation state_eqn = [Form(exp_qref, test=phi_x, trial=phi_x), Form(1, test=phi)] state_dbc = {'left': 0, 'right': 1} # 2. Perturbed Equation perturbed_eqn = [ Form(exp_qper, test=phi_x, trial=phi_x), Form(1, test=phi) ] perturbed_dbc = {'left': 0, 'right': 1} # 3. Adjoint Equation adjoint_eqn = [Form(exp_qref, test=phi_x, trial=phi_x), Form(0, test=phi)] adjoint_dbc = {'left': 0, 'right': -1} # Combine eqns = [state_eqn, perturbed_eqn, adjoint_eqn] bcs = [state_dbc, perturbed_dbc, adjoint_dbc] # # Assembly # assembler = Assembler(eqns) # Boundary conditions for i, bc in zip(range(3), bcs): for loc, val in bc.items(): assembler.add_dirichlet(loc, val, i_problem=i) # Assemble assembler.assemble() # # Solve # # Solve state ur = assembler.solve(i_problem=0) u_ref = Nodal(data=ur, basis=phi) ux_ref = Nodal(data=ur, basis=phi_x) # Solve perturbed state u_per = Nodal(basis=phi) ue_per = Nodal(basis=phi) for i in range(n_eps): # FEM solution up = assembler.solve(i_problem=1, i_matrix=i) u_per.add_samples(up) # Exact perturbed solution eps = epsilons[i] ue_per.add_samples(0.5 * np.exp(-eps) * (x - x**2) + x) ux_per = Nodal(data=u_per.data(), basis=phi_x) # Solve adjoint equation v = assembler.solve(i_problem=2) v_adj = Nodal(data=v, basis=phi) vx_adj = Nodal(data=v, basis=phi_x) # # Check against exact solution # ue = -0.5 * x**2 + 1.5 * x ve = -x assert np.allclose(ue, u_ref.data()) assert np.allclose(ve, v_adj.data()) assert np.allclose(ue_per.data(), u_per.data()) # # Quantities of Interest # # Reference k_ref = Kernel(f=[exp_qref, ux_ref], F=lambda eq, ux: eq * ux) ref_qoi = [Form(k_ref, dmu='dv', flag='right')] # Perturbed k_per = Kernel(f=[exp_qper, ux_per], F=lambda eq, ux: eq * ux) per_qoi = [Form(k_per, dmu='dv', flag='right')] # Adjoint k_adj = Kernel(f=[exp_qref, ux_ref, vx_adj], F=lambda eq, ux, vx: -eq * ux * vx) adj_qoi = [Form(k_adj, test=phi)] qois = [ref_qoi, per_qoi, adj_qoi] # Assemble assembler = Assembler(qois) assembler.assemble() # Evaluate J_ref = assembler.get_scalar(0) J_per = [] for i in range(n_eps): J_per.append(assembler.get_scalar(1, i)) # Finite difference approximation dJ = [] for eps, J_p in zip(epsilons, J_per): dJ.append((J_p - J_ref) / eps) # Adjoint differential dJ_adj = assembler.get_vector(2).dot(dq) # # Check against exact qois # # Check reference sol Je_ref = 0.5 assert np.allclose(Je_ref, J_ref) # Check perturbed cost Je_per = -0.5 + np.exp(np.array(epsilons)) assert np.allclose(Je_per, J_per) # Check derivative by the adjoint equation dJdq = 1 assert np.allclose(dJ_adj, dJdq)
def test06_linearization(): """ Compute samples on fine grid via the linearization """ plot = Plot() # # Computational mesh # mesh = Mesh1D(resolution=(100, )) mesh.mark_region('left', lambda x: np.abs(x) < 1e-10) mesh.mark_region('right', lambda x: np.abs(x - 1) < 1e-10) # # Element # Q1 = QuadFE(mesh.dim(), 'Q1') dQ1 = DofHandler(mesh, Q1) dQ1.distribute_dofs() nx = dQ1.n_dofs() x = dQ1.get_dof_vertices() Q3 = QuadFE(mesh.dim(), 'Q3') dQ3 = DofHandler(mesh, Q3) dQ3.distribute_dofs() # # Basis # phi = Basis(dQ1, 'u') phi_x = Basis(dQ1, 'ux') psi = Basis(dQ3, 'u') psi_x = Basis(dQ3, 'ux') # # Covariance # cov = Covariance(dQ1, name='gaussian', parameters={'l': 0.05}) cov.compute_eig_decomp() lmd, V = cov.get_eig_decomp() d = len(lmd) # Fix coarse truncation level d0 = 10 # # Build Sparse Grid # grid = TasmanianSG.TasmanianSparseGrid() dimensions = d0 outputs = 1 depth = 2 type = 'level' rule = 'gauss-hermite' grid.makeGlobalGrid(dimensions, outputs, depth, type, rule) # Sample Points zzSG = grid.getPoints() zSG = np.sqrt(2) * zzSG # transform to N(0,1) # Quadrature Weights wSG = grid.getQuadratureWeights() wSG /= np.sqrt(np.pi)**d0 # normalize weights # Number of grid points n0 = grid.getNumPoints() # # Sample low dimensional input parameter # q0 = sample_q0(V, lmd, d0, zSG.T) J0 = sample_qoi(q0, dQ1) # # Sample conditional expectation # # Pick a single coarse sample to check i0 = np.random.randint(0, high=n0) # Sample fine, conditional on coarse n_samples = 1 z1 = np.random.randn(d - d0, n_samples) q = sample_q_given_q0(q0[:, i0], V, lmd, d0, z1) # Perturbation log_qref = np.log(q0[:, i0]) dlog_q = np.log(q.ravel()) - log_qref dlog_qfn = Nodal(data=dlog_q, basis=phi) # Perturbed q n_eps = 12 # Number of refinements epsilons = [10**(-l) for l in range(n_eps)] log_qper = np.empty((nx, n_eps)) for i in range(n_eps): log_qper[:, i] = log_qref + epsilons[i] * dlog_q """ plt.plot(x, log_qref, label='ref') for i in range(n_eps): plt.plot(x, log_qper[:,i],label='%d'%(i)) """ assert np.allclose(log_qper[:, 0], np.log(q.ravel())) plt.legend() plt.show() # Define finite element function exp_qref = Nodal(data=q0[:, i0], basis=phi) exp_qper = Nodal(data=np.exp(log_qper), basis=phi) # # PDEs # # 1. State Equation state_eqn = [Form(exp_qref, test=phi_x, trial=phi_x), Form(1, test=phi)] state_dbc = {'left': 0, 'right': 1} # 2. Perturbed Equation perturbed_eqn = [ Form(exp_qper, test=phi_x, trial=phi_x), Form(1, test=phi) ] perturbed_dbc = {'left': 0, 'right': 1} # 3. Adjoint Equation adjoint_eqn = [Form(exp_qref, test=psi_x, trial=psi_x), Form(0, test=psi)] adjoint_dbc = {'left': 0, 'right': -1} # Combine eqns = [state_eqn, perturbed_eqn, adjoint_eqn] bcs = [state_dbc, perturbed_dbc, adjoint_dbc] # # Assembly # assembler = Assembler(eqns, n_gauss=(6, 36)) # Boundary conditions for i, bc in zip(range(3), bcs): for loc, val in bc.items(): assembler.add_dirichlet(loc, val, i_problem=i) # Assemble assembler.assemble() # # Solve # # Solve state ur = assembler.solve(i_problem=0) u_ref = Nodal(data=ur, basis=phi) ux_ref = Nodal(data=ur, basis=phi_x) # Solve perturbed state u_per = Nodal(basis=phi) for i in range(n_eps): # FEM solution up = assembler.solve(i_problem=1, i_matrix=i) u_per.add_samples(up) plt.plot(x, up - ur) plt.show() ux_per = Nodal(data=u_per.data(), basis=phi_x) # Solve adjoint equation v = assembler.solve(i_problem=2) v_adj = Nodal(data=v, basis=psi) vx_adj = Nodal(data=v, basis=psi_x) # # Sensitivity # # Sensitivity Equation ker_sen = Kernel(f=[exp_qref, dlog_qfn, ux_ref], F=lambda eq, dq, ux: -eq * dq * ux) sensitivity_eqn = [ Form(exp_qref, test=phi_x, trial=phi_x), Form(ker_sen, test=phi_x) ] sensitivity_dbc = {'left': 0, 'right': 0} assembler = Assembler(sensitivity_eqn, n_gauss=(6, 36)) for loc in sensitivity_dbc: assembler.add_dirichlet(loc, sensitivity_dbc[loc]) assembler.assemble() s = assembler.solve() sx = Nodal(data=s, basis=phi_x) plt.plot(x, s) plt.show() # # Quantities of Interest # # Reference k_ref = Kernel(f=[exp_qref, ux_ref], F=lambda eq, ux: eq * ux) ref_qoi = [Form(k_ref, dmu='dv', flag='right')] # Perturbed k_per = Kernel(f=[exp_qper, ux_per], F=lambda eq, ux: eq * ux) per_qoi = [Form(k_per, dmu='dv', flag='right')] # Adjoint k_adj = Kernel(f=[exp_qref, dlog_qfn, ux_ref, vx_adj], F=lambda eq, dq, ux, vx: -eq * dq * ux * vx) adj_qoi = [Form(k_adj)] # Sensitivity k_sens = Kernel(f=[exp_qref, dlog_qfn, ux_ref, sx], F=lambda eq, dq, ux, sx: eq * dq * ux + eq * sx) sens_qoi = Form(k_sens, dmu='dv', flag='right') qois = [ref_qoi, per_qoi, adj_qoi, sens_qoi] # Assemble assembler = Assembler(qois, mesh=mesh) assembler.assemble() # Evaluate J_ref = assembler.get_scalar(0) J_per = [] for i in range(n_eps): J_per.append(assembler.get_scalar(1, i)) # Finite difference approximation dJ = [] for eps, J_p in zip(epsilons, J_per): dJ.append((J_p - J_ref) / eps) # Adjoint differential dJ_adj = assembler.get_scalar(2) # Sensitivity differential dJ_sen = assembler.get_scalar(3) print(dJ_adj) print(dJ_sen) print(dJ) """
def test_integrals_1d(self): """ Test system assembly """ # # Constant form # # Mesh mesh = Mesh1D(box=[1, 2], resolution=(1, )) # Kernel kernel = Kernel(Explicit(f=lambda x: x[:, 0], dim=1)) problem = Form(kernel) assembler = Assembler(problem, mesh=mesh) assembler.assemble() self.assertAlmostEqual(assembler.get_scalar(), 3 / 2) # # Linear forms (x,x) and (x,x') over [1,2] = 7/3, 3/2 # # Elements Q1 = QuadFE(mesh.dim(), 'Q1') Q2 = QuadFE(mesh.dim(), 'Q2') Q3 = QuadFE(mesh.dim(), 'Q3') # Dofhandlers dQ1 = DofHandler(mesh, Q1) dQ2 = DofHandler(mesh, Q2) dQ3 = DofHandler(mesh, Q3) # Distribute dofs [d.distribute_dofs() for d in [dQ1, dQ2, dQ3]] for dQ in [dQ1, dQ2, dQ3]: # Basis phi = Basis(dQ, 'u') phi_x = Basis(dQ, 'ux') # Kernel function xfn = Nodal(f=lambda x: x[:, 0], basis=phi) # Kernel kernel = Kernel(xfn) # Form problem = [[Form(kernel, test=phi)], [Form(kernel, test=phi_x)]] # Assembly assembler = Assembler(problem, mesh) assembler.assemble() # Check b^Tx = (x,x) b0 = assembler.get_vector(0) self.assertAlmostEqual(np.sum(b0 * xfn.data()[:, 0]), 7 / 3) b1 = assembler.get_vector(1) self.assertAlmostEqual(np.sum(b1 * xfn.data()[:, 0]), 3 / 2) # # Bilinear forms # # Compute (1,x,x) = 7/3, or (x^2, 1, 1) = 7/3 for dQ in [dQ1, dQ2, dQ3]: # Basis phi = Basis(dQ, 'u') phi_x = Basis(dQ, 'ux') # Kernel function x2fn = Explicit(f=lambda x: x[:, 0]**2, dim=1) xfn = Nodal(f=lambda x: x[:, 0], basis=phi) # Form problems = [[Form(1, test=phi, trial=phi)], [Form(Kernel(xfn), test=phi, trial=phi_x)], [Form(Kernel(x2fn), test=phi_x, trial=phi_x)]] # Assemble assembler = Assembler(problems, mesh) assembler.assemble() x = xfn.data()[:, 0] for i_problem in range(3): A = assembler.get_matrix(i_problem) self.assertAlmostEqual(x.T.dot(A.dot(x)), 7 / 3) # ====================================================================== # Test 1: Assemble simple bilinear form (u,v) on Mesh1D # ====================================================================== # Mesh mesh = Mesh1D(resolution=(1, )) Q1 = QuadFE(mesh.dim(), 'Q1') Q2 = QuadFE(mesh.dim(), 'Q2') Q3 = QuadFE(mesh.dim(), 'Q3') # Test and trial functions dhQ1 = DofHandler(mesh, QuadFE(1, 'Q1')) dhQ1.distribute_dofs() u = Basis(dhQ1, 'u') v = Basis(dhQ1, 'v') # Form form = Form(trial=u, test=v) # Define system system = Assembler(form, mesh) # Get local information cell = mesh.cells.get_child(0) si = system.shape_info(cell) # Compute local Gauss nodes xg, wg, phi, dofs = system.shape_eval(cell) self.assertTrue(cell in xg) self.assertTrue(cell in wg) # Compute local shape functions self.assertTrue(cell in phi) self.assertTrue(u in phi[cell]) self.assertTrue(v in phi[cell]) self.assertTrue(u in dofs[cell]) # Assemble system system.assemble() # Extract system bilinear form A = system.get_matrix() # Use bilinear form to integrate x^2 over [0,1] f = Nodal(lambda x: x, basis=u) fv = f.data()[:, 0] self.assertAlmostEqual(np.sum(fv * A.dot(fv)), 1 / 3) # ====================================================================== # Test 3: Constant form (x^2,.,.) over 1D mesh # ====================================================================== # Mesh mesh = Mesh1D(resolution=(10, )) # Nodal kernel function Q2 = QuadFE(1, 'Q2') dhQ2 = DofHandler(mesh, Q2) dhQ2.distribute_dofs() phiQ2 = Basis(dhQ2) f = Nodal(lambda x: x**2, basis=phiQ2) kernel = Kernel(f=f) # Form form = Form(kernel=kernel) # Generate and assemble the system system = Assembler(form, mesh) system.assemble() # Check self.assertAlmostEqual(system.get_scalar(), 1 / 3) # ===================================================================== # Test 4: Periodic Mesh # ===================================================================== # # TODO: NO checks yet # mesh = Mesh1D(resolution=(2, ), periodic=True) # Q1 = QuadFE(1, 'Q1') dhQ1 = DofHandler(mesh, Q1) dhQ1.distribute_dofs() u = Basis(dhQ1, 'u') form = Form(trial=u, test=u) system = Assembler(form, mesh) system.assemble() # ===================================================================== # Test 5: Assemble simple sampled form # ====================================================================== mesh = Mesh1D(resolution=(3, )) Q1 = QuadFE(1, 'Q1') dofhandler = DofHandler(mesh, Q1) dofhandler.distribute_dofs() phi = Basis(dofhandler) xv = dofhandler.get_dof_vertices() n_points = dofhandler.n_dofs() n_samples = 6 a = np.arange(n_samples) f = lambda x, a: a * x fdata = np.zeros((n_points, n_samples)) for i in range(n_samples): fdata[:, i] = f(xv, a[i]).ravel() # Define sampled function fn = Nodal(data=fdata, basis=phi) kernel = Kernel(fn) # # Integrate I[0,1] ax^2 dx by using the linear form (ax,x) # v = Basis(dofhandler, 'v') form = Form(kernel=kernel, test=v) system = Assembler(form, mesh) system.assemble() one = np.ones(n_points) for i in range(n_samples): b = system.get_vector(i_sample=i) self.assertAlmostEqual(one.dot(b), 0.5 * a[i]) # # Integrate I[0,1] ax^4 dx using bilinear form (ax, x^2, x) # Q2 = QuadFE(1, 'Q2') dhQ2 = DofHandler(mesh, Q2) dhQ2.distribute_dofs() u = Basis(dhQ2, 'u') # Define form form = Form(kernel=kernel, test=v, trial=u) # Define and assemble system system = Assembler(form, mesh) system.assemble() # Express x^2 in terms of trial function basis dhQ2.distribute_dofs() xvQ2 = dhQ2.get_dof_vertices() xv_squared = xvQ2**2 for i in range(n_samples): # # Iterate over samples # # Form sparse matrix A = system.get_matrix(i_sample=i) # Evaluate the integral I = np.sum(xv * A.dot(xv_squared)) # Compare with expected result self.assertAlmostEqual(I, 0.2 * a[i]) # ===================================================================== # Test 6: Working with submeshes # ===================================================================== mesh = Mesh1D(resolution=(2, ))
def test_set_hanging_nodes(self): """ Check that functions in the finite element space can be interpolated by linear combinations of shape functions at supporting nodes. TODO: Move this test to tests for system """ # # Define QuadMesh with hanging node # mesh = QuadMesh(resolution=(1,1)) mesh.cells.refine() mesh.cells.get_child(0).get_child(0).mark(flag=0) mesh.cells.refine(refinement_flag=0) c_00 = mesh.cells.get_child(0).get_child(0) # # Define test functions to interpolate # test_functions = {'Q1': lambda x,y: x + y, \ 'Q2': lambda x,y: x**2 + y**2,\ 'Q3': lambda x,y: x**3*y + y**2*x**2} etypes = ['Q1', 'Q2', 'Q3'] plot = Plot() for etype in etypes: #print(etype) # Define new element element = QuadFE(2,etype) # Distribute dofs and set vertices dofhandler = DofHandler(mesh, element) dofhandler.distribute_dofs() dofhandler.set_dof_vertices() # Determine hanging nodes dofhandler.set_hanging_nodes() hanging_nodes = dofhandler.get_hanging_nodes() for dof, support in hanging_nodes.items(): # Get hanging node vertex x_hgnd = dofhandler.get_dof_vertices(dof) # Extract support indices and weights js, ws = support # Extract dofs_glb = dofhandler.get_cell_dofs(c_00) #print(dof, js) # Local dof numbers for supporting nodes dofs_loc_supp = [i for i in range(element.n_dofs()) if dofs_glb[i] in js] #x_dofs = c_00.reference_map(element.reference_nodes()) #phi_supp = element.shape(x_hgnd, cell=c_00, local_dofs=dofs_loc_supp) #print(phi_supp, js) # Evaluate test function at hanging node #f_hgnd = test_functions[etype](x_hgnd[0],x_hgnd[1]) #print('Weighted sum of support function', np.dot(phi_supp,ws)) #print(f_hgnd - np.dot(phi_supp, ws)) #phi_hgnd = element.shape(x_dofs, cell=c_00, local_dofs=dofs_loc_hgnd) #print(phi_supp) #print(phi_hgnd) #plot.mesh(mesh, dofhandler=dofhandler, dofs=True) # Evaluate c_01 = mesh.cells.get_child(0).get_child(1) c_022 = mesh.cells.get_child(0).get_child(2).get_child(2) #print(dofhandler.get_global_dofs(c_022)) x_ref = element.reference_nodes() #print(dofhandler.get_global_dofs(c_01)) #print(dofhandler.get_hanging_nodes()) x = c_01.reference_map(x_ref)
dh_y = DofHandler(mesh, Q1) dh_y.distribute_dofs() ny = dh_y.n_dofs() # Basis functions phi = Basis(dh_y, 'v') phi_x = Basis(dh_y, 'vx') # ----------------------------------------------------------------------------- # Observations # ----------------------------------------------------------------------------- # Determine vertices corresponding to production wells n_prod = 4 h = (x_max-x_min)/(n_prod+2) x_prod = np.array([(i+1)*h for i in range(n_prod)]) v = dh_y.get_dof_vertices() dofs_prod = [] for x in x_prod: dofs_prod.append(np.argmin(abs(v-x))) # # Target pressure at production wells # z_fn = Explicit(f=lambda x: 3-4*(x[:,0]-1)**2, dim=1, mesh=mesh) z_data = z_fn.eval(v[dofs_prod]) # ----------------------------------------------------------------------------- # Control # ----------------------------------------------------------------------------- # Determine the vertices corresponding to the injection wells n_inj = 6 h = (x_max-x_min)/(n_inj+2)
# Mark Dirichlet Vertices mesh.mark_region('left', lambda x: np.abs(x) < 1e-9) mesh.mark_region('right', lambda x: np.abs(x - 2) < 1e-9) # # Finite element spaces # Q1 = QuadFE(mesh.dim(), 'Q1') # Dofhandler for state dh = DofHandler(mesh, Q1) dh.distribute_dofs() m = dh.n_dofs() dh.set_dof_vertices() x = dh.get_dof_vertices() # Basis functions phi = Basis(dh, 'v') phi_x = Basis(dh, 'vx') state = LS(phi) state.add_dirichlet_constraint('left', 1) state.add_dirichlet_constraint('right', 0) state.set_constraint_relation() adjoint = LS(phi) adjoint.add_dirichlet_constraint('left', 0) adjoint.add_dirichlet_constraint('right', 0) adjoint.set_constraint_relation()
def test_eval(self): # # Out of the box covariance kernels # fig, ax = plt.subplots(6, 4, figsize=(5, 7)) cov_names = [ 'constant', 'linear', 'gaussian', 'exponential', 'matern', 'rational' ] anisotropies = {1: [None, 2], 2: [None, np.diag([2, 1])]} m_count = 0 for mesh in [Mesh1D(resolution=(10, )), QuadMesh(resolution=(10, 10))]: # Dimension dim = mesh.dim() # # Construct computational mesh # # Piecewise constant elements element = QuadFE(dim, 'DQ0') # Define dofhandler -> get vertices dofhandler = DofHandler(mesh, element) dofhandler.distribute_dofs() dofhandler.set_dof_vertices() v = dofhandler.get_dof_vertices() # Define meshgrid for 1 and 2 dimensions n_dofs = dofhandler.n_dofs() M1, M2 = np.mgrid[0:n_dofs, 0:n_dofs] if dim == 1: X = v[:, 0][M1].ravel() Y = v[:, 0][M2].ravel() elif dim == 2: X = np.array([v[:, 0][M1].ravel(), v[:, 1][M1].ravel()]).T Y = np.array([v[:, 0][M2].ravel(), v[:, 1][M2].ravel()]).T x = convert_to_array(X, dim=dim) y = convert_to_array(Y, dim=dim) a_count = 0 isotropic_label = ['isotropic', 'anisotropic'] for M in anisotropies[dim]: # Cycle through anisotropies # Define covariance parameters cov_pars = { 'constant': { 'sgm': 1 }, 'linear': { 'sgm': 1, 'M': M }, 'gaussian': { 'sgm': 1, 'l': 0.1, 'M': M }, 'exponential': { 'l': 0.1, 'M': M }, 'matern': { 'sgm': 1, 'nu': 2, 'l': 0.5, 'M': M }, 'rational': { 'a': 3, 'M': M } } c_count = 0 for cov_name in cov_names: C = CovKernel(cov_name, cov_pars[cov_name]) Z = C.eval((x, y)).reshape(M1.shape) col = int(m_count * 2**1 + a_count * 2**0) row = c_count ax[row, col].imshow(Z) if col == 0: ax[row, col].set_ylabel(cov_name) if row == 0: ax[row, col].set_title('%dD mesh\n %s' % (dim, isotropic_label[a_count])) ax[row, col].set_xticks([], []) ax[row, col].set_yticks([], []) c_count += 1 a_count += 1 m_count += 1 fig.savefig('test_covkernel_eval.eps')