def flip_vectors(mesh, eigs, mesh_ref, eigs_ref, test_it=False): x_c = 1e-3 for i in range(len(eigs)): s = FESolution(mesh, eigs[i]) s_ref = FESolution(mesh_ref, eigs_ref[i]) if s.value(x_c) < 0: #print " Multiplying %d-th coarse eigenvector by (-1)" % i eigs[i] = -eigs[i] if s_ref.value(x_c) < 0: #print " Multiplying %d-th ref. eigenvector by (-1)" % i eigs_ref[i] = -eigs_ref[i] if test_it: # Test it: s = FESolution(mesh, eigs[i]).to_discrete_function() s_ref = FESolution(mesh_ref, eigs_ref[i]).to_discrete_function() same_norm = (s - s_ref).l2_norm() flipped_norm = (s + s_ref).l2_norm() print same_norm, flipped_norm if same_norm > flipped_norm: c = min(same_norm, flipped_norm) / max(same_norm, flipped_norm) print "Warning: the flip is wrong, c=", c # If "c" is almost one, then the vectors can't really be # aligned anyway: assert c > 0.9
def calculate_FE_coeffs(self): if self._fe_sol is None: from hermes1d.h1d_wrapper.h1d_wrapper import \ (assemble_projection_matrix_rhs, Mesh, FESolution) from hermes_common._hermes_common import CooMatrix pts, orders = self._mesh.get_mesh_data() m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CooMatrix(n_dof) rhs = empty(n_dof) assemble_projection_matrix_rhs(m, A, rhs, self, projection_type="L2") coeffs = solve(A.to_scipy_coo().todense(), rhs) self._fe_sol = FESolution(m, coeffs)
def project_onto(self, mesh, proj_type="Fekete"): """ Projects 'self' onto the 'mesh' using the 'proj_type' projection. proj_type == "Fekete"/"L2"/"H1" """ if mesh == self._mesh: return self if proj_type == "Fekete": return Function(self, mesh) elif proj_type in ["L2", "H1"]: from hermes1d.h1d_wrapper.h1d_wrapper import \ (assemble_projection_matrix_rhs, Mesh, FESolution) from hermes1d.hermes_common.matrix import CSCMatrix, AVector pts, orders = mesh.get_mesh_data() m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, self, projection_type=proj_type) coeffs = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) return FESolution(m, coeffs).to_discrete_function() else: raise ValueError("Unknown projection type")
def refine_mesh_romanowski(mesh, solutions): """ Uses Romanowski refinement for all solutions in 'solutions'. Solutions are given as vectors coming from the matrix solver. """ els2refine = [] errors = [] for sol in solutions: s = FESolution(mesh, sol) id, error = find_element_romanowski(s.get_element_coeffs()) els2refine.append(id) errors.append(error) els2refine = list(set(els2refine)) print "Will refine the elements:", els2refine mesh = refine_mesh(mesh, els2refine) return mesh
def flip_vectors(mesh, eigs, mesh_ref, eigs_ref, test_it=False): x_c = 1e-3 for i in range(len(eigs)): s = FESolution(mesh, eigs[i]) s_ref = FESolution(mesh_ref, eigs_ref[i]) if s.value(x_c) < 0: #print " Multiplying %d-th coarse eigenvector by (-1)" % i eigs[i] = -eigs[i] if s_ref.value(x_c) < 0: #print " Multiplying %d-th ref. eigenvector by (-1)" % i eigs_ref[i] = -eigs_ref[i] if test_it: # Test it: s = FESolution(mesh, eigs[i]).to_discrete_function() s_ref = FESolution(mesh_ref, eigs_ref[i]).to_discrete_function() same_norm = (s-s_ref).l2_norm() flipped_norm = (s+s_ref).l2_norm() print same_norm, flipped_norm if same_norm > flipped_norm: c = min(same_norm, flipped_norm) / max(same_norm, flipped_norm) print "Warning: the flip is wrong, c=", c # If "c" is almost one, then the vectors can't really be # aligned anyway: assert c > 0.9
def test_l2_h1_proj3(): """ Tests conversion to FE basis. """ pts = arange(0, 2 * pi, 0.1) orders = [2] * (len(pts) - 1) m = Mesh(pts, orders) f = Function(lambda x: sin(x), Mesh1D(pts, orders)) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f, projection_type="L2") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_l2 = FESolution(m, x).to_discrete_function() A = CSCMatrix(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f, projection_type="H1") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_h1 = FESolution(m, x).to_discrete_function() assert sol_l2 == f assert sol_h1 == f
def calculate_FE_coeffs(self): if self._fe_sol is None: from hermes1d.h1d_wrapper.h1d_wrapper import \ (assemble_projection_matrix_rhs, Mesh, FESolution) from hermes1d.hermes_common.matrix import CSCMatrix, AVector pts, orders = self._mesh.get_mesh_data() m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, self, projection_type="L2") coeffs = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) self._fe_sol = FESolution(m, coeffs)
def test_l2_h1_proj2(): """ Tests the correctness of the projections. """ pts = arange(0, 2 * pi, 3) orders = [4] * (len(pts) - 1) m = Mesh(pts, orders) pts = array(list(arange(0, pts[-1], 0.1)) + [pts[-1]]) orders = [6] * (len(pts) - 1) f_exact = Function(lambda x: sin(x), Mesh1D(pts, orders)) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f_sin, projection_type="L2") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_l2 = FESolution(m, x).to_discrete_function() A = CSCMatrix(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f_sin, projection_type="H1") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_h1 = FESolution(m, x).to_discrete_function() assert (sol_l2 - f_exact).l2_norm() < 0.002 assert (sol_h1 - f_exact).l2_norm() < 0.002
def test_l2_h1_proj_run(): """ Test that the projections run. It doesn't test if it's correct. """ pts = arange(0, 2 * pi, 1) orders = [3] * (len(pts) - 1) m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f_sin, projection_type="L2") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_l2 = FESolution(m, x).to_discrete_function() A = CSCMatrix(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f_sin, projection_type="H1") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_h1 = FESolution(m, x).to_discrete_function() sol_l2.plot(False) sol_h1.plot(False)
def test_l2_h1_proj_run(): """ Test that the projections run. It doesn't test if it's correct. """ pts = arange(0, 2*pi, 1) orders = [3]*(len(pts)-1) m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f_sin, projection_type="L2") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_l2 = FESolution(m, x).to_discrete_function() A = CSCMatrix(n_dof) assemble_projection_matrix_rhs(m, A, rhs, f_sin, projection_type="H1") x = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) sol_h1 = FESolution(m, x).to_discrete_function() sol_l2.plot(False) sol_h1.plot(False)
class Function(h1d_wrapper.Function): """ Represents a function on a mesh. The values are given in the Fekete points. """ def __init__(self, obj, mesh=None): if not isinstance(mesh, Mesh1D): raise Exception("You need to specify a mesh.") self._mesh = mesh if isinstance(obj, (tuple, list, ndarray)): self._values = obj else: self._values = [] for a, b, order in mesh.iter_elems(): fekete_points = _fekete.get_fekete_points_phys(order, a, b) # Note: this is not a projection (it only evaluates obj in # fekete points), so the result is not the best # approximation possible: elem_values = [obj(p) for p in fekete_points] self._values.append(array(elem_values)) self._poly_coeffs = {} self._fe_sol = None self.logger = _logger_Function def get_polynomial(self, x, values, a, b): """ Returns the interpolating polynomial's coeffs. The len(values) specifies the order and we work in the element <a, b> """ # Note: the version in _fekete is 2.6x faster n = len(values) A = empty((n, n), dtype="double") y = empty((n,), dtype="double") assert len(x) == n for i in range(n): for j in range(n): A[i, j] = _fekete.get_x_phys(x[i], a, b)**(n-j-1) y[i] = values[i] a = solve(A, y) return a def restrict_to_interval(self, A, B): """ Returns the same function, with the mesh (domain) restricted to the interval (A, B). """ m = self._mesh.restrict_to_interval(A, B) return Function(self, m) def eval_polynomial(self, coeffs, x): # This is about 15x faster return _fekete.eval_polynomial(coeffs, x) # than this: r = 0 n = len(coeffs) for i, a in enumerate(coeffs): r += a*x**(n-i-1) return r def eval_polynomial_array(self, coeffs, x): # This is about 6x faster return _fekete.eval_polynomial_array(coeffs, x) # than this: r = zeros(len(x)) n = len(coeffs) for i, a in enumerate(coeffs): r += a*x**(n-i-1) return r def __call__(self, x): for n, (a, b, order) in enumerate(self._mesh.iter_elems()): if b < x: continue y = _fekete.eval_poly(array([float(x)]), self._values[n], a, b)[0] return y def eval_f(self, x): return self(x) def eval_dfdx(self, x): self.calculate_FE_coeffs() return self._fe_sol.deriv(x) def calculate_FE_coeffs(self): if self._fe_sol is None: from hermes1d.h1d_wrapper.h1d_wrapper import \ (assemble_projection_matrix_rhs, Mesh, FESolution) from hermes1d.hermes_common.matrix import CSCMatrix, AVector pts, orders = self._mesh.get_mesh_data() m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, self, projection_type="L2") coeffs = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) self._fe_sol = FESolution(m, coeffs) def get_values_in_element(self, n, x): """ Return the values in points 'x' in the element 'n'. 'x' is a numpy array of points 'n' is an element id It returns a numpy array of values of the function in points 'x'. All points 'x' must be in the element 'n'. """ a, b, order = self._mesh.get_element_by_id(n) assert (a<=x).all() assert (x<=b).all() return _fekete.eval_poly(x, self._values[n], a, b) def get_polynomial_coeffs(self, n, values, a, b): if n not in self._poly_coeffs: vals = array(values) x = array(points[len(vals)-1]) self._poly_coeffs[n] = _fekete.get_polynomial(x, vals, a, b) return self._poly_coeffs[n] def project_onto(self, mesh, proj_type="Fekete"): """ Projects 'self' onto the 'mesh' using the 'proj_type' projection. proj_type == "Fekete"/"L2"/"H1" """ if mesh == self._mesh: return self if proj_type == "Fekete": return Function(self, mesh) elif proj_type in ["L2", "H1"]: from hermes1d.h1d_wrapper.h1d_wrapper import \ (assemble_projection_matrix_rhs, Mesh, FESolution) from hermes1d.hermes_common.matrix import CSCMatrix, AVector pts, orders = mesh.get_mesh_data() m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, self, projection_type=proj_type) coeffs = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) return FESolution(m, coeffs).to_discrete_function() else: raise ValueError("Unknown projection type") def project_onto_union(self, mesh): """ The same as project_onto, only "mesh" is a subset of self._mesh. """ if mesh == self._mesh: return self values = [] n = 0 for a, b, order in mesh.iter_elems(): if a >= self._mesh._points[n+1]: n += 1 fekete_points = _fekete.get_fekete_points_phys(order, a, b) elem_values = [] # Note: this is not a projection (it only evaluates obj in # fekete points), so the result is not the best # approximation possible: elem_values = self.get_values_in_element(n, fekete_points) values.append(elem_values) return Function(values, mesh) def plot(self, call_show=True): try: from jsplot import plot, show except ImportError: from pylab import plot, show odd = False for n, (a, b, order) in enumerate(self._mesh.iter_elems()): fekete_points = _fekete.get_fekete_points_phys(order, a, b) vals = self._values[n] assert len(vals) == len(fekete_points) x = arange(a, b, 0.1) y = [self(_x) for _x in x] if odd: format = "g-" else: format = "r-" odd = not odd plot(x, y, format) plot(fekete_points, vals, "ko") if call_show: show() def __eq__(self, o): if isinstance(o, Function): for a, b, order in self._mesh.iter_elems(): fekete_points = _fekete.get_fekete_points_phys(order, a, b) for p in fekete_points: if not feq(self(p), o(p)): return False for a, b, order in o._mesh.iter_elems(): fekete_points = _fekete.get_fekete_points_phys(order, a, b) for p in fekete_points: if not feq(self(p), o(p)): return False return True else: return False def __ne__(self, o): return not self.__eq__(o) def __add__(self, o): if self._mesh == o._mesh: values = [array(x)+array(y) for x, y in zip(self._values, o._values)] return Function(values, self._mesh) else: union_mesh = self._mesh.union(o._mesh) return self.project_onto_union(union_mesh) + o.project_onto_union(union_mesh) def __sub__(self, o): return self + (-o) def __neg__(self): values = [-x for x in self._values] return Function(values, self._mesh) def __pow__(self, o): if isinstance(o, (int, long)): pts = self._mesh._points orders = empty(len(self._mesh._orders), dtype="int") values = [] for n, (a, b, order) in enumerate(self._mesh.iter_elems()): order = o*order x = _fekete.get_fekete_points_phys(order, a, b) vals = _fekete.eval_poly(x, self._values[n], a, b)**o values.append(vals) orders[n] = order mesh = Mesh1D(pts, orders) return Function(values, mesh) else: return NotImplemented def get_mesh_adapt(self, max_order=12): return self._mesh def l2_norm(self, method="Fekete"): """ Calculates the L2 norm of the function. method == "Fekete" or "FE": Use Legendre interpolation, or first project to a FE basis and then calculate the L2 norm """ if method=="Fekete": r = 0 for n, (a, b, order) in enumerate(self._mesh.iter_elems()): x, w = _fekete.get_gauss_points_phys(a, b, order+1) vals = _fekete.eval_poly(x, self._values[n], a, b) r += _fekete.int_f2(w, vals) return sqrt(r) elif method=="FE": self.calculate_FE_coeffs() return self._fe_sol.l2_norm() def h1_norm(self): """ Calculates the H1 norm of the function. """ self.calculate_FE_coeffs() return self._fe_sol.h1_norm() def get_candidates_with_errors(self, f, elems=None): """ Returns a sorted list of all candidates and their errors. The best candidate is first, the worst candidate is last. The "f" is the reference function which we want to approximate using "self". """ cand_with_errors = [] orig = f.project_onto(self._mesh) dof_orig = orig.dofs() err_orig = (f - orig).l2_norm() for a, b, order in self._mesh.iter_elems(elems): self.logger.info("Considering element: %s %s %s", a, b, order) cands = generate_candidates(a, b, order) #self.logger.debug("Candidates %s", cands) #print "-"*40 #print a, b, order best_crit = 1e10 for m in cands: #self.logger.debug("Candidate: %s", m) #self.logger.debug(" create_mesh...") cand_mesh = self._mesh.use_candidate(m) #self.logger.debug(" project...") cand = f.project_onto(cand_mesh) dof_cand = cand.dofs() #self.logger.debug(" l2_norm...") # This is slow, because we are calculating the l2_norm over the # whole mesh and we are doing the union mesh as well. All we # need to do is integrate over the element we are interested # and cache the rest of the elements. diff = f - cand err_cand = diff.l2_norm() #self.logger.debug(" Choose...") if dof_cand == dof_orig: if err_cand < err_orig: # if this happens, it means that we can get better # approximation with the same DOFs, so we definitely take # this candidate: print "DOF_cand == DOF_orig:", dof_cand, dof_orig, err_cand, err_orig crit = -1e10 else: crit = 1e10 # forget this candidate elif dof_cand > dof_orig: # if DOF rises, we take the candidate that goes the steepest in # the log/sqrt error/DOFs convergence graph # we want 'crit' as negative as possible: crit = (log(err_cand) - log(err_orig)) / \ sqrt(dof_cand - dof_orig) #print crit, err_cand, err_orig, dof_cand, dof_orig else: if err_cand < err_orig: # if this happens, it means that we can get better # approximation with less DOFs, so we definitely take # this candidate: print "Nice!", dof_cand, dof_orig, err_cand, err_orig crit = -1e10 else: crit = 1e10 # forget this candidate if crit < best_crit: best_m = m best_crit = crit cand_with_errors.append((best_m, best_crit)) #if crit < -1e9: # cand_with_errors.sort(key=lambda x: x[1]) # return cand_with_errors cand_with_errors.sort(key=lambda x: x[1]) return cand_with_errors def dofs(self): return self._mesh.dofs()
def adapt_mesh(mesh, eigs, l=0, Z=1, adapt_type="hp", eqn_type="R"): """ Adapts the mesh using the adaptivity type 'adapt_type'. Returns a new instance of the H1D mesh. adapt_type .... one of: h, hp, p, uniform-p, romanowski """ if adapt_type == "romanowski": m = refine_mesh_romanowski(mesh, eigs) pts, orders = m.get_mesh_data() return Mesh(pts, orders) elif adapt_type == "uniform-p": pts, orders = mesh.get_mesh_data() orders = array(orders) + 1 return Mesh(pts, orders) elif adapt_type in ["h", "p", "hp"]: NORM = 1 # 1 ... H1; 0 ... L2; THRESHOLD = 0.7 mesh_ref = mesh.reference_refinement() print "Fine mesh created (%d DOF)." % mesh_ref.get_n_dof() N_dof, energies, eigs_ref = solve_schroedinger(mesh_ref, l=l, Z=Z, eqn_type=eqn_type, eig_num=len(eigs)) flip_vectors(mesh, eigs, mesh_ref, eigs_ref) print " Done." sols = [] sols_ref = [] print "Normalizing solutions..." for i in range(len(eigs)): e = (eigs[i]).copy() coarse_h1_norm = FESolution(mesh, e).h1_norm() e /= coarse_h1_norm sols.append(e) e = (eigs_ref[i]).copy() reference_h1_norm = FESolution(mesh_ref, e).h1_norm() e /= reference_h1_norm sols_ref.append(e) #print "H1 norms:" #print "coarse (%d):" % i, coarse_h1_norm #print "reference (%d):" % i, reference_h1_norm print " Done." meshes = [] mesh_orig = mesh.copy() mesh_orig.assign_dofs() errors = [] for sol, sol_ref in zip(sols, sols_ref): mesh = mesh_orig.copy() mesh.assign_dofs() mesh_ref = mesh.reference_refinement() mesh_ref.assign_dofs() mesh.copy_vector_to_mesh(sol, 0) mesh_ref.copy_vector_to_mesh(sol_ref, 0) err_est_total, err_est_array = calc_error_estimate( NORM, mesh, mesh_ref) ref_sol_norm = calc_solution_norm(NORM, mesh_ref) err_est_rel = err_est_total / ref_sol_norm print "Relative error (est) = %g %%\n" % (100. * err_est_rel) errors.append(err_est_rel) # TODO: adapt using all the vectors: # 0 ... hp, 1 ... h, 2 ... p if adapt_type == "hp": ADAPT_TYPE = 0 elif adapt_type == "h": ADAPT_TYPE = 1 elif adapt_type == "p": ADAPT_TYPE = 2 else: raise ValueError("Unkown adapt_type") adapt(NORM, ADAPT_TYPE, THRESHOLD, err_est_array, mesh, mesh_ref) meshes.append(mesh) pts, orders = mesh_orig.get_mesh_data() mesh = Mesh1D(pts, orders) for m in meshes: pts, orders = m.get_mesh_data() m = Mesh1D(pts, orders) mesh = mesh.union(m) pts, orders = mesh.get_mesh_data() mesh = Mesh(pts, orders) return mesh else: raise ValueError("Unknown adapt_type")
class Function(h1d_wrapper.Function): """ Represents a function on a mesh. The values are given in the Fekete points. """ def __init__(self, obj, mesh=None): if not isinstance(mesh, Mesh1D): raise Exception("You need to specify a mesh.") self._mesh = mesh if isinstance(obj, (tuple, list, ndarray)): self._values = obj else: self._values = [] for a, b, order in mesh.iter_elems(): fekete_points = _fekete.get_fekete_points_phys(order, a, b) # Note: this is not a projection (it only evaluates obj in # fekete points), so the result is not the best # approximation possible: elem_values = [obj(p) for p in fekete_points] self._values.append(array(elem_values)) self._poly_coeffs = {} self._fe_sol = None self.logger = _logger_Function def get_polynomial(self, x, values, a, b): """ Returns the interpolating polynomial's coeffs. The len(values) specifies the order and we work in the element <a, b> """ # Note: the version in _fekete is 2.6x faster n = len(values) A = empty((n, n), dtype="double") y = empty((n, ), dtype="double") assert len(x) == n for i in range(n): for j in range(n): A[i, j] = _fekete.get_x_phys(x[i], a, b)**(n - j - 1) y[i] = values[i] a = solve(A, y) return a def restrict_to_interval(self, A, B): """ Returns the same function, with the mesh (domain) restricted to the interval (A, B). """ m = self._mesh.restrict_to_interval(A, B) return Function(self, m) def eval_polynomial(self, coeffs, x): # This is about 15x faster return _fekete.eval_polynomial(coeffs, x) # than this: r = 0 n = len(coeffs) for i, a in enumerate(coeffs): r += a * x**(n - i - 1) return r def eval_polynomial_array(self, coeffs, x): # This is about 6x faster return _fekete.eval_polynomial_array(coeffs, x) # than this: r = zeros(len(x)) n = len(coeffs) for i, a in enumerate(coeffs): r += a * x**(n - i - 1) return r def __call__(self, x): for n, (a, b, order) in enumerate(self._mesh.iter_elems()): if b < x: continue y = _fekete.eval_poly(array([float(x)]), self._values[n], a, b)[0] return y def eval_f(self, x): return self(x) def eval_dfdx(self, x): self.calculate_FE_coeffs() return self._fe_sol.deriv(x) def calculate_FE_coeffs(self): if self._fe_sol is None: from hermes1d.h1d_wrapper.h1d_wrapper import \ (assemble_projection_matrix_rhs, Mesh, FESolution) from hermes1d.hermes_common.matrix import CSCMatrix, AVector pts, orders = self._mesh.get_mesh_data() m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, self, projection_type="L2") coeffs = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) self._fe_sol = FESolution(m, coeffs) def get_values_in_element(self, n, x): """ Return the values in points 'x' in the element 'n'. 'x' is a numpy array of points 'n' is an element id It returns a numpy array of values of the function in points 'x'. All points 'x' must be in the element 'n'. """ a, b, order = self._mesh.get_element_by_id(n) assert (a <= x).all() assert (x <= b).all() return _fekete.eval_poly(x, self._values[n], a, b) def get_polynomial_coeffs(self, n, values, a, b): if n not in self._poly_coeffs: vals = array(values) x = array(points[len(vals) - 1]) self._poly_coeffs[n] = _fekete.get_polynomial(x, vals, a, b) return self._poly_coeffs[n] def project_onto(self, mesh, proj_type="Fekete"): """ Projects 'self' onto the 'mesh' using the 'proj_type' projection. proj_type == "Fekete"/"L2"/"H1" """ if mesh == self._mesh: return self if proj_type == "Fekete": return Function(self, mesh) elif proj_type in ["L2", "H1"]: from hermes1d.h1d_wrapper.h1d_wrapper import \ (assemble_projection_matrix_rhs, Mesh, FESolution) from hermes1d.hermes_common.matrix import CSCMatrix, AVector pts, orders = mesh.get_mesh_data() m = Mesh(pts, orders) n_dof = m.assign_dofs() A = CSCMatrix(n_dof) rhs = AVector(n_dof) assemble_projection_matrix_rhs(m, A, rhs, self, projection_type=proj_type) coeffs = solve(A.to_scipy_csc().todense(), rhs.to_numpy()) return FESolution(m, coeffs).to_discrete_function() else: raise ValueError("Unknown projection type") def project_onto_union(self, mesh): """ The same as project_onto, only "mesh" is a subset of self._mesh. """ if mesh == self._mesh: return self values = [] n = 0 for a, b, order in mesh.iter_elems(): if a >= self._mesh._points[n + 1]: n += 1 fekete_points = _fekete.get_fekete_points_phys(order, a, b) elem_values = [] # Note: this is not a projection (it only evaluates obj in # fekete points), so the result is not the best # approximation possible: elem_values = self.get_values_in_element(n, fekete_points) values.append(elem_values) return Function(values, mesh) def plot(self, call_show=True): try: from jsplot import plot, show except ImportError: from pylab import plot, show odd = False for n, (a, b, order) in enumerate(self._mesh.iter_elems()): fekete_points = _fekete.get_fekete_points_phys(order, a, b) vals = self._values[n] assert len(vals) == len(fekete_points) x = arange(a, b, 0.1) y = [self(_x) for _x in x] if odd: format = "g-" else: format = "r-" odd = not odd plot(x, y, format) plot(fekete_points, vals, "ko") if call_show: show() def __eq__(self, o): if isinstance(o, Function): for a, b, order in self._mesh.iter_elems(): fekete_points = _fekete.get_fekete_points_phys(order, a, b) for p in fekete_points: if not feq(self(p), o(p)): return False for a, b, order in o._mesh.iter_elems(): fekete_points = _fekete.get_fekete_points_phys(order, a, b) for p in fekete_points: if not feq(self(p), o(p)): return False return True else: return False def __ne__(self, o): return not self.__eq__(o) def __add__(self, o): if self._mesh == o._mesh: values = [ array(x) + array(y) for x, y in zip(self._values, o._values) ] return Function(values, self._mesh) else: union_mesh = self._mesh.union(o._mesh) return self.project_onto_union(union_mesh) + o.project_onto_union( union_mesh) def __sub__(self, o): return self + (-o) def __neg__(self): values = [-x for x in self._values] return Function(values, self._mesh) def __pow__(self, o): if isinstance(o, (int, long)): pts = self._mesh._points orders = empty(len(self._mesh._orders), dtype="int") values = [] for n, (a, b, order) in enumerate(self._mesh.iter_elems()): order = o * order x = _fekete.get_fekete_points_phys(order, a, b) vals = _fekete.eval_poly(x, self._values[n], a, b)**o values.append(vals) orders[n] = order mesh = Mesh1D(pts, orders) return Function(values, mesh) else: return NotImplemented def get_mesh_adapt(self, max_order=12): return self._mesh def l2_norm(self, method="Fekete"): """ Calculates the L2 norm of the function. method == "Fekete" or "FE": Use Legendre interpolation, or first project to a FE basis and then calculate the L2 norm """ if method == "Fekete": r = 0 for n, (a, b, order) in enumerate(self._mesh.iter_elems()): x, w = _fekete.get_gauss_points_phys(a, b, order + 1) vals = _fekete.eval_poly(x, self._values[n], a, b) r += _fekete.int_f2(w, vals) return sqrt(r) elif method == "FE": self.calculate_FE_coeffs() return self._fe_sol.l2_norm() def h1_norm(self): """ Calculates the H1 norm of the function. """ self.calculate_FE_coeffs() return self._fe_sol.h1_norm() def get_candidates_with_errors(self, f, elems=None): """ Returns a sorted list of all candidates and their errors. The best candidate is first, the worst candidate is last. The "f" is the reference function which we want to approximate using "self". """ cand_with_errors = [] orig = f.project_onto(self._mesh) dof_orig = orig.dofs() err_orig = (f - orig).l2_norm() for a, b, order in self._mesh.iter_elems(elems): self.logger.info("Considering element: %s %s %s", a, b, order) cands = generate_candidates(a, b, order) #self.logger.debug("Candidates %s", cands) #print "-"*40 #print a, b, order best_crit = 1e10 for m in cands: #self.logger.debug("Candidate: %s", m) #self.logger.debug(" create_mesh...") cand_mesh = self._mesh.use_candidate(m) #self.logger.debug(" project...") cand = f.project_onto(cand_mesh) dof_cand = cand.dofs() #self.logger.debug(" l2_norm...") # This is slow, because we are calculating the l2_norm over the # whole mesh and we are doing the union mesh as well. All we # need to do is integrate over the element we are interested # and cache the rest of the elements. diff = f - cand err_cand = diff.l2_norm() #self.logger.debug(" Choose...") if dof_cand == dof_orig: if err_cand < err_orig: # if this happens, it means that we can get better # approximation with the same DOFs, so we definitely take # this candidate: print "DOF_cand == DOF_orig:", dof_cand, dof_orig, err_cand, err_orig crit = -1e10 else: crit = 1e10 # forget this candidate elif dof_cand > dof_orig: # if DOF rises, we take the candidate that goes the steepest in # the log/sqrt error/DOFs convergence graph # we want 'crit' as negative as possible: crit = (log(err_cand) - log(err_orig)) / \ sqrt(dof_cand - dof_orig) #print crit, err_cand, err_orig, dof_cand, dof_orig else: if err_cand < err_orig: # if this happens, it means that we can get better # approximation with less DOFs, so we definitely take # this candidate: print "Nice!", dof_cand, dof_orig, err_cand, err_orig crit = -1e10 else: crit = 1e10 # forget this candidate if crit < best_crit: best_m = m best_crit = crit cand_with_errors.append((best_m, best_crit)) #if crit < -1e9: # cand_with_errors.sort(key=lambda x: x[1]) # return cand_with_errors cand_with_errors.sort(key=lambda x: x[1]) return cand_with_errors def dofs(self): return self._mesh.dofs()