def compute_err(self, is_tent, velocity, t): if self.doErrControl: er_list_L2 = self.listDict['u2L2' if is_tent else 'u_L2']['list'] er_list_H1 = self.listDict['u2H1' if is_tent else 'u_H1']['list'] self.tc.start('errorV') # assemble is faster than errornorm errorL2_sq = assemble(inner(velocity - self.solution, velocity - self.solution) * dx) errorH1seminorm_sq = assemble(inner(grad(velocity - self.solution), grad(velocity - self.solution)) * dx) info(' H1 seminorm error: %f' % sqrt(errorH1seminorm_sq)) errorL2 = sqrt(errorL2_sq) errorH1 = sqrt(errorL2_sq + errorH1seminorm_sq) info(" Relative L2 error in velocity = %f" % (errorL2 / self.analytic_v_norm_L2)) self.last_error = errorH1 / self.analytic_v_norm_H1 self.last_status_functional = self.last_error info(" Relative H1 error in velocity = %f" % self.last_error) er_list_L2.append(errorL2) er_list_H1.append(errorH1) self.tc.end('errorV') if self.testErrControl: er_list_test_H1 = self.listDict['u2H1test' if is_tent else 'u_H1test']['list'] er_list_test_L2 = self.listDict['u2L2test' if is_tent else 'u_L2test']['list'] self.tc.start('errorVtest') er_list_test_L2.append(errornorm(velocity, self.solution, norm_type='L2', degree_rise=0)) er_list_test_H1.append(errornorm(velocity, self.solution, norm_type='H1', degree_rise=0)) self.tc.end('errorVtest') # stopping criteria for detecting diverging solution if self.last_error > self.divergence_treshold: raise RuntimeError('STOPPED: Failed divergence test!')
def __init__(self, coarse_mesh, nref, p_coarse, p_fine): super(LaplaceEigenvalueProblem, self).__init__(coarse_mesh, nref, p_coarse, p_fine) print0("Assembling fine-mesh problem") self.dirichlet_bdry = lambda x,on_boundary: on_boundary bc = DirichletBC(self.V_fine, 0.0, self.dirichlet_bdry) u = TrialFunction(self.V_fine) v = TestFunction(self.V_fine) a = inner(grad(u), grad(v))*dx m = u*v*dx # Assemble the stiffness matrix and the mass matrix. b = v*dx # just need this to feed an argument to assemble_system assemble_system(a, b, bc, A_tensor=self.A_fine) assemble_system(m, b, bc, A_tensor=self.B_fine) # set the diagonal elements of M corresponding to boundary nodes to zero to # remove spurious eigenvalues. bc.zero(self.B_fine) print0("Assembling coarse-mesh problem") self.bc_coarse = DirichletBC(self.V_coarse, 0.0, self.dirichlet_bdry) u = TrialFunction(self.V_coarse) v = TestFunction(self.V_coarse) a = inner(grad(u), grad(v))*dx m = u*v*dx # Assemble the stiffness matrix and the mass matrix, without Dirichlet BCs. Dirichlet DOFs will be removed later. assemble(a, tensor=self.A_coarse) assemble(m, tensor=self.B_coarse)
def update_time(self, actual_time, step_number): super(Problem, self).update_time(actual_time, step_number) if self.actual_time > 0.5 and int(round(self.actual_time * 1000)) % 1000 == 0: self.isWholeSecond = True seconds = int(round(self.actual_time)) self.second_list.append(seconds) self.N1 = seconds*self.stepsInSecond self.N0 = (seconds-1)*self.stepsInSecond else: self.isWholeSecond = False self.solution = self.assemble_solution(self.actual_time) # Update boundary condition self.tc.start('updateBC') self.v_in.assign(self.solution) self.tc.end('updateBC') # construct analytic pressure (used for computing pressure and force errors) self.tc.start('analyticP') analytic_pressure = womersleyBC.analytic_pressure(self.factor, self.actual_time) self.sol_p = interpolate(analytic_pressure, self.pSpace) self.tc.end('analyticP') self.tc.start('analyticVnorms') self.analytic_v_norm_L2 = norm(self.solution, norm_type='L2') self.analytic_v_norm_H1 = norm(self.solution, norm_type='H1') self.analytic_v_norm_H1w = sqrt(assemble((inner(grad(self.solution), grad(self.solution)) + inner(self.solution, self.solution)) * self.dsWall)) self.listDict['av_norm_L2']['list'].append(self.analytic_v_norm_L2) self.listDict['av_norm_H1']['list'].append(self.analytic_v_norm_H1) self.listDict['av_norm_H1w']['list'].append(self.analytic_v_norm_H1w) self.tc.end('analyticVnorms')
def __init__(self, v, v_out, solver_parameters=None): if isinstance(v, expression.Expression) or \ not isinstance(v, (ufl.core.expr.Expr, function.Function)): raise ValueError("Can only project UFL expression or Functions not '%s'" % type(v)) self._same_fspace = (isinstance(v, function.Function) and v.function_space() == v_out.function_space()) self.v = v self.v_out = v_out if not self._same_fspace: V = v_out.function_space() p = ufl_expr.TestFunction(V) q = ufl_expr.TrialFunction(V) a = ufl.inner(p, q)*ufl.dx L = ufl.inner(p, v)*ufl.dx problem = vs.LinearVariationalProblem(a, L, v_out) if solver_parameters is None: solver_parameters = {} solver_parameters.setdefault("ksp_type", "cg") self.solver = vs.LinearVariationalSolver(problem, solver_parameters=solver_parameters)
def compute_err(self, is_tent, velocity, t): super(Problem, self).compute_err(is_tent, velocity, t) er_list_H1w = self.listDict['u2H1w' if is_tent else 'u_H1w']['list'] errorH1wall = sqrt(assemble((inner(grad(velocity - self.solution), grad(velocity - self.solution)) + inner(velocity - self.solution, velocity - self.solution)) * self.dsWall)) er_list_H1w.append(errorH1wall) print(' Relative H1wall error:', errorH1wall / self.analytic_v_norm_H1w)
def compute_functionals(self, velocity, pressure, t): if self.args.wss: info('Computing stress tensor') I = Identity(velocity.geometric_dimension()) T = TensorFunctionSpace(self.mesh, 'Lagrange', 1) stress = project(-pressure*I + 2*sym(grad(velocity)), T) info('Generating boundary mesh') wall_mesh = BoundaryMesh(self.mesh, 'exterior') # wall_mesh = SubMesh(self.mesh, self.facet_function, 1) # QQ why does not work? # plot(wall_mesh, interactive=True) info(' Boundary mesh geometric dim: %d' % wall_mesh.geometry().dim()) info(' Boundary mesh topologic dim: %d' % wall_mesh.topology().dim()) info('Projecting stress to boundary mesh') Tb = TensorFunctionSpace(wall_mesh, 'Lagrange', 1) stress_b = interpolate(stress, Tb) self.fileDict['wss']['file'] << stress_b if False: # does not work info('Computing WSS') n = FacetNormal(wall_mesh) info(stress_b, True) # wss = stress_b*n - inner(stress_b*n, n)*n wss = dot(stress_b, n) - inner(dot(stress_b, n), n)*n # equivalent Vb = VectorFunctionSpace(wall_mesh, 'Lagrange', 1) Sb = FunctionSpace(wall_mesh, 'Lagrange', 1) # wss_func = project(wss, Vb) wss_norm = project(sqrt(inner(wss, wss)), Sb) plot(wss_norm, interactive=True)
def initialize(self, V, Q, PS, D): super(Problem, self).initialize(V, Q, PS, D) print("IC type: " + self.ic) print("Velocity scale factor = %4.2f" % self.factor) reynolds = 728.761 * self.factor print("Computing with Re = %f" % reynolds) # set constants for self.area = assemble(interpolate(Expression("1.0"), Q) * self.dsIn) # inflow area self.solution = interpolate(Expression(("0.0", "0.0", "factor*(1081.48-43.2592*(x[0]*x[0]+x[1]*x[1]))"), factor=self.factor), self.vSpace) analytic_pressure = womersleyBC.average_analytic_pressure_expr(self.factor) self.sol_p = interpolate(analytic_pressure, self.pSpace) self.analytic_gradient = womersleyBC.average_analytic_pressure_grad(self.factor) self.analytic_pressure_norm = norm(self.sol_p, norm_type='L2') self.analytic_v_norm_L2 = norm(self.solution, norm_type='L2') self.analytic_v_norm_H1 = norm(self.solution, norm_type='H1') self.analytic_v_norm_H1w = sqrt(assemble((inner(grad(self.solution), grad(self.solution)) + inner(self.solution, self.solution)) * self.dsWall)) print("Prepared analytic solution.") self.pg_normalization_factor.append(womersleyBC.average_analytic_pressure_grad(self.factor)) self.p_normalization_factor.append(self.analytic_pressure_norm) self.vel_normalization_factor.append(norm(self.solution, norm_type='L2')) print('Normalisation factors (vel, p, pg):', self.vel_normalization_factor[0], self.p_normalization_factor[0], self.pg_normalization_factor[0]) one = (interpolate(Expression('1.0'), Q)) self.outflow_area = assemble(one*self.dsOut) print('Outflow area:', self.outflow_area)
def compute_force(self, velocity, pressure, t): self.tc.start('errorForce') I = Identity(3) # Identity tensor def T(p, v): return -p * I + 2.0 * self.nu * sym(grad(v)) error_force = sqrt( assemble(inner((T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal, (T(pressure, velocity) - T(self.sol_p, self.solution)) * self.normal) * self.dsWall)) an_force = sqrt(assemble(inner(T(self.sol_p, self.solution) * self.normal, T(self.sol_p, self.solution) * self.normal) * self.dsWall)) an_f_normal = sqrt(assemble(inner(inner(T(self.sol_p, self.solution) * self.normal, self.normal), inner(T(self.sol_p, self.solution) * self.normal, self.normal)) * self.dsWall)) error_f_normal = sqrt( assemble(inner(inner((T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal), inner((T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, self.normal)) * self.dsWall)) an_f_shear = sqrt( assemble(inner((I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal, (I - outer(self.normal, self.normal)) * T(self.sol_p, self.solution) * self.normal) * self.dsWall)) error_f_shear = sqrt( assemble(inner((I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal, (I - outer(self.normal, self.normal)) * (T(self.sol_p, self.solution) - T(pressure, velocity)) * self.normal) * self.dsWall)) self.listDict['a_force_wall']['list'].append(an_force) self.listDict['a_force_wall_normal']['list'].append(an_f_normal) self.listDict['a_force_wall_shear']['list'].append(an_f_shear) self.listDict['force_wall']['list'].append(error_force) self.listDict['force_wall_normal']['list'].append(error_f_normal) self.listDict['force_wall_shear']['list'].append(error_f_shear) if self.isWholeSecond: self.listDict['force_wall']['slist'].append( sqrt(sum([i*i for i in self.listDict['force_wall']['list'][self.N0:self.N1]])/self.stepsInSecond)) print(' Relative force error:', error_force/an_force) self.tc.end('errorForce')
def update_time(self, actual_time, step_number): super(Problem, self).update_time(actual_time, step_number) if self.actual_time > 0.5 and abs(math.modf(actual_time)[0]) < 0.5*self.metadata['dt']: self.second_list.append(int(round(self.actual_time))) self.solution = self.assemble_solution(self.actual_time) # Update boundary condition self.tc.start('updateBC') self.v_in.assign(self.onset_factor * self.solution) self.tc.end('updateBC') # construct analytic pressure (used for computing pressure and force errors) self.tc.start('analyticP') analytic_pressure = womersleyBC.analytic_pressure(self.factor, self.actual_time) self.sol_p = interpolate(analytic_pressure, self.pSpace) self.tc.end('analyticP') self.tc.start('analyticVnorms') self.analytic_v_norm_L2 = norm(self.solution, norm_type='L2') self.analytic_v_norm_H1 = norm(self.solution, norm_type='H1') self.analytic_v_norm_H1w = sqrt(assemble((inner(grad(self.solution), grad(self.solution)) + inner(self.solution, self.solution)) * self.dsWall)) self.listDict['av_norm_L2']['list'].append(self.analytic_v_norm_L2) self.listDict['av_norm_H1']['list'].append(self.analytic_v_norm_H1) self.listDict['av_norm_H1w']['list'].append(self.analytic_v_norm_H1w) self.tc.end('analyticVnorms')
def nonlinearity(function): if self.use_ema: return 2*inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function)*u_ext, v1) * dx # return 2*inner(dot(sym(grad(function)), u_ext), v) * dx + inner(div(u_ext)*function, v) * dx # QQ implement this way? else: return inner(dot(grad(function), u_ext), v1) * dx
def update_time(self, actual_time, step_number): super(Problem, self).update_time(actual_time, step_number) if self.actual_time > 0.5 and int(round(self.actual_time * 1000)) % 1000 == 0: self.isWholeSecond = True seconds = int(round(self.actual_time)) self.second_list.append(seconds) self.N1 = seconds*self.stepsInSecond self.N0 = (seconds-1)*self.stepsInSecond else: self.isWholeSecond = False # Update boundary condition self.tc.start('updateBC') if not self.ic == 'correct': self.v_in.t = self.actual_time self.tc.end('updateBC') self.tc.start('analyticVnorms') self.analytic_v_norm_L2 = norm(self.solution, norm_type='L2') self.analytic_v_norm_H1 = norm(self.solution, norm_type='H1') self.analytic_v_norm_H1w = sqrt(assemble((inner(grad(self.solution), grad(self.solution)) + inner(self.solution, self.solution)) * self.dsWall)) self.listDict['av_norm_L2']['list'].append(self.analytic_v_norm_L2) self.listDict['av_norm_H1']['list'].append(self.analytic_v_norm_H1) self.listDict['av_norm_H1w']['list'].append(self.analytic_v_norm_H1w) self.tc.end('analyticVnorms')
def compute_err(self, is_tent, velocity, t): super(Problem, self).compute_err(is_tent, velocity, t) er_list_H1w = self.listDict['u2H1w' if is_tent else 'u_H1w']['list'] errorH1wall = sqrt(assemble((inner(grad(velocity - self.solution), grad(velocity - self.solution)) + inner(velocity - self.solution, velocity - self.solution)) * self.dsWall)) er_list_H1w.append(errorH1wall) print(' Relative H1wall error:', errorH1wall / self.analytic_v_norm_H1w) if self.isWholeSecond: self.listDict['u2H1w' if is_tent else 'u_H1w']['slist'].append( sqrt(sum([i*i for i in er_list_H1w[self.N0:self.N1]])/self.stepsInSecond))
def diffusion(fce): if self.useLaplace: return nu * inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': return form if self.bcv == 'LAP': return form - inner(nu * dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form() if self.bcv == 'DDN': return form # additional term must be added to non-constant part
def diffusion(fce): if self.useLaplace: return nu*inner(grad(fce), grad(v1)) * dx else: form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx if self.bcv == 'CDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form if self.bcv == 'LAP': return form - inner(nu*dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form() if self.bcv == 'DDN': # IMP will work only if p=0 on output, or we must add term # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer return form # additional term must be added to non-constant part
def form(V, itype, request): if request.param == "functional": u = ufl.Coefficient(V) v = ufl.Coefficient(V) elif request.param == "1-form": u = ufl.Coefficient(V) v = ufl.TestFunction(V) elif request.param == "2-form": u = ufl.TrialFunction(V) v = ufl.TestFunction(V) if itype == "cell": return ufl.inner(u, v)*ufl.dx elif itype == "ext_facet": return ufl.inner(u, v)*ufl.ds elif itype == "int_facet": return ufl.inner(u('+'), v('-'))*ufl.dS
def error_indicators(self): """ Generate and return linear form defining error indicators """ # Extract these to increase readability R_T = self._R_T R_dT = self._R_dT z = self._Ez_h z_h = self._z_h # Define linear form for computing error indicators v = self.module.TestFunction(self._DG0) eta_T = (v * inner(R_T, z - z_h) * dx(self.domain) + avg(v)*(inner(R_dT('+'), (z - z_h)('+')) + inner(R_dT('-'), (z - z_h)('-'))) * dS(self.domain) + v * inner(R_dT, z - z_h) * ds(self.domain)) return eta_T
def forms(arguments, coefficients): v, u = arguments c, f = coefficients n = FacetNormal(triangle) a = u * v * dx L = f * v * dx b = u * v * dx(0) + inner(c * grad(u), grad(v)) * \ dx(1) + dot(n, grad(u)) * v * ds + f * v * dx return (a, L, b)
def compute_functionals(self, velocity, pressure, t, step): if self.args.wss == 'all' or \ (step >= self.stepsInCycle and self.args.wss == 'peak' and (self.distance_from_chosen_steps < 0.5 * self.metadata['dt'])): # TODO check if choosing time steps works properly # QQ might skip step? change 0.5 to 0.51? self.tc.start('WSS') begin('WSS (%dth step)' % step) if self.args.wss_method == 'expression': stress = project(self.nu*2*sym(grad(velocity)), self.T) # pressure is not used as it contributes only to the normal component stress.set_allow_extrapolation(True) # need because of some inaccuracies in BoundaryMesh coordinates stress_b = interpolate(stress, self.Tb) # restrict stress to boundary mesh # self.fileDict['stress']['file'].write(stress_b, self.actual_time) # info('Saved stress tensor') info('Computing WSS') wss = dot(stress_b, self.nb) - inner(dot(stress_b, self.nb), self.nb)*self.nb wss_func = project(wss, self.Vb) wss_norm = project(sqrt_ufl(inner(wss, wss)), self.Sb) info('Saving WSS') self.fileDict['wss']['file'].write(wss_func, self.actual_time) self.fileDict['wss_norm']['file'].write(wss_norm, self.actual_time) if self.args.wss_method == 'integral': wss_norm = Function(self.SDG) mS = TestFunction(self.SDG) scaling = 1/FacetArea(self.mesh) stress = self.nu*2*sym(grad(velocity)) wss = dot(stress, self.normal) - inner(dot(stress, self.normal), self.normal)*self.normal wss_norm_form = scaling*mS*sqrt_ufl(inner(wss, wss))*ds # ds is integral over exterior facets only assemble(wss_norm_form, tensor=wss_norm.vector()) self.fileDict['wss_norm']['file'].write(wss_norm, self.actual_time) # to get vector WSS values: # NT this works, but in ParaView for (DG,1)-vector space glyphs are displayed in cell centers # wss_vector = [] # for i in range(3): # wss_component = Function(self.SDG) # wss_vector_form = scaling*wss[i]*mS*ds # assemble(wss_vector_form, tensor=wss_component.vector()) # wss_vector.append(wss_component) # wss_func = project(as_vector(wss_vector), self.VDG) # self.fileDict['wss']['file'].write(wss_func, self.actual_time) self.tc.end('WSS') end()
def test_gradient_error(cell, degree): """Test that tabulating gradient evaluations of the trace element triggers `gem.Failure` to raise the TraceError exception. """ trace_element = FiniteElement("HDiv Trace", cell, degree) lambdar = TrialFunction(trace_element) gammar = TestFunction(trace_element) with pytest.raises(TraceError): compile_form(inner(grad(lambdar('+')), grad(gammar('+'))) * dS)
def save_vel(self, is_tent, field, t): self.vFunction.assign(field) self.fileDict['u2' if is_tent else 'u']['file'] << self.vFunction if self.doSaveDiff: self.vFunction.assign((1.0 / self.vel_normalization_factor[0]) * (field - self.solution)) self.fileDict['u2D' if is_tent else 'uD']['file'] << self.vFunction if self.args.ldsg: # info(div(2.*sym(grad(field))-grad(field)).ufl_shape) form = div(2.*sym(grad(field))-grad(field)) self.pFunction.assign(project(sqrt_ufl(inner(form, form)), self.pSpace)) self.fileDict['ldsg2' if is_tent else 'ldsg']['file'] << self.pFunction
def compute_functionals(self, velocity, pressure, t, step): super(Problem, self).compute_functionals(velocity, pressure, t, step) out = 0 for obj in self.outflows: outflow = assemble(inner(velocity, self.normal)*obj['measure']) out += outflow self.listDict['outflow'+obj['number']]['list'].append(outflow) self.listDict['outflow']['list'].append(out) info('Outflow: %f' % out) self.last_status_functional = out/abs(self.last_inflow) self.listDict['oiratio']['list'].append(self.last_status_functional) info('Outflow/Inflow: %f' % self.last_status_functional)
def facet_residual(self): """ Generate and return (bilinear, linear) forms defining linear variational problem for the strong facet residual(s) """ # Define trial and test functions for the facet residuals on # discontinuous version of primal trial space R_e = self.module.TrialFunction(self._dV) v = self.module.TestFunction(self._dV) # Extract original test function in the weak residual v_h = self.weak_residual.arguments()[0] # Define forms defining linear variational problem for facet # residual v_e = self._b_e*v a_R_dT = ((inner(v_e('+'), R_e('+')) + inner(v_e('-'), R_e('-')))*dS(self.domain) + inner(v_e, R_e)*ds(self.domain)) L_R_dT = (replace(self.weak_residual, {v_h: v_e}) - inner(v_e, self._R_T)*dx(self.domain)) return (a_R_dT, L_R_dT)
def update_time(self, actual_time, step_number): super(Problem, self).update_time(actual_time, step_number) if self.actual_time > 0.5 and abs(math.modf(actual_time)[0]) < 0.5*self.metadata['dt']: self.second_list.append(int(round(self.actual_time))) # Update boundary condition self.tc.start('updateBC') self.last_inflow = 0 for obj in self.inflows: obj['velocity_profile'].t = actual_time obj['velocity_profile'].onset_factor = self.onset_factor self.last_inflow += assemble(inner(obj['velocity_profile'], self.normal)*obj['measure']) info('Inflow: %f' % self.last_inflow) self.listDict['inflow']['list'].append(self.last_inflow) self.tc.end('updateBC')
def norm(v, norm_type="L2", mesh=None): """Compute the norm of ``v``. :arg v: a :class:`.Function` to compute the norm of :arg norm_type: the type of norm to compute, see below for options. :arg mesh: an optional mesh on which to compute the norm (currently ignored). Available norm types are: * L2 .. math:: ||v||_{L^2}^2 = \int (v, v) \mathrm{d}x * H1 .. math:: ||v||_{H^1}^2 = \int (v, v) + (\\nabla v, \\nabla v) \mathrm{d}x * Hdiv .. math:: ||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\\nabla\cdot v, \\nabla \cdot v) \mathrm{d}x * Hcurl .. math:: ||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\\nabla \wedge v, \\nabla \wedge v) \mathrm{d}x """ assert isinstance(v, function.Function) typ = norm_type.lower() mesh = v.function_space().mesh() dx = mesh._dx if typ == 'l2': form = inner(v, v)*dx elif typ == 'h1': form = inner(v, v)*dx + inner(grad(v), grad(v))*dx elif typ == "hdiv": form = inner(v, v)*dx + div(v)*div(v)*dx elif typ == "hcurl": form = inner(v, v)*dx + inner(curl(v), curl(v))*dx else: raise RuntimeError("Unknown norm type '%s'" % norm_type) return sqrt(solving.assemble(form))
def cell_residual(self): """ Generate and return (bilinear, linear) forms defining linear variational problem for the strong cell residual """ # Define trial and test functions for the cell residuals on # discontinuous version of primal trial space R_T = self.module.TrialFunction(self._dV) v = self.module.TestFunction(self._dV) # Extract original test function in the weak residual v_h = self.weak_residual.arguments()[0] # Define forms defining linear variational problem for cell # residual v_T = self._b_T * v a_R_T = inner(v_T, R_T) * dx(self.domain) L_R_T = replace(self.weak_residual, {v_h: v_T}) return (a_R_T, L_R_T)
def norm(v, norm_type="L2", mesh=None): """Compute the norm of ``v``. :arg v: a ufl expression (:class:`~.ufl.classes.Expr`) to compute the norm of :arg norm_type: the type of norm to compute, see below for options. :arg mesh: an optional mesh on which to compute the norm (currently ignored). Available norm types are: * L2 .. math:: ||v||_{L^2}^2 = \int (v, v) \mathrm{d}x * H1 .. math:: ||v||_{H^1}^2 = \int (v, v) + (\\nabla v, \\nabla v) \mathrm{d}x * Hdiv .. math:: ||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\\nabla\cdot v, \\nabla \cdot v) \mathrm{d}x * Hcurl .. math:: ||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\\nabla \wedge v, \\nabla \wedge v) \mathrm{d}x """ typ = norm_type.lower() if typ == 'l2': form = inner(v, v)*dx elif typ == 'h1': form = inner(v, v)*dx + inner(grad(v), grad(v))*dx elif typ == "hdiv": form = inner(v, v)*dx + div(v)*div(v)*dx elif typ == "hcurl": form = inner(v, v)*dx + inner(curl(v), curl(v))*dx else: raise RuntimeError("Unknown norm type '%s'" % norm_type) return sqrt(assemble(form))
def norm(v, norm_type="L2", mesh=None): r"""Compute the norm of ``v``. :arg v: a ufl expression (:class:`~.ufl.classes.Expr`) to compute the norm of :arg norm_type: the type of norm to compute, see below for options. :arg mesh: an optional mesh on which to compute the norm (currently ignored). Available norm types are: - Lp :math:`||v||_{L^p} = (\int |v|^p)^{\frac{1}{p}} \mathrm{d}x` - H1 :math:`||v||_{H^1}^2 = \int (v, v) + (\nabla v, \nabla v) \mathrm{d}x` - Hdiv :math:`||v||_{H_\mathrm{div}}^2 = \int (v, v) + (\nabla\cdot v, \nabla \cdot v) \mathrm{d}x` - Hcurl :math:`||v||_{H_\mathrm{curl}}^2 = \int (v, v) + (\nabla \wedge v, \nabla \wedge v) \mathrm{d}x` """ typ = norm_type.lower() p = 2 if typ == 'l2': expr = inner(v, v) elif typ.startswith('l'): try: p = int(typ[1:]) if p < 1: raise ValueError except ValueError: raise ValueError("Don't know how to interpret %s-norm" % norm_type) expr = inner(v, v) elif typ == 'h1': expr = inner(v, v) + inner(grad(v), grad(v)) elif typ == "hdiv": expr = inner(v, v) + div(v)*div(v) elif typ == "hcurl": expr = inner(v, v) + inner(curl(v), curl(v)) else: raise RuntimeError("Unknown norm type '%s'" % norm_type) return assemble((expr**(p/2))*dx)**(1/p)
def solve(self, f, filename): # redifine constans for specific frequency self.f = f self.omega = 2 * np.pi * f self.lmda = self.c / f self.k0 = self.omega / self.c # Load mesh mesh, cell_tags, facet_tags = read_from_msh(filename, cell_data=True, facet_data=True, gdim=2) # Define function space V = dolfinx.FunctionSpace(mesh, ("Lagrange", self.degree)) # Interpolate wavenumber k onto V k = dolfinx.Constant(V, self.k0) # Interpolate absorbing layer piece of wavenumber k_absorb onto V k_absorb = dolfinx.Function(V) adiabatic_layer = AdiabaticLayer(self.deg_absorber, self.k0, self.lmda) k_absorb.interpolate(adiabatic_layer.eval) # Interpolate incident wave field onto V ui = dolfinx.Function(V) ui_expr = IncidentWave(self.k0) ui.interpolate(ui_expr.eval) # Define variational problem u = ufl.TrialFunction(V) v = ufl.TestFunction(V) ds_exited = ufl.Measure("ds", domain=mesh, subdomain_data=facet_tags, subdomain_id=1) a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx \ - k ** 2 * ufl.inner(u, v) * ufl.dx \ - k_absorb * ufl.inner(u, v) * ufl.dx L = -1j * self.omega * self.rho_0 * ufl.inner(ui, v) * ufl.dx # Assemble matrix and vector and set up direct solver A = dolfinx.fem.assemble_matrix(a) A.assemble() b = dolfinx.fem.assemble_vector(L) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) solver = PETSc.KSP().create(mesh.mpi_comm()) opts = PETSc.Options() opts["ksp_type"] = "preonly" opts["pc_type"] = "lu" opts["pc_factor_mat_solver_type"] = "mumps" solver.setFromOptions() solver.setOperators(A) # Solve linear system u = dolfinx.Function(V) start = time.time() solver.solve(b, u.vector) end = time.time() time_elapsed = end - start print('Solve time: ', time_elapsed) u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) save(mesh, u, f"../Solution/sol_{f}Hz.xdmf")
def J2(A): """Second (main) invariant J2 = (I_1)^2 - 2*(I_2) with I_1, I_2 principal invariants.""" return 0.5 * ufl.inner(A, A)
# :py:class:`FunctionSpace <dolfinx.functions.FunctionSpace>` ``V``. # # Further, the source :math:`f` and the boundary normal derivative # :math:`g` are involved in the variational forms, and hence we must # specify these. # # With these ingredients, we can write down the bilinear form ``a`` and # the linear form ``L`` (using UFL operators). In summary, this reads :: # Define variational problem u = ufl.TrialFunction(V) v = ufl.TestFunction(V) x = ufl.SpatialCoordinate(mesh) f = 10 * ufl.exp(-((x[0] - 0.5)**2 + (x[1] - 0.5)**2) / 0.02) g = ufl.sin(5 * x[0]) a = inner(grad(u), grad(v)) * dx L = inner(f, v) * dx + inner(g, v) * ds # Now, we have specified the variational forms and can consider the # solution of the variational problem. First, we need to define a # :py:class:`Function <dolfinx.functions.function.Function>` ``u`` to # represent the solution. (Upon initialization, it is simply set to the # zero function.) A :py:class:`Function # <dolfinx.functions.function.Function>` represents a function living in # a finite element function space. Next, we can call the :py:func:`solve # <dolfinx.fem.solving.solve>` function with the arguments ``a == L``, # ``u`` and ``bc`` as follows: :: # Compute solution u = Function(V) solve(a == L, u, bc, petsc_options={"ksp_type": "preonly", "pc_type": "lu"})
def test_matrix_assembly_block(): """Test assembly of block matrices and vectors into (a) monolithic blocked structures, PETSc Nest structures, and monolithic structures. """ # mesh = dolfin.generation.UnitSquareMesh(dolfin.MPI.comm_world, 4, 8) mesh = dolfin.generation.UnitSquareMesh(dolfin.MPI.comm_world, 2, 1) p0, p1 = 1, 2 P0 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p0) P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p1) V0 = dolfin.function.functionspace.FunctionSpace(mesh, P0) V1 = dolfin.function.functionspace.FunctionSpace(mesh, P1) def boundary(x): return numpy.logical_or(x[:, 0] < 1.0e-6, x[:, 0] > 1.0 - 1.0e-6) u_bc = dolfin.function.constant.Constant(50.0) bc = dolfin.fem.dirichletbc.DirichletBC(V1, u_bc, boundary) # Define variational problem u, p = dolfin.function.argument.TrialFunction( V0), dolfin.function.argument.TrialFunction(V1) v, q = dolfin.function.argument.TestFunction( V0), dolfin.function.argument.TestFunction(V1) f = dolfin.function.constant.Constant(1.0) g = dolfin.function.constant.Constant(-3.0) zero = dolfin.function.constant.Constant(0.0) a00 = inner(u, v) * dx a01 = inner(p, v) * dx a10 = inner(u, q) * dx a11 = inner(p, q) * dx # a11 = None L0 = zero * inner(f, v) * dx L1 = inner(g, q) * dx a_block = [[a00, a01], [a10, a11]] L_block = [L0, L1] # Monolithic blocked A0 = dolfin.fem.assemble_matrix(a_block, [bc], dolfin.cpp.fem.BlockType.monolithic) b0 = dolfin.fem.assemble_vector(L_block, a_block, [bc], dolfin.cpp.fem.BlockType.monolithic) assert A0.mat().getType() != "nest" Anorm0 = A0.mat().norm() bnorm0 = b0.vec().norm() # Nested (MatNest) A1 = dolfin.fem.assemble_matrix(a_block, [bc], dolfin.cpp.fem.BlockType.nested) b1 = dolfin.fem.assemble_vector(L_block, a_block, [bc], dolfin.cpp.fem.BlockType.nested) bnorm1 = math.sqrt(sum([x.norm()**2 for x in b1.vec().getNestSubVecs()])) assert bnorm0 == pytest.approx(bnorm1, 1.0e-12) try: Anorm1 = 0.0 nrows, ncols = A1.mat().getNestSize() for row in range(nrows): for col in range(ncols): A_sub = A1.mat().getNestSubMatrix(row, col) norm = A_sub.norm() Anorm1 += norm * norm # A_sub.view() # is_rows, is_cols = A1.mat().getNestLocalISs() # for is0 in is_rows: # for is1 in is_cols: # A_sub = A1.mat().getLocalSubMatrix(is0, is1) # norm = A_sub.norm() # Anorm1 += norm * norm Anorm1 = math.sqrt(Anorm1) assert Anorm0 == pytest.approx(Anorm1, 1.0e-12) except AttributeError: print("Recent petsc4py(-dev) required to get MatNest sub-matrix.") # Monolithic version E = P0 * P1 W = dolfin.function.functionspace.FunctionSpace(mesh, E) u0, u1 = dolfin.function.argument.TrialFunctions(W) v0, v1 = dolfin.function.argument.TestFunctions(W) a = inner(u0, v0) * dx + inner(u1, v1) * dx + inner(u0, v1) * dx + inner( u1, v0) * dx L = zero * inner(f, v0) * ufl.dx + inner(g, v1) * dx bc = dolfin.fem.dirichletbc.DirichletBC(W.sub(1), u_bc, boundary) A2 = dolfin.fem.assemble_matrix([[a]], [bc], dolfin.cpp.fem.BlockType.monolithic) b2 = dolfin.fem.assemble_vector([L], [[a]], [bc], dolfin.cpp.fem.BlockType.monolithic) assert A2.mat().getType() != "nest" Anorm2 = A2.mat().norm() bnorm2 = b2.vec().norm() assert Anorm0 == pytest.approx(Anorm2, 1.0e-9) assert bnorm0 == pytest.approx(bnorm2, 1.0e-9)
def __init__(self, mesh, Vh, prior, misfit, simulation_times, wind_velocity, gls_stab): self.mesh = mesh self.Vh = Vh self.prior = prior self.misfit = misfit # Assume constant timestepping self.simulation_times = simulation_times dt = simulation_times[1] - simulation_times[0] u = dl.TrialFunction(Vh[STATE]) v = dl.TestFunction(Vh[STATE]) kappa = dl.Constant(.001) dt_expr = dl.Constant(dt) r_trial = u + dt_expr*( -ufl.div(kappa*ufl.grad(u))+ ufl.inner(wind_velocity, ufl.grad(u)) ) r_test = v + dt_expr*( -ufl.div(kappa*ufl.grad(v))+ ufl.inner(wind_velocity, ufl.grad(v)) ) h = dl.CellDiameter(mesh) vnorm = ufl.sqrt(ufl.inner(wind_velocity, wind_velocity)) if gls_stab: tau = ufl.min_value((h*h)/(dl.Constant(2.)*kappa), h/vnorm ) else: tau = dl.Constant(0.) self.M = dl.assemble( ufl.inner(u,v)*ufl.dx ) self.M_stab = dl.assemble( ufl.inner(u, v+tau*r_test)*ufl.dx ) self.Mt_stab = dl.assemble( ufl.inner(u+tau*r_trial,v)*ufl.dx ) Nvarf = (ufl.inner(kappa * ufl.grad(u), ufl.grad(v)) + ufl.inner(wind_velocity, ufl.grad(u))*v )*ufl.dx Ntvarf = (ufl.inner(kappa *ufl.grad(v), ufl.grad(u)) + ufl.inner(wind_velocity, ufl.grad(v))*u )*ufl.dx self.N = dl.assemble( Nvarf ) self.Nt = dl.assemble(Ntvarf) stab = dl.assemble( tau*ufl.inner(r_trial, r_test)*ufl.dx) self.L = self.M + dt*self.N + stab self.Lt = self.M + dt*self.Nt + stab self.solver = PETScLUSolver( self.mesh.mpi_comm() ) self.solver.set_operator( dl.as_backend_type(self.L) ) self.solvert = PETScLUSolver( self.mesh.mpi_comm() ) self.solvert.set_operator(dl.as_backend_type(self.Lt) ) # Part of model public API self.gauss_newton_approx = False
def test_custom_mesh_loop_rank1(): # Create mesh and function space mesh = create_unit_square(MPI.COMM_WORLD, 64, 64) V = FunctionSpace(mesh, ("Lagrange", 1)) # Unpack mesh and dofmap data num_owned_cells = mesh.topology.index_map(mesh.topology.dim).size_local num_cells = num_owned_cells + mesh.topology.index_map( mesh.topology.dim).num_ghosts x_dofs = mesh.geometry.dofmap.array.reshape(num_cells, 3) x = mesh.geometry.x dofmap = V.dofmap.list.array.reshape(num_cells, 3) dofmap_t = transpose_dofmap(V.dofmap.list, num_owned_cells) # Assemble with pure Numba function (two passes, first will include # JIT overhead) b0 = Function(V) for i in range(2): b = b0.x.array b[:] = 0.0 start = time.time() assemble_vector(b, (x_dofs, x), dofmap, num_owned_cells) end = time.time() print("Time (numba, pass {}): {}".format(i, end - start)) b0.vector.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) assert b0.vector.sum() == pytest.approx(1.0) # Assemble with pure Numba function using parallel loop (two passes, # first will include JIT overhead) btmp = Function(V) for i in range(2): b = btmp.x.array b[:] = 0.0 start = time.time() assemble_vector_parallel(b, x_dofs, x, dofmap_t.array, dofmap_t.offsets, num_owned_cells) end = time.time() print("Time (numba parallel, pass {}): {}".format(i, end - start)) btmp.vector.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) assert (btmp.vector - b0.vector).norm() == pytest.approx(0.0) # Test against generated code and general assembler v = ufl.TestFunction(V) L = inner(1.0, v) * dx Lf = form(L) start = time.time() b1 = dolfinx.fem.petsc.assemble_vector(Lf) end = time.time() print("Time (C++, pass 0):", end - start) with b1.localForm() as b_local: b_local.set(0.0) start = time.time() dolfinx.fem.petsc.assemble_vector(b1, Lf) end = time.time() print("Time (C++, pass 1):", end - start) b1.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) assert (b1 - b0.vector).norm() == pytest.approx(0.0) # Assemble using generated tabulate_tensor kernel and Numba assembler ffcxtype = "double _Complex" if np.issubdtype( PETSc.ScalarType, np.complexfloating) else "double" b3 = Function(V) ufcx_form, module, code = dolfinx.jit.ffcx_jit( mesh.comm, L, form_compiler_params={"scalar_type": ffcxtype}) nptype = "complex128" if np.issubdtype(PETSc.ScalarType, np.complexfloating) else "float64" # First 0 for "cell" integrals, second 0 for the first one, i.e. default domain kernel = getattr(ufcx_form.integrals(0)[0], f"tabulate_tensor_{nptype}") for i in range(2): b = b3.x.array b[:] = 0.0 start = time.time() assemble_vector_ufc(b, kernel, (x_dofs, x), dofmap, num_owned_cells) end = time.time() print("Time (numba/cffi, pass {}): {}".format(i, end - start)) b3.vector.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) assert (b3.vector - b0.vector).norm() == pytest.approx(0.0)
# Source amplitude if has_petsc_complex: A = 1 + 1j else: A = 1 # Test and trial function space V = FunctionSpace(mesh, ("Lagrange", deg)) # Define variational problem u = TrialFunction(V) v = TestFunction(V) f = Function(V) f.interpolate(lambda x: A * k0**2 * np.cos(k0 * x[0]) * np.cos(k0 * x[1])) a = inner(grad(u), grad(v)) * dx - k0**2 * inner(u, v) * dx L = inner(f, v) * dx # Compute solution u = Function(V) solve(a == L, u, []) # Save solution in XDMF format (to be viewed in Paraview, for example) with XDMFFile(MPI.COMM_WORLD, "plane_wave.xdmf", "w", encoding=XDMFFile.Encoding.HDF5) as file: file.write_mesh(mesh) file.write_function(u) """Calculate L2 and H1 errors of FEM solution and best approximation. This demonstrates the error bounds given in Ihlenburg. Pollution errors
def dg_injection_kernel(Vf, Vc, ncell): from firedrake import Tensor, AssembledVector, TestFunction, TrialFunction from firedrake.slate.slac import compile_expression if complex_mode: raise NotImplementedError("In complex mode we are waiting for Slate") macro_builder = MacroKernelBuilder(ScalarType_c, ncell) f = ufl.Coefficient(Vf) macro_builder.set_coefficients([f]) macro_builder.set_coordinates(Vf.mesh()) Vfe = create_element(Vf.ufl_element()) macro_quadrature_rule = make_quadrature( Vfe.cell, estimate_total_polynomial_degree(ufl.inner(f, f))) index_cache = {} parameters = default_parameters() integration_dim, entity_ids = lower_integral_type(Vfe.cell, "cell") macro_cfg = dict(interface=macro_builder, ufl_cell=Vf.ufl_cell(), integration_dim=integration_dim, entity_ids=entity_ids, index_cache=index_cache, quadrature_rule=macro_quadrature_rule, scalar_type=parameters["scalar_type"]) fexpr, = fem.compile_ufl(f, **macro_cfg) X = ufl.SpatialCoordinate(Vf.mesh()) C_a, = fem.compile_ufl(X, **macro_cfg) detJ = ufl_utils.preprocess_expression(abs( ufl.JacobianDeterminant(f.ufl_domain())), complex_mode=complex_mode) macro_detJ, = fem.compile_ufl(detJ, **macro_cfg) Vce = create_element(Vc.ufl_element()) coarse_builder = firedrake_interface.KernelBuilder("cell", "otherwise", 0, ScalarType_c) coarse_builder.set_coordinates(Vc.mesh()) argument_multiindices = (Vce.get_indices(), ) argument_multiindex, = argument_multiindices return_variable, = coarse_builder.set_arguments((ufl.TestFunction(Vc), ), argument_multiindices) integration_dim, entity_ids = lower_integral_type(Vce.cell, "cell") # Midpoint quadrature for jacobian on coarse cell. quadrature_rule = make_quadrature(Vce.cell, 0) coarse_cfg = dict(interface=coarse_builder, ufl_cell=Vc.ufl_cell(), integration_dim=integration_dim, entity_ids=entity_ids, index_cache=index_cache, quadrature_rule=quadrature_rule, scalar_type=parameters["scalar_type"]) X = ufl.SpatialCoordinate(Vc.mesh()) K = ufl_utils.preprocess_expression(ufl.JacobianInverse(Vc.mesh()), complex_mode=complex_mode) C_0, = fem.compile_ufl(X, **coarse_cfg) K, = fem.compile_ufl(K, **coarse_cfg) i = gem.Index() j = gem.Index() C_0 = gem.Indexed(C_0, (j, )) C_0 = gem.index_sum(C_0, quadrature_rule.point_set.indices) C_a = gem.Indexed(C_a, (j, )) X_a = gem.Sum(C_0, gem.Product(gem.Literal(-1), C_a)) K_ij = gem.Indexed(K, (i, j)) K_ij = gem.index_sum(K_ij, quadrature_rule.point_set.indices) X_a = gem.index_sum(gem.Product(K_ij, X_a), (j, )) C_0, = quadrature_rule.point_set.points C_0 = gem.Indexed(gem.Literal(C_0), (i, )) # fine quad points in coarse reference space. X_a = gem.Sum(C_0, gem.Product(gem.Literal(-1), X_a)) X_a = gem.ComponentTensor(X_a, (i, )) # Coarse basis function evaluated at fine quadrature points phi_c = fem.fiat_to_ufl( Vce.point_evaluation(0, X_a, (Vce.cell.get_dimension(), 0)), 0) tensor_indices = tuple(gem.Index(extent=d) for d in f.ufl_shape) phi_c = gem.Indexed(phi_c, argument_multiindex + tensor_indices) fexpr = gem.Indexed(fexpr, tensor_indices) quadrature_weight = macro_quadrature_rule.weight_expression expr = gem.Product(gem.IndexSum(gem.Product(phi_c, fexpr), tensor_indices), gem.Product(macro_detJ, quadrature_weight)) quadrature_indices = macro_builder.indices + macro_quadrature_rule.point_set.indices reps = spectral.Integrals([expr], quadrature_indices, argument_multiindices, parameters) assignments = spectral.flatten([(return_variable, reps)], index_cache) return_variables, expressions = zip(*assignments) expressions = impero_utils.preprocess_gem(expressions, **spectral.finalise_options) assignments = list(zip(return_variables, expressions)) impero_c = impero_utils.compile_gem(assignments, quadrature_indices + argument_multiindex, remove_zeros=True) index_names = [] def name_index(index, name): index_names.append((index, name)) if index in index_cache: for multiindex, suffix in zip(index_cache[index], string.ascii_lowercase): name_multiindex(multiindex, name + suffix) def name_multiindex(multiindex, name): if len(multiindex) == 1: name_index(multiindex[0], name) else: for i, index in enumerate(multiindex): name_index(index, name + str(i)) name_multiindex(quadrature_indices, 'ip') for multiindex, name in zip(argument_multiindices, ['j', 'k']): name_multiindex(multiindex, name) index_names.extend(zip(macro_builder.indices, ["entity"])) body = generate_coffee(impero_c, index_names, ScalarType) retarg = ast.Decl(ScalarType_c, ast.Symbol("R", rank=(Vce.space_dimension(), ))) local_tensor = coarse_builder.local_tensor local_tensor.init = ast.ArrayInit( numpy.zeros(Vce.space_dimension(), dtype=ScalarType)) body.children.insert(0, local_tensor) args = [retarg] + macro_builder.kernel_args + [ macro_builder.coordinates_arg, coarse_builder.coordinates_arg ] # Now we have the kernel that computes <f, phi_c>dx_c # So now we need to hit it with the inverse mass matrix on dx_c u = TrialFunction(Vc) v = TestFunction(Vc) expr = Tensor(ufl.inner(u, v) * ufl.dx).inv * AssembledVector( ufl.Coefficient(Vc)) Ainv, = compile_expression(expr, coffee=True) Ainv = Ainv.kinfo.kernel A = ast.Symbol(local_tensor.sym.symbol) R = ast.Symbol("R") body.children.append( ast.FunCall(Ainv.name, R, coarse_builder.coordinates_arg.sym, A)) from coffee.base import Node assert isinstance(Ainv._code, Node) return op2.Kernel(ast.Node([ Ainv._code, ast.FunDecl("void", "pyop2_kernel_injection_dg", args, body, pred=["static", "inline"]) ]), name="pyop2_kernel_injection_dg", cpp=True, include_dirs=Ainv._include_dirs, headers=Ainv._headers)
def define_forms(z, x_test, x): f = fenics.Constant(100.0) return ( ufl.inner(ufl.exp(z) * ufl.grad(x), ufl.grad(x_test)) * ufl.dx, f * x_test * ufl.dx, )
def test_matrix_assembly_block_nl(): """Test assembly of block matrices and vectors into (a) monolithic blocked structures, PETSc Nest structures, and monolithic structures in the nonlinear setting """ mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 4, 8) p0, p1 = 1, 2 P0 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p0) P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p1) V0 = dolfinx.fem.FunctionSpace(mesh, P0) V1 = dolfinx.fem.FunctionSpace(mesh, P1) def boundary(x): return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6) def initial_guess_u(x): return numpy.sin(x[0]) * numpy.sin(x[1]) def initial_guess_p(x): return -x[0]**2 - x[1]**3 def bc_value(x): return numpy.cos(x[0]) * numpy.cos(x[1]) facetdim = mesh.topology.dim - 1 bndry_facets = locate_entities_boundary(mesh, facetdim, boundary) u_bc = dolfinx.fem.Function(V1) u_bc.interpolate(bc_value) bdofs = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets) bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofs) # Define variational problem du, dp = ufl.TrialFunction(V0), ufl.TrialFunction(V1) u, p = dolfinx.fem.Function(V0), dolfinx.fem.Function(V1) v, q = ufl.TestFunction(V0), ufl.TestFunction(V1) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) f = 1.0 g = -3.0 F0 = inner(u, v) * dx + inner(p, v) * dx - inner(f, v) * dx F1 = inner(u, q) * dx + inner(p, q) * dx - inner(g, q) * dx a_block = [[derivative(F0, u, du), derivative(F0, p, dp)], [derivative(F1, u, du), derivative(F1, p, dp)]] L_block = [F0, F1] # Monolithic blocked x0 = dolfinx.fem.create_vector_block(L_block) dolfinx.cpp.la.scatter_local_vectors( x0, [u.vector.array_r, p.vector.array_r], [(u.function_space.dofmap.index_map, u.function_space.dofmap.index_map_bs), (p.function_space.dofmap.index_map, p.function_space.dofmap.index_map_bs)]) x0.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # Ghosts are updated inside assemble_vector_block A0 = dolfinx.fem.assemble_matrix_block(a_block, [bc]) b0 = dolfinx.fem.assemble_vector_block(L_block, a_block, [bc], x0=x0, scale=-1.0) A0.assemble() assert A0.getType() != "nest" Anorm0 = A0.norm() bnorm0 = b0.norm() # Nested (MatNest) x1 = dolfinx.fem.create_vector_nest(L_block) for x1_soln_pair in zip(x1.getNestSubVecs(), (u, p)): x1_sub, soln_sub = x1_soln_pair soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) soln_sub.vector.copy(result=x1_sub) x1_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) A1 = dolfinx.fem.assemble_matrix_nest(a_block, [bc]) b1 = dolfinx.fem.assemble_vector_nest(L_block) dolfinx.fem.apply_lifting_nest(b1, a_block, [bc], x1, scale=-1.0) for b_sub in b1.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = dolfinx.cpp.fem.bcs_rows( dolfinx.fem.assemble._create_cpp_form(L_block), [bc]) dolfinx.fem.set_bc_nest(b1, bcs0, x1, scale=-1.0) A1.assemble() assert A1.getType() == "nest" assert nest_matrix_norm(A1) == pytest.approx(Anorm0, 1.0e-12) assert b1.norm() == pytest.approx(bnorm0, 1.0e-12) # Monolithic version E = P0 * P1 W = dolfinx.fem.FunctionSpace(mesh, E) dU = ufl.TrialFunction(W) U = dolfinx.fem.Function(W) u0, u1 = ufl.split(U) v0, v1 = ufl.TestFunctions(W) U.sub(0).interpolate(initial_guess_u) U.sub(1).interpolate(initial_guess_p) F = inner(u0, v0) * dx + inner(u1, v0) * dx + inner(u0, v1) * dx + inner(u1, v1) * dx \ - inner(f, v0) * ufl.dx - inner(g, v1) * dx J = derivative(F, U, dU) bdofsW_V1 = dolfinx.fem.locate_dofs_topological((W.sub(1), V1), facetdim, bndry_facets) bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofsW_V1, W.sub(1)) A2 = dolfinx.fem.assemble_matrix(J, [bc]) A2.assemble() b2 = dolfinx.fem.assemble_vector(F) dolfinx.fem.apply_lifting(b2, [J], bcs=[[bc]], x0=[U.vector], scale=-1.0) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.set_bc(b2, [bc], x0=U.vector, scale=-1.0) assert A2.getType() != "nest" assert A2.norm() == pytest.approx(Anorm0, 1.0e-12) assert b2.norm() == pytest.approx(bnorm0, 1.0e-12)
def test_assembly_solve_block_nl(): """Solve a two-field nonlinear diffusion like problem with block matrix approaches and test that solution is the same. """ mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 12, 11) p = 1 P = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p) V0 = dolfinx.fem.FunctionSpace(mesh, P) V1 = V0.clone() def bc_val_0(x): return x[0]**2 + x[1]**2 def bc_val_1(x): return numpy.sin(x[0]) * numpy.cos(x[1]) def initial_guess_u(x): return numpy.sin(x[0]) * numpy.sin(x[1]) def initial_guess_p(x): return -x[0]**2 - x[1]**3 def boundary(x): return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6) facetdim = mesh.topology.dim - 1 bndry_facets = locate_entities_boundary(mesh, facetdim, boundary) u_bc0 = dolfinx.fem.Function(V0) u_bc0.interpolate(bc_val_0) u_bc1 = dolfinx.fem.Function(V1) u_bc1.interpolate(bc_val_1) bdofs0 = dolfinx.fem.locate_dofs_topological(V0, facetdim, bndry_facets) bdofs1 = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets) bcs = [ dolfinx.fem.dirichletbc.DirichletBC(u_bc0, bdofs0), dolfinx.fem.dirichletbc.DirichletBC(u_bc1, bdofs1) ] # Block and Nest variational problem u, p = dolfinx.fem.Function(V0), dolfinx.fem.Function(V1) du, dp = ufl.TrialFunction(V0), ufl.TrialFunction(V1) v, q = ufl.TestFunction(V0), ufl.TestFunction(V1) f = 1.0 g = -3.0 F = [ inner((u**2 + 1) * ufl.grad(u), ufl.grad(v)) * dx - inner(f, v) * dx, inner((p**2 + 1) * ufl.grad(p), ufl.grad(q)) * dx - inner(g, q) * dx ] J = [[derivative(F[0], u, du), derivative(F[0], p, dp)], [derivative(F[1], u, du), derivative(F[1], p, dp)]] def blocked_solve(): """Blocked version""" Jmat = dolfinx.fem.create_matrix_block(J) Fvec = dolfinx.fem.create_vector_block(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs) snes.setFunction(problem.F_block, Fvec) snes.setJacobian(problem.J_block, J=Jmat, P=None) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x = dolfinx.fem.create_vector_block(F) dolfinx.cpp.la.scatter_local_vectors( x, [u.vector.array_r, p.vector.array_r], [(u.function_space.dofmap.index_map, u.function_space.dofmap.index_map_bs), (p.function_space.dofmap.index_map, p.function_space.dofmap.index_map_bs)]) x.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) snes.solve(None, x) assert snes.getKSP().getConvergedReason() > 0 assert snes.getConvergedReason() > 0 return x.norm() def nested_solve(): """Nested version""" Jmat = dolfinx.fem.create_matrix_nest(J) assert Jmat.getType() == "nest" Fvec = dolfinx.fem.create_vector_nest(F) assert Fvec.getType() == "nest" snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) nested_IS = Jmat.getNestISs() snes.getKSP().setType("gmres") snes.getKSP().setTolerances(rtol=1e-12) snes.getKSP().getPC().setType("fieldsplit") snes.getKSP().getPC().setFieldSplitIS(["u", nested_IS[0][0]], ["p", nested_IS[1][1]]) ksp_u, ksp_p = snes.getKSP().getPC().getFieldSplitSubKSP() ksp_u.setType("preonly") ksp_u.getPC().setType('lu') ksp_p.setType("preonly") ksp_p.getPC().setType('lu') problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs) snes.setFunction(problem.F_nest, Fvec) snes.setJacobian(problem.J_nest, J=Jmat, P=None) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x = dolfinx.fem.create_vector_nest(F) assert x.getType() == "nest" for x_soln_pair in zip(x.getNestSubVecs(), (u, p)): x_sub, soln_sub = x_soln_pair soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) soln_sub.vector.copy(result=x_sub) x_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) snes.solve(None, x) assert snes.getKSP().getConvergedReason() > 0 assert snes.getConvergedReason() > 0 return x.norm() def monolithic_solve(): """Monolithic version""" E = P * P W = dolfinx.fem.FunctionSpace(mesh, E) U = dolfinx.fem.Function(W) dU = ufl.TrialFunction(W) u0, u1 = ufl.split(U) v0, v1 = ufl.TestFunctions(W) F = inner((u0**2 + 1) * ufl.grad(u0), ufl.grad(v0)) * dx \ + inner((u1**2 + 1) * ufl.grad(u1), ufl.grad(v1)) * dx \ - inner(f, v0) * ufl.dx - inner(g, v1) * dx J = derivative(F, U, dU) u0_bc = dolfinx.fem.Function(V0) u0_bc.interpolate(bc_val_0) u1_bc = dolfinx.fem.Function(V1) u1_bc.interpolate(bc_val_1) bdofsW0_V0 = dolfinx.fem.locate_dofs_topological( (W.sub(0), V0), facetdim, bndry_facets) bdofsW1_V1 = dolfinx.fem.locate_dofs_topological( (W.sub(1), V1), facetdim, bndry_facets) bcs = [ dolfinx.fem.dirichletbc.DirichletBC(u0_bc, bdofsW0_V0, W.sub(0)), dolfinx.fem.dirichletbc.DirichletBC(u1_bc, bdofsW1_V1, W.sub(1)) ] Jmat = dolfinx.fem.create_matrix(J) Fvec = dolfinx.fem.create_vector(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, U, bcs) snes.setFunction(problem.F_mono, Fvec) snes.setJacobian(problem.J_mono, J=Jmat, P=None) U.sub(0).interpolate(initial_guess_u) U.sub(1).interpolate(initial_guess_p) x = dolfinx.fem.create_vector(F) x.array = U.vector.array_r snes.solve(None, x) assert snes.getKSP().getConvergedReason() > 0 assert snes.getConvergedReason() > 0 return x.norm() norm0 = blocked_solve() norm1 = nested_solve() norm2 = monolithic_solve() assert norm1 == pytest.approx(norm0, 1.0e-12) assert norm2 == pytest.approx(norm0, 1.0e-12)
tol=1e-9, bounds=((0, 0.8),) * W.dim(), options={"gtol": 1e-10, "ftol": 0, "disp": True, "maxiter": 50}, ) # Define the expressions of the analytical solution alpha = 1e-6 x = ufl.SpatialCoordinate(mesh) w = ufl.sin(ufl.pi * x[0]) * ufl.sin(ufl.pi * x[1]) f_analytic = 1 / (1 + alpha * 4 * ufl.pi ** 4) * w u_analytic = 1 / (2 * ufl.pi ** 2) * f_analytic f_opt = from_numpy(res.x, fn.Function(W)) u = fn.Function(V) v = fn.TestFunction(V) F = (ufl.inner(ufl.grad(u), ufl.grad(v)) - f_opt * v) * ufl.dx bc = fn.DirichletBC(V, 0.0, "on_boundary") fn.solve(F == 0, u, bc) print(f"norm of f_opt is {fn.norm(f_opt)}") # interpolatation of UFL forms does not work in FEniCS, hence projection CG3 = fn.FunctionSpace(mesh, "CG", 3) control_error = fn.errornorm(fn.project(f_analytic, CG3), f_opt) state_error = fn.errornorm(fn.project(u_analytic, CG3), u) print("h(min): %e." % mesh.hmin()) print("Error in state: %e." % state_error) print("Error in control: %e." % control_error) # Write solutions to XDMFFile, can be visualized with paraview # First time step is approximated solution, second timestep is analytic # solution
for p in args.form_compiler_parameters: k, v = p.split("=") form_compiler_parameters[k] = v # Plane wave omega2 = 15**2 + 11**2 def u_exact(x): return math.cos(-15 * x[0] + 12 * x[1]) # UFL form element = ufl.FiniteElement("P", ufl.triangle, 3) u, v = ufl.TrialFunction(element), ufl.TestFunction(element) a = (ufl.inner(ufl.grad(u), ufl.grad(v)) - omega2 * ufl.dot(u, v)) * ufl.dx # Build mesh mesh = build_unit_square_mesh(args.n, args.n) tdim = mesh.reference_cell.get_dimension() print('Number cells: {}'.format(mesh.num_entities(tdim))) # Build dofmap dofmap = build_dofmap(element, mesh) print('Number dofs: {}'.format(dofmap.dim)) # Run and time assembly t = -timeit.default_timer() A = assemble(dofmap, a, dtype=numpy.float32) t += timeit.default_timer() print('Assembly time a: {}'.format(t))
def run_scalar_test(mesh, V, degree): """ Manufactured Poisson problem, solving u = x[1]**p, where p is the degree of the Lagrange function space. """ u, v = TrialFunction(V), TestFunction(V) a = inner(grad(u), grad(v)) * dx # Get quadrature degree for bilinear form integrand (ignores effect of non-affine map) a = inner(grad(u), grad(v)) * dx(metadata={"quadrature_degree": -1}) a.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(a) # Source term x = SpatialCoordinate(mesh) u_exact = x[1]**degree f = -div(grad(u_exact)) # Set quadrature degree for linear form integrand (ignores effect of non-affine map) L = inner(f, v) * dx(metadata={"quadrature_degree": -1}) L.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(L) L = fem.Form(L) u_bc = Function(V) u_bc.interpolate(lambda x: x[1]**degree) # Create Dirichlet boundary condition mesh.topology.create_connectivity_all() facetdim = mesh.topology.dim - 1 bndry_facets = np.where( np.array(cpp.mesh.compute_boundary_facets(mesh.topology)) == 1)[0] bdofs = locate_dofs_topological(V, facetdim, bndry_facets) bc = DirichletBC(u_bc, bdofs) b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) a = fem.Form(a) A = assemble_matrix(a, [bc]) A.assemble() # Create LU linear solver solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A) uh = Function(V) solver.solve(b, uh.vector) uh.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) M = (u_exact - uh)**2 * dx M = fem.Form(M) error = mesh.mpi_comm().allreduce(assemble_scalar(M), op=MPI.SUM) assert np.absolute(error) < 1.0e-14
dfdc = diff(f, c) # The first line declares that ``c`` is a variable that some function can # be differentiated with respect to. The next line is the function # :math:`f` defined in the problem statement, and the third line performs # the differentiation of ``f`` with respect to the variable ``c``. # # It is convenient to introduce an expression for :math:`\mu_{n+\theta}`:: # mu_(n+theta) mu_mid = (1.0 - theta) * mu0 + theta * mu # which is then used in the definition of the variational forms:: # Weak statement of the equations L0 = inner(c, q) * dx - inner(c0, q) * dx + dt * inner(grad(mu_mid), grad(q)) * dx L1 = inner(mu, v) * dx - inner(dfdc, v) * dx - lmbda * inner(grad(c), grad(v)) * dx L = L0 + L1 # This is a statement of the time-discrete equations presented as part of # the problem statement, using UFL syntax. The linear forms for the two # equations can be summed into one form ``L``, and then the directional # derivative of ``L`` can be computed to form the bilinear form which # represents the Jacobian matrix:: # Compute directional derivative about u in the direction of du (Jacobian) a = derivative(L, u, du) # .. index::
def amg_solve(N, method): # Elasticity parameters E = 1.0e9 nu = 0.3 mu = E / (2.0 * (1.0 + nu)) lmbda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu)) # Stress computation def sigma(v): return 2.0 * mu * sym(grad(v)) + lmbda * tr(sym( grad(v))) * Identity(2) # Define problem mesh = UnitSquareMesh(MPI.COMM_WORLD, N, N) V = VectorFunctionSpace(mesh, 'Lagrange', 1) bc0 = Function(V) with bc0.vector.localForm() as bc_local: bc_local.set(0.0) def boundary(x): return np.full(x.shape[1], True) facetdim = mesh.topology.dim - 1 bndry_facets = locate_entities_geometrical(mesh, facetdim, boundary, boundary_only=True) bdofs = locate_dofs_topological(V.sub(0), V, facetdim, bndry_facets) bc = DirichletBC(bc0, bdofs, V.sub(0)) u = TrialFunction(V) v = TestFunction(V) # Forms a, L = inner(sigma(u), grad(v)) * dx, dot(ufl.as_vector((1.0, 1.0)), v) * dx # Assemble linear algebra objects A = assemble_matrix(a, [bc]) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) # Create solution function u = Function(V) # Create near null space basis and orthonormalize null_space = build_nullspace(V, u.vector) # Attached near-null space to matrix A.set_near_nullspace(null_space) # Test that basis is orthonormal assert null_space.is_orthonormal() # Create PETSC smoothed aggregation AMG preconditioner, and # create CG solver solver = PETSc.KSP().create(mesh.mpi_comm) solver.setType("cg") # Set matrix operator solver.setOperators(A) # Compute solution and return number of iterations return solver.solve(b, u.vector)
def test_assembly_ds_domains(mesh): V = dolfin.FunctionSpace(mesh, ("CG", 1)) u, v = dolfin.TrialFunction(V), dolfin.TestFunction(V) marker = dolfin.MeshFunction("size_t", mesh, mesh.topology.dim - 1, 0) def bottom(x): return numpy.isclose(x[:, 1], 0.0) def top(x): return numpy.isclose(x[:, 1], 1.0) def left(x): return numpy.isclose(x[:, 0], 0.0) def right(x): return numpy.isclose(x[:, 0], 1.0) marker.mark(bottom, 111) marker.mark(top, 222) marker.mark(left, 333) marker.mark(right, 444) ds = ufl.Measure('ds', subdomain_data=marker, domain=mesh) w = dolfin.Function(V) with w.vector.localForm() as w_local: w_local.set(0.5) # # Assemble matrix # a = w * ufl.inner(u, v) * (ds(111) + ds(222) + ds(333) + ds(444)) A = dolfin.fem.assemble_matrix(a) A.assemble() norm1 = A.norm() a2 = w * ufl.inner(u, v) * ds A2 = dolfin.fem.assemble_matrix(a2) A2.assemble() norm2 = A2.norm() assert norm1 == pytest.approx(norm2, 1.0e-12) # # Assemble vector # L = ufl.inner(w, v) * (ds(111) + ds(222) + ds(333) + ds(444)) b = dolfin.fem.assemble_vector(L) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) L2 = ufl.inner(w, v) * ds b2 = dolfin.fem.assemble_vector(L2) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) assert b.norm() == pytest.approx(b2.norm(), 1.0e-12) # # Assemble scalar # L = w * (ds(111) + ds(222) + ds(333) + ds(444)) s = dolfin.fem.assemble_scalar(L) s = dolfin.MPI.sum(mesh.mpi_comm(), s) L2 = w * ds s2 = dolfin.fem.assemble_scalar(L2) s2 = dolfin.MPI.sum(mesh.mpi_comm(), s2) assert (s == pytest.approx(s2, 1.0e-12) and 2.0 == pytest.approx(s, 1.0e-12))
def xtest_assembly_solve_block(): """Solve a two-field mass-matrix like problem with block matrix approaches and test that solution is the same. """ mesh = dolfin.generation.UnitSquareMesh(dolfin.MPI.comm_world, 32, 31) p0, p1 = 1, 1 P0 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p0) P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p1) V0 = dolfin.function.functionspace.FunctionSpace(mesh, P0) V1 = dolfin.function.functionspace.FunctionSpace(mesh, P1) def boundary(x): return numpy.logical_or(x[:, 0] < 1.0e-6, x[:, 0] > 1.0 - 1.0e-6) u_bc0 = dolfin.function.constant.Constant(50.0) u_bc1 = dolfin.function.constant.Constant(20.0) bc0 = dolfin.fem.dirichletbc.DirichletBC(V0, u_bc0, boundary) bc1 = dolfin.fem.dirichletbc.DirichletBC(V1, u_bc1, boundary) # Variational problem u, p = dolfin.function.argument.TrialFunction( V0), dolfin.function.argument.TrialFunction(V1) v, q = dolfin.function.argument.TestFunction( V0), dolfin.function.argument.TestFunction(V1) f = dolfin.function.constant.Constant(1.0) g = dolfin.function.constant.Constant(-3.0) zero = dolfin.function.constant.Constant(0.0) a00 = inner(u, v) * dx a01 = zero * inner(p, v) * dx a10 = zero * inner(u, q) * dx a11 = inner(p, q) * dx L0 = inner(f, v) * dx L1 = inner(g, q) * dx def monitor(ksp, its, rnorm): pass # print("Norm:", its, rnorm) # Create assembler assembler = dolfin.fem.Assembler([[a00, a01], [a10, a11]], [L0, L1], [bc0, bc1]) # Monolithic blocked A0, b0 = assembler.assemble( mat_type=dolfin.cpp.fem.Assembler.BlockType.monolithic) A0norm = A0.mat().norm() b0norm = b0.vec().norm() x0 = A0.mat().createVecLeft() ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setOperators(A0.mat()) ksp.setTolerances(rtol=1.0e-12) ksp.setMonitor(monitor) ksp.setType('cg') ksp.setFromOptions() # ksp.view() ksp.solve(b0.vec(), x0) x0norm = x0.norm() # Nested (MatNest) A1, b1 = assembler.assemble( mat_type=dolfin.cpp.fem.Assembler.BlockType.nested) b1norm = b1.vec().norm() assert b1norm == pytest.approx(b0norm, 1.0e-12) x1 = dolfin.la.PETScVector(b1) ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setMonitor(monitor) ksp.setTolerances(rtol=1.0e-12) ksp.setOperators(A1.mat()) ksp.setType('cg') ksp.setFromOptions() # ksp.view() ksp.solve(b1.vec(), x1.vec()) x1norm = x1.vec().norm() assert x1norm == pytest.approx(x0norm, rel=1.0e-10) # Monolithic version E = P0 * P1 W = dolfin.function.functionspace.FunctionSpace(mesh, E) u0, u1 = dolfin.function.argument.TrialFunctions(W) v0, v1 = dolfin.function.argument.TestFunctions(W) a = inner(u0, v0) * dx + inner(u1, v1) * dx L = inner(f, v0) * ufl.dx + inner(g, v1) * dx u_bc = dolfin.function.constant.Constant((50.0, 20.0)) bc = dolfin.fem.dirichletbc.DirichletBC(W, u_bc, boundary) assembler = dolfin.fem.assembler.Assembler([[a]], [L], [bc]) A2, b2 = assembler.assemble( mat_type=dolfin.cpp.fem.Assembler.BlockType.monolithic) A2norm = A2.mat().norm() b2norm = b2.vec().norm() assert A2norm == pytest.approx(A0norm, 1.0e-12) assert b2norm == pytest.approx(b0norm, 1.0e-12) x2 = dolfin.cpp.la.PETScVector(b2) ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setMonitor(monitor) ksp.setOperators(A2.mat()) ksp.setType('cg') ksp.setTolerances(rtol=1.0e-9) ksp.setFromOptions() # ksp.view() ksp.solve(b2.vec(), x2.vec()) x2norm = x2.vec().norm() assert x2norm == pytest.approx(x0norm, 1.0e-10) # Old assembler (reference) A3, b3 = dolfin.fem.assembling.assemble_system(a, L, [bc]) x3 = dolfin.cpp.la.PETScVector(b3) ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setMonitor(monitor) ksp.setOperators(A3.mat()) ksp.setType('cg') ksp.setTolerances(rtol=1.0e-9) ksp.setFromOptions() # ksp.view() ksp.solve(b3.vec(), x3.vec()) x3norm = x3.vec().norm() assert x3norm == pytest.approx(x0norm, 1.0e-10)
def test_assembly_dx_domains(mesh): V = dolfin.FunctionSpace(mesh, ("CG", 1)) u, v = dolfin.TrialFunction(V), dolfin.TestFunction(V) marker = dolfin.MeshFunction("size_t", mesh, mesh.topology.dim, 0) values = marker.values # Mark first, second and all other # Their union is the whole domain values[0] = 111 values[1] = 222 values[2:] = 333 dx = ufl.Measure('dx', subdomain_data=marker, domain=mesh) w = dolfin.Function(V) with w.vector.localForm() as w_local: w_local.set(0.5) # # Assemble matrix # a = w * ufl.inner(u, v) * (dx(111) + dx(222) + dx(333)) A = dolfin.fem.assemble_matrix(a) A.assemble() norm1 = A.norm() a2 = w * ufl.inner(u, v) * dx A2 = dolfin.fem.assemble_matrix(a2) A2.assemble() norm2 = A2.norm() assert norm1 == pytest.approx(norm2, 1.0e-12) # # Assemble vector # L = ufl.inner(w, v) * (dx(111) + dx(222) + dx(333)) b = dolfin.fem.assemble_vector(L) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) L2 = ufl.inner(w, v) * dx b2 = dolfin.fem.assemble_vector(L2) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) assert b.norm() == pytest.approx(b2.norm(), 1.0e-12) # # Assemble scalar # L = w * (dx(111) + dx(222) + dx(333)) s = dolfin.fem.assemble_scalar(L) s = dolfin.MPI.sum(mesh.mpi_comm(), s) L2 = w * dx s2 = dolfin.fem.assemble_scalar(L2) s2 = dolfin.MPI.sum(mesh.mpi_comm(), s2) assert (s == pytest.approx(s2, 1.0e-12) and 0.5 == pytest.approx(s, 1.0e-12))
def test_eigen_assembly(tempdir): # noqa: F811 """Compare assembly into scipy.CSR matrix with PETSc assembly""" def compile_eigen_csr_assembler_module(): cpp_code_header = f""" <% setup_pybind11(cfg) cfg['include_dirs'] = {dolfinx_pc["include_dirs"] + [petsc4py.get_include()] + [str(pybind_inc())]} cfg['compiler_args'] = {["-D" + dm for dm in dolfinx_pc["define_macros"]]} cfg['compiler_args'] = ['-std=c++17'] cfg['libraries'] = {dolfinx_pc["libraries"]} cfg['library_dirs'] = {dolfinx_pc["library_dirs"]} %> """ cpp_code = """ #include <pybind11/pybind11.h> #include <pybind11/eigen.h> #include <pybind11/stl.h> #include <vector> #include <Eigen/Sparse> #include <petscsys.h> #include <dolfinx/fem/assembler.h> #include <dolfinx/fem/DirichletBC.h> #include <dolfinx/fem/Form.h> template<typename T> Eigen::SparseMatrix<T, Eigen::RowMajor> assemble_csr(const dolfinx::fem::Form<T>& a, const std::vector<std::shared_ptr<const dolfinx::fem::DirichletBC<T>>>& bcs) { std::vector<Eigen::Triplet<T>> triplets; const auto mat_add = [&triplets](std::int32_t nrow, const std::int32_t* rows, std::int32_t ncol, const std::int32_t* cols, const T* v) { for (int i = 0; i < nrow; ++i) for (int j = 0; j < ncol; ++j) triplets.emplace_back(rows[i], cols[j], v[i * ncol + j]); return 0; }; dolfinx::fem::assemble_matrix<T>(mat_add, a, bcs); auto map0 = a.function_space(0)->dofmap()->index_map; auto map1 = a.function_space(1)->dofmap()->index_map; Eigen::SparseMatrix<T, Eigen::RowMajor> mat( map0->block_size() * (map0->size_local() + map0->num_ghosts()), map1->block_size() * (map1->size_local() + map1->num_ghosts())); mat.setFromTriplets(triplets.begin(), triplets.end()); return mat; } PYBIND11_MODULE(eigen_csr, m) { m.def("assemble_matrix", &assemble_csr<PetscScalar>); } """ path = pathlib.Path(tempdir) open(pathlib.Path(tempdir, "eigen_csr.cpp"), "w").write(cpp_code + cpp_code_header) rel_path = path.relative_to(pathlib.Path(__file__).parent) p = str(rel_path).replace("/", ".") + ".eigen_csr" return cppimport.imp(p) def assemble_csr_matrix(a, bcs): """Assemble bilinear form into an SciPy CSR matrix, in serial.""" module = compile_eigen_csr_assembler_module() _a = dolfinx.fem.assemble._create_cpp_form(a) A = module.assemble_matrix(_a, bcs) if _a.function_spaces[0].id == _a.function_spaces[1].id: for bc in bcs: if _a.function_spaces[0].contains(bc.function_space): bc_dofs = bc.dof_indices[:, 0] A[bc_dofs, bc_dofs] = 1.0 return A mesh = UnitSquareMesh(MPI.COMM_SELF, 12, 12) Q = dolfinx.FunctionSpace(mesh, ("Lagrange", 1)) u = ufl.TrialFunction(Q) v = ufl.TestFunction(Q) a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx bdofsQ = dolfinx.fem.locate_dofs_geometrical( Q, lambda x: numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6)) u_bc = dolfinx.function.Function(Q) with u_bc.vector.localForm() as u_local: u_local.set(1.0) bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofsQ) A1 = dolfinx.fem.assemble_matrix(a, [bc]) A1.assemble() A2 = assemble_csr_matrix(a, [bc]) assert numpy.isclose(A1.norm(), scipy.sparse.linalg.norm(A2))
def _butcher_scheme_generator(a, b, c, time, solution, rhs_form): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. time (_Constant_) A Constant holding the time at the start of the time step solution (_Function_) The prognostic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation """ a = _check_abc(a, b, c) size = a.shape[0] DX = _check_form(rhs_form) # Get test function arguments = rhs_form.arguments() coefficients = rhs_form.coefficients() v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [Function(solution.function_space(), name="k_%d"%i) for i in range(size)] jacobian_indices = [] # Create the stage forms y_ = solution time_ = time time_dep_expressions = _time_dependent_expressions(rhs_form, time) zero_ = ufl.zero(*y_.ufl_shape) for i, ki in enumerate(k): # Check whether the stage is explicit explicit = a[i,i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], zero_) time = time_ + dt*c[i] replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions, time_, dt, c[i]) replace_dict[y_] = evalargs replace_dict[time_] = time stage_form = ufl.replace(rhs_form, replace_dict) if explicit: stage_forms = [stage_form] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form -= ufl.inner(ki, v)*DX stage_forms = [stage_form, derivative(stage_form, ki)] jacobian_indices.append(0) ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) # Only one last stage if len(b.shape) == 1: last_stage = Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \ zip(b, k)], zero_), v)*DX) else: # FIXME: Add support for adaptivity in RKSolver and MultiStageScheme last_stage = [Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \ zip(b[0,:], k)], zero_), v)*DX), Form(ufl.inner(y_+sum([dt*float(bi)*ki for bi, ki in \ zip(b[1,:], k)], zero_), v)*DX)] # Create the Function holding the solution at end of time step #k.append(solution.copy()) # Generate human form of MultiStageScheme human_form = [] for i in range(size): kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \ "%s*"% a[i,j], j) \ for j in range(size) if a[i,j] != 0) if c[i] in [0.0, 1.0]: cih = " + h" if c[i] == 1.0 else "" else: cih = " + %s*h" % c[i] if len(kterm) == 0: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {"i": i, "cih": cih}) else: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \ {"i": i, "cih": cih, "kterm": kterm}) parentheses = "(%s)" if np.sum(b>0) > 1 else "%s" human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(\ "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \ for i in range(size) if b[i] > 0))) human_form = "\n".join(human_form) return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \ k, dt, human_form, None
ds = ufl.Measure("ds", subdomain_data=mf) # Elastic stiffness tensor and Poisson ratio E, nu = 1.0, 1.0 / 3.0 def sigma_u(u): """Consitutive relation for stress-strain. Assuming plane-stress in XY""" eps = 0.5 * (ufl.grad(u) + ufl.grad(u).T) sigma = E / (1. - nu**2) * ( (1. - nu) * eps + nu * ufl.Identity(2) * ufl.tr(eps)) return sigma a00 = ufl.inner(sigma, tau) * ufl.dx a10 = -ufl.inner(sigma, ufl.grad(v)) * ufl.dx a01 = -ufl.inner(sigma_u(u), tau) * ufl.dx f = ufl.as_vector([0.0, 1.0 / 16]) b1 = -ufl.inner(f, v) * ds(1) # JIT compile individual blocks tabulation kernels ufc_form00 = dolfin.jit.ffc_jit(a00) kernel00 = ufc_form00.create_cell_integral(-1).tabulate_tensor ufc_form01 = dolfin.jit.ffc_jit(a01) kernel01 = ufc_form01.create_cell_integral(-1).tabulate_tensor ufc_form10 = dolfin.jit.ffc_jit(a10) kernel10 = ufc_form10.create_cell_integral(-1).tabulate_tensor
def _butcher_scheme_generator_adm(a, b, c, time, solution, rhs_form, adj): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. time (_Constant_) A Constant holding the time at the start of the time step solution (_Function_) The prognostic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation adj (_Function_) The derivative of the functional with respect to y_n+1 """ a = _check_abc(a, b, c) size = a.shape[0] DX = _check_form(rhs_form) # Get test function arguments = rhs_form.arguments() coefficients = rhs_form.coefficients() v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [Function(solution.function_space(), name="k_%d"%i) for i in range(size)] kbar = [Function(solution.function_space(), name="kbar_%d"%i) \ for i in range(size)] # Create the stage forms y_ = solution time_ = time time_dep_expressions = _time_dependent_expressions(rhs_form, time) zero_ = ufl.zero(*y_.ufl_shape) forward_forms = [] stage_solutions = [] jacobian_indices = [] # The recomputation of the forward run: for i, ki in enumerate(k): # Check whether the stage is explicit explicit = a[i,i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], zero_) time = time_ + dt*c[i] replace_dict = _replace_dict_time_dependent_expression(\ time_dep_expressions, time_, dt, c[i]) replace_dict[y_] = evalargs replace_dict[time_] = time stage_form = ufl.replace(rhs_form, replace_dict) forward_forms.append(stage_form) if explicit: stage_forms = [stage_form] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_implicit = stage_form - ufl.inner(ki, v)*DX stage_forms = [stage_form_implicit, derivative(\ stage_form_implicit, ki)] jacobian_indices.append(0) ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) stage_solutions.append(ki) for i, kbari in reversed(list(enumerate(kbar))): # Check whether the stage is explicit explicit = a[i,i] == 0 # And now the adjoint linearisation: stage_form_adm = ufl.inner(dt * b[i] * adj, v)*DX + sum(\ [dt * float(a[j,i]) * safe_action(safe_adjoint(derivative(\ forward_forms[j], y_)), kbar[j]) for j in range(i, size)]) if explicit: stage_forms_adm = [stage_form_adm] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_adm -= ufl.inner(kbar[i], v)*DX stage_forms_adm = [stage_form_adm, derivative(stage_form_adm, kbari)] jacobian_indices.append(1) ufl_stage_forms.append(stage_forms_adm) dolfin_stage_forms.append([Form(form) for form in stage_forms_adm]) stage_solutions.append(kbari) # Only one last stage if len(b.shape) == 1: last_stage = Form(ufl.inner(adj, v)*DX + sum(\ [safe_action(safe_adjoint(derivative(forward_forms[i], y_)), kbar[i]) \ for i in range(size)])) else: raise Exception("Not sure what to do here") human_form = "unimplemented" return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage,\ stage_solutions, dt, human_form, adj
mu = E / (2.0 * (1.0 + nu)) lmbda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu)) def sigma(v): return 2.0 * mu * sym(grad(v)) + lmbda * tr(sym(grad(v))) * Identity( len(v)) # Create function space V = VectorFunctionSpace(mesh, ("Lagrange", 1)) # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = inner(sigma(u), grad(v)) * dx L = inner(f, v) * dx u0 = Function(V) with u0.vector.localForm() as bc_local: bc_local.set(0.0) # Set up boundary condition on inner surface bc = DirichletBC(V, u0, boundary) # Assemble system, applying boundary conditions and preserving symmetry) A = assemble_matrix(a, [bc]) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], [[bc]])
def demo_periodic3D(celltype: CellType): # Create mesh and finite element if celltype == CellType.tetrahedron: # Tet setup N = 10 mesh = create_unit_cube(MPI.COMM_WORLD, N, N, N) V = fem.VectorFunctionSpace(mesh, ("CG", 1)) else: # Hex setup N = 10 mesh = create_unit_cube(MPI.COMM_WORLD, N, N, N, CellType.hexahedron) V = fem.VectorFunctionSpace(mesh, ("CG", 2)) def dirichletboundary(x: NDArray[np.float64]) -> NDArray[np.bool_]: return np.logical_or( np.logical_or(np.isclose(x[1], 0), np.isclose(x[1], 1)), np.logical_or(np.isclose(x[2], 0), np.isclose(x[2], 1))) # Create Dirichlet boundary condition zero = PETSc.ScalarType([0, 0, 0]) geometrical_dofs = fem.locate_dofs_geometrical(V, dirichletboundary) bc = fem.dirichletbc(zero, geometrical_dofs, V) bcs = [bc] def PeriodicBoundary(x): return np.isclose(x[0], 1) facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, PeriodicBoundary) arg_sort = np.argsort(facets) mt = meshtags(mesh, mesh.topology.dim - 1, facets[arg_sort], np.full(len(facets), 2, dtype=np.int32)) def periodic_relation(x): out_x = np.zeros(x.shape) out_x[0] = 1 - x[0] out_x[1] = x[1] out_x[2] = x[2] return out_x with Timer("~~Periodic: Compute mpc condition"): mpc = dolfinx_mpc.MultiPointConstraint(V) mpc.create_periodic_constraint_topological(V.sub(0), mt, 2, periodic_relation, bcs, 1) mpc.finalize() # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = inner(grad(u), grad(v)) * dx x = SpatialCoordinate(mesh) dx_ = x[0] - 0.9 dy_ = x[1] - 0.5 dz_ = x[2] - 0.1 f = as_vector((x[0] * sin(5.0 * pi * x[1]) + 1.0 * exp(-(dx_ * dx_ + dy_ * dy_ + dz_ * dz_) / 0.02), 0.1 * dx_ * dz_, 0.1 * dx_ * dy_)) rhs = inner(f, v) * dx petsc_options: Dict[str, Union[str, float, int]] if complex_mode: rtol = 1e-16 petsc_options = {"ksp_type": "preonly", "pc_type": "lu"} else: rtol = 1e-8 petsc_options = { "ksp_type": "cg", "ksp_rtol": rtol, "pc_type": "hypre", "pc_hypre_typ": "boomeramg", "pc_hypre_boomeramg_max_iter": 1, "pc_hypre_boomeramg_cycle_type": "v", "pc_hypre_boomeramg_print_statistics": 1 } problem = LinearProblem(a, rhs, mpc, bcs, petsc_options=petsc_options) u_h = problem.solve() # --------------------VERIFICATION------------------------- print("----Verification----") u_ = fem.Function(V) u_.x.array[:] = 0 org_problem = fem.petsc.LinearProblem(a, rhs, u=u_, bcs=bcs, petsc_options=petsc_options) with Timer("~Periodic: Unconstrained solve"): org_problem.solve() it = org_problem.solver.getIterationNumber() print(f"Unconstrained solver iterations: {it}") # Write solutions to file ext = "tet" if celltype == CellType.tetrahedron else "hex" u_.name = "u_" + ext + "_unconstrained" # NOTE: Workaround as tabulate dof coordinates does not like extra ghosts u_out = fem.Function(V) old_local = u_out.x.map.size_local * u_out.x.bs old_ghosts = u_out.x.map.num_ghosts * u_out.x.bs mpc_local = u_h.x.map.size_local * u_h.x.bs assert (old_local == mpc_local) u_out.x.array[:old_local + old_ghosts] = u_h.x.array[:mpc_local + old_ghosts] u_out.name = "u_" + ext fname = f"results/demo_periodic3d_{ext}.bp" out_periodic = VTXWriter(MPI.COMM_WORLD, fname, u_out) out_periodic.write(0) out_periodic.close() root = 0 with Timer("~Demo: Verification"): dolfinx_mpc.utils.compare_mpc_lhs(org_problem.A, problem.A, mpc, root=root) dolfinx_mpc.utils.compare_mpc_rhs(org_problem.b, problem.b, mpc, root=root) # Gather LHS, RHS and solution on one process A_csr = dolfinx_mpc.utils.gather_PETScMatrix(org_problem.A, root=root) K = dolfinx_mpc.utils.gather_transformation_matrix(mpc, root=root) L_np = dolfinx_mpc.utils.gather_PETScVector(org_problem.b, root=root) u_mpc = dolfinx_mpc.utils.gather_PETScVector(u_h.vector, root=root) if MPI.COMM_WORLD.rank == root: KTAK = K.T * A_csr * K reduced_L = K.T @ L_np # Solve linear system d = scipy.sparse.linalg.spsolve(KTAK, reduced_L) # Back substitution to full solution vector uh_numpy = K @ d assert np.allclose(uh_numpy, u_mpc, rtol=rtol)
# Second invariant rJ2_ = rJ2(D_) # Regularisation mu_effective = mu + tau_zero * 1.0 / (2. * (rJ2_ + tau_zero_regularisation)) # Cauchy stress T = -p * ufl.Identity(2) + 2.0 * mu_effective * D_ return T # Helper n_vec = ufl.FacetNormal(mesh) # outward unit normal vector t_vec = ufl.as_vector([n_vec[1], -n_vec[0]]) # tangent 2D # Weak form (as one-form) f = ufl.inner(δv, rho * vt + rho * ufl.grad(v) * v) * dx \ + ufl.inner(ufl.grad(δv), T(v, p)) * dx \ + ufl.inner(δp, ufl.div(v)) * dx \ - ufl.inner(δv, n_vec) * n * ds(ring_inner) \ - ufl.inner(δv, t_vec) * t * ds(ring_inner) \ - δn * (v_n - ufl.inner(v, n_vec)) * ds(ring_inner) \ - δt * (v_t - ufl.inner(v, t_vec)) * ds(ring_inner) # Overall form (as one-form) F = odeint.discretise_in_time(f) # Overall form (as list of forms) F = dolfiny.function.extract_blocks(F, δm) # Create output xdmf file -- open in Paraview with Xdmf3ReaderT ofile = dolfiny.io.XDMFFile(comm, f"{name}.xdmf", "w") # Write mesh, meshtags
def run_dg_test(mesh, V, degree): """ Manufactured Poisson problem, solving u = x[component]**n, where n is the degree of the Lagrange function space. """ u, v = TrialFunction(V), TestFunction(V) # Exact solution x = SpatialCoordinate(mesh) u_exact = x[1]**degree # Coefficient k = Function(V) k.vector.set(2.0) k.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # Source term f = -div(k * grad(u_exact)) # Mesh normals and element size n = FacetNormal(mesh) h = CellDiameter(mesh) h_avg = (h("+") + h("-")) / 2.0 # Penalty parameter alpha = 32 dx_ = dx(metadata={"quadrature_degree": -1}) ds_ = ds(metadata={"quadrature_degree": -1}) dS_ = dS(metadata={"quadrature_degree": -1}) a = inner(k * grad(u), grad(v)) * dx_ \ - k("+") * inner(avg(grad(u)), jump(v, n)) * dS_ \ - k("+") * inner(jump(u, n), avg(grad(v))) * dS_ \ + k("+") * (alpha / h_avg) * inner(jump(u, n), jump(v, n)) * dS_ \ - inner(k * grad(u), v * n) * ds_ \ - inner(u * n, k * grad(v)) * ds_ \ + (alpha / h) * inner(k * u, v) * ds_ L = inner(f, v) * dx_ - inner(k * u_exact * n, grad(v)) * ds_ \ + (alpha / h) * inner(k * u_exact, v) * ds_ for integral in a.integrals(): integral.metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree( a) for integral in L.integrals(): integral.metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree( L) b = assemble_vector(L) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) A = assemble_matrix(a, []) A.assemble() # Create LU linear solver solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A) # Solve uh = Function(V) solver.solve(b, uh.vector) uh.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # Calculate error M = (u_exact - uh)**2 * dx M = fem.Form(M) error = mesh.mpi_comm().allreduce(assemble_scalar(M), op=MPI.SUM) assert np.absolute(error) < 1.0e-14
def _butcher_scheme_generator(a, b, c, solution, rhs_form): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. solution (_Function_) The prognastic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation """ if not (isinstance(a, np.ndarray) and (len(a) == 1 or \ (len(a.shape)==2 and a.shape[0] == a.shape[1]))): raise TypeError("Expected an m x m numpy array as the first argument") if not (isinstance(b, np.ndarray) and len(b.shape) in [1,2]): raise TypeError("Expected a 1 or 2 dimensional numpy array as the second argument") if not (isinstance(c, np.ndarray) and len(c.shape) == 1): raise TypeError("Expected a 1 dimensional numpy array as the third argument") # Make sure a is a "matrix" if len(a) == 1: a.shape = (1, 1) # Get size of system size = a.shape[0] # If b is a matrix we expect it to have two rows if len(b.shape) == 2: if not (b.shape[0] == 2 and b.shape[1] == size): raise ValueError("Expected a 2 row matrix with the same number "\ "of collumns as the first dimension of the a matrix.") elif len(b) != size: raise ValueError("Expected the length of the b vector to have the "\ "same size as the first dimension of the a matrix.") if len(c) != size: raise ValueError("Expected the length of the c vector to have the "\ "same size as the first dimension of the a matrix.") # Check if tableau is fully implicit for i in range(size): for j in range(i): if a[j, i] != 0: raise ValueError("Does not support fully implicit Butcher tableau.") if not isinstance(rhs_form, ufl.Form): raise TypeError("Expected a ufl.Form as the 5th argument.") # Check if form contains a cell or point integral if "cell" in rhs_form.integral_groups(): DX = ufl.dx elif "point" in rhs_form.integral_groups(): DX = ufl.dP else: raise ValueError("Expected either a cell or point integral in the form.") # Get test function arguments, coefficients = ufl.algorithms.extract_arguments_and_coefficients(rhs_form) if len(arguments) != 1: raise ValueError("Expected the form to have rank 1") v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [solution.copy(deepcopy=True) for i in range(size)] # Create the stage forms y_ = solution for i, ki in enumerate(k): # Check wether the stage is explicit explicit = a[i,i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], ufl.zero(*y_.shape())) stage_form = ufl.replace(rhs_form, {y_:evalargs}) if explicit: stage_forms = [stage_form] else: # Create a F=0 form and differentiate it stage_form -= ufl.inner(ki, v)*DX stage_forms = [stage_form, derivative(stage_form, ki)] ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) # Only one last stage if len(b.shape) == 1: last_stage = cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b, k)]) else: # FIXME: Add support for addaptivity in RKSolver and MultiStageScheme last_stage = [cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b[0,:], k)]), cpp.FunctionAXPY([(float(bi), ki) for bi, ki in zip(b[1,:], k)])] # Create the Function holding the solution at end of time step #k.append(solution.copy()) # Generate human form of MultiStageScheme human_form = [] for i in range(size): kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \ "%s*"% a[i,j], j) \ for j in range(size) if a[i,j] != 0) if c[i] in [0.0, 1.0]: cih = " + h" if c[i] == 1.0 else "" else: cih = " + %s*h" % c[i] if len(kterm) == 0: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {"i": i, "cih": cih}) else: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \ {"i": i, "cih": cih, "kterm": kterm}) parentheses = "(%s)" if np.sum(b>0) > 1 else "%s" human_form.append("y_{n+1} = y_n + h*" + parentheses % (" + ".join(\ "%sk_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \ for i in range(size) if b[i] > 0))) human_form = "\n".join(human_form) return ufl_stage_forms, dolfin_stage_forms, last_stage, k, dt, human_form
def pde_varf(u, m, p): return ufl.exp(m) * ufl.inner(ufl.grad(u), ufl.grad(p)) * ufl.dx - f * p * ufl.dx
def _butcher_scheme_generator_tlm(a, b, c, time, solution, rhs_form, perturbation): """ Generates a list of forms and solutions for a given Butcher tableau *Arguments* a (2 dimensional numpy array) The a matrix of the Butcher tableau. b (1-2 dimensional numpy array) The b vector of the Butcher tableau. If b is 2 dimensional the scheme includes an error estimator and can be used in adaptive solvers. c (1 dimensional numpy array) The c vector the Butcher tableau. time (_Constant_) A Constant holding the time at the start of the time step solution (_Function_) The prognostic variable rhs_form (ufl.Form) A UFL form representing the rhs for a time differentiated equation perturbation (_Function_) The perturbation in the initial condition of the solution """ a = _check_abc(a, b, c) size = a.shape[0] DX = _check_form(rhs_form) # Get test function arguments = rhs_form.arguments() coefficients = rhs_form.coefficients() v = arguments[0] # Create time step dt = Constant(0.1) # rhs forms dolfin_stage_forms = [] ufl_stage_forms = [] # Stage solutions k = [Function(solution.function_space(), name="k_%d"%i) for i in range(size)] kdot = [Function(solution.function_space(), name="kdot_%d"%i) \ for i in range(size)] # Create the stage forms y_ = solution time_ = time time_dep_expressions = _time_dependent_expressions(rhs_form, time) zero_ = ufl.zero(*y_.ufl_shape) forward_forms = [] stage_solutions = [] jacobian_indices = [] for i, ki in enumerate(k): # Check whether the stage is explicit explicit = a[i,i] == 0 # Evaluation arguments for the ith stage evalargs = y_ + dt * sum([float(a[i,j]) * k[j] \ for j in range(i+1)], zero_) time = time_ + dt*c[i] replace_dict = _replace_dict_time_dependent_expression(time_dep_expressions, time_, dt, c[i]) replace_dict[y_] = evalargs replace_dict[time_] = time stage_form = ufl.replace(rhs_form, replace_dict) forward_forms.append(stage_form) # The recomputation of the forward run: if explicit: stage_forms = [stage_form] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_implicit = stage_form - ufl.inner(ki, v)*DX stage_forms = [stage_form_implicit, derivative(stage_form_implicit, ki)] jacobian_indices.append(0) ufl_stage_forms.append(stage_forms) dolfin_stage_forms.append([Form(form) for form in stage_forms]) stage_solutions.append(ki) # And now the tangent linearisation: stage_form_tlm = safe_action(derivative(stage_form, y_), perturbation) + \ sum([dt*float(a[i,j]) * safe_action(derivative(\ forward_forms[j], y_), kdot[j]) for j in range(i+1)]) if explicit: stage_forms_tlm = [stage_form_tlm] jacobian_indices.append(-1) else: # Create a F=0 form and differentiate it stage_form_tlm -= ufl.inner(kdot[i], v)*DX stage_forms_tlm = [stage_form_tlm, derivative(stage_form_tlm, kdot[i])] jacobian_indices.append(1) ufl_stage_forms.append(stage_forms_tlm) dolfin_stage_forms.append([Form(form) for form in stage_forms_tlm]) stage_solutions.append(kdot[i]) # Only one last stage if len(b.shape) == 1: last_stage = Form(ufl.inner(perturbation + sum(\ [dt*float(bi)*kdoti for bi, kdoti in zip(b, kdot)], zero_), v)*DX) else: raise Exception("Not sure what to do here") human_form = [] for i in range(size): kterm = " + ".join("%sh*k_%s" % ("" if a[i,j] == 1.0 else \ "%s*"% a[i,j], j) \ for j in range(size) if a[i,j] != 0) if c[i] in [0.0, 1.0]: cih = " + h" if c[i] == 1.0 else "" else: cih = " + %s*h" % c[i] kdotterm = " + ".join("%(a)sh*action(derivative(f(t_n%(cih)s, y_n + "\ "%(kterm)s), kdot_%(i)s" % \ {"a": ("" if a[i,j] == 1.0 else "%s*"% a[i,j], j), "i": i, "cih": cih, "kterm": kterm} \ for j in range(size) if a[i,j] != 0) if len(kterm) == 0: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n)" % {"i": i, "cih": cih}) human_form.append("kdot_%(i)s = action(derivative("\ "f(t_n%(cih)s, y_n), y_n), ydot_n)" % \ {"i": i, "cih": cih}) else: human_form.append("k_%(i)s = f(t_n%(cih)s, y_n + %(kterm)s)" % \ {"i": i, "cih": cih, "kterm": kterm}) human_form.append("kdot_%(i)s = action(derivative(f(t_n%(cih)s, "\ "y_n + %(kterm)s), y_n) + %(kdotterm)s" % \ {"i": i, "cih": cih, "kterm": kterm, "kdotterm": kdotterm}) parentheses = "(%s)" if np.sum(b>0) > 1 else "%s" human_form.append("ydot_{n+1} = ydot_n + h*" + parentheses % (" + ".join(\ "%skdot_%s" % ("" if b[i] == 1.0 else "%s*" % b[i], i) \ for i in range(size) if b[i] > 0))) human_form = "\n".join(human_form) return ufl_stage_forms, dolfin_stage_forms, jacobian_indices, last_stage, \ stage_solutions, dt, human_form, perturbation
def solve_navier_stokes_equation(interior_circle=True, num_mesh_refinements=0): """ Solve the Navier-Stokes equation on a hard-coded mesh with hard-coded initial and boundary conditions """ mesh, om, im, nm, ymax, sub_domains = setup_geometry( interior_circle, num_mesh_refinements) dsi = Measure("ds", domain=mesh, subdomain_data=sub_domains) # Setup FEM function spaces # Function space for the velocity P1 = VectorElement("CG", mesh.ufl_cell(), 1) V = FunctionSpace(mesh, P1) # Function space for the pressure P2 = FiniteElement("CG", mesh.ufl_cell(), 1) Q = FunctionSpace(mesh, P2) # Mixed function space for velocity and pressure M = P1 * P2 W = FunctionSpace(mesh, M) # Setup FEM functions v, q = TestFunctions(W) w = Function(W) u, p = split(w) u0 = Function(V) # Inlet velocity uin = Expression(("4*(x[1]*(YMAX-x[1]))/(YMAX*YMAX)", "0."), YMAX=ymax, degree=1) # Viscosity and stabilization parameters nu = 1e-6 h = CellSize(mesh) d = 0.2 * h**(3.0 / 2.0) # Time parameters time_step = 0.1 t_start, t_end = 0.0, 10.0 # Penalty parameter gamma = 10 / h # Time stepping t = t_start step = 0 while t < t_end: # Time discretization (Crank–Nicolson method) um = 0.5 * u + 0.5 * u0 # Navier-Stokes equations in weak residual form (stabilized FEM) # Basic residual r = (inner((u - u0) / time_step + grad(p) + grad(um) * um, v) + nu * inner(grad(um), grad(v)) + div(um) * q) * dx # Weak boundary conditions r += gamma * (om * p * q + im * inner(u - uin, v) + nm * inner(u, v)) * ds # Stabilization r += d * (inner(grad(p) + grad(um) * um, grad(q) + grad(um) * v) + inner(div(um), div(v))) * dx # Solve the Navier-Stokes equation (one time step) solve(r == 0, w) if step % 5 == 0: # Plot norm of velocity at current time step nov = project(sqrt(inner(u, u)), Q) fig = plt.figure() plot(nov, fig=fig) plt.show() # Compute drag force on circle n = FacetNormal(mesh) drag_force_measure = p * n[0] * dsi(1) # Drag (only pressure) drag_force = assemble(drag_force_measure) print("Drag force = " + str(drag_force)) # Shift to next time step t += time_step step += 1 u0 = project(u, V)
def test_assembly_solve_taylor_hood_nl(mesh): """Assemble Stokes problem with Taylor-Hood elements and solve.""" gdim = mesh.geometry.dim P2 = dolfinx.fem.VectorFunctionSpace(mesh, ("Lagrange", 2)) P1 = dolfinx.fem.FunctionSpace(mesh, ("Lagrange", 1)) def boundary0(x): """Define boundary x = 0""" return x[0] < 10 * numpy.finfo(float).eps def boundary1(x): """Define boundary x = 1""" return x[0] > (1.0 - 10 * numpy.finfo(float).eps) def initial_guess_u(x): u_init = numpy.row_stack((numpy.sin(x[0]) * numpy.sin(x[1]), numpy.cos(x[0]) * numpy.cos(x[1]))) if gdim == 3: u_init = numpy.row_stack((u_init, numpy.cos(x[2]))) return u_init def initial_guess_p(x): return -x[0]**2 - x[1]**3 u_bc_0 = dolfinx.Function(P2) u_bc_0.interpolate( lambda x: numpy.row_stack(tuple(x[j] + float(j) for j in range(gdim)))) u_bc_1 = dolfinx.Function(P2) u_bc_1.interpolate( lambda x: numpy.row_stack(tuple(numpy.sin(x[j]) for j in range(gdim)))) facetdim = mesh.topology.dim - 1 bndry_facets0 = locate_entities_boundary(mesh, facetdim, boundary0) bndry_facets1 = locate_entities_boundary(mesh, facetdim, boundary1) bdofs0 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets0) bdofs1 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets1) bcs = [ dolfinx.DirichletBC(u_bc_0, bdofs0), dolfinx.DirichletBC(u_bc_1, bdofs1) ] u, p = dolfinx.Function(P2), dolfinx.Function(P1) du, dp = ufl.TrialFunction(P2), ufl.TrialFunction(P1) v, q = ufl.TestFunction(P2), ufl.TestFunction(P1) F = [ inner(ufl.grad(u), ufl.grad(v)) * dx + inner(p, ufl.div(v)) * dx, inner(ufl.div(u), q) * dx ] J = [[derivative(F[0], u, du), derivative(F[0], p, dp)], [derivative(F[1], u, du), derivative(F[1], p, dp)]] P = [[J[0][0], None], [None, inner(dp, q) * dx]] # -- Blocked and monolithic Jmat0 = dolfinx.fem.create_matrix_block(J) Pmat0 = dolfinx.fem.create_matrix_block(P) Fvec0 = dolfinx.fem.create_vector_block(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("minres") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs, P=P) snes.setFunction(problem.F_block, Fvec0) snes.setJacobian(problem.J_block, J=Jmat0, P=Pmat0) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x0 = dolfinx.fem.create_vector_block(F) with u.vector.localForm() as _u, p.vector.localForm() as _p: dolfinx.cpp.la.scatter_local_vectors( x0, [_u.array_r, _p.array_r], [(u.function_space.dofmap.index_map, u.function_space.dofmap.index_map_bs), (p.function_space.dofmap.index_map, p.function_space.dofmap.index_map_bs)]) x0.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) snes.solve(None, x0) assert snes.getConvergedReason() > 0 # -- Blocked and nested Jmat1 = dolfinx.fem.create_matrix_nest(J) Pmat1 = dolfinx.fem.create_matrix_nest(P) Fvec1 = dolfinx.fem.create_vector_nest(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) nested_IS = Jmat1.getNestISs() snes.getKSP().setType("minres") snes.getKSP().setTolerances(rtol=1e-12) snes.getKSP().getPC().setType("fieldsplit") snes.getKSP().getPC().setFieldSplitIS(["u", nested_IS[0][0]], ["p", nested_IS[1][1]]) ksp_u, ksp_p = snes.getKSP().getPC().getFieldSplitSubKSP() ksp_u.setType("preonly") ksp_u.getPC().setType('lu') ksp_p.setType("preonly") ksp_p.getPC().setType('lu') problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs, P=P) snes.setFunction(problem.F_nest, Fvec1) snes.setJacobian(problem.J_nest, J=Jmat1, P=Pmat1) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x1 = dolfinx.fem.create_vector_nest(F) for x1_soln_pair in zip(x1.getNestSubVecs(), (u, p)): x1_sub, soln_sub = x1_soln_pair soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) soln_sub.vector.copy(result=x1_sub) x1_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) x1.set(0.0) snes.solve(None, x1) assert snes.getConvergedReason() > 0 assert nest_matrix_norm(Jmat1) == pytest.approx(Jmat0.norm(), 1.0e-12) assert Fvec1.norm() == pytest.approx(Fvec0.norm(), 1.0e-12) assert x1.norm() == pytest.approx(x0.norm(), 1.0e-12) # -- Monolithic P2_el = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2) P1_el = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) TH = P2_el * P1_el W = dolfinx.FunctionSpace(mesh, TH) U = dolfinx.Function(W) dU = ufl.TrialFunction(W) u, p = ufl.split(U) du, dp = ufl.split(dU) v, q = ufl.TestFunctions(W) F = inner(ufl.grad(u), ufl.grad(v)) * dx + inner(p, ufl.div(v)) * dx \ + inner(ufl.div(u), q) * dx J = derivative(F, U, dU) P = inner(ufl.grad(du), ufl.grad(v)) * dx + inner(dp, q) * dx bdofsW0_P2_0 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets0) bdofsW0_P2_1 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets1) bcs = [ dolfinx.DirichletBC(u_bc_0, bdofsW0_P2_0, W.sub(0)), dolfinx.DirichletBC(u_bc_1, bdofsW0_P2_1, W.sub(0)) ] Jmat2 = dolfinx.fem.create_matrix(J) Pmat2 = dolfinx.fem.create_matrix(P) Fvec2 = dolfinx.fem.create_vector(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("minres") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, U, bcs, P=P) snes.setFunction(problem.F_mono, Fvec2) snes.setJacobian(problem.J_mono, J=Jmat2, P=Pmat2) U.sub(0).interpolate(initial_guess_u) U.sub(1).interpolate(initial_guess_p) x2 = dolfinx.fem.create_vector(F) x2.array = U.vector.array_r snes.solve(None, x2) assert snes.getConvergedReason() > 0 assert Jmat2.norm() == pytest.approx(Jmat0.norm(), 1.0e-12) assert Fvec2.norm() == pytest.approx(Fvec0.norm(), 1.0e-12) assert x2.norm() == pytest.approx(x0.norm(), 1.0e-12)
# Copyright (C) 2005-2007 Anders Logg # # This file is part of UFL. # # UFL is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # UFL is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with UFL. If not, see <http://www.gnu.org/licenses/>. # # The bilinear form a(v, u) and linear form L(v) for # tensor-weighted Poisson's equation. from ufl import (Coefficient, FiniteElement, TensorElement, TestFunction, TrialFunction, dx, grad, inner, triangle) P1 = FiniteElement("Lagrange", triangle, 1) P0 = TensorElement("Discontinuous Lagrange", triangle, 0, shape=(2, 2)) v = TestFunction(P1) u = TrialFunction(P1) C = Coefficient(P0) a = inner(grad(v), C * grad(u)) * dx