def plot_heuristics(f, tht, basis, region, condition): """ Parameters ---------- f : lambda, Function of θ to be integrated. n : int, Sample size for Monte Carlo sample """ tht.update_support() # # Plot samples of the random field # n = 10000 tht_sample = tht.sample(n) tht_fn = Nodal(data=tht_sample, basis=basis) # # Compute the quantity of interest # # Define the kernel kf = Kernel(tht_fn, F=f) # Assemble over the mesh problems = [[Form(kernel=kf, flag=region)], [Form(flag=region)]] assembler = Assembler(problems, basis.mesh()) assembler.assemble() # Extract sample dx = assembler.get_scalar(i_problem=1) q_sample = assembler.get_scalar(i_sample=None) / dx # # Compute correlation coefficients of q with spatial data # plot = Plot(quickview=False) fig, ax = plt.subplots() plt_args = {'linewidth': 0.5, 'color': 'k'} ax = plot.line(tht_fn, axis=ax, i_sample=list(range(100)), plot_kwargs=plt_args) fig.savefig('ex01_sample_paths.eps') fig, ax = plt.subplots() ftht = Nodal(data=f(tht_sample), basis=basis) ax = plot.line(ftht, axis=ax, i_sample=list(range(100)), plot_kwargs=plt_args) fig.savefig('ex01_integrand.eps') fig, ax = plt.subplots(1, 1) plt.hist(q_sample, bins=50, density=True) ax.set_title(r'Histogram $Q(\theta)$') fig.savefig('ex01_histogram.eps') plt.close('all') dh = basis.dofhandler() n_dofs = dh.n_dofs() # Extract the region on which we condition cnd_dofs = dh.get_region_dofs(entity_flag=condition) I = np.eye(n_dofs) I = I[cnd_dofs, :] # Measured tht tht_msr = tht_sample[cnd_dofs, 0][:, None] n_cnd = 30 cnd_tht = tht.condition(I, tht_msr, n_samples=n_cnd) #cnd_tht_data = np.array([tht_sample[:,0] for dummy in range(n_cnd)]) #cnd_tht_data[cnd_dofs,:] = cnd_tht cnd_tht_fn = Nodal(data=f(cnd_tht), basis=basis) fig, ax = plt.subplots() ax = plot.line(cnd_tht_fn, axis=ax, i_sample=np.arange(n_cnd), plot_kwargs=plt_args) fig.tight_layout() plt.show()
def reference_qoi(f, tht, basis, region, n=1000000, verbose=True): """ Parameters ---------- f : lambda function, Function of θ to be integrated. tht : GaussianField, Random field θ defined on mesh in terms of its mean and covariance basis : Basis, Basis function defining the nodal interpolant. It incorporates the mesh, the dofhandler, and the derivative. region : meshflag, Flag indicating the region of integration n : int, default=1000000 Sample size Returns ------- Q_ref : double, Reference quantity of interest err : double, Expected RMSE given by var(Q)/n. """ # # Assemble integral # batch_size = 100000 n_batches = n // batch_size + (0 if (n % batch_size) == 0 else 1) if verbose: print('Computing Reference Quantity of Interest') print('========================================') print('Sample size: ', n) print('Batch size: ', batch_size) print('Number of batches: ', n_batches) Q_smpl = np.empty(n) for k in range(n_batches): # Determine sample sizes for each batch if k < n_batches - 1: n_sample = batch_size else: # Last sample may be smaller than batch_size n_sample = n - k * batch_size if verbose: print(' - Batch Number ', k) print(' - Sample Size: ', n_sample) print(' - Sampling random field') # Sample from field tht_smpl = tht.sample(n_sample) # Define kernel tht_n = Nodal(data=tht_smpl, basis=basis) kf = Kernel(tht_n, F=f) # Define forms if k == 0: problems = [[Form(kernel=kf, flag=region)], [Form(flag=region)]] else: problems = [Form(kernel=kf, flag=region)] if verbose: print(' - Assembling.') # Compute the integral assembler = Assembler(problems, basis.mesh()) assembler.assemble() # # Compute statistic # # Get samples if k == 0: dx = assembler.get_scalar(i_problem=1) if verbose: print(' - Updating samples \n') batch_sample = assembler.get_scalar(i_problem=0, i_sample=None) Q_smpl[k * batch_size:k * batch_size + n_sample] = batch_sample / dx # Compute mean and MSE Q_ref = np.mean(Q_smpl) err = np.var(Q_smpl) / n # Return reference return Q_ref, err
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)
eta_trunc_sg = V[:, :k].dot(np.diag(np.sqrt(D[:k])).dot(z.T)) # Generate a Monte Carlo sample on top of sparse grid n_mc = 20 zz = np.random.randn(n_dofs - k, n_mc) eta_tail_mc = V[:, k:].dot(np.diag(np.sqrt(D[k:]))).dot(zz) # ----------------------------------------------------------------------------- # Sample and Integrate # ----------------------------------------------------------------------------- # Samples of random field theta_trunc = Nodal(data=eta_trunc_sg[:, [50]] + eta_tail_mc, basis=phi_1) # Assembler k = Kernel(theta_trunc, F=lambda tht: tht**2) problem = Form(flag='integration', kernel=k) assembler = Assembler(problem, mesh) assembler.assemble() v = assembler.get_scalar(i_sample=3) #plot = Plot(quickview=False) #fig, ax = plt.subplots() #plot.mesh(mesh,regions=[('region','cell')]) """ ax = plot.line(theta_trunc, axis=ax, i_sample=np.arange(n_mc), plot_kwargs={'linewidth':0.2, 'color':'k'}) """ #plt.show()
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)
# Solve the steady state flow equations # ============================================================================= # Define problem flow_problem = [ Form(fNodal), Form(1, test=p_uy, trial=p_uy), Form(1, test=p_u) ] # Assembler tic = time.time() assembler = Assembler(flow_problem, mesh) toc = time.time() - tic print('initializing assembler', toc) tic = time.time() assembler.assemble() toc = time.time() - tic print('assembly time', toc) tic = time.time() A = assembler.get_matrix() print('forming bilinear array', time.time() - tic) b = assembler.get_vector() c = assembler.get_scalar()
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 test_integrals_2d(self): """ Test Assembly of some 2D systems """ mesh = QuadMesh(box=[1, 2, 1, 2], resolution=(2, 2)) mesh.cells.get_leaves()[0].mark(0) mesh.cells.refine(refinement_flag=0) # Kernel kernel = Kernel(Explicit(f=lambda x: x[:, 0] * x[:, 1], dim=2)) problem = Form(kernel) assembler = Assembler(problem, mesh=mesh) assembler.assemble() self.assertAlmostEqual(assembler.get_scalar(), 9 / 4) # # Linear forms (x,x) and (x,x') over [1,2]^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) yfn = Nodal(f=lambda x: x[:, 1], 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,y) b0 = assembler.get_vector(0) self.assertAlmostEqual(np.sum(b0 * yfn.data()[:, 0]), 9 / 4) b1 = assembler.get_vector(1) self.assertAlmostEqual(np.sum(b1 * xfn.data()[:, 0]), 3 / 2) self.assertAlmostEqual(np.sum(b1 * yfn.data()[:, 0]), 0) # # Bilinear forms # # Compute (1,x,y) = 9/4, or (xy, 1, 1) = 9/4 for dQ in [dQ1, dQ2, dQ3]: # Basis phi = Basis(dQ, 'u') phi_x = Basis(dQ, 'ux') phi_y = Basis(dQ, 'uy') # Kernel function xyfn = Explicit(f=lambda x: x[:, 0] * x[:, 1], dim=2) xfn = Nodal(f=lambda x: x[:, 0], basis=phi) yfn = Nodal(f=lambda x: x[:, 1], basis=phi) # Form problems = [[Form(1, test=phi, trial=phi)], [Form(Kernel(xfn), test=phi, trial=phi_x)], [Form(Kernel(xyfn), test=phi_y, trial=phi_x)]] # Assemble assembler = Assembler(problems, mesh) assembler.assemble() x = xfn.data()[:, 0] y = yfn.data()[:, 0] for i_problem in range(3): A = assembler.get_matrix(i_problem) self.assertAlmostEqual(y.T.dot(A.dot(x)), 9 / 4)
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, ))