def test01_solve_2d(self): """ Solve a simple 2D problem with no hanging nodes """ mesh = QuadMesh(resolution=(5, 5)) # Mark dirichlet boundaries mesh.mark_region('left', lambda x, dummy: np.abs(x) < 1e-9, entity_type='half_edge') mesh.mark_region('right', lambda x, dummy: np.abs(x - 1) < 1e-9, entity_type='half_edge') Q1 = QuadFE(mesh.dim(), 'Q1') dQ1 = DofHandler(mesh, Q1) dQ1.distribute_dofs() phi = Basis(dQ1, 'u') phi_x = Basis(dQ1, 'ux') phi_y = Basis(dQ1, 'uy') problem = [ Form(1, test=phi_x, trial=phi_x), Form(1, test=phi_y, trial=phi_y), Form(0, test=phi) ] assembler = Assembler(problem, mesh) assembler.add_dirichlet('left', dir_fn=0) assembler.add_dirichlet('right', dir_fn=1) assembler.assemble() # Get matrix dirichlet correction and right hand side A = assembler.get_matrix().toarray() x0 = assembler.assembled_bnd() b = assembler.get_vector() ua = np.zeros((phi.n_dofs(), 1)) int_dofs = assembler.get_dofs('interior') ua[int_dofs, 0] = np.linalg.solve(A, b - x0) dir_bc = assembler.get_dirichlet() dir_vals = np.array([dir_bc[dof] for dof in dir_bc]) dir_dofs = [dof for dof in dir_bc] ua[dir_dofs] = dir_vals ue_fn = Nodal(f=lambda x: x[:, 0], basis=phi) ue = ue_fn.data() self.assertTrue(np.allclose(ue, ua)) self.assertTrue(np.allclose(x0 + A.dot(ua[int_dofs, 0]), b))
def sensitivity_sample_qoi(exp_q, dofhandler): """ Sample QoI by means of Taylor expansion J(q+dq) ~= J(q) + dJdq(q)dq """ # Basis phi = Basis(dofhandler, 'v') phi_x = Basis(dofhandler, 'vx') # Define problem exp_q_fn = Nodal(data=exp_q, basis=phi) primal = [Form(exp_q_fn, test=phi_x, trial=phi_x), Form(1, test=phi)] adjoint = [Form(exp_q_fn, test=phi_x, trial=phi_x), Form(0, test=phi)] qoi = [Form(exp_q_fn, test=phi_x)] problems = [primal, adjoint, qoi] # Define assembler assembler = Assembler(problems) # # Dirichlet conditions for primal problem # assembler.add_dirichlet('left', 0, i_problem=0) assembler.add_dirichlet('right', 1, i_problem=0) # Dirichlet conditions for adjoint problem assembler.add_dirichlet('left', 0, i_problem=1) assembler.add_dirichlet('right', -1, i_problem=1) # Assemble system assembler.assemble() # Compute solution and qoi at q (primal) u = assembler.solve(i_problem=0) # Compute solution of the adjoint problem v = assembler.solve(i_problem=1) # Evaluate J J = u.dot(assembler.get_vector(2)) # # Assemble gradient # ux_fn = Nodal(data=u, basis=phi_x) vx_fn = Nodal(data=v, basis=phi_x) k_int = Kernel(f=[exp_q_fn, ux_fn, vx_fn], F=lambda exp_q, ux, vx: exp_q * ux * vx) problem = [Form(k_int, test=phi)] assembler = Assembler(problem) assembler.assemble() dJ = -assembler.get_vector() return dJ
def test01_solve_1d(self): """ Test solving 1D systems """ mesh = Mesh1D(resolution=(20, )) mesh.mark_region('left', lambda x: np.abs(x) < 1e-9) mesh.mark_region('right', lambda x: np.abs(x - 1) < 1e-9) Q1 = QuadFE(1, 'Q1') dQ1 = DofHandler(mesh, Q1) dQ1.distribute_dofs() phi = Basis(dQ1, 'u') phi_x = Basis(dQ1, 'ux') problem = [Form(1, test=phi_x, trial=phi_x), Form(0, test=phi)] assembler = Assembler(problem, mesh) assembler.add_dirichlet('left', dir_fn=0) assembler.add_dirichlet('right', dir_fn=1) assembler.assemble() # Get matrix dirichlet correction and right hand side A = assembler.get_matrix().toarray() x0 = assembler.assembled_bnd() b = assembler.get_vector() ua = np.zeros((phi.n_dofs(), 1)) int_dofs = assembler.get_dofs('interior') ua[int_dofs, 0] = np.linalg.solve(A, b - x0) dir_bc = assembler.get_dirichlet() dir_vals = np.array([dir_bc[dof] for dof in dir_bc]) dir_dofs = [dof for dof in dir_bc] ua[dir_dofs] = dir_vals ue_fn = Nodal(f=lambda x: x[:, 0], basis=phi) ue = ue_fn.data() self.assertTrue(np.allclose(ue, ua)) self.assertTrue(np.allclose(x0 + A.dot(ua[int_dofs, 0]), b))
# # Define weak form # a_diff_x = Form(eps, trial=ux, test=ux) a_diff_y = Form(eps, trial=uy, test=uy) a_adv_x = Form(vx, trial=ux, test=u) a_adv_y = Form(vy, trial=uy, test=u) b = Form(f, test=u) problem = [a_diff_x, a_diff_y, a_adv_x, a_adv_y, b] # # Assembler system # assembler = Assembler([problem], mesh) assembler.add_dirichlet(None, ue) assembler.assemble() # # Get solution # ua = assembler.solve() # # Compute the error # e_vec = ua[:, None] - ue.interpolant(dofhandler).data() efn = Nodal(data=e_vec, basis=u, dim=2) # # Record error
plot.contour(eq, n_sample=25) # # Compute state # # Define weak form state = [[ Form(eq, test=phix_1, trial=phix_1), Form(eq, test=phiy_1, trial=phiy_1), Form(1, test=phi_1) ], [Form(1, test=phi_1, flag='dmn')]] # Assemble system assembler = Assembler(state) assembler.add_dirichlet('bnd') assembler.assemble() J = assembler.get_vector(1) # Solve system u_vec = assembler.solve() u = Nodal(basis=phi_1, data=u_vec) plot.contour(u) plt.title('Sample Path') # Solve the adjoint system adjoint = [ Form(eq, test=phix_2, trial=phix_2), Form(eq, test=phiy_2, trial=phiy_2),
# Sample from field tht_fn = Nodal(data=tht.sample(n_samples=3), basis=phi) plot = Plot() plot.contour(tht_fn) # # Advection # v = [0.1, -0.1] plot.mesh(mesh, regions=[('in', 'edge'), ('out', 'edge'), ('reg', 'cell')]) k = Kernel(tht_fn, F=lambda tht: np.exp(tht)) adv_diff = [ Form(k, trial=phi_x, test=phi_x), Form(k, trial=phi_y, test=phi_y), Form(0, test=phi), Form(v[0], trial=phi_x, test=phi), Form(v[1], trial=phi_y, test=phi) ] average = [Form(1, test=phi, flag='reg'), Form(1, flag='reg')] assembler = Assembler([adv_diff, average], mesh) assembler.add_dirichlet('out', dir_fn=0) assembler.add_dirichlet('in', dir_fn=10) assembler.assemble() u_vec = assembler.solve(i_problem=0, i_matrix=0, i_vector=0) plot.contour(Nodal(data=u_vec, basis=phi))
def test_ft(): plot = Plot() vb = Verbose() # ============================================================================= # Parameters # ============================================================================= # # Flow # # permeability field phi = Constant(1) # porosity D = Constant(0.0252) # dispersivity K = Constant(1) # permeability # ============================================================================= # Mesh and Elements # ============================================================================= # Mesh mesh = QuadMesh(resolution=(30, 30)) # Mark left and right regions mesh.mark_region('left', lambda x, y: np.abs(x) < 1e-9, entity_type='half_edge') mesh.mark_region('right', lambda x, y: np.abs(x - 1) < 1e-9, entity_type='half_edge') # Elements p_element = QuadFE(2, 'Q1') # element for pressure c_element = QuadFE(2, 'Q1') # element for concentration # Dofhandlers p_dofhandler = DofHandler(mesh, p_element) c_dofhandler = DofHandler(mesh, c_element) p_dofhandler.distribute_dofs() c_dofhandler.distribute_dofs() # Basis functions p_ux = Basis(p_dofhandler, 'ux') p_uy = Basis(p_dofhandler, 'uy') p_u = Basis(p_dofhandler, 'u') p_inflow = lambda x, y: np.ones(shape=x.shape) p_outflow = lambda x, y: np.zeros(shape=x.shape) c_inflow = lambda x, y: np.zeros(shape=x.shape) # ============================================================================= # Solve the steady state flow equations # ============================================================================= vb.comment('Solving flow equations') # Define problem flow_problem = [ Form(1, test=p_ux, trial=p_ux), Form(1, test=p_uy, trial=p_uy), Form(0, test=p_u) ] # Assemble vb.tic('assembly') assembler = Assembler(flow_problem) assembler.add_dirichlet('left', 1) assembler.add_dirichlet('right', 0) assembler.assemble() vb.toc() # Solve linear system vb.tic('solve') A = assembler.get_matrix().tocsr() b = assembler.get_vector() x0 = assembler.assembled_bnd() # Interior nodes pa = np.zeros((p_u.n_dofs(), 1)) int_dofs = assembler.get_dofs('interior') pa[int_dofs, 0] = spla.spsolve(A, b - x0) # Resolve Dirichlet conditions dir_dofs, dir_vals = assembler.get_dirichlet(asdict=False) pa[dir_dofs] = dir_vals vb.toc() # Pressure function pfn = Nodal(data=pa, basis=p_u) px = pfn.differentiate((1, 0)) py = pfn.differentiate((1, 1)) #plot.contour(px) #plt.show() # ============================================================================= # Transport Equations # ============================================================================= # Specify initial condition c0 = Constant(1) dt = 1e-1 T = 6 N = int(np.ceil(T / dt)) c = Basis(c_dofhandler, 'c') cx = Basis(c_dofhandler, 'cx') cy = Basis(c_dofhandler, 'cy') print('assembling transport equations') k_phi = Kernel(f=phi) k_advx = Kernel(f=[K, px], F=lambda K, px: -K * px) k_advy = Kernel(f=[K, py], F=lambda K, py: -K * py) tht = 1 m = [Form(kernel=k_phi, test=c, trial=c)] s = [ Form(kernel=k_advx, test=c, trial=cx), Form(kernel=k_advy, test=c, trial=cy), Form(kernel=Kernel(D), test=cx, trial=cx), Form(kernel=Kernel(D), test=cy, trial=cy) ] problems = [m, s] assembler = Assembler(problems) assembler.add_dirichlet('left', 0, i_problem=0) assembler.add_dirichlet('left', 0, i_problem=1) assembler.assemble() x0 = assembler.assembled_bnd() # Interior nodes int_dofs = assembler.get_dofs('interior') # Dirichlet conditions dir_dofs, dir_vals = assembler.get_dirichlet(asdict=False) # System matrices M = assembler.get_matrix(i_problem=0) S = assembler.get_matrix(i_problem=1) # Initialize c0 and cp c0 = np.ones((c.n_dofs(), 1)) cp = np.zeros((c.n_dofs(), 1)) c_fn = Nodal(data=c0, basis=c) # # Compute solution # print('time stepping') for i in range(N): # Build system A = M + tht * dt * S b = M.dot(c0[int_dofs]) - (1 - tht) * dt * S.dot(c0[int_dofs]) # Solve linear system cp[int_dofs, 0] = spla.spsolve(A, b) # Add Dirichlet conditions cp[dir_dofs] = dir_vals # Record current iterate c_fn.add_samples(data=cp) # Update c0 c0 = cp.copy() #plot.contour(c_fn, n_sample=i) # # Quantity of interest # def F(c, px, py, entity=None): """ Compute c(x,y,t)*(grad p * n) """ n = entity.unit_normal() return c * (px * n[0] + py * n[1]) px.set_subsample(i=np.arange(41)) py.set_subsample(i=np.arange(41)) #kernel = Kernel(f=[c_fn,px,py], F=F) kernel = Kernel(c_fn) #print(kernel.n_subsample()) form = Form(kernel, flag='right', dmu='ds') assembler = Assembler(form, mesh=mesh) assembler.assemble() QQ = assembler.assembled_forms()[0].aggregate_data()['array'] Q = np.array([assembler.get_scalar(i_sample=i) for i in np.arange(N + 1)]) t = np.linspace(0, T, N + 1) plt.plot(t, Q) plt.show() print(Q)
def sample_qoi(q, dofhandler, return_state=False): """ Compute the Quantity of Interest J(u) = -exp(q(1))*u'(1), where u solves -d/dx ( exp(q)* du/dx) = 1 u(0) = 0, u(1) = 1 for a sample of q's. Inputs: q: Nodal, (n_dofs, n_samples) function representing the log porosity dofhandler: DofHandler """ # Basis phi = Basis(dofhandler, 'v') phi_x = Basis(dofhandler, 'vx') n_dofs = phi.n_dofs() # Define problem expq_fn = Nodal(data=np.exp(q), basis=phi) problem = [[Form(expq_fn, test=phi_x, trial=phi_x), Form(1, test=phi)], [Form(expq_fn, test=phi_x, dmu='dv', flag='right')]] # Define assembler assembler = Assembler(problem, dofhandler.mesh) # Incorporate Dirichlet conditions assembler.add_dirichlet('left', 0) assembler.add_dirichlet('right', 1) n_samples = expq_fn.n_subsample() # Assemble system assembler.assemble() if return_state: U = np.empty((n_dofs, n_samples)) J = np.zeros(n_samples) for i in range(n_samples): # Solve system u = assembler.solve(i_problem=0, i_matrix=i, i_vector=0) # Compute quantity of interest J[i] = -u.dot(assembler.get_vector(1, i)) if return_state: U[:, i] = u if return_state: return J, U else: return J
def experiment01_problem(): """ Illustrate the problem: Plot sample paths of the input q, of the output, and histogram of the QoI. """ # # 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() # # Basis # phi = Basis(dQ1, 'v') phi_x = Basis(dQ1, 'vx') # # Covariance # cov = Covariance(dQ1, name='gaussian', parameters={'l': 0.05}) cov.compute_eig_decomp() lmd, V = cov.get_eig_decomp() d = len(lmd) # # Sample and plot full dimensional parameter and solution # n_samples = 20000 z = np.random.randn(d, n_samples) q = sample_q0(V, lmd, d, z) # Define finite element function qfn = Nodal(data=q, basis=phi) problem = [[Form(qfn, test=phi_x, trial=phi_x), Form(1, test=phi)], [Form(qfn, test=phi_x, dmu='dv', flag='right')]] # Define assembler assembler = Assembler(problem) # Incorporate Dirichlet conditions assembler.add_dirichlet('left', 0) assembler.add_dirichlet('right', 1) comment.tic('assembly') # Assemble system assembler.assemble() comment.toc() comment.tic('solver') ufn = Nodal(basis=phi, data=None) J = np.zeros(n_samples) for i in range(n_samples): # Solve system u = assembler.solve(i_problem=0, i_matrix=i, i_vector=0) # Compute quantity of interest J[i] = u.dot(assembler.get_vector(1, i)) # Update sample paths ufn.add_samples(u) comment.toc() # # Plots # """ # Formatting plt.rc('text', usetex=True) # Figure sizes fs2 = (3,2) fs1 = (4,3) plot = Plot(quickview=False) plot_kwargs = {'color':'k', 'linewidth':0.05} # # Plot qfn # # Figure fig = plt.figure(figsize=fs2) ax = fig.add_subplot(111) ax = plot.line(qfn, axis=ax, i_sample=np.arange(100), plot_kwargs=plot_kwargs) ax.set_xlabel(r'$x$') ax.set_ylabel(r'$q$') plt.tight_layout() fig.savefig('fig/ex02_gauss_qfn.eps') plt.close() # # Plot ufn # fig = plt.figure(figsize=fs2) ax = fig.add_subplot(111) ax = plot.line(ufn, axis=ax, i_sample=np.arange(100), plot_kwargs=plot_kwargs) ax.set_xlabel(r'$x$') ax.set_ylabel(r'$u$') plt.tight_layout() fig.savefig('fig/ex02_gauss_ufn.eps') plt.close() """ # Formatting plt.rc('text', usetex=True) # Figure sizes fs2 = (3, 2) fs1 = (4, 3) fig = plt.figure(figsize=fs2) ax = fig.add_subplot(111) plt.hist(J, bins=100, density=True) ax.set_xlabel(r'$J(u)$') plt.tight_layout() fig.savefig('fig/ex02_gauss_jhist.eps')
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 test01_finite_elements(): """ Test accuracy of the finite element approximation """ # # Construct reference solution # plot = Plot(quickview=False) # Mesh mesh = Mesh1D(resolution=(2**11, )) 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() # Basis phi = Basis(dQ1, 'v') phi_x = Basis(dQ1, 'vx') # # Covariance # cov = Covariance(dQ1, name='gaussian', parameters={'l': 0.05}) cov.compute_eig_decomp() lmd, V = cov.get_eig_decomp() d = len(lmd) # # Sample and plot full dimensional parameter and solution # n_samples = 1 z = np.random.randn(d, n_samples) q_ref = sample_q0(V, lmd, d, z) print(q_ref.shape) # Define finite element function q_ref_fn = Nodal(data=q_ref, basis=phi) problem = [[Form(q_ref_fn, test=phi_x, trial=phi_x), Form(1, test=phi)], [Form(q_ref_fn, test=phi_x, dmu='dv', flag='right')]] # Define assembler assembler = Assembler(problem) # Incorporate Dirichlet conditions assembler.add_dirichlet('left', 0) assembler.add_dirichlet('right', 1) # Assemble system assembler.assemble() # Solve system u_ref = assembler.solve() # Compute quantity of interest J_ref = u_ref.dot(assembler.get_vector(1)) # Plot fig = plt.figure(figsize=(6, 4)) ax = fig.add_subplot(111) u_ref_fn = Nodal(basis=phi, data=u_ref) ax = plot.line(u_ref_fn, axis=ax) n_levels = 10 J = np.zeros(n_levels) for l in range(10): comment.comment('level: %d' % (l)) # # Mesh # mesh = Mesh1D(resolution=(2**l, )) 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() # # Basis # phi = Basis(dQ1, 'v') phi_x = Basis(dQ1, 'vx') # Define problem problem = [[ Form(q_ref_fn, test=phi_x, trial=phi_x), Form(1, test=phi) ], [Form(q_ref_fn, test=phi_x, dmu='dv', flag='right')]] assembler = Assembler(problem) # Incorporate Dirichlet conditions assembler.add_dirichlet('left', 0) assembler.add_dirichlet('right', 1) assembler.assemble() A = assembler.get_matrix() print('A shape', A.shape) u = assembler.solve() J[l] = u.dot(assembler.get_vector(1)) print(u.shape) print(phi.n_dofs()) ufn = Nodal(basis=phi, data=u) ax = plot.line(ufn, axis=ax) plt.show() # # Plots # # Formatting plt.rc('text', usetex=True) # Figure sizes fs2 = (3, 2) fs1 = (4, 3) print(J_ref) print(J) # # Plot truncation error for mean and variance of J # fig = plt.figure(figsize=fs2) ax = fig.add_subplot(111) err = np.array([np.abs(J[i] - J_ref) for i in range(n_levels)]) h = np.array([2**(-l) for l in range(n_levels)]) plt.loglog(h, err, '.-') ax.set_xlabel(r'$h$') ax.set_ylabel(r'$|J-J^h|$') plt.tight_layout() fig.savefig('fig/ex02_gauss_fem_error.eps')
def dJdq_sen(q, u, dq): """ Compute the directional derivative dJ(q,dq) by means of the sensitivity equation. -(exp(q)*s')' = (exp(q)*dq*u')' s(0) = s(1) = 0 and computing dJ(q,dq) = -exp(q(1))*dq(1)*u'(1) - exp(q(1))*s'(1) """ # # Finite Element Specification # # Reference state u_dh = u.basis().dofhandler() mesh = u_dh.mesh phi = Basis(u_dh, 'u') phi_x = Basis(u_dh, 'ux') ux_fn = Nodal(data=u.data(), basis=phi_x) # Reference diffusivitity q_dh = q.basis().dofhandler() psi = q.basis() exp_q = Nodal(data=np.exp(q.data()), basis=phi) # Define sensitivity equation ker_sen = Kernel(f=[exp_q, dq, ux_fn], F=lambda eq, dq, ux: -eq * dq * ux) sensitivity_eqn = [ Form(exp_q, test=phi_x, trial=phi_x), Form(ker_sen, test=phi_x) ] # Assembler assembler = Assembler(sensitivity_eqn, u_dh.mesh, n_gauss=(6, 36)) # Apply Dirichlet Boundary conditions assembler.add_dirichlet('left', 0) assembler.add_dirichlet('right', 0) # Assemble system assembler.assemble() # Solve for sensitivity s_fn = Nodal(basis=phi) for i in range(dq.n_samples()): # Solve for ith sensitivity s = assembler.solve(i_vector=i) s_fn.add_samples(s) # Derivative of sensitivity sx_fn = Nodal(data=s_fn.data(), basis=phi_x) # Sensitivity k_sens = Kernel(f=[exp_q, dq, ux_fn, sx_fn], F=lambda eq, dq, ux, sx: -eq * dq * ux - eq * sx) sens_qoi = Form(k_sens, dmu='dv', flag='right') # Assemble assembler = Assembler(sens_qoi, mesh=mesh) assembler.assemble() # Differential dJ = np.array([assembler.get_scalar(i_sample=i) \ for i in range(dq.n_samples())]) return dJ
def dJdq_adj(q, u, dq=None): """ Compute the directional derivative dJ(q,dq) by solving the adjoint equation -(exp(q)v')' = 0, v(0)=0, v(1)=1 and computing ( v, (exp(q)*dq(1)*u')' ) + exp(q(1))*dq(1)*u'(1) = -(exp(q)*dq u', v') Inputs: q: Nodal, single reference log diffusitivity u: Nodal, single reference system response dq: Nodal/None, sampled perturbation vector (or None) Output: dJdq: Derivative of J wrt q If dq = Nodal, return directional derivative in direction dq If dq = None, return gradient """ # # Finite Element Specification # # Reference solution u_dh = u.basis().dofhandler() phi = Basis(u_dh, 'u') phi_x = Basis(u_dh, 'ux') ux = Nodal(data=u.data(), basis=phi_x) # Reference diffusivity q_dh = q.basis().dofhandler() psi = Basis(q_dh, 'q') exp_q = Nodal(data=np.exp(q.data()), basis=psi) # Define adjoint equations adjoint_eqn = [Form(exp_q, test=phi_x, trial=phi_x), Form(0, test=phi)] # Assembler assembler = Assembler(adjoint_eqn) # Apply Dirichlet BC assembler.add_dirichlet('left', 0) assembler.add_dirichlet('right', 1) # Assemble assembler.assemble() # Solve for adjoint v = assembler.solve() # Get derivative vx = Nodal(data=v, basis=phi_x) # Assemble if dq is None: # # Compute the gradient # # Kernel k_adj = Kernel(f=[exp_q, ux, vx], F=lambda eq, ux, vx: -eq * ux * vx) # Linear form adj_qoi = [Form(k_adj, test=psi)] # Assemble assembler = Assembler(adj_qoi) assembler.assemble() # Get gradient vector dJdq = assembler.get_vector() else: # # Compute the directional derivative # # Kernel k_adj = Kernel(f=[exp_q, dq, ux, vx], F=lambda eq, dq, ux, vx: -eq * dq * ux * vx) # Constant form adj_qoi = [Form(k_adj)] # Assemble assembler = Assembler(adj_qoi, mesh=u_dh.mesh) assembler.assemble() # Get directional derivatives for each direction dJdq = np.array( [assembler.get_scalar(i_sample=i) for i in range(dq.n_samples())]) # Return return 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 test03_solve_2d(self): """ Test problem with Neumann conditions """ # # Define Mesh # mesh = QuadMesh(resolution=(2, 1)) mesh.cells.get_child(1).mark(1) mesh.cells.refine(refinement_flag=1) # Mark left and right boundaries bm_left = lambda x, dummy: np.abs(x) < 1e-9 bm_right = lambda x, dummy: np.abs(1 - x) < 1e-9 mesh.mark_region('left', bm_left, entity_type='half_edge') mesh.mark_region('right', bm_right, entity_type='half_edge') for etype in ['Q1', 'Q2', 'Q3']: # # Define element and basis type # element = QuadFE(2, etype) dofhandler = DofHandler(mesh, element) dofhandler.distribute_dofs() u = Basis(dofhandler, 'u') ux = Basis(dofhandler, 'ux') uy = Basis(dofhandler, 'uy') # # Exact solution # ue = Nodal(f=lambda x: x[:, 0], basis=u) # # Set up forms # one = Constant(1) ax = Form(kernel=Kernel(one), trial=ux, test=ux) ay = Form(kernel=Kernel(one), trial=uy, test=uy) L = Form(kernel=Kernel(Constant(0)), test=u) Ln = Form(kernel=Kernel(one), test=u, dmu='ds', flag='right') problem = [ax, ay, L, Ln] assembler = Assembler(problem, mesh) assembler.add_dirichlet('left', dir_fn=0) assembler.add_hanging_nodes() assembler.assemble() # # Automatic solve # ya = assembler.solve() self.assertTrue(np.allclose(ue.data()[:, 0], ya)) # # Explicit solve # # System Matrices A = assembler.get_matrix().toarray() b = assembler.get_vector() x0 = assembler.assembled_bnd() # Solve linear system xa = np.zeros(u.n_dofs()) int_dofs = assembler.get_dofs('interior') xa[int_dofs] = np.linalg.solve(A, b - x0) # Resolve Dirichlet conditions dir_dofs, dir_vals = assembler.get_dirichlet(asdict=False) xa[dir_dofs] = dir_vals[:, 0] # Resolve hanging nodes C = assembler.hanging_node_matrix() xa += C.dot(xa) self.assertTrue(np.allclose(ue.data()[:, 0], xa))
def test02_solve_2d(self): """ Solve 2D problem with hanging nodes """ # Mesh mesh = QuadMesh(resolution=(2, 2)) mesh.cells.get_leaves()[0].mark(0) mesh.cells.refine(refinement_flag=0) mesh.mark_region('left', lambda x, y: abs(x) < 1e-9, entity_type='half_edge') mesh.mark_region('right', lambda x, y: abs(x - 1) < 1e-9, entity_type='half_edge') # Element Q1 = QuadFE(2, 'Q1') dofhandler = DofHandler(mesh, Q1) dofhandler.distribute_dofs() dofhandler.set_hanging_nodes() # Basis functions phi = Basis(dofhandler, 'u') phi_x = Basis(dofhandler, 'ux') phi_y = Basis(dofhandler, 'uy') # # Define problem # problem = [ Form(1, trial=phi_x, test=phi_x), Form(1, trial=phi_y, test=phi_y), Form(0, test=phi) ] ue = Nodal(f=lambda x: x[:, 0], basis=phi) xe = ue.data().ravel() # # Assemble without Dirichlet and without Hanging Nodes # assembler = Assembler(problem, mesh) assembler.add_dirichlet('left', dir_fn=0) assembler.add_dirichlet('right', dir_fn=1) assembler.add_hanging_nodes() assembler.assemble() # Get dofs for different regions int_dofs = assembler.get_dofs('interior') # Get matrix and vector A = assembler.get_matrix().toarray() b = assembler.get_vector() x0 = assembler.assembled_bnd() # Solve linear system xa = np.zeros(phi.n_dofs()) xa[int_dofs] = np.linalg.solve(A, b - x0) # Resolve Dirichlet conditions dir_dofs, dir_vals = assembler.get_dirichlet(asdict=False) xa[dir_dofs] = dir_vals[:, 0] # Resolve hanging nodes C = assembler.hanging_node_matrix() xa += C.dot(xa) self.assertTrue(np.allclose(xa, xe))