def merge(self, solution: df.Function) -> None: """ Combine solutions from the PDE and the ODE to form a single mixed function. `solution` holds the solution from the PDEsolver. """ v = self.vur.sub(0) self.merger.assign(solution.sub(0), v)
def merge(self, solution: df.Function) -> None: """Combine solutions from the PDE and the ODE to form a single mixed function. Arguments: function holding the combined result """ timer = df.Timer("Merge step") if self._parameters["pde_solver"] == "bidomain": v = self.vur.sub(0) else: v = self.vur self.merger.assign(solution.sub(0), v) timer.stop()
def assign_restart_ic(receiving_function: df.Function, assigning_func_iterator: Iterable[df.Function]) -> None: """Assign a seriess of functions to the `receiving_function`. This function is indended for use when restarting simulations, using previously computed solutions as initial conditions. """ # Get receiving function space mixed_function_space = receiving_function.function_space() assigning_function_space = df.FunctionSpace(mixed_function_space.mesh(), "CG", 1) for subfunc_idx, assigning_sub_function in enumerate( assigning_func_iterator): assigner = df.FunctionAssigner(mixed_function_space.sub(subfunc_idx), assigning_function_space) assigner.assign(receiving_function.sub(subfunc_idx), assigning_sub_function)
class MockProblem(ParametrizedProblem): def __init__(self, V, **kwargs): # Call parent ParametrizedProblem.__init__( self, os.path.join("test_eim_approximation_20_tempdir", expression_type, basis_generation, "mock_problem")) # Minimal subset of a ParametrizedDifferentialProblem self.V = V self._solution = Function(V) self.components = ["u", "s", "p"] # Parametrized function to be interpolated x = SpatialCoordinate(V.mesh()) mu = SymbolicParameters(self, V, (-1., -1.)) self.f00 = 1. / sqrt( pow(x[0] - mu[0], 2) + pow(x[1] - mu[1], 2) + 0.01) self.f01 = 1. / sqrt( pow(x[0] - mu[0], 4) + pow(x[1] - mu[1], 4) + 0.01) # Inner product f = TrialFunction(self.V) g = TestFunction(self.V) self.inner_product = assemble(inner(f, g) * dx) # Collapsed vector and space self.V0 = V.sub(0).collapse() self.V00 = V.sub(0).sub(0).collapse() self.V1 = V.sub(1).collapse() def name(self): return "MockProblem_20_" + expression_type + "_" + basis_generation def init(self): pass def solve(self): print("solving mock problem at mu =", self.mu) assert not hasattr(self, "_is_solving") self._is_solving = True f00 = project(self.f00, self.V00) f01 = project(self.f01, self.V00) assign(self._solution.sub(0).sub(0), f00) assign(self._solution.sub(0).sub(1), f01) delattr(self, "_is_solving") return self._solution
def new_assign_ic(receiving_function: df.Function, ic_generator: NonuniformIC, degree: int = 1) -> None: """ Assign receiving_function(x, y) <- `ic_function`(x, y), for x, in the mesh. Arguments: receiving_function: The function which is assigned the initial condition. ic_function_tuple: A tuple of python callables which return the initial condition for each point (x, y). The number of functions must match the number of subfunctions in `receiving_function`. """ mixed_func_space = receiving_function.function_space() mesh = mixed_func_space.mesh() V = df.FunctionSpace(mesh, "CG", 1) # TODO: infer this somehow class InitialConditionInterpolator(df.UserExpression): def __init__(self, **kwargs): super().__init__(kwargs) self._ic_func = None def set_interpolator(self, interpolation_function): self._ic_func = interpolation_function def eval(self, value, x): value[0] = self._ic_func(x[0]) # TODO: 1D for now ic_interpolator = InitialConditionInterpolator() # Copy functions to be able to assign to them functions = receiving_function.split(deepcopy=True) for i, (f, ic_func) in enumerate(zip(functions, ic_generator())): class IC(df.Expression): def eval(self, value, x): value[0] = ic_func(x[0]) # TODO: 1D for now ic = IC(degree=degree) assigner = df.FunctionAssigner(mixed_func_space.sub(i), V) assigner.assign(receiving_function.sub(i), df.project(ic, V))
def assign_ic_subdomain( *, brain: CoupledBrainModel, vs_prev: df.Function, value: float, subdomain_id: int, subfunction_index: int ) -> None: """ Compute a function with `value` in the subdomain corresponding to `subdomain_id`. Assign this function to subfunction `subfunction_index` of vs_prev. """ mesh = brain._mesh cell_function = brain._cell_function dX = df.Measure("dx", domain=mesh, subdomain_data=cell_function) V = df.FunctionSpace(mesh, "DG", 0) u = df.TrialFunction(V) v = df.TestFunction(V) sol = df.Function(V) sol.vector().zero() # Make sure it is initialised to zero F = -u*v*dX(subdomain_id) + df.Constant(value)*v*dX(subdomain_id) a = df.lhs(F) L = df.rhs(F) A = df.assemble(a, keep_diagonal=True) A.ident_zeros() b = df.assemble(L) solver = df.KrylovSolver("cg", "petsc_amg") solver.set_operator(A) solver.solve(sol.vector(), b) VCG = df.FunctionSpace(mesh, "CG", 1) v_new = df.Function(VCG) v_new.interpolate(sol) Vp = vs_prev.function_space().sub(subfunction_index) merger = df.FunctionAssigner(Vp, VCG) merger.assign(vs_prev.sub(subfunction_index), v_new)
def interpolate_ic(time: Sequence[float], data: np.ndarray, receiving_function: df.Function, boundaries: Iterable[np.ndarray], wavespeed: float = 1.0) -> None: mixed_func_space = receiving_function.function_space() mesh = mixed_func_space.mesh() V = df.FunctionSpace(mesh, "CG", 1) # TODO: infer this somehow class InitialConditionInterpolator(df.UserExpression): def __init__(self, **kwargs): super().__init__(kwargs) self._ic_func = None self._nearest_edge_interpolator = NearestEdgeTree(boundaries) def set_interpolator(self, interpolation_function): self._ic_func = interpolation_function def eval(self, value, x): _, r = self._nearest_edge_interpolator.query(x) value[0] = self._ic_func(r / wavespeed) # TODO: 1D for now # value[0] = r # value[0] = self._ic_func(x[0]/wavespeed) # TODO: 1D for now ic_interpolator = InitialConditionInterpolator() # Copy functions to be able to assign to them subfunction_copy = receiving_function.split(deepcopy=True) for i, f in enumerate(subfunction_copy): # from IPython import embed; embed() # assert False ic_interpolator.set_interpolator( lambda x: np.interp(x, time, data[i, :])) assigner = df.FunctionAssigner(mixed_func_space.sub(i), V) assigner.assign(receiving_function.sub(i), df.project(ic_interpolator, V))
class VTV(): """ Define Vectorial Total Variation regularization for 2 parameters """ def __init__(self, Vm, parameters=[]): """ Vm = FunctionSpace for the parameters m1, and m2 """ self.parameters = {} self.parameters['k'] = 1.0 self.parameters['eps'] = 1e-2 self.parameters['correctcost'] = True self.parameters.update(parameters) k = self.parameters['k'] eps = self.parameters['eps'] self.m1 = Function(Vm) self.m2 = Function(Vm) self.m1h = Function(Vm) self.m2h = Function(Vm) VmVm = createMixedFS(Vm, Vm) self.m12h = Function(VmVm) testm = TestFunction(VmVm) testm1, testm2 = testm trialm = TrialFunction(VmVm) trialm1, trialm2 = trialm # cost function normm1 = inner(nabla_grad(self.m1), nabla_grad(self.m1)) normm2 = inner(nabla_grad(self.m2), nabla_grad(self.m2)) TVnormsq = normm1 + normm2 + Constant(eps) TVnorm = sqrt(TVnormsq) if self.parameters['correctcost']: meshtmp = UnitSquareMesh(Vm.mesh().mpi_comm(), 10, 10) Vtmp = FunctionSpace(meshtmp, 'CG', 1) x = SpatialCoordinate(meshtmp) correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx) print '[VTV] correction cost with factor={}'.format(correctioncost) else: correctioncost = 1.0 self.wkformcost = Constant(k * correctioncost) * TVnorm * dx # gradient gradm1 = Constant(k) / TVnorm * inner(nabla_grad(self.m1), nabla_grad(testm1)) * dx gradm2 = Constant(k) / TVnorm * inner(nabla_grad(self.m2), nabla_grad(testm2)) * dx self.gradm = gradm1 + gradm2 # Hessian H11 = Constant(k)/TVnorm*(inner(nabla_grad(trialm1), nabla_grad(testm1)) - \ inner(nabla_grad(testm1),nabla_grad(self.m1))* \ inner(nabla_grad(self.m1), nabla_grad(trialm1))/TVnormsq)*dx H12 = -Constant(k)/(TVnorm*TVnormsq)*(inner(nabla_grad(testm1),nabla_grad(self.m1))* \ inner(nabla_grad(self.m2), nabla_grad(trialm2)))*dx H21 = -Constant(k)/(TVnorm*TVnormsq)*(inner(nabla_grad(testm2),nabla_grad(self.m2))* \ inner(nabla_grad(self.m1), nabla_grad(trialm1)))*dx H22 = Constant(k)/TVnorm*(inner(nabla_grad(trialm2), nabla_grad(testm2)) - \ inner(nabla_grad(testm2),nabla_grad(self.m2))* \ inner(nabla_grad(self.m2), nabla_grad(trialm2))/TVnormsq)*dx self.hessian = H11 + H12 + H21 + H22 # for preconditioning self.amgprecond = amg_solver() M = assemble(inner(testm, trialm) * dx) factM = 1e-2 * k self.sMass = M * factM def isTV(self): return True def isPD(self): return False def costab(self, m1, m2): """ Compute value of cost function at (m1,m2) """ setfct(self.m1, m1) setfct(self.m2, m2) return assemble(self.wkformcost) def costabvect(self, m1, m2): return self.costab(m1, m2) def gradab(self, m1, m2): """ returns gradient at (m1,m2) as a vector """ setfct(self.m1, m1) setfct(self.m2, m2) return assemble(self.gradm) def gradabvect(self, m1, m2): return self.gradab(m1, m2) def assemble_hessianab(self, m1, m2): """ Assemble Hessian matrix for regularization """ setfct(self.m1, m1) setfct(self.m2, m2) self.H = assemble(self.hessian) def hessianab(self, m1h, m2h): """ m1h, m2h = Vector(V) """ setfct(self.m1h, m1h) setfct(self.m2h, m2h) assign(self.m12h.sub(0), self.m1h) assign(self.m12h.sub(1), self.m2h) return self.H * self.m12h.vector() def getprecond(self): """ precondition by TV + small fraction of mass matrix """ solver = PETScKrylovSolver('cg', self.amgprecond) solver.parameters["maximum_iterations"] = 2000 solver.parameters["relative_tolerance"] = 1e-24 solver.parameters["absolute_tolerance"] = 1e-24 solver.parameters["error_on_nonconvergence"] = True solver.parameters["nonzero_initial_guess"] = False solver.set_operator(self.H + self.sMass) return solver
# deep copy. If no argument is given we will get a shallow copy. We want # a deep copy for further computations on the coefficient vectors:: # Compute solution w = Function(W) solve(a == L, w, bcs, petsc_options={ "ksp_type": "preonly", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps" }) # Split the mixed solution and collapse u = w.sub(0).collapse() p = w.sub(1).collapse() # We can calculate the :math:`L^2` norms of u and p as follows:: print("Norm of velocity coefficient vector: %.15g" % u.vector().norm()) print("Norm of pressure coefficient vector: %.15g" % p.vector().norm()) # Check pressure norm pnorm = p.vector().norm() assert np.isclose(pnorm, 4147.69457577) # Finally, we can save and plot the solutions:: # Save solution in XDMF format with XDMFFile(MPI.comm_world, "velocity.xdmf") as ufile_xdmf:
# Set-up Stokes Solve forms_stokes = FormsStokes(mesh, mixedL, mixedG, alpha, ds=ds).forms_multiphase(rho, ustar, dt, rho * nu, f) ssc = StokesStaticCondensation(mesh, forms_stokes['A_S'], forms_stokes['G_S'], forms_stokes['B_S'], forms_stokes['Q_S'], forms_stokes['S_S']) # Loop and output step = 0 t = 0. # Store tstep 0 assign(rho, rho0) xdmf_rho.write_checkpoint(rho, "rho", t) xdmf_u.write(Uh.sub(0), t) xdmf_p.write(Uh.sub(1), t) p.dump2file(mesh, fname_list, property_list, 'wb') comm.barrier() # Save some data in txt files nc = mesh.num_entities_global(2) npt = p.number_of_particles() with open(meta_data, "w") as write_file: write_file.write("%-12s %-12s %-15s %-20s %-15s %-15s \n" % ("Time step", "Number of steps", "Number of cells", "Number of particles", "Projection", "Solver")) write_file.write("%-12.5g %-15d %-15d %-20d %-15s %-15s \n" % (float(dt), num_steps, nc, npt, projection_type, solver))
forms_stokes['B_S'], forms_stokes['Q_S'], forms_stokes['S_S']) # Set pressure in upper left corner to zero bc1 = DirichletBC(mixedG.sub(1), Constant(0), Corner(xmin, ymax), "pointwise") bcs = [bc1] lstsq_u = l2projection(p, W_2, 2) # Loop and output step = 0 t = 0. # Store at step 0 xdmf_rho.write(rho0, t) xdmf_u.write(Uh.sub(0), t) xdmf_p.write(Uh.sub(1), t) p.dump2file(mesh, fname_list, property_list, 'wb') dump_list = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.] with open(pressure_table, "wb") as PT: pickle.dump(dump_list, PT) timer = Timer("[P] Total time consumed") timer.start() while step < num_steps: step += 1 t += float(dt)
class ObjectiveAcoustic(LinearOperator): """ Computes data misfit, gradient and Hessian evaluation for the seismic inverse problem using acoustic wave data """ # CONSTRUCTORS: def __init__(self, mpicomm_global, acousticwavePDE, sources, \ sourcesindex, timestepsindex, \ invparam='ab', regularization=None): """ Input: acousticwavePDE should be an instantiation from class AcousticWave """ self.mpicomm_global = mpicomm_global self.PDE = acousticwavePDE self.PDE.exact = None self.obsop = None # Observation operator self.dd = None # observations self.fwdsource = sources self.srcindex = sourcesindex self.tsteps = timestepsindex self.PDEcount = 0 self.inverta = False self.invertb = False if 'a' in invparam: self.inverta = True if 'b' in invparam: self.invertb = True assert self.inverta + self.invertb > 0 Vm = self.PDE.Vm V = self.PDE.V VmVm = createMixedFS(Vm, Vm) self.ab = Function(VmVm) # used for conversion (Vm,Vm)->VmVm self.invparam = invparam self.MG = Function(VmVm) self.MGv = self.MG.vector() self.Grad = Function(VmVm) self.srchdir = Function(VmVm) self.delta_m = Function(VmVm) self.m_bkup = Function(VmVm) LinearOperator.__init__(self, self.MGv, self.MGv) self.GN = False if regularization == None: print '[ObjectiveAcoustic] *** Warning: Using zero regularization' self.regularization = ZeroRegularization(Vm) else: self.regularization = regularization self.PD = self.regularization.isPD() self.alpha_reg = 1.0 self.p, self.q = Function(V), Function(V) self.phat, self.qhat = Function(V), Function(V) self.ahat, self.bhat = Function(Vm), Function(Vm) self.ptrial, self.ptest = TrialFunction(V), TestFunction(V) self.mtest, self.mtrial = TestFunction(Vm), TrialFunction(Vm) if self.PDE.parameters['lumpM']: self.Mprime = LumpedMassMatrixPrime(Vm, V, self.PDE.M.ratio) self.get_gradienta = self.get_gradienta_lumped self.get_hessiana = self.get_hessiana_lumped self.get_incra = self.get_incra_lumped else: self.wkformgrada = inner(self.mtest*self.p, self.q)*dx self.get_gradienta = self.get_gradienta_full self.wkformhessa = inner(self.phat*self.mtest, self.q)*dx \ + inner(self.p*self.mtest, self.qhat)*dx self.wkformhessaGN = inner(self.p*self.mtest, self.qhat)*dx self.get_hessiana = self.get_hessiana_full self.wkformrhsincra = inner(self.ahat*self.ptrial, self.ptest)*dx self.get_incra = self.get_incra_full self.wkformgradb = inner(self.mtest*nabla_grad(self.p), nabla_grad(self.q))*dx self.wkformgradbout = assemble(self.wkformgradb) self.wkformrhsincrb = inner(self.bhat*nabla_grad(self.ptrial), nabla_grad(self.ptest))*dx self.wkformhessb = inner(nabla_grad(self.phat)*self.mtest, nabla_grad(self.q))*dx \ + inner(nabla_grad(self.p)*self.mtest, nabla_grad(self.qhat))*dx self.wkformhessbGN = inner(nabla_grad(self.p)*self.mtest, nabla_grad(self.qhat))*dx # Mass matrix: self.mmtest, self.mmtrial = TestFunction(VmVm), TrialFunction(VmVm) weak_m = inner(self.mmtrial, self.mmtest)*dx self.Mass = assemble(weak_m) self.solverM = PETScKrylovSolver("cg", "jacobi") self.solverM.parameters["maximum_iterations"] = 2000 self.solverM.parameters["absolute_tolerance"] = 1e-24 self.solverM.parameters["relative_tolerance"] = 1e-24 self.solverM.parameters["report"] = False self.solverM.parameters["error_on_nonconvergence"] = True self.solverM.parameters["nonzero_initial_guess"] = False # True? self.solverM.set_operator(self.Mass) # Time-integration factors self.factors = np.ones(self.PDE.times.size) self.factors[0], self.factors[-1] = 0.5, 0.5 self.factors *= self.PDE.Dt self.invDt = 1./self.PDE.Dt # Absorbing BCs if self.PDE.parameters['abc']: assert not self.PDE.parameters['lumpD'] self.wkformgradaABC = inner( self.mtest*sqrt(self.PDE.b/self.PDE.a)*self.p, self.q)*self.PDE.ds(1) self.wkformgradbABC = inner( self.mtest*sqrt(self.PDE.a/self.PDE.b)*self.p, self.q)*self.PDE.ds(1) self.wkformgradaABCout = assemble(self.wkformgradaABC) self.wkformgradbABCout = assemble(self.wkformgradbABC) self.wkformincrrhsABC = inner( (self.ahat*sqrt(self.PDE.b/self.PDE.a) + self.bhat*sqrt(self.PDE.a/self.PDE.b))*self.ptrial, self.ptest)*self.PDE.ds(1) self.wkformhessaABC = inner( (self.bhat/sqrt(self.PDE.a*self.PDE.b) - self.ahat*sqrt(self.PDE.b/(self.PDE.a*self.PDE.a*self.PDE.a))) *self.p*self.mtest, self.q)*self.PDE.ds(1) self.wkformhessbABC = inner( (self.ahat/sqrt(self.PDE.a*self.PDE.b) - self.bhat*sqrt(self.PDE.a/(self.PDE.b*self.PDE.b*self.PDE.b))) *self.p*self.mtest, self.q)*self.PDE.ds(1) def copy(self): """(hard) copy constructor""" newobj = self.__class__(self.PDE.copy()) setfct(newobj.MG, self.MG) setfct(newobj.Grad, self.Grad) setfct(newobj.srchdir, self.srchdir) newobj.obsop = self.obsop newobj.dd = self.dd newobj.fwdsource = self.fwdsource newobj.srcindex = self.srcindex newobj.tsteps = self.tsteps return newobj # FORWARD PROBLEM + COST: #@profile def solvefwd(self, cost=False): self.PDE.set_fwd() self.solfwd, self.solpfwd, self.solppfwd = [], [], [] self.Bp = [] #TODO: make fwdsource iterable to return source term Ricker = self.fwdsource[0] srcv = self.fwdsource[2] for sii in self.srcindex: ptsrc = self.fwdsource[1][sii] def srcterm(tt): srcv.zero() srcv.axpy(Ricker(tt), ptsrc) return srcv self.PDE.ftime = srcterm solfwd, solpfwd, solppfwd,_ = self.PDE.solve() self.solfwd.append(solfwd) self.solpfwd.append(solpfwd) self.solppfwd.append(solppfwd) self.PDEcount += 1 #TODO: come back and parallellize this too (over time steps) Bp = np.zeros((len(self.obsop.PtwiseObs.Points),len(solfwd))) for index, sol in enumerate(solfwd): setfct(self.p, sol[0]) Bp[:,index] = self.obsop.obs(self.p) self.Bp.append(Bp) if cost: assert not self.dd == None, "Provide data observations to compute cost" self.cost_misfit_local = 0.0 for Bp, dd in izip(self.Bp, self.dd): self.cost_misfit_local += self.obsop.costfct(\ Bp[:,self.tsteps], dd[:,self.tsteps],\ self.PDE.times[self.tsteps], self.factors[self.tsteps]) self.cost_misfit = MPI.sum(self.mpicomm_global, self.cost_misfit_local) self.cost_misfit /= len(self.fwdsource[1]) self.cost_reg = self.regularization.costab(self.PDE.a, self.PDE.b) self.cost = self.cost_misfit + self.alpha_reg*self.cost_reg if DEBUG: print 'cost_misfit={}, cost_reg={}'.format(\ self.cost_misfit, self.cost_reg) def solvefwd_cost(self): self.solvefwd(True) # ADJOINT PROBLEM + GRADIENT: #@profile def solveadj(self, grad=False): self.PDE.set_adj() self.soladj, self.solpadj, self.solppadj = [], [], [] for Bp, dd in zip(self.Bp, self.dd): self.obsop.assemble_rhsadj(Bp, dd, self.PDE.times, self.PDE.bc) self.PDE.ftime = self.obsop.ftimeadj soladj,solpadj,solppadj,_ = self.PDE.solve() self.soladj.append(soladj) self.solpadj.append(solpadj) self.solppadj.append(solppadj) self.PDEcount += 1 if grad: self.MG.vector().zero() MGa_local, MGb_local = self.MG.split(deepcopy=True) MGav_local, MGbv_local = MGa_local.vector(), MGb_local.vector() t0, t1 = self.tsteps[0], self.tsteps[-1]+1 for solfwd, solpfwd, solppfwd, soladj in \ izip(self.solfwd, self.solpfwd, self.solppfwd, self.soladj): for fwd, fwdp, fwdpp, adj, fact in \ izip(solfwd[t0:t1], solpfwd[t0:t1], solppfwd[t0:t1],\ soladj[::-1][t0:t1], self.factors[t0:t1]): setfct(self.q, adj[0]) if self.inverta: # gradient a setfct(self.p, fwdpp[0]) MGav_local.axpy(fact, self.get_gradienta()) if self.invertb: # gradient b setfct(self.p, fwd[0]) assemble(form=self.wkformgradb, tensor=self.wkformgradbout) MGbv_local.axpy(fact, self.wkformgradbout) if self.PDE.parameters['abc']: setfct(self.p, fwdp[0]) if self.inverta: assemble(form=self.wkformgradaABC, tensor=self.wkformgradaABCout) MGav_local.axpy(0.5*fact, self.wkformgradaABCout) if self.invertb: assemble(form=self.wkformgradbABC, tensor=self.wkformgradbABCout) MGbv_local.axpy(0.5*fact, self.wkformgradbABCout) MGa, MGb = self.MG.split(deepcopy=True) MPIAllReduceVector(MGav_local, MGa.vector(), self.mpicomm_global) MPIAllReduceVector(MGbv_local, MGb.vector(), self.mpicomm_global) setfct(MGa, MGa.vector()/len(self.fwdsource[1])) setfct(MGb, MGb.vector()/len(self.fwdsource[1])) self.MG.vector().zero() if self.inverta: assign(self.MG.sub(0), MGa) if self.invertb: assign(self.MG.sub(1), MGb) if DEBUG: print 'grad_misfit={}, grad_reg={}'.format(\ self.MG.vector().norm('l2'),\ self.regularization.gradab(self.PDE.a, self.PDE.b).norm('l2')) self.MG.vector().axpy(self.alpha_reg, \ self.regularization.gradab(self.PDE.a, self.PDE.b)) try: self.solverM.solve(self.Grad.vector(), self.MG.vector()) except: # if |G|<<1, first residuals may diverge # caveat: Hope that ALL processes throw an exception pseudoGradnorm = np.sqrt(self.MGv.inner(self.MGv)) if pseudoGradnorm < 1e-8: print '*** Warning: Increasing divergence_limit for Mass matrix solver' self.solverM.parameters["divergence_limit"] = 1e6 self.solverM.solve(self.Grad.vector(), self.MG.vector()) else: print '*** Error: Problem with Mass matrix solver' sys.exit(1) def solveadj_constructgrad(self): self.solveadj(True) def get_gradienta_lumped(self): return self.Mprime.get_gradient(self.p.vector(), self.q.vector()) def get_gradienta_full(self): return assemble(self.wkformgrada) # HESSIAN: #@profile def ftimeincrfwd(self, tt): """ Compute rhs for incremental forward at time tt """ try: index = int(np.where(isequal(self.PDE.times, tt, 1e-14))[0]) except: print 'Error in ftimeincrfwd at time {}'.format(tt) print np.min(np.abs(self.PDE.times-tt)) sys.exit(0) # bhat: bhat*grad(p).grad(qtilde) # assert isequal(tt, self.solfwdi[index][1], 1e-16) setfct(self.p, self.solfwdi[index][0]) self.q.vector().zero() self.q.vector().axpy(1.0, self.C*self.p.vector()) # ahat: ahat*p''*qtilde: setfct(self.p, self.solppfwdi[index][0]) self.q.vector().axpy(1.0, self.get_incra(self.p.vector())) # ABC: if self.PDE.parameters['abc']: setfct(self.phat, self.solpfwdi[index][0]) self.q.vector().axpy(0.5, self.Dp*self.phat.vector()) return -1.0*self.q.vector() #@profile def ftimeincradj(self, tt): """ Compute rhs for incremental adjoint at time tt """ try: indexf = int(np.where(isequal(self.PDE.times, tt, 1e-14))[0]) indexa = int(np.where(isequal(self.PDE.times[::-1], tt, 1e-14))[0]) except: print 'Error in ftimeincradj at time {}'.format(tt) print np.min(np.abs(self.PDE.times-tt)) sys.exit(0) # B* B phat # assert isequal(tt, self.solincrfwd[indexf][1], 1e-16) setfct(self.phat, self.solincrfwd[indexf][0]) self.qhat.vector().zero() self.qhat.vector().axpy(1.0, self.obsop.incradj(self.phat, tt)) if not self.GN: # bhat: bhat*grad(ptilde).grad(v) # assert isequal(tt, self.soladji[indexa][1], 1e-16) setfct(self.q, self.soladji[indexa][0]) self.qhat.vector().axpy(1.0, self.C*self.q.vector()) # ahat: ahat*ptilde*q'': setfct(self.q, self.solppadji[indexa][0]) self.qhat.vector().axpy(1.0, self.get_incra(self.q.vector())) # ABC: if self.PDE.parameters['abc']: setfct(self.phat, self.solpadji[indexa][0]) self.qhat.vector().axpy(-0.5, self.Dp*self.phat.vector()) return -1.0*self.qhat.vector() def get_incra_full(self, pvector): return self.E*pvector def get_incra_lumped(self, pvector): return self.Mprime.get_incremental(self.ahat.vector(), pvector) #@profile def mult(self, abhat, y): """ mult(self, abhat, y): return y = Hessian * abhat inputs: y, abhat = Function(V).vector() """ setfct(self.ab, abhat) ahat, bhat = self.ab.split(deepcopy=True) setfct(self.ahat, ahat) setfct(self.bhat, bhat) if not self.inverta: self.ahat.vector().zero() if not self.invertb: self.bhat.vector().zero() self.C = assemble(self.wkformrhsincrb) if not self.PDE.parameters['lumpM']: self.E = assemble(self.wkformrhsincra) if self.PDE.parameters['abc']: self.Dp = assemble(self.wkformincrrhsABC) t0, t1 = self.tsteps[0], self.tsteps[-1]+1 # Compute Hessian*abhat self.ab.vector().zero() yaF_local, ybF_local = self.ab.split(deepcopy=True) ya_local, yb_local = yaF_local.vector(), ybF_local.vector() # iterate over sources: for self.solfwdi, self.solpfwdi, self.solppfwdi, \ self.soladji, self.solpadji, self.solppadji \ in izip(self.solfwd, self.solpfwd, self.solppfwd, \ self.soladj, self.solpadj, self.solppadj): # incr. fwd self.PDE.set_fwd() self.PDE.ftime = self.ftimeincrfwd self.solincrfwd,solpincrfwd,self.solppincrfwd,_ = self.PDE.solve() self.PDEcount += 1 # incr. adj self.PDE.set_adj() self.PDE.ftime = self.ftimeincradj solincradj,_,_,_ = self.PDE.solve() self.PDEcount += 1 # assemble Hessian-vect product: for fwd, adj, fwdp, incrfwdp, \ fwdpp, incrfwdpp, incrfwd, incradj, fact \ in izip(self.solfwdi[t0:t1], self.soladji[::-1][t0:t1],\ self.solpfwdi[t0:t1], solpincrfwd[t0:t1], \ self.solppfwdi[t0:t1], self.solppincrfwd[t0:t1],\ self.solincrfwd[t0:t1], solincradj[::-1][t0:t1], self.factors[t0:t1]): # ttf, tta, ttf2 = incrfwd[1], incradj[1], fwd[1] # assert isequal(ttf, tta, 1e-16), 'tfwd={}, tadj={}, reldiff={}'.\ # format(ttf, tta, abs(ttf-tta)/ttf) # assert isequal(ttf, ttf2, 1e-16), 'tfwd={}, tadj={}, reldiff={}'.\ # format(ttf, ttf2, abs(ttf-ttf2)/ttf) setfct(self.q, adj[0]) setfct(self.qhat, incradj[0]) if self.invertb: # Hessian b setfct(self.p, fwd[0]) setfct(self.phat, incrfwd[0]) if self.GN: yb_local.axpy(fact, assemble(self.wkformhessbGN)) else: yb_local.axpy(fact, assemble(self.wkformhessb)) if self.inverta: # Hessian a setfct(self.p, fwdpp[0]) setfct(self.phat, incrfwdpp[0]) ya_local.axpy(fact, self.get_hessiana()) if self.PDE.parameters['abc']: if not self.GN: setfct(self.p, incrfwdp[0]) if self.inverta: ya_local.axpy(0.5*fact, assemble(self.wkformgradaABC)) if self.invertb: yb_local.axpy(0.5*fact, assemble(self.wkformgradbABC)) setfct(self.p, fwdp[0]) setfct(self.q, incradj[0]) if self.inverta: ya_local.axpy(0.5*fact, assemble(self.wkformgradaABC)) if self.invertb: yb_local.axpy(0.5*fact, assemble(self.wkformgradbABC)) if not self.GN: setfct(self.q, adj[0]) if self.inverta: ya_local.axpy(0.25*fact, assemble(self.wkformhessaABC)) if self.invertb: yb_local.axpy(0.25*fact, assemble(self.wkformhessbABC)) yaF, ybF = self.ab.split(deepcopy=True) MPIAllReduceVector(ya_local, yaF.vector(), self.mpicomm_global) MPIAllReduceVector(yb_local, ybF.vector(), self.mpicomm_global) self.ab.vector().zero() if self.inverta: assign(self.ab.sub(0), yaF) if self.invertb: assign(self.ab.sub(1), ybF) y.zero() y.axpy(1.0/len(self.fwdsource[1]), self.ab.vector()) if DEBUG: print 'Hess_misfit={}, Hess_reg={}'.format(\ y.norm('l2'),\ self.regularization.hessianab(self.ahat.vector(),\ self.bhat.vector()).norm('l2')) y.axpy(self.alpha_reg, \ self.regularization.hessianab(self.ahat.vector(), self.bhat.vector())) def get_hessiana_full(self): if self.GN: return assemble(self.wkformhessaGN) else: return assemble(self.wkformhessa) def get_hessiana_lumped(self): if self.GN: return self.Mprime.get_gradient(self.p.vector(), self.qhat.vector()) else: return self.Mprime.get_gradient(self.phat.vector(), self.q.vector()) +\ self.Mprime.get_gradient(self.p.vector(), self.qhat.vector()) def assemble_hessian(self): self.regularization.assemble_hessianab(self.PDE.a, self.PDE.b) # SETTERS + UPDATE: def update_PDE(self, parameters): self.PDE.update(parameters) def update_m(self, medparam): """ medparam contains both med parameters """ setfct(self.ab, medparam) a, b = self.ab.split(deepcopy=True) self.update_PDE({'a':a, 'b':b}) def backup_m(self): """ back-up current value of med param a and b """ assign(self.m_bkup.sub(0), self.PDE.a) assign(self.m_bkup.sub(1), self.PDE.b) def restore_m(self): """ restore backed-up values of a and b """ a, b = self.m_bkup.split(deepcopy=True) self.update_PDE({'a':a, 'b':b}) def mediummisfit(self, target_medium): """ Compute medium misfit at current position """ assign(self.ab.sub(0), self.PDE.a) assign(self.ab.sub(1), self.PDE.b) try: diff = self.ab.vector() - target_medium.vector() except: diff = self.ab.vector() - target_medium Md = self.Mass*diff self.ab.vector().zero() self.ab.vector().axpy(1.0, Md) Mda, Mdb = self.ab.split(deepcopy=True) self.ab.vector().zero() self.ab.vector().axpy(1.0, diff) da, db = self.ab.split(deepcopy=True) medmisfita = np.sqrt(da.vector().inner(Mda.vector())) medmisfitb = np.sqrt(db.vector().inner(Mdb.vector())) return medmisfita, medmisfitb def compare_ab_global(self): """ Check that med param (a, b) are the same across all proc """ assign(self.ab.sub(0), self.PDE.a) assign(self.ab.sub(1), self.PDE.b) ab_recv = self.ab.vector().copy() normabloc = np.linalg.norm(self.ab.vector().array()) MPIAllReduceVector(self.ab.vector(), ab_recv, self.mpicomm_global) ab_recv /= MPI.size(self.mpicomm_global) diff = ab_recv - self.ab.vector() reldiff = np.linalg.norm(diff.array())/normabloc assert reldiff < 2e-16, 'Diff in (a,b) across proc: {:.2e}'.format(reldiff) # GETTERS: def getmbkup(self): return self.m_bkup.vector() def getMG(self): return self.MGv def getprecond(self): if self.PC == 'prior': return self.regularization.getprecond() elif self.PC == 'bfgs': return self.bfgsop else: print 'Wrong keyword for choice of preconditioner' sys.exit(1) # SOLVE INVERSE PROBLEM #@profile def inversion(self, initial_medium, target_medium, parameters_in=[], \ boundsLS=None, myplot=None): """ Solve inverse problem with that objective function parameters: solverNS = solver for Newton system ('steepest', 'Newton', 'BFGS') retolgrad = relative tolerance for stopping criterion (grad) abstolgrad = absolute tolerance for stopping criterion (grad) tolcost = tolerance for stopping criterion (cost) maxiterNewt = max nb of Newton iterations nbGNsteps = nb of Newton steps with GN Hessian maxtolcg = max value of the tolerance for CG solver checkab = nb of steps in-between check of param inexactCG = [bool] inexact CG solver or exact CG isprint = [bool] print results to screen avgPC = [bool] average Preconditioned step over all proc in CG PC = choice of preconditioner ('prior', or 'bfgs') """ parameters = {} parameters['solverNS'] = 'Newton' parameters['reltolgrad'] = 1e-10 parameters['abstolgrad'] = 1e-14 parameters['tolcost'] = 1e-24 parameters['maxiterNewt'] = 100 parameters['nbGNsteps'] = 10 parameters['maxtolcg'] = 0.5 parameters['checkab'] = 10 parameters['inexactCG'] = True parameters['isprint'] = False parameters['avgPC'] = True parameters['PC'] = 'prior' parameters['BFGS_damping'] = 0.2 parameters['memory_limit'] = 50 parameters['H0inv'] = 'Rinv' parameters.update(parameters_in) solverNS = parameters['solverNS'] isprint = parameters['isprint'] maxiterNewt = parameters['maxiterNewt'] reltolgrad = parameters['reltolgrad'] abstolgrad = parameters['abstolgrad'] tolcost = parameters['tolcost'] nbGNsteps = parameters['nbGNsteps'] checkab = parameters['checkab'] avgPC = parameters['avgPC'] if parameters['inexactCG']: maxtolcg = parameters['maxtolcg'] else: maxtolcg = 1e-12 if solverNS == 'BFGS': maxtolcg = -1.0 self.PC = parameters['PC'] # BFGS (preconditioner or solver): if self.PC == 'bfgs' or solverNS == 'BFGS': self.bfgsop = BFGS_operator(parameters) H0inv = self.bfgsop.parameters['H0inv'] else: self.bfgsop = [] self.PDEcount = 0 # reset if isprint: print '\t{:12s} {:10s} {:12s} {:12s} {:12s} {:16s}\t\t\t {:10s} {:12s} {:10s} {:10s}'.format(\ 'iter', 'cost', 'misfit', 'reg', '|G|', 'medmisf', 'a_ls', 'tol_cg', 'n_cg', 'PDEsolves') a0, b0 = initial_medium.split(deepcopy=True) self.update_PDE({'a':a0, 'b':b0}) self._plotab(myplot, 'init') Mab = self.Mass*target_medium.vector() self.ab.vector().zero() self.ab.vector().axpy(1.0, Mab) Ma, Mb = self.ab.split(deepcopy=True) at, bt = target_medium.split(deepcopy=True) atnorm = np.sqrt(at.vector().inner(Ma.vector())) btnorm = np.sqrt(bt.vector().inner(Mb.vector())) alpha = -1.0 # dummy value for print outputs self.solvefwd_cost() for it in xrange(maxiterNewt): MGv_old = self.MGv.copy() self.solveadj_constructgrad() gradnorm = np.sqrt(self.MGv.inner(self.Grad.vector())) if it == 0: gradnorm0 = gradnorm medmisfita, medmisfitb = self.mediummisfit(target_medium) self._plotab(myplot, str(it)) self._plotgrad(myplot, str(it)) # Stopping criterion (gradient) if gradnorm < gradnorm0*reltolgrad or gradnorm < abstolgrad: print '{:12d} {:12.4e} {:12.2e} {:12.2e} {:11.4e} {:10.2e} ({:4.1f}%) {:10.2e} ({:4.1f}%)'.\ format(it, self.cost, self.cost_misfit, self.cost_reg, gradnorm,\ medmisfita, 100.0*medmisfita/atnorm, medmisfitb, 100.0*medmisfitb/btnorm), print '{:11.3f} {:12.2} {:10} {:10d}'.format(\ alpha, "", "", self.PDEcount) if isprint: print '\nGradient sufficiently reduced' print 'Optimization converged' return # Assemble Hessian of regularization for nonlinear regularization: self.assemble_hessian() # Update BFGS approx (s, y, H0) if self.PC == 'bfgs' or solverNS == 'BFGS': if it > 0: s = self.srchdir.vector() * alpha y = self.MGv - MGv_old theta = self.bfgsop.update(s, y) else: theta = 1.0 if H0inv == 'Rinv': self.bfgsop.set_H0inv(self.regularization.getprecond()) elif H0inv == 'Minv': print 'H0inv = Minv? That is not a good idea' sys.exit(1) # Compute search direction and plot tolcg = min(maxtolcg, np.sqrt(gradnorm/gradnorm0)) self.GN = (it < nbGNsteps) # use GN or full Hessian? # most time spent here: if avgPC: cgiter, cgres, cgid = compute_searchdirection(self, {'method':solverNS, 'tolcg':tolcg,\ 'max_iter':250+1250*(self.GN==False)},\ comm=self.mpicomm_global, BFGSop=self.bfgsop) else: cgiter, cgres, cgid = compute_searchdirection(self, {'method':solverNS, 'tolcg':tolcg,\ 'max_iter':250+1250*(self.GN==False)}, BFGSop=self.bfgsop) # addt'l safety: zero-out entries of 'srchdir' corresponding to # param that are not inverted for if not self.inverta*self.invertb: srcha, srchb = self.srchdir.split(deepcopy=True) if not self.inverta: srcha.vector().zero() assign(self.srchdir.sub(0), srcha) if not self.invertb: srchb.vector().zero() assign(self.srchdir.sub(1), srchb) self._plotsrchdir(myplot, str(it)) if isprint: print '{:12d} {:12.4e} {:12.2e} {:12.2e} {:11.4e} {:10.2e} ({:4.1f}%) {:10.2e} ({:4.1f}%)'.\ format(it, self.cost, self.cost_misfit, self.cost_reg, gradnorm,\ medmisfita, 100.0*medmisfita/atnorm, medmisfitb, 100.0*medmisfitb/btnorm), print '{:11.3f} {:12.2e} {:10d} {:10d}'.format(\ alpha, tolcg, cgiter, self.PDEcount) # Backtracking line search cost_old = self.cost statusLS, LScount, alpha = bcktrcklinesearch(self, parameters, boundsLS) cost = self.cost # Perform line search for dual variable (TV-PD): if self.PD: self.regularization.update_w(self.srchdir.vector(), alpha) if it%checkab == 0: self.compare_ab_global() # Stopping criterion (LS) if not statusLS: if isprint: print '\nLine search failed' print 'Optimization aborted' return # Stopping criterion (cost) if np.abs(cost-cost_old)/np.abs(cost_old) < tolcost: if isprint: print '\nCost function stagnates' print 'Optimization aborted' return if isprint: print '\nMaximum number of Newton iterations reached' print 'Optimization aborted' # PLOTS: def _plotab(self, myplot, index): """ plot media during inversion """ if not myplot == None: if self.invparam == 'a' or self.invparam == 'ab': myplot.set_varname('a'+index) myplot.plot_vtk(self.PDE.a) if self.invparam == 'b' or self.invparam == 'ab': myplot.set_varname('b'+index) myplot.plot_vtk(self.PDE.b) def _plotgrad(self, myplot, index): """ plot grad during inversion """ if not myplot == None: if self.invparam == 'a': myplot.set_varname('Grad_a'+index) myplot.plot_vtk(self.Grad) elif self.invparam == 'b': myplot.set_varname('Grad_b'+index) myplot.plot_vtk(self.Grad) elif self.invparam == 'ab': Ga, Gb = self.Grad.split(deepcopy=True) myplot.set_varname('Grad_a'+index) myplot.plot_vtk(Ga) myplot.set_varname('Grad_b'+index) myplot.plot_vtk(Gb) def _plotsrchdir(self, myplot, index): """ plot srchdir during inversion """ if not myplot == None: if self.invparam == 'a': myplot.set_varname('srchdir_a'+index) myplot.plot_vtk(self.srchdir) elif self.invparam == 'b': myplot.set_varname('srchdir_b'+index) myplot.plot_vtk(self.srchdir) elif self.invparam == 'ab': Ga, Gb = self.srchdir.split(deepcopy=True) myplot.set_varname('srchdir_a'+index) myplot.plot_vtk(Ga) myplot.set_varname('srchdir_b'+index) myplot.plot_vtk(Gb) # SHOULD BE REMOVED: def set_abc(self, mesh, class_bc_abc, lumpD): self.PDE.set_abc(mesh, class_bc_abc, lumpD) def init_vector(self, x, dim): self.Mass.init_vector(x, dim) def getmcopyarray(self): return self.getmcopy().array() def getMGarray(self): return self.MGv.array() def setsrcterm(self, ftime): self.PDE.ftime = ftime
# Solve Stokes t1 = Timer("[P] Stokes assemble") ssc.assemble_global_system(True) del t1 t1 = Timer("[P] Stokes solve") for bc in bcs: ssc.apply_boundary(bc) ssc.solve_problem(Uhbar.cpp_object(), Uh.cpp_object(), "mumps", "default") del t1 t1 = Timer("[P] Assign and output") # Needed for particle advection assign(Udiv, Uh.sub(0)) # Needed for constrained map assign(ubar0_a, Uhbar.sub(0)) assign(u0_a, ustar) assign(duh00, duh0) assign(duh0, project(Uh.sub(0) - ustar, W_2)) p.increment(Udiv.cpp_object(), ustar.cpp_object(), np.array([1, 2], dtype=np.uintp), theta_p, step) if step == 2: theta_L.assign(theta_next) # Probably can be combined into one file? xdmf_u.write(Uh.sub(0), t) xdmf_p.write(Uh.sub(1), t) del t1
class NuclearNormformula(): def __init__(self, mesh, parameters=[], isprint=False): self.parameters = {} self.parameters['eps'] = 0.0 self.parameters['k'] = 1.0 self.parameters['correctcost'] = True self.parameters.update(parameters) eps = self.parameters['eps'] k = self.parameters['k'] self.V = FunctionSpace(mesh, 'CG', 1) self.tmp1, self.tmp2 = Function(self.V), Function(self.V) self.VV = VectorFunctionSpace(mesh, 'CG', 1, 2) self.m = Function(self.VV) self.mtest = TestFunction(self.VV) self.mtrial = TrialFunction(self.VV) self.m1, self.m2 = split(self.m) self.mh = Function(self.VV) normg1 = inner(nabla_grad(self.m1), nabla_grad(self.m1)) normg2 = inner(nabla_grad(self.m2), nabla_grad(self.m2)) if self.parameters['correctcost']: meshtmp = UnitSquareMesh(mesh.mpi_comm(), 10, 10) Vtmp = FunctionSpace(meshtmp, 'CG', 1) x = SpatialCoordinate(meshtmp) self.correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx) print '[NuclearNormformula] Correction cost with factor={}'.format( self.correctioncost) else: self.correctioncost = 1.0 self.cost = 1./np.sqrt(2.0) * Constant(k) * (\ sqrt(normg1 + normg2 + Constant(np.sqrt(eps)) + sqrt((normg1 - normg2)**2 + Constant(eps) + 4.0*inner(nabla_grad(self.m1), nabla_grad(self.m2))**2)) + sqrt(normg1 + normg2 + Constant(np.sqrt(eps)*(1.0+1e-15)) - sqrt((normg1 - normg2)**2 + Constant(eps) + 4.0*inner(nabla_grad(self.m1), nabla_grad(self.m2))**2)))*dx self.grad = derivative(self.cost, self.m, self.mtest) self.hessian = derivative(self.grad, self.m, self.mtrial) M = assemble(inner(self.mtest, self.mtrial) * dx) factM = 1e-2 * k self.sMass = M * factM if isprint: print '[NuclearNormformula] eps={}, k={}'.format(eps, k) self.amgprecond = amg_solver() def isTV(self): return False def isPD(self): return False def costab(self, m1, m2): assign(self.m.sub(0), m1) assign(self.m.sub(1), m2) return assemble(self.cost) * self.correctioncost def costabvect(self, m1, m2): setfct(self.tmp1, m1) setfct(self.tmp2, m2) return self.costab(self.tmp1, self.tmp2) def gradab(self, m1, m2): assign(self.m.sub(0), m1) assign(self.m.sub(1), m2) return assemble(self.grad) def gradabvect(self, m1, m2): setfct(self.tmp1, m1) setfct(self.tmp2, m2) return self.gradab(self.tmp1, self.tmp2) def assemble_hessianab(self, m1, m2): setfct(self.tmp1, m1) setfct(self.tmp2, m2) assign(self.m.sub(0), self.tmp1) assign(self.m.sub(1), self.tmp2) self.H = assemble(self.hessian) def hessianab(self, m1h, m2h): """ m1h, m2h = Vector(V) """ setfct(self.tmp1, m1h) setfct(self.tmp2, m2h) assign(self.mh.sub(0), self.tmp1) assign(self.mh.sub(1), self.tmp2) return self.H * self.mh.vector() def getprecond(self): """ precondition by TV + small fraction of mass matrix """ #TODO: does not appear to be a great way to apply preconditioner (DIVERGED_ITS) solver = PETScKrylovSolver('cg', self.amgprecond) solver.parameters["maximum_iterations"] = 3000 solver.parameters["relative_tolerance"] = 1e-24 solver.parameters["absolute_tolerance"] = 1e-24 solver.parameters["error_on_nonconvergence"] = True solver.parameters["nonzero_initial_guess"] = False self.precond = self.H + self.sMass solver.set_operator(self.precond) return solver
class V_TVPD(): """ Definite Vectorial Total Variation regularization from Total Variation class """ def __init__(self, Vm, parameters=[]): """ Vm = FunctionSpace for the parameters m1, and m2 """ self.parameters = {} self.parameters['k'] = 1.0 self.parameters['eps'] = 1e-2 self.parameters['rescaledradiusdual'] = 1.0 self.parameters['print'] = False self.parameters['amg'] = 'default' self.parameters['nb_param'] = 2 self.parameters['use_i'] = False self.parameters.update(parameters) n = self.parameters['nb_param'] use_i = self.parameters['use_i'] assert ((not use_i) * (n > 2)) Vw = FunctionSpace(Vm.mesh(), 'DG', 0) if not use_i: VmVm = createMixedFS(Vm, Vm) VwVw = createMixedFS(Vw, Vw) else: if self.parameters['print']: print '[V_TVPD] Using createMixedFSi' Vms, Vws = [], [] for ii in range(n): Vms.append(Vm) Vws.append(Vw) VmVm = createMixedFSi(Vms) VwVw = createMixedFSi(Vws) self.parameters['Vm'] = VmVm self.parameters['Vw'] = VwVw self.regTV = TVPD(self.parameters) if not use_i: self.m1, self.m2 = Function(Vm), Function(Vm) self.m = Function(VmVm) self.w_loc = Function(VwVw) self.factorw = Function(Vw) self.factorww = Function(VwVw) tmp = interpolate(Constant("1.0"), Vw) self.one = tmp.vector() def isTV(self): return True def isPD(self): return True def costab(self, m1, m2): assign(self.m.sub(0), m1) assign(self.m.sub(1), m2) return self.regTV.cost(self.m) def costabvect(self, m1, m2): setfct(self.m1, m1) setfct(self.m2, m2) return self.costab(self.m1, self.m2) def costabvecti(self, m): return self.regTV.cost(m) def gradab(self, m1, m2): assign(self.m.sub(0), m1) assign(self.m.sub(1), m2) return self.regTV.grad(self.m) def gradabvect(self, m1, m2): setfct(self.m1, m1) setfct(self.m2, m2) return self.gradab(self.m1, self.m2) def gradabvecti(self, m): return self.regTV.grad(m) def assemble_hessianab(self, m1, m2): setfct(self.m1, m1) setfct(self.m2, m2) assign(self.m.sub(0), self.m1) assign(self.m.sub(1), self.m2) self.regTV.assemble_hessian(self.m) def assemble_hessianabi(self, m): self.regTV.assemble_hessian(m) def hessianab(self, m1h, m2h): """ m1h, m2h = Vector(V) """ setfct(self.m1, m1h) setfct(self.m2, m2h) assign(self.m.sub(0), self.m1) assign(self.m.sub(1), self.m2) return self.regTV.hessian(self.m.vector()) def hessianabi(self, mh): return self.regTV.hessian(mh) def getprecond(self): return self.regTV.getprecond() def compute_what(self, mhat): self.regTV.compute_what(mhat) #TODO: modify to accept n>=2 def update_w(self, mhat, alphaLS, compute_what=True): """ update dual variable in direction what and update re-scaled version """ # Update wx and wy if compute_what: self.compute_what(mhat) self.regTV.wx.vector().axpy(alphaLS, self.regTV.wxhat.vector()) self.regTV.wy.vector().axpy(alphaLS, self.regTV.wyhat.vector()) # Update rescaled variables rescaledradiusdual = self.parameters['rescaledradiusdual'] # wx**2 as_backend_type(self.regTV.wxsq).vec().pointwiseMult(\ as_backend_type(self.regTV.wx.vector()).vec(),\ as_backend_type(self.regTV.wx.vector()).vec()) # wy**2 as_backend_type(self.regTV.wysq).vec().pointwiseMult(\ as_backend_type(self.regTV.wy.vector()).vec(),\ as_backend_type(self.regTV.wy.vector()).vec()) # |w| self.w_loc.vector().zero() self.w_loc.vector().axpy(1.0, self.regTV.wxsq + self.regTV.wysq) normw1, normw2 = self.w_loc.split(deepcopy=True) normw = normw1.vector() + normw2.vector() as_backend_type(normw).vec().sqrtabs() # |w|/r as_backend_type(normw).vec().pointwiseDivide(\ as_backend_type(normw).vec(),\ as_backend_type(self.one*rescaledradiusdual).vec()) # max(1.0, |w|/r) count = pointwiseMaxCount(self.factorw.vector(), normw, 1.0) # rescale wx and wy assign(self.factorww.sub(0), self.factorw) assign(self.factorww.sub(1), self.factorw) as_backend_type(self.regTV.wxrs.vector()).vec().pointwiseDivide(\ as_backend_type(self.regTV.wx.vector()).vec(),\ as_backend_type(self.factorww.vector()).vec()) as_backend_type(self.regTV.wyrs.vector()).vec().pointwiseDivide(\ as_backend_type(self.regTV.wy.vector()).vec(),\ as_backend_type(self.factorww.vector()).vec()) minf = self.factorw.vector().min() maxf = self.factorw.vector().max() if self.parameters['print']: print ('[V_TVPD] perc. dual entries rescaled={:.2f} %, ' +\ 'min(factorw)={}, max(factorw)={}').format(\ 100.*float(count)/self.factorw.vector().size(), minf, maxf)
class V_TV(): """ Definite Vectorial Total Variation regularization from Total Variation class """ def __init__(self, Vm, parameters=[]): """ Vm = FunctionSpace for the parameters m1, and m2 """ self.parameters = {} self.parameters['k'] = 1.0 self.parameters['eps'] = 1e-2 self.parameters['amg'] = 'default' self.parameters['nb_param'] = 2 self.parameters['use_i'] = False self.parameters['print'] = False self.parameters.update(parameters) n = self.parameters['nb_param'] use_i = self.parameters['use_i'] assert not ((not use_i) * (n > 2)) if not use_i: VmVm = createMixedFS(Vm, Vm) else: if self.parameters['print']: print '[V_TV] Using createMixedFSi' Vms = [] for ii in range(n): Vms.append(Vm) VmVm = createMixedFSi(Vms) self.parameters['Vm'] = VmVm self.regTV = TV(self.parameters) if not use_i: self.m1, self.m2 = Function(Vm), Function(Vm) self.m = Function(VmVm) def isTV(self): return True def isPD(self): return False def costab(self, m1, m2): assign(self.m.sub(0), m1) assign(self.m.sub(1), m2) return self.regTV.cost(self.m) def costabvect(self, m1, m2): setfct(self.m1, m1) setfct(self.m2, m2) return self.costab(self.m1, self.m2) def costabvecti(self, m): return self.regTV.cost(m) def gradab(self, m1, m2): assign(self.m.sub(0), m1) assign(self.m.sub(1), m2) return self.regTV.grad(self.m) def gradabvect(self, m1, m2): setfct(self.m1, m1) setfct(self.m2, m2) return self.gradab(self.m1, self.m2) def gradabvecti(self, m): return self.regTV.grad(m) def assemble_hessianab(self, m1, m2): setfct(self.m1, m1) setfct(self.m2, m2) assign(self.m.sub(0), self.m1) assign(self.m.sub(1), self.m2) self.regTV.assemble_hessian(self.m) def assemble_hessianabi(self, m): self.regTV.assemble_hessian(m) def hessianab(self, m1h, m2h): """ m1h, m2h = Vector(V) """ setfct(self.m1, m1h) setfct(self.m2, m2h) assign(self.m.sub(0), self.m1) assign(self.m.sub(1), self.m2) return self.regTV.hessian(self.m.vector()) def hessianabi(self, mh): return self.regTV.hessian(mh) def getprecond(self): return self.regTV.getprecond()
def solve(self, problem): self.problem = problem doSave = problem.doSave save_this_step = False onlyVel = problem.saveOnlyVel dt = self.metadata['dt'] nu = Constant(self.problem.nu) # TODO check proper use of watches self.tc.init_watch('init', 'Initialization', True, count_to_percent=False) self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True) self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True) self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True) self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True) self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True) self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True) self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True) self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True) self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True) self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True) self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True) self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True) self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True) self.tc.init_watch('saveVel', 'Saved velocity', True) self.tc.start('init') # Define function spaces (P2-P1) mesh = self.problem.mesh self.V = VectorFunctionSpace(mesh, "Lagrange", 2) # velocity self.Q = FunctionSpace(mesh, "Lagrange", 1) # pressure self.PS = FunctionSpace(mesh, "Lagrange", 2) # partial solution (must be same order as V) self.D = FunctionSpace(mesh, "Lagrange", 1) # velocity divergence space if self.bc == 'lagrange': L = FunctionSpace(mesh, "R", 0) QL = self.Q*L problem.initialize(self.V, self.Q, self.PS, self.D) # Define trial and test functions u = TrialFunction(self.V) v = TestFunction(self.V) if self.bc == 'lagrange': (pQL, rQL) = TrialFunction(QL) (qQL, lQL) = TestFunction(QL) else: p = TrialFunction(self.Q) q = TestFunction(self.Q) n = FacetNormal(mesh) I = Identity(u.geometric_dimension()) # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt}, {'type': 'v', 'time': 0.0}, {'type': 'p', 'time': 0.0}]) if doSave: problem.save_vel(False, u0, 0.0) problem.save_vel(True, u0, 0.0) u_ = Function(self.V) # current tentative velocity u_cor = Function(self.V) # current corrected velocity if self.bc == 'lagrange': p_QL = Function(QL) # current pressure or pressure help function from rotation scheme pQ = Function(self.Q) # auxiliary function for conversion between QL.sub(0) and Q else: p_ = Function(self.Q) # current pressure or pressure help function from rotation scheme p_mod = Function(self.Q) # current modified pressure from rotation scheme # Define coefficients k = Constant(self.metadata['dt']) f = Constant((0, 0, 0)) # Define forms # step 1: Tentative velocity, solve to u_ u_ext = 1.5*u0 - 0.5*u1 # extrapolation for convection term # Stabilisation h = CellSize(mesh) # CBC delta: if self.cbcDelta: delta = Constant(self.stabCoef)*h/(sqrt(inner(u_ext, u_ext))+h) else: delta = Constant(self.stabCoef)*h**2/(2*nu*k + k*h*inner(u_ext, u_ext)+h**2) if self.use_full_SUPG: v1 = v + delta*0.5*k*dot(grad(v), u_ext) parameters['form_compiler']['quadrature_degree'] = 6 else: v1 = v 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 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 pressure_rhs(): if self.useLaplace or self.bcv == 'LAP': return inner(p0, div(v1)) * dx - inner(p0*n, v1) * problem.get_outflow_measure_form() # NT term inner(inner(p, n), v) is 0 when p=0 on outflow else: return inner(p0, div(v1)) * dx a1_const = (1./k)*inner(u, v1)*dx + diffusion(0.5*u) a1_change = nonlinearity(0.5*u) if self.bcv == 'DDN': # IMP Problem: Does not penalize influx for current step, only for the next one # IMP this can lead to oscilation: DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx... # u and u_ext cannot be switched, min_value is nonlinear function a1_change += -0.5*min_value(Constant(0.), inner(u_ext, n))*inner(u, v1)*problem.get_outflow_measure_form() # IMP works only with uflacs compiler L1 = (1./k)*inner(u0, v1)*dx - nonlinearity(0.5*u0) - diffusion(0.5*u0) + pressure_rhs() if self.bcv == 'DDN': L1 += 0.5*min_value(0., inner(u_ext, n))*inner(u0, v1)*problem.get_outflow_measure_form() # Non-consistent SUPG stabilisation if self.stabilize and not self.use_full_SUPG: # a1_stab = delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx a1_stab = 0.5*delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) # NT optional: use Crank Nicolson in stabilisation term: change RHS # L1 += -0.5*delta*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6}) outflow_area = Constant(problem.outflow_area) need_outflow = Constant(0.0) if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F2 = inner(grad(pQL), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: F2 = inner(grad(p), grad(q))*dx + (1./k)*q*div(u_)*dx else: # Projection, solve to p_ if self.bc == 'lagrange': F2 = inner(grad(pQL - p0), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx else: if self.forceOutflow and problem.can_force_outflow: info('Forcing outflow.') F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx for m in problem.get_outflow_measures(): F2 += (1./k)*(1./outflow_area)*need_outflow*q*m else: F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx a2, L2 = system(F2) # step 3: Finalize, solve to u_ if self.useRotationScheme: # Rotation scheme if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0)), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_), v)*dx else: if self.bc == 'lagrange': F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0) - p0), v)*dx else: F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_ - p0), v)*dx a3, L3 = system(F3) if self.useRotationScheme: # Rotation scheme: modify pressure if self.bc == 'lagrange': pr = TrialFunction(self.Q) qr = TestFunction(self.Q) F4 = (pr - p0 - p_QL.sub(0) + nu*div(u_))*qr*dx else: F4 = (p - p0 - p_ + nu*div(u_))*q*dx # TODO zkusit, jestli to nebude rychlejsi? nepocitat soustavu, ale p.assign(...), nutno project(div(u),Q) coz je pocitani podobne soustavy # TODO zkusit v project zadat solver_type='lu' >> primy resic by mel byt efektivnejsi a4, L4 = system(F4) # Assemble matrices self.tc.start('assembleMatrices') A1_const = assemble(a1_const) # need to be here, so A1 stays one Python object during repeated assembly A1_change = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) if self.stabilize and not self.use_full_SUPG: A1_stab = A1_const.copy() # copy to get matrix with same sparse structure (data will be overwriten) A2 = assemble(a2) A3 = assemble(a3) if self.useRotationScheme: A4 = assemble(a4) self.tc.end('assembleMatrices') if self.solvers == 'direct': self.solver_vel_tent = LUSolver('mumps') self.solver_vel_cor = LUSolver('mumps') self.solver_p = LUSolver('umfpack') if self.useRotationScheme: self.solver_rot = LUSolver('umfpack') else: # NT not needed, chosen not to use hypre_parasails # if self.prec_v == 'hypre_parasails': # in FEniCS 1.6.0 inaccessible using KrylovSolver class # self.solver_vel_tent = PETScKrylovSolver('gmres') # PETSc4py object # self.solver_vel_tent.ksp().getPC().setType('hypre') # PETScOptions.set('pc_hypre_type', 'parasails') # # this is global setting, but preconditioners for pressure solvers are set by their constructors # else: self.solver_vel_tent = KrylovSolver('gmres', self.prec_v) # nonsymetric > gmres # IMP cannot use 'ilu' in parallel (choose different default option) self.solver_vel_cor = KrylovSolver('cg', 'hypre_amg') # nonsymetric > gmres self.solver_p = KrylovSolver('cg', self.prec_p) # symmetric > CG if self.useRotationScheme: self.solver_rot = KrylovSolver('cg', self.prec_p) solver_options = {'monitor_convergence': True, 'maximum_iterations': 1000, 'nonzero_initial_guess': True} # 'nonzero_initial_guess': True with solver.solbe(A, u, b) means that # Solver will use anything stored in u as an initial guess # Get the nullspace if there are no pressure boundary conditions foo = Function(self.Q) # auxiliary vector for setting pressure nullspace if self.bc in ['nullspace', 'nullspace_s']: null_vec = Vector(foo.vector()) self.Q.dofmap().set(null_vec, 1.0) null_vec *= 1.0/null_vec.norm('l2') self.null_space = VectorSpaceBasis([null_vec]) if self.bc == 'nullspace': as_backend_type(A2).set_nullspace(self.null_space) # apply global options for Krylov solvers self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.precision_rel_v_tent) self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.precision_abs_v_tent) self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12 self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4 self.solver_p.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_p.parameters['absolute_tolerance'] = 10E-10 if self.useRotationScheme: self.solver_rot.parameters['relative_tolerance'] = 10**(-self.precision_p) self.solver_rot.parameters['absolute_tolerance'] = 10E-10 if self.solvers == 'krylov': for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_p, self.solver_rot] if \ self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]: for key, value in solver_options.items(): try: solver.parameters[key] = value except KeyError: info('Invalid option %s for KrylovSolver' % key) return 1 solver.parameters['preconditioner']['structure'] = 'same' # matrices A2-A4 do not change, so we can reuse preconditioners self.solver_vel_tent.parameters['preconditioner']['structure'] = 'same_nonzero_pattern' # matrix A1 changes every time step, so change of preconditioner must be allowed if self.bc == 'lagrange': fa = FunctionAssigner(self.Q, QL.sub(0)) # boundary conditions bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.V, self.Q) self.tc.end('init') # Time-stepping info("Running of Incremental pressure correction scheme n. 1") ttime = self.metadata['time'] t = dt step = 1 while t < (ttime + dt/2.0): info("t = %f" % t) self.problem.update_time(t, step) if self.MPI_rank == 0: problem.write_status_file(t) if doSave: save_this_step = problem.save_this_step # DDN debug # u_ext_in = assemble(inner(u_ext, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_ext, n))*problem.get_outflow_measure_form()) # print('DDN: u_ext*n dSout = ', u_ext_in) # print('DDN: negative part of u_ext*n dSout = ', DDN_triggered) # assemble matrix (it depends on solution) self.tc.start('assembleA1') assemble(a1_change, tensor=A1_change) # assembling into existing matrix is faster than assembling new one A1 = A1_const.copy() # we dont want to change A1_const A1.axpy(1, A1_change, True) self.tc.end('assembleA1') self.tc.start('assembleA1stab') if self.stabilize and not self.use_full_SUPG: assemble(a1_stab, tensor=A1_stab) # assembling into existing matrix is faster than assembling new one A1.axpy(1, A1_stab, True) self.tc.end('assembleA1stab') # Compute tentative velocity step begin("Computing tentative velocity") self.tc.start('rhs') b = assemble(L1) self.tc.end('rhs') self.tc.start('applybc1') [bc.apply(A1, b) for bc in bcu] self.tc.end('applybc1') try: self.tc.start('solve 1') self.solver_vel_tent.solve(A1, u_.vector(), b) self.tc.end('solve 1') if save_this_step: self.tc.start('saveVel') problem.save_vel(True, u_, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(True, u_) problem.compute_err(True, u_, t) problem.compute_div(True, u_) except RuntimeError as inst: problem.report_fail(t) return 1 end() # DDN debug # u_ext_in = assemble(inner(u_, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_, n))*problem.get_outflow_measure_form()) # print('DDN: u_tent*n dSout = ', u_ext_in) # print('DDN: negative part of u_tent*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Computing tentative pressure") else: begin("Computing pressure") if self.forceOutflow and problem.can_force_outflow: out = problem.compute_outflow(u_) info('Tentative outflow: %f' % out) n_o = -problem.last_inflow-out info('Needed outflow: %f' % n_o) need_outflow.assign(n_o) self.tc.start('rhs') b = assemble(L2) self.tc.end('rhs') self.tc.start('applybcP') [bc.apply(A2, b) for bc in bcp] if self.bc in ['nullspace', 'nullspace_s']: self.null_space.orthogonalize(b) self.tc.end('applybcP') try: self.tc.start('solve 2') if self.bc == 'lagrange': self.solver_p.solve(A2, p_QL.vector(), b) else: self.solver_p.solve(A2, p_.vector(), b) self.tc.end('solve 2') except RuntimeError as inst: problem.report_fail(t) return 1 if self.useRotationScheme: foo = Function(self.Q) if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) foo.assign(pQ + p0) else: foo.assign(p_+p0) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(True, foo) else: if self.bc == 'lagrange': fa.assign(pQ, p_QL.sub(0)) problem.averaging_pressure(pQ) if save_this_step and not onlyVel: problem.save_pressure(False, pQ) else: # we do not want to change p=0 on outflow, it conflicts with do-nothing conditions foo = Function(self.Q) foo.assign(p_) problem.averaging_pressure(foo) if save_this_step and not onlyVel: problem.save_pressure(False, foo) end() begin("Computing corrected velocity") self.tc.start('rhs') b = assemble(L3) self.tc.end('rhs') if not self.B: self.tc.start('applybc3') [bc.apply(A3, b) for bc in bcu] self.tc.end('applybc3') try: self.tc.start('solve 3') self.solver_vel_cor.solve(A3, u_cor.vector(), b) self.tc.end('solve 3') problem.compute_err(False, u_cor, t) problem.compute_div(False, u_cor) except RuntimeError as inst: problem.report_fail(t) return 1 if save_this_step: self.tc.start('saveVel') problem.save_vel(False, u_cor, t) self.tc.end('saveVel') if save_this_step and not onlyVel: problem.save_div(False, u_cor) end() # DDN debug # u_ext_in = assemble(inner(u_cor, n)*problem.get_outflow_measure_form()) # DDN_triggered = assemble(min_value(Constant(0.), inner(u_cor, n))*problem.get_outflow_measure_form()) # print('DDN: u_cor*n dSout = ', u_ext_in) # print('DDN: negative part of u_cor*n dSout = ', DDN_triggered) if self.useRotationScheme: begin("Rotation scheme pressure correction") self.tc.start('rhs') b = assemble(L4) self.tc.end('rhs') try: self.tc.start('solve 4') self.solver_rot.solve(A4, p_mod.vector(), b) self.tc.end('solve 4') except RuntimeError as inst: problem.report_fail(t) return 1 problem.averaging_pressure(p_mod) if save_this_step and not onlyVel: problem.save_pressure(False, p_mod) end() # compute functionals (e. g. forces) problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else (pQ if self.bc == 'lagrange' else p_), t) # Move to next time step self.tc.start('next') u1.assign(u0) u0.assign(u_cor) u_.assign(u_cor) # use corretced velocity as initial guess in first step if self.useRotationScheme: p0.assign(p_mod) else: if self.bc == 'lagrange': p0.assign(pQ) else: p0.assign(p_) t = round(t + dt, 6) # round time step to 0.000001 step += 1 self.tc.end('next') info("Finished: Incremental pressure correction scheme n. 1") problem.report() return 0
u = Function(ME) V = FunctionSpace(mesh, P1) an_int = interpolate( Expression( '((pow((x[0] - 7.5E-3), 2) + pow((x[1] - 7.5E-3), 2)) <= pow(2.5E-3, 2)) ? 0.4142 : 1.0', degree=1), V) ca_int = interpolate( Expression( '((pow((x[0] - 7.5E-3), 2) + pow((x[1] - 7.5E-3), 2)) <= pow(2.5E-3, 2)) ? 2.4142 : 1.0', degree=1), V) psi_int = interpolate( Expression( '((pow((x[0] - 7.5E-3), 2) + pow((x[1] - 7.5E-3), 2)) <= pow(2.5E-3, 2)) ? -22.252E-3 : 0.0', degree=1), V) assign(u.sub(0), an_int) assign(u.sub(1), ca_int) assign(u.sub(2), psi_int) an, ca, psi = split(u) van, vca, vpsi = TestFunctions(ME) Fan = D_an * (-inner(grad(an), grad(van)) * dx - Farad / R / Temp * z_an * an * inner(grad(psi), grad(van)) * dx) Fca = D_ca * (-inner(grad(ca), grad(vca)) * dx - Farad / R / Temp * z_ca * ca * inner(grad(psi), grad(vca)) * dx) Fpsi = inner(grad(psi), grad(vpsi)) * dx - (Farad / (eps0 * epsR)) * ( z_an * an + z_ca * ca + z_fc * fc_function) * vpsi * dx F = Fpsi + Fan + Fca
def solve( mesh, W_element, P_element, Q_element, u0, p0, theta0, kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, dx_submesh, ds_submesh ): # First do a fixed_point iteration. This is usually quite robust and leads # to a point from where Newton can converge reliably. u0, p0, theta0 = solve_fixed_point( mesh, W_element, P_element, Q_element, theta0, kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, my_dx=dx_submesh, my_ds=ds_submesh, max_iter=100, tol=1.0e-8 ) WPQ = FunctionSpace( mesh, MixedElement([W_element, P_element, Q_element]) ) uptheta0 = Function(WPQ) # Initial guess assign(uptheta0.sub(0), u0) assign(uptheta0.sub(1), p0) assign(uptheta0.sub(2), theta0) rho_const = rho(_average(theta0)) stokes_heat_problem = StokesHeat( WPQ, kappa, rho, rho_const, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs=theta_dirichlet_bcs, theta_neumann_bcs=theta_neumann_bcs, my_dx=dx_submesh, my_ds=ds_submesh ) # solver = FixedPointSolver() from dolfin import PETScSNESSolver solver = PETScSNESSolver() # http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/SNES/SNESType.html solver.parameters['method'] = 'newtonls' # The Jacobian system for Stokes (+heat) are hard to solve. # Use LU for now. solver.parameters['linear_solver'] = 'lu' solver.parameters['maximum_iterations'] = 100 # TODO tighten tolerance. might not always work though... solver.parameters['absolute_tolerance'] = 1.0e-3 solver.parameters['relative_tolerance'] = 0.0 solver.parameters['report'] = True solver.solve(stokes_heat_problem, uptheta0.vector()) # u0, p0, theta0 = split(uptheta0) # Create a *deep* copy of u0, p0, theta0 to be able to deal with them # as actually separate entities vectors. u0, p0, theta0 = uptheta0.split(deepcopy=True) return u0, p0, theta0
def solve(mesh, W_element, P_element, Q_element, u0, p0, theta0, kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, dx_submesh, ds_submesh): # First do a fixed_point iteration. This is usually quite robust and leads # to a point from where Newton can converge reliably. u0, p0, theta0 = solve_fixed_point(mesh, W_element, P_element, Q_element, theta0, kappa, rho, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs, theta_neumann_bcs, my_dx=dx_submesh, my_ds=ds_submesh, max_iter=100, tol=1.0e-8) WPQ = FunctionSpace(mesh, MixedElement([W_element, P_element, Q_element])) uptheta0 = Function(WPQ) # Initial guess assign(uptheta0.sub(0), u0) assign(uptheta0.sub(1), p0) assign(uptheta0.sub(2), theta0) rho_const = rho(_average(theta0)) stokes_heat_problem = StokesHeat(WPQ, kappa, rho, rho_const, mu, cp, g, extra_force, heat_source, u_bcs, p_bcs, theta_dirichlet_bcs=theta_dirichlet_bcs, theta_neumann_bcs=theta_neumann_bcs, my_dx=dx_submesh, my_ds=ds_submesh) # solver = FixedPointSolver() from dolfin import PETScSNESSolver solver = PETScSNESSolver() # http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/SNES/SNESType.html solver.parameters['method'] = 'newtonls' # The Jacobian system for Stokes (+heat) are hard to solve. # Use LU for now. solver.parameters['linear_solver'] = 'lu' solver.parameters['maximum_iterations'] = 100 # TODO tighten tolerance. might not always work though... solver.parameters['absolute_tolerance'] = 1.0e-3 solver.parameters['relative_tolerance'] = 0.0 solver.parameters['report'] = True solver.solve(stokes_heat_problem, uptheta0.vector()) # u0, p0, theta0 = split(uptheta0) # Create a *deep* copy of u0, p0, theta0 to be able to deal with them # as actually separate entities vectors. u0, p0, theta0 = uptheta0.split(deepcopy=True) return u0, p0, theta0
class EllipticSNFluxModule(flux_module.FluxModule): """ Flux solver based on elliptic discrete ordinates formulation """ class AngularTensors: def __init__(self, angular_quad, L): from transport_data import angular_tensors_ext_module i,j,k1,k2,p,q = ufl.indices(6) tensors = angular_tensors_ext_module.AngularTensors(angular_quad, L) self.Y = ufl.as_tensor( numpy.reshape(tensors.Y(), tensors.shape_Y()) ) self.Q = ufl.as_tensor( numpy.reshape(tensors.Q(), tensors.shape_Q()) ) self.QT = ufl.transpose(self.Q) self.Qt = ufl.as_tensor( numpy.reshape(tensors.Qt(), tensors.shape_Qt()) ) self.QtT = ufl.as_tensor( self.Qt[k1,p,i], (p,i,k1) ) self.G = ufl.as_tensor( numpy.reshape(tensors.G(), tensors.shape_G()) ) self.T = ufl.as_tensor( numpy.reshape(tensors.T(), tensors.shape_T()) ) self.Wp = ufl.as_vector( angular_quad.get_pw() ) self.W = ufl.diag(self.Wp) def __init__(self, PD, DD, verbosity): """ Constructor :param ProblemData PD: Problem information and various mesh-region <-> xs-material mappings :param SNDiscretization DD: Discretization data :param int verbosity: Verbosity level. """ self.max_group_GS_it = parameters["flux_module"]["group_GS"]["max_niter"] self.group_GS = self.max_group_GS_it > 0 try: PD.eigenproblem except AttributeError: PD.distribute_material_data(DD.cell_regions, DD.M) if PD.eigenproblem and self.group_GS: print "Group Gauss-Seidel for eigenproblem not yet supported - switching to all-group coupled solution method." self.group_GS = False if DD.G == 1: self.group_GS = True self.max_group_GS_it = 1 DD.init_solution_spaces(self.group_GS) super(EllipticSNFluxModule, self).__init__(PD, DD, verbosity) if PD.fixed_source_problem: self.vals_Q = numpy.empty(self.DD.ndof,dtype='float64') if self.verb > 1: print0("Defining coefficient functions and tensors") if self.DD.V is self.DD.Vpsi1 or DD.G == 1: # shallow copies of trial/test functions - allows unified treatment of both mixed/single versions self.u = [self.u]*self.DD.G self.v = [self.v]*self.DD.G self.slns_mg = [self.sln] for g in range(1, self.DD.G): self.slns_mg.append(Function(self.DD.Vpsi1)) else: self.u = split(self.u) self.v = split(self.v) # self.group_assigner = [] # for g in range(self.DD.G): # self.group_assigner.append(FunctionAssigner(self.DD.V.sub(g), self.DD.Vpsi1)) # auxiliary single-group angular fluxes (used for monitoring convergence of the group GS iteration and computing # the true forward/adjoint angular fluxes) self.aux_slng = Function(self.DD.Vpsi1) # multigroup angular fluxes self.psi_mg = [] for g in range(self.DD.G): self.psi_mg.append(Function(self.DD.Vpsi1)) # multigroup adjoint angular fluxes self.adj_psi_mg = [] for g in range(self.DD.G): self.adj_psi_mg.append(Function(self.DD.Vpsi1)) self.D = Function(self.DD.V0) self.L = PD.scattering_order-1 self.tensors = self.AngularTensors(self.DD.angular_quad, self.L) self.C = numpy.empty(self.L+1, Function) self.S = numpy.empty(self.L+1, Function) for l in range(self.L+1): self.C[l] = Function(self.DD.V0) self.S[l] = Function(self.DD.V0) for var in {"angular_flux", "adjoint_angular_flux"}: self.vis_files[var] = \ [ [ File(os.path.join(self.vis_folder, "{}_g{}_m{}.pvd".format(var,g,m)), "compressed") for m in range(self.DD.M) ] for g in range(self.DD.G) ] self.__define_boundary_terms() # TODO: Reflective boundary conditions # noinspection PyAttributeOutsideInit,PyUnboundLocalVariable,PyTypeChecker def __define_boundary_terms(self): if self.verb > 2: print0("Defining boundary terms") self.bnd_vector_form = numpy.empty(self.DD.G, dtype=object) self.bnd_matrix_form = numpy.empty(self.DD.G, dtype=object) n = FacetNormal(self.DD.mesh) i,p,q = ufl.indices(3) natural_boundaries = self.BC.vacuum_boundaries.union(self.BC.incoming_fluxes.keys()) nonzero = lambda x: numpy.all(x > 0) nonzero_inc_flux = any(map(nonzero, self.BC.incoming_fluxes.values())) if nonzero_inc_flux and not self.fixed_source_problem: coupled_solver_error(__file__, "define boundary terms", "Incoming flux specified for an eigenvalue problem"+\ "(Q must be given whenever phi_inc is; it may possibly be zero everywhere)") for g in range(self.DD.G): self.bnd_vector_form[g] = ufl.zero() self.bnd_matrix_form[g] = ufl.zero() if natural_boundaries: try: ds = Measure("ds")[self.DD.boundaries] except TypeError: coupled_solver_error(__file__, "define boundary terms", "File assigning boundary indices to facets required if vacuum or incoming flux boundaries " "are specified.") for bnd_idx in natural_boundaries: # NOTE: The following doesn't work because ufl.abs requires a function # #for g in range(self.DD.G): # self.bnd_matrix_form[g] += \ # abs(self.tensors.G[p,q,i]*n[i])*self.u[g][q]*self.v[g][p]*ds(bnd_idx) # # NOTE: Instead, the following explicit loop has to be used; this makes tensors.G unneccessary # for pp in range(self.DD.M): omega_p_dot_n = self.DD.ordinates_matrix[i,pp]*n[i] for g in range(self.DD.G): self.bnd_matrix_form[g] += \ abs(omega_p_dot_n)*self.tensors.Wp[pp]*self.u[g][pp]*self.v[g][pp]*ds(bnd_idx) if nonzero_inc_flux: for pp in range(self.DD.M): omega_p_dot_n = self.DD.ordinates_matrix[i,pp]*n[i] for bnd_idx, psi_inc in self.BC.incoming_fluxes.iteritems(): if psi_inc.shape != (self.DD.M, self.DD.G): coupled_solver_error(__file__, "define boundary terms", "Incoming flux with incorrect number of groups and directions specified: "+ "{}, expected ({}, {})".format(psi_inc.shape, self.DD.M, self.DD.G)) for g in range(self.DD.G): self.bnd_vector_form[g] += \ ufl.conditional(omega_p_dot_n < 0, omega_p_dot_n*self.tensors.Wp[pp]*psi_inc[pp,g]*self.v[g][pp]*ds(bnd_idx), ufl.zero()) # FIXME: This assumes zero adjoint outgoing flux else: # Apply vacuum b.c. everywhere ds = Measure("ds") for pp in range(self.DD.M): omega_p_dot_n = self.DD.ordinates_matrix[i,pp]*n[i] for g in range(self.DD.G): self.bnd_matrix_form[g] += abs(omega_p_dot_n)*self.tensors.Wp[pp]*self.u[g][pp]*self.v[g][pp]*ds() def solve_group_GS(self, it=0, init_slns_ary=None): if self.verb > 1: print0(self.print_prefix + "Solving..." ) if self.eigenproblem: coupled_solver_error(__file__, "solve using group GS", "Group Gauss-Seidel for eigenproblem not yet supported") sol_timer = Timer("-- Complete solution") mat_timer = Timer("---- MTX: Complete construction") ass_timer = Timer("---- MTX: Assembling") sln_timer = Timer("---- SOL: Solving") # To simplify the weak forms u = self.u[0] v = self.v[0] if init_slns_ary is None: init_slns_ary = numpy.zeros((self.DD.G, self.local_sln_size)) for g in range(self.DD.G): self.slns_mg[g].vector()[:] = init_slns_ary[g] err = 0. for gsi in range(self.max_group_GS_it): #========================================== GAUSS-SEIDEL LOOP ============================================ if self.verb > 2: print self.print_prefix + 'Gauss-Seidel iteration {}'.format(gsi) for gto in range(self.DD.G): #========================================= LOOP OVER GROUPS =============================================== self.sln_vec = self.slns_mg[gto].vector() prev_slng_vec = self.aux_slng.vector() prev_slng_vec.zero() prev_slng_vec.axpy(1.0, self.sln_vec) prev_slng_vec.apply("insert") spc = self.print_prefix + " " if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gto, '] :' #==================================== ASSEMBLE WITHIN-GROUP PROBLEM ======================================== mat_timer.start() pres_fiss = self.PD.get_xs('chi', self.chi, gto) self.PD.get_xs('D', self.D, gto) self.PD.get_xs('St', self.R, gto) i,j,p,q,k1,k2 = ufl.indices(6) form = ( self.D*self.tensors.T[p,q,i,j]*u[q].dx(j)*v[p].dx(i) + self.R*self.tensors.W[p,q]*u[q]*v[p] ) * dx + self.bnd_matrix_form[gto] ass_timer.start() add_values_A = False add_values_Q = False assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) add_values_A = True ass_timer.stop() if self.fixed_source_problem: if self.PD.isotropic_source_everywhere: self.PD.get_Q(self.fixed_source, 0, gto) # FIXME: This assumes that adjoint source == forward source form = self.fixed_source*self.tensors.Wp[p]*v[p]*dx + self.bnd_vector_form[gto] else: form = ufl.zero() for n in range(self.DD.M): self.PD.get_Q(self.fixed_source, n, gto) # FIXME: This assumes that adjoint source == forward source form += self.fixed_source[n,gto] * self.tensors.Wp[n] * v[n] * dx + self.bnd_vector_form[gto] ass_timer.start() assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) add_values_Q = True ass_timer.stop() for gfrom in range(self.DD.G): if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gfrom, '] :' pres_Ss = False # TODO: Enlarge self.S and self.C to (L+1)^2 (or 1./2.*(L+1)*(L+2) in 2D) to accomodate for anisotropic # scattering (lines below using CC, SS are correct only for L = 0, when the following inner loop runs only # once. for l in range(self.L+1): for m in range(-l, l+1): if self.DD.angular_quad.get_D() == 2 and divmod(l+m,2)[1] == 0: continue pres_Ss |= self.PD.get_xs('Ss', self.S[l], gto, gfrom, l) self.PD.get_xs('C', self.C[l], gto, gfrom, l) if pres_Ss: Sd = ufl.diag(self.S) SS = self.tensors.QT[p,k1]*Sd[k1,k2]*self.tensors.Q[k2,q] Cd = ufl.diag(self.C) CC = self.tensors.QtT[p,i,k1]*Cd[k1,k2]*self.tensors.Qt[k2,q,j] ass_timer.start() if gfrom != gto: form = ( SS[p,q]*self.slns_mg[gfrom][q]*v[p] - CC[p,i,q,j]*self.slns_mg[gfrom][q].dx(j)*v[p].dx(i) ) * dx assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) else: form = ( CC[p,i,q,j]*u[q].dx(j)*v[p].dx(i) - SS[q,p]*u[q]*v[p] ) * dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) ass_timer.stop() if pres_fiss: pres_nSf = self.PD.get_xs('nSf', self.R, gfrom) if pres_nSf: ass_timer.start() if gfrom != gto: form = self.chi*self.R/(4*numpy.pi)*\ self.tensors.QT[p,0]*self.tensors.Q[0,q]*self.slns_mg[gfrom][q]*v[p]*dx assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) else: # NOTE: Fixed-source case (eigenproblems can currently be solved only by the coupled-group scheme) if self.fixed_source_problem: form = -self.chi*self.R/(4*numpy.pi)*self.tensors.QT[p,0]*self.tensors.Q[0,q]*u[q]*v[p]*dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) ass_timer.stop() #================================== END ASSEMBLE WITHIN-GROUP PROBLEM ======================================= self.A.apply("add") self.Q.apply("add") mat_timer.stop() self.save_algebraic_system({'A':'A_{}'.format(gto), 'Q':'Q_{}'.format(gto)}, it) #==================================== SOLVE WITHIN-GROUP PROBLEM ========================================== sln_timer.start() dolfin_solve(self.A, self.sln_vec, self.Q, "cg", "petsc_amg") sln_timer.stop() self.up_to_date["flux"] = False err = max(err, delta(self.sln_vec.array(), prev_slng_vec.array())) #==================================== END LOOP OVER GROUPS ==================================== if err < self.parameters["group_GS"]["tol"]: break def assemble_algebraic_system(self): if self.verb > 1: print0(self.print_prefix + "Assembling algebraic system.") mat_timer = Timer("---- MTX: Complete construction") ass_timer = Timer("---- MTX: Assembling") add_values_A = False add_values_B = False add_values_Q = False for gto in range(self.DD.G): #=============================== LOOP OVER GROUPS AND ASSEMBLE ================================ spc = self.print_prefix + " " if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gto, '] :' pres_fiss = self.PD.get_xs('chi', self.chi, gto) self.PD.get_xs('D', self.D, gto) self.PD.get_xs('St', self.R, gto) i,j,p,q,k1,k2 = ufl.indices(6) form = ( self.D*self.tensors.T[p,q,i,j]*self.u[gto][q].dx(j)*self.v[gto][p].dx(i) + self.R*self.tensors.W[p,q]*self.u[gto][q]*self.v[gto][p] ) * dx + self.bnd_matrix_form[gto] ass_timer.start() assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) add_values_A = True ass_timer.stop() if self.fixed_source_problem: if self.PD.isotropic_source_everywhere: self.PD.get_Q(self.fixed_source, 0, gto) # FIXME: This assumes that adjoint source == forward source form = self.fixed_source * self.tensors.Wp[p] * self.v[gto][p] * dx + self.bnd_vector_form[gto] else: form = ufl.zero() for n in range(self.DD.M): self.PD.get_Q(self.fixed_source, n, gto) # FIXME: This assumes that adjoint source == forward source form += self.fixed_source[n,gto] * self.tensors.Wp[n] * self.v[gto][n] * dx + self.bnd_vector_form[gto] ass_timer.start() assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) add_values_Q = True ass_timer.stop() for gfrom in range(self.DD.G): if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gfrom, '] :' pres_Ss = False # TODO: Enlarge self.S and self.C to (L+1)^2 (or 1./2.*(L+1)*(L+2) in 2D) to accomodate for anisotropic # scattering (lines below using CC, SS are correct only for L = 0, when the following inner loop runs only # once. for l in range(self.L+1): for m in range(-l, l+1): if self.DD.angular_quad.get_D() == 2 and divmod(l+m,2)[1] == 0: continue pres_Ss |= self.PD.get_xs('Ss', self.S[l], gto, gfrom, l) self.PD.get_xs('C', self.C[l], gto, gfrom, l) if pres_Ss: Sd = ufl.diag(self.S) SS = self.tensors.QT[p,k1]*Sd[k1,k2]*self.tensors.Q[k2,q] Cd = ufl.diag(self.C) CC = self.tensors.QtT[p,i,k1]*Cd[k1,k2]*self.tensors.Qt[k2,q,j] ass_timer.start() form = ( CC[p,i,q,j]*self.u[gfrom][q].dx(j)*self.v[gto][p].dx(i) - SS[q,p]*self.u[gfrom][q]*self.v[gto][p] ) * dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) ass_timer.stop() if pres_fiss: pres_nSf = self.PD.get_xs('nSf', self.R, gfrom) if pres_nSf: ass_timer.start() if self.fixed_source_problem: form = -self.chi*self.R/(4*numpy.pi)*\ self.tensors.QT[p,0]*self.tensors.Q[0,q]*self.u[gfrom][q]*self.v[gto][p]*dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) else: form = self.chi*self.R/(4*numpy.pi)*\ self.tensors.QT[p,0]*self.tensors.Q[0,q]*self.u[gfrom][q]*self.v[gto][p]*dx assemble(form, tensor=self.B, finalize_tensor=False, add_values=add_values_B) add_values_B = True ass_timer.stop() #============================= END LOOP OVER GROUPS AND ASSEMBLE =============================== self.A.apply("add") if self.fixed_source_problem: self.Q.apply("add") elif self.eigenproblem: self.B.apply("add") def solve(self, it=0): if self.group_GS: self.solve_group_GS(it) else: super(EllipticSNFluxModule, self).solve(it) self.slns_mg = split(self.sln) i,p,q,k1,k2 = ufl.indices(5) sol_timer = Timer("-- Complete solution") aux_timer = Timer("---- SOL: Computing angular flux + adjoint") # TODO: Move to Discretization V11 = FunctionSpace(self.DD.mesh, "CG", self.DD.parameters["p"]) for gto in range(self.DD.G): self.PD.get_xs('D', self.D, gto) form = self.D * ufl.diag_vector(ufl.as_matrix(self.DD.ordinates_matrix[i,p]*self.slns_mg[gto][q].dx(i), (p,q))) for gfrom in range(self.DD.G): pres_Ss = False # TODO: Enlarge self.S and self.C to (L+1)^2 (or 1./2.*(L+1)*(L+2) in 2D) to accomodate for anisotropic # scattering (lines below using CC, SS are correct only for L = 0, when the following inner loop runs only # once. for l in range(self.L+1): for m in range(-l, l+1): if self.DD.angular_quad.get_D() == 2 and divmod(l+m, 2)[1] == 0: continue pres_Ss |= self.PD.get_xs('Ss', self.S[l], gto, gfrom, l) self.PD.get_xs('C', self.C[l], gto, gfrom, l) if pres_Ss: Cd = ufl.diag(self.C) CC = self.tensors.Y[p,k1] * Cd[k1,k2] * self.tensors.Qt[k2,q,i] form += ufl.as_vector(CC[p,q,i] * self.slns_mg[gfrom][q].dx(i), p) # project(form, self.DD.Vpsi1, function=self.aux_slng, preconditioner_type="petsc_amg") # FASTER, but requires form compilation for each dir.: for pp in range(self.DD.M): assign(self.aux_slng.sub(pp), project(form[pp], V11, preconditioner_type="petsc_amg")) self.psi_mg[gto].assign(self.slns_mg[gto] + self.aux_slng) self.adj_psi_mg[gto].assign(self.slns_mg[gto] - self.aux_slng) def update_phi(self): timer = Timer("Scalar flux update") for g in range(self.DD.G): phig = self.phi_mg[g] phig_v = phig.vector() psig = self.psi_mg[g] for n in range(self.DD.M): # This doesn't work (Dolfin Issue #454) # assign(self.phi.sub(g), self.phi.sub(g) + self.tensors.Wp[n]*self.psi.sub(g).sub(n)) psign = psig.sub(n, deepcopy=True) phig_v.axpy(float(self.tensors.Wp[n]), psign.vector()) self.up_to_date["flux"] = True def eigenvalue_residual_norm(self,norm_type='l2'): if self.group_GS: warning("Residual norm can currently be computed only when the whole system is assembled.") return 0. else: super(EllipticSNFluxModule, self).eigenvalue_residual_norm(norm_type) def fixed_source_residual_norm(self,norm_type='l2'): if self.group_GS: warning("Residual norm can currently be computed only when the whole system is assembled.") return 0. else: super(EllipticSNFluxModule,self).fixed_source_residual_norm(norm_type) def visualize(self, it=0): super(EllipticSNFluxModule, self).visualize() labels = ["psi", "adj_psi"] functs = [self.psi_mg, self.adj_psi_mg] for var,lbl,fnc in zip(["angular_flux", "adjoint_angular_flux"], labels, functs): try: should_vis = divmod(it, self.parameters["visualization"][var])[1] == 0 except ZeroDivisionError: should_vis = False if should_vis: for g in range(self.DD.G): for n in range(self.DD.M): fgn = fnc[g].sub(n) fgn.rename(lbl, "{}_g{}_{}".format(lbl, g, n)) self.vis_files[var][g][n] << (fgn, float(it))
def test_unsteady_stokes(): nx, ny = 15, 15 k = 1 nu = Constant(1.0e-0) dt = Constant(2.5e-2) num_steps = 20 theta0 = 1.0 # Initial theta value theta1 = 0.5 # Theta after 1 step theta = Constant(theta0) mesh = UnitSquareMesh(nx, ny) # The 'unsteady version' of the benchmark in the 2012 paper by Labeur&Wells u_exact = Expression( ( "sin(t) * x[0]*x[0]*(1.0 - x[0])*(1.0 - x[0])*(2.0*x[1] \ -6.0*x[1]*x[1] + 4.0*x[1]*x[1]*x[1])", "-sin(t)* x[1]*x[1]*(1.0 - x[1])*(1.0 - x[1])*(2.0*x[0] \ - 6.0*x[0]*x[0] + 4.0*x[0]*x[0]*x[0])", ), t=0, degree=7, domain=mesh, ) p_exact = Expression("sin(t) * x[0]*(1.0 - x[0])", t=0, degree=7, domain=mesh) du_exact = Expression( ( "cos(t) * x[0]*x[0]*(1.0 - x[0])*(1.0 - x[0])*(2.0*x[1] \ - 6.0*x[1]*x[1] + 4.0*x[1]*x[1]*x[1])", "-cos(t)* x[1]*x[1]*(1.0 - x[1])*(1.0 - x[1])*(2.0*x[0] \ -6.0*x[0]*x[0] + 4.0*x[0]*x[0]*x[0])", ), t=0, degree=7, domain=mesh, ) ux_exact = Expression( ( "x[0]*x[0]*(1.0 - x[0])*(1.0 - x[0])*(2.0*x[1] \ - 6.0*x[1]*x[1] + 4.0*x[1]*x[1]*x[1])", "-x[1]*x[1]*(1.0 - x[1])*(1.0 - x[1])*(2.0*x[0] \ - 6.0*x[0]*x[0] + 4.0*x[0]*x[0]*x[0])", ), degree=7, domain=mesh, ) px_exact = Expression("x[0]*(1.0 - x[0])", degree=7, domain=mesh) sin_ext = Expression("sin(t)", t=0, degree=7, domain=mesh) f = du_exact + sin_ext * div(px_exact * Identity(2) - 2 * sym(grad(ux_exact))) Vhigh = VectorFunctionSpace(mesh, "DG", 7) Phigh = FunctionSpace(mesh, "DG", 7) # New syntax: V = VectorElement("DG", mesh.ufl_cell(), k) Q = FiniteElement("DG", mesh.ufl_cell(), k - 1) Vbar = VectorElement("DGT", mesh.ufl_cell(), k) Qbar = FiniteElement("DGT", mesh.ufl_cell(), k) mixedL = FunctionSpace(mesh, MixedElement([V, Q])) mixedG = FunctionSpace(mesh, MixedElement([Vbar, Qbar])) V2 = FunctionSpace(mesh, V) Uh = Function(mixedL) Uhbar = Function(mixedG) U0 = Function(mixedL) Uhbar0 = Function(mixedG) u0, p0 = split(U0) ubar0, pbar0 = split(Uhbar0) ustar = Function(V2) # Then the boundary conditions bc0 = DirichletBC(mixedG.sub(0), Constant((0, 0)), Gamma) bc1 = DirichletBC(mixedG.sub(1), Constant(0), Corner, "pointwise") bcs = [bc0, bc1] alpha = Constant(6 * k * k) forms_stokes = FormsStokes(mesh, mixedL, mixedG, alpha).forms_unsteady(ustar, dt, nu, f) ssc = StokesStaticCondensation( mesh, forms_stokes["A_S"], forms_stokes["G_S"], forms_stokes["G_ST"], forms_stokes["B_S"], forms_stokes["Q_S"], forms_stokes["S_S"], ) t = 0.0 step = 0 for step in range(num_steps): step += 1 t += float(dt) if comm.Get_rank() == 0: print("Step " + str(step) + " Time " + str(t)) # Set time level in exact solution u_exact.t = t p_exact.t = t du_exact.t = t - (1 - float(theta)) * float(dt) sin_ext.t = t - (1 - float(theta)) * float(dt) ssc.assemble_global_lhs() ssc.assemble_global_rhs() for bc in bcs: ssc.apply_boundary(bc) ssc.solve_problem(Uhbar, Uh, "none", "default") assign(U0, Uh) assign(ustar, U0.sub(0)) assign(Uhbar0, Uhbar) if step == 1: theta.assign(theta1) udiv_e = sqrt(assemble(div(Uh.sub(0)) * div(Uh.sub(0)) * dx)) u_ex_h = interpolate(u_exact, Vhigh) p_ex_h = interpolate(p_exact, Phigh) u_error = sqrt(assemble(dot(Uh.sub(0) - u_ex_h, Uh.sub(0) - u_ex_h) * dx)) p_error = sqrt(assemble(dot(Uh.sub(1) - p_ex_h, Uh.sub(1) - p_ex_h) * dx)) assert udiv_e < 1e-12 assert u_error < 1.5e-4 assert p_error < 1e-2
class crossgradient(): """ Define cross-gradient joint regularization """ #TODO: introduce constant eps def __init__(self, VV): """ Input argument: VV = MixedFunctionSpace for both inversion parameters """ self.ab = Function(VV) self.abv = self.ab.vector() self.MG = Function(VV) # cost self.a, self.b = self.ab.split(deepcopy=True) self.cost = 0.5*( inner(nabla_grad(self.a), nabla_grad(self.a))*\ inner(nabla_grad(self.b), nabla_grad(self.b))*dx - \ inner(nabla_grad(self.a), nabla_grad(self.b))*\ inner(nabla_grad(self.a), nabla_grad(self.b))*dx ) # gradient testa, testb = TestFunction(VV) grada = inner( nabla_grad(testa), \ inner(nabla_grad(self.b), nabla_grad(self.b))*nabla_grad(self.a) - \ inner(nabla_grad(self.a), nabla_grad(self.b))*nabla_grad(self.b) )*dx gradb = inner( nabla_grad(testb), \ inner(nabla_grad(self.a), nabla_grad(self.a))*nabla_grad(self.b) - \ inner(nabla_grad(self.a), nabla_grad(self.b))*nabla_grad(self.a) )*dx self.grad = grada + gradb # Hessian self.ahat, self.bhat = self.ab.split(deepcopy=True) self.abhat = Function(VV) at, bt = TestFunction(VV) ah, bh = TrialFunction(VV) wkform11 = inner( nabla_grad(at), \ inner(nabla_grad(self.b), nabla_grad(self.b))*nabla_grad(ah) - \ inner(nabla_grad(ah), nabla_grad(self.b))*nabla_grad(self.b) )*dx # wkform21 = inner( nabla_grad(bt), \ 2*inner(nabla_grad(self.a), nabla_grad(ah))*nabla_grad(self.b) - \ inner(nabla_grad(self.a), nabla_grad(self.b))*nabla_grad(ah) - \ inner(nabla_grad(ah), nabla_grad(self.b))*nabla_grad(self.a) )*dx # wkform12 = inner( nabla_grad(at), \ 2*inner(nabla_grad(self.b), nabla_grad(bh))*nabla_grad(self.a) - \ inner(nabla_grad(self.a), nabla_grad(self.b))*nabla_grad(bh) - \ inner(nabla_grad(self.a), nabla_grad(bh))*nabla_grad(self.b) )*dx # wkform22 = inner( nabla_grad(bt), \ inner(nabla_grad(self.a), nabla_grad(self.a))*nabla_grad(bh) - \ inner(nabla_grad(self.a), nabla_grad(bh))*nabla_grad(self.a) )*dx # self.hessian = wkform11 + wkform21 + wkform12 + wkform22 self.precond = wkform11 + wkform22 def costab(self, ma_in, mb_in): """ ma_in, mb_in = Function(V) """ setfct(self.a, ma_in) setfct(self.b, mb_in) return assemble(self.cost) # this is global (in parallel) def gradab(self, ma_in, mb_in): """ ma_in, mb_in = Function(V) """ setfct(self.a, ma_in) setfct(self.b, mb_in) return assemble(self.grad) def assemble_hessianab(self, a, b): setfct(self.a, a) setfct(self.b, b) self.H = assemble(self.hessian) self.Hprecond = assemble(self.precond) def hessianab(self, ahat, bhat): """ ahat, bhat = Vector(V) """ setfct(self.ahat, ahat) setfct(self.bhat, bhat) assign(self.abhat.sub(0), self.ahat) assign(self.abhat.sub(1), self.bhat) return self.H * self.abhat.vector()
class TVPD(TV): """ Total variation using primal-dual Newton """ def isPD(self): return True def updatePD(self): """ Update the parameters. parameters should be: - k(x) = factor inside TV - eps = regularization parameter - Vm = FunctionSpace for parameter. ||f||_TV = int k(x) sqrt{|grad f|^2 + eps} dx """ # primal dual variables self.Vw = FunctionSpace(self.Vm.mesh(), 'DG', 0) self.wH = Function(self.Vw*self.Vw) # dual variable used in Hessian (re-scaled) #self.wH = nabla_grad(self.m)/sqrt(self.fTV) # full Hessian self.w = Function(self.Vw*self.Vw) # dual variable for primal-dual, initialized at 0 self.dm = Function(self.Vm) self.dw = Function(self.Vw*self.Vw) self.testw = TestFunction(self.Vw*self.Vw) self.trialw = TrialFunction(self.Vw*self.Vw) # investigate convergence of dual variable self.dualres = self.w*sqrt(self.fTV) - nabla_grad(self.m) self.dualresnorm = inner(self.dualres, self.dualres)*dx self.normgraddm = inner(nabla_grad(self.dm), nabla_grad(self.dm))*dx # Hessian self.wkformPDhess = self.kovsq * ( \ inner(nabla_grad(self.trial), nabla_grad(self.test)) - \ 0.5*( inner(self.wH, nabla_grad(self.test))*\ inner(nabla_grad(self.trial), nabla_grad(self.m)) + \ inner(nabla_grad(self.m), nabla_grad(self.test))*\ inner(nabla_grad(self.trial), self.wH) ) / sqrt(self.fTV) \ )*dx # update dual variable self.Mw = assemble(inner(self.trialw, self.testw)*dx) self.rhswwk = inner(-self.w, self.testw)*dx + \ inner(nabla_grad(self.m)+nabla_grad(self.dm), self.testw) \ /sqrt(self.fTV)*dx + \ inner(-inner(nabla_grad(self.m),nabla_grad(self.dm))* \ self.wH/sqrt(self.fTV), self.testw)*dx def compute_dw(self, dm): """ Compute dw """ setfct(self.dm, dm) b = assemble(self.rhswwk) solve(self.Mw, self.dw.vector(), b) def update_w(self, alpha): """ update w and re-scale wH """ self.w.vector().axpy(alpha, self.dw.vector()) # project each w (coord-wise) onto unit sphere to get wH (wx, wy) = self.w.split(deepcopy=True) wxa, wya = wx.vector().array(), wy.vector().array() normw = np.sqrt(wxa**2 + wya**2) factorw = [max(1.0, ii) for ii in normw] setfct(wx, wxa/factorw) setfct(wy, wya/factorw) assign(self.wH.sub(0), wx) assign(self.wH.sub(1), wy) # check (wx,wy) = self.wH.split(deepcopy=True) wxa, wya = wx.vector().array(), wy.vector().array() assert np.amax(np.sqrt(wxa**2 + wya**2)) <= 1.0 + 1e-14 # Print results dualresnorm = assemble(self.dualresnorm) normgraddm = assemble(self.normgraddm) print 'line search dual variable: max(|w|)={}, err(w,df)={}, |grad(dm)|={}'.\ format(np.amax(np.sqrt(normw)), np.sqrt(dualresnorm), np.sqrt(normgraddm))
class normalizedcrossgradient(): """ Defined normalized cross-gradient |nabla m1 x nabla m2|^2/(|nabla m1|^2|nabla m2|^2) """ def __init__(self, VV, parameters=[]): self.parameters = {} self.parameters['eps'] = 1e-4 self.parameters['correctcost'] = True self.parameters.update(parameters) eps = Constant(self.parameters['eps']) self.ab = Function(VV) self.a, self.b = self.ab.split(deepcopy=True) normgrada = sqrt(inner(nabla_grad(self.a), nabla_grad(self.a)) + eps) ngrada = nabla_grad(self.a) / normgrada normgradb = sqrt(inner(nabla_grad(self.b), nabla_grad(self.b)) + eps) ngradb = nabla_grad(self.b) / normgradb # cost if self.parameters['correctcost']: meshtmp = UnitSquareMesh(VV.mesh().mpi_comm(), 10, 10) Vtmp = FunctionSpace(meshtmp, 'CG', 1) x = SpatialCoordinate(meshtmp) correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx) print '[NCG] Correction cost with factor={}'.format(correctioncost) else: correctioncost = 1.0 self.cost = 0.5 * correctioncost * ( 1.0 - inner(ngrada, ngradb) * inner(ngrada, ngradb)) * dx # gradient testa, testb = TestFunction(VV) grada = - inner(ngrada, ngradb)* \ (inner(ngradb/normgrada, nabla_grad(testa)) - inner(ngrada, ngradb)* \ inner(ngrada/normgrada, nabla_grad(testa)))*dx gradb = - inner(ngrada, ngradb)* \ (inner(ngrada/normgradb, nabla_grad(testb)) - inner(ngrada, ngradb)* \ inner(ngradb/normgradb, nabla_grad(testb)))*dx self.grad = grada + gradb # Hessian self.ahat, self.bhat = self.ab.split(deepcopy=True) self.abhat = Function(VV) triala, trialb = TrialFunction(VV) wkform11 = -((inner(ngradb / normgrada, nabla_grad(testa)) - inner( ngrada, ngradb) * inner(ngrada / normgrada, nabla_grad(testa))) * (inner(ngradb / normgrada, nabla_grad(triala)) - inner(ngrada, ngradb) * inner(ngrada / normgrada, nabla_grad(triala))) + inner(ngrada, ngradb) * (-inner(ngradb / normgrada, nabla_grad(testa)) * inner(ngrada / normgrada, nabla_grad(triala)) - inner(ngradb / normgrada, nabla_grad(triala)) * inner(ngrada / normgrada, nabla_grad(testa)) - inner(ngrada / normgrada, ngradb / normgrada) * inner(nabla_grad(testa), nabla_grad(triala)) + 3 * inner(ngrada, ngradb) * inner(ngrada / normgrada, nabla_grad(testa)) * inner(ngrada / normgrada, nabla_grad(triala)))) * dx # wkform12 = -( (inner(ngradb / normgrada, nabla_grad(testa)) - inner(ngrada, ngradb) * inner(ngrada / normgrada, nabla_grad(testa))) * (inner(ngrada / normgradb, nabla_grad(trialb)) - inner( ngrada, ngradb) * inner(ngradb / normgradb, nabla_grad(trialb)) ) + inner(ngrada, ngradb) * (inner( nabla_grad(testa) / normgrada, nabla_grad(trialb) / normgradb) - inner(ngradb / normgrada, nabla_grad(testa)) * inner(ngradb / normgradb, nabla_grad(trialb)) - inner(ngrada / normgrada, nabla_grad(testa)) * (inner(ngrada / normgradb, nabla_grad(trialb)) - inner(ngrada, ngradb) * inner(ngradb / normgradb, nabla_grad(trialb))))) * dx # wkform22 = -((inner(ngrada / normgradb, nabla_grad(testb)) - inner( ngrada, ngradb) * inner(ngradb / normgradb, nabla_grad(testb))) * (inner(ngrada / normgradb, nabla_grad(trialb)) - inner(ngrada, ngradb) * inner(ngradb / normgradb, nabla_grad(trialb))) + inner(ngrada, ngradb) * (-inner(ngrada / normgradb, nabla_grad(testb)) * inner(ngradb / normgradb, nabla_grad(trialb)) - inner(ngrada / normgradb, nabla_grad(trialb)) * inner(ngradb / normgradb, nabla_grad(testb)) - inner(ngrada / normgradb, ngradb / normgradb) * inner(nabla_grad(testb), nabla_grad(trialb)) + 3 * inner(ngrada, ngradb) * inner(ngradb / normgradb, nabla_grad(testb)) * inner(ngradb / normgradb, nabla_grad(trialb)))) * dx # wkform21 = -( (inner(ngrada / normgradb, nabla_grad(testb)) - inner(ngrada, ngradb) * inner(ngradb / normgradb, nabla_grad(testb))) * (inner(ngradb / normgrada, nabla_grad(triala)) - inner( ngrada, ngradb) * inner(ngrada / normgrada, nabla_grad(triala)) ) + inner(ngrada, ngradb) * (inner( nabla_grad(testb) / normgradb, nabla_grad(triala) / normgrada) - inner(ngrada / normgradb, nabla_grad(testb)) * inner(ngrada / normgrada, nabla_grad(triala)) - inner(ngradb / normgradb, nabla_grad(testb)) * (inner(ngradb / normgrada, nabla_grad(triala)) - inner(ngrada, ngradb) * inner(ngrada / normgrada, nabla_grad(triala))))) * dx # self.hessian = wkform11 + wkform22 + wkform12 + wkform21 self.precond = wkform11 + wkform22 #+ wkform12 + wkform21 def costab(self, ma_in, mb_in): setfct(self.a, ma_in) setfct(self.b, mb_in) return assemble(self.cost) def gradab(self, ma_in, mb_in): """ ma_in, mb_in = Function(V) """ setfct(self.a, ma_in) setfct(self.b, mb_in) return assemble(self.grad) def assemble_hessianab(self, a, b): setfct(self.a, a) setfct(self.b, b) self.H = assemble(self.hessian) self.Hprecond = assemble(self.precond) def hessianab(self, ahat, bhat): """ ahat, bhat = Vector(V) """ setfct(self.ahat, ahat) setfct(self.bhat, bhat) assign(self.abhat.sub(0), self.ahat) assign(self.abhat.sub(1), self.bhat) return self.H * self.abhat.vector()
# Step in time t = 0.0 # Check if we are running on CI server and reduce run time if "CI" in os.environ.keys(): T = 3 * dt else: T = 50 * dt u.vector.copy(result=u0.vector) u0.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) while (t < T): t += dt r = solver.solve(problem, u.vector) print("Step, num iterations:", int(t / dt), r[0]) u.vector.copy(result=u0.vector) file.write(u.sub(0), t) # The string ``"compressed"`` indicates that the output data should be # compressed to reduce the file size. Within the time stepping loop, the # solution vector associated with ``u`` is copied to ``u0`` at the # beginning of each time step, and the nonlinear problem is solved by # calling # :py:func:`solver.solve(problem,u.vector)<dolfin.cpp.NewtonSolver.solve>`, # with the new solution vector returned in # :py:func:`u.vector<dolfin.cpp.Function.vector>`. The ``c`` component # of the solution (the first component of ``u``) is then written to file # at every time step.
pde_projection.solve_problem(ubar_a.cpp_object(), ustar.cpp_object(), lamb.cpp_object(), "mumps", "default") del t1 # Solve Stokes t1 = Timer("[P] Stokes assemble ") ssc.assemble_global_system(True) for bc in bcs: ssc.apply_boundary(bc) del t1 t1 = Timer("[P] Stokes solve") ssc.solve_problem(Uhbar.cpp_object(), Uh.cpp_object(), "mumps", "none") del t1 # Needed for particle advection assign(Udiv, Uh.sub(0)) # Needed for constrained map assign(ubar0_a, Uhbar.sub(0)) assign(u0_a, ustar) assign(duh00, duh0) assign(duh0, project(Uh.sub(0) - ustar, W_2)) p.increment(Udiv.cpp_object(), ustar.cpp_object(), np.array([1, 2], dtype=np.uintp), theta_p, step) if step == 2: theta_L.assign(theta_next) xdmf_u.write(Uh.sub(0), t) xdmf_p.write(Uh.sub(1), t)