def SpaceTimeWeakSet(gfu_e, cf, space_fes): """ Ondocumented feature """ gfu_e_repl = GridFunction(space_fes) gfu_e_repl.Set(cf) gfu_e.vec[:].data = gfu_e_repl.vec
def PartialApproximateWithFESpace(N=64, order=3, f="sin(3·pi·x)", j=2): i = j if f == "sin(3·pi·x)": f = Sin(3 * pi * X) elif f == "sin(3·pi·x)·cos(5·pi·x)": f = Sin(3 * pi * X) * Cos(5 * pi * X) else: print("no function provided") return mesh1D = Mesh1D(N) try: f(mesh1D(0.5)) except: print("expression for f not valid!") return fes = H1(mesh1D, order=order) if (i > fes.ndof): print("j is too large. Setting j = ", fes.ndof - 1) i = fes.ndof gf = GridFunction(fes) gf.Set(f) for j in range(i, fes.ndof): gf.vec[j] = 0 Draw1D(mesh1D, [(gf, "FE Approximation"), (f, "exakte Funktion")], n_p=20) for j in range(i): print("{:5.2f}".format(gf.vec[j]), end=" ") if (j > 0 and j % 18 == 17): print("")
def ApproximateWithFESpace(N=64, order=3, f="sin(3·pi·x)"): if f == "sin(3·pi·x)": f = Sin(3 * pi * X) elif f == "sin(3·pi·x)·cos(5·pi·x)": f = Sin(3 * pi * X) * Cos(5 * pi * X) else: print("no function provided") return mesh1D = Mesh1D(N) try: f(mesh1D(0.5)) except: print("expression for f not valid!") return fes = H1(mesh1D, order=order) gf = GridFunction(fes) gf.Set(f) Draw1D(mesh1D, [(gf, "FE Approximation"), (f, "exakte Funktion")], n_p=20) for i in range(fes.ndof): print("{:5.2f}".format(gf.vec[i]), end=" ") if (i > 0 and i % 18 == 17): print("")
def create_and_load_gridfunction_from_file(filename: str, fes: FESpace) -> GridFunction: """ Function to create a gridfunction and load the contents of a file into it. The gridfunction will be constructed from the same finite element space that the file data is assumed to come from. The file data must also fit the exact same dimensions and number of components as the given finite element space. NOTE: It is assumed that the gridfunction in the file is from the same FES and mesh as the one passed in, there is no way for the code to check. NOTE: If the FES and mesh are not the same, NGSolve will not necesarily crash, it will instead silently return garbage. Args: filename: File to load. fes: The finite element space the file data was created for. Returns: gfu: A gridfunction containing the values from the file """ # Check that the file exists. if not os.path.isfile(filename): raise FileNotFoundError( 'The file \"{}\" does not exist.'.format(filename)) # Load gridfunction from file. gfu = GridFunction(fes) gfu.Load(filename) return gfu
def dxtref(mesh, order=None, time_order=-1, **kwargs): """ Differential symbol for the integration over all elements extruded by the reference interval [0,1] to space-time prisms. Parameters ---------- mesh : ngsolve.Mesh The spatial mesh. The domain type of interest. order : int Modify the order of the integration rule used. definedon : Region Domain description on where the integrator is defined. vb : {VOL, BND, BBND} Integration on domains volume or boundary. Default: VOL (if combined with skeleton VOL means interior facets, BND means boundary facets) element_boundary : bool Integration on each element boundary. Default: False element_vb : {VOL, BND, BBND} Integration on each element or its (B)boundary. Default: VOL (is overwritten by element_boundary if element_boundary is True) skeleton : bool Integration over element-interface. Default: False. deformation : ngsolve.GridFunction Mesh deformation. Default: None. definedonelements : ngsolve.BitArray Allows integration only on elements or facets (if skeleton=True) that are marked True. Default: None. time_order : int Order in time that is used in the space-time integration. Default: time_order=-1 means that no space-time rule will be applied. This is only relevant for space-time discretizations. Return ------ CutDifferentialSymbol(VOL) """ tFE = ScalarTimeFE(1) STFES = tFE*H1(mesh) gflset = GridFunction(STFES) gflset.vec[:] = 1 for i in range(gflset.space.ndof): gflset.vec[i] = i+1 lsetdom = {"levelset": gflset, "domain_type": POS} if order is not None: if type(order) != int: raise Exception("dxtref: order is not an integer! use keyword arguments for vb=VOL/BND.") lsetdom["order"] = order if type(time_order) != int: raise Exception("dxtref: time_order is not an integer! use keyword arguments for vb=VOL/BND.") if time_order > -1: lsetdom["time_order"] = time_order return _dCut_raw(lsetdom, **kwargs)
def __init__(self, mesh=None, levelset=None): self.deform = GridFunction(H1(mesh, order=1, dim=mesh.dim), "dummy_deform") self.deform.vec.data[:] = 0.0 if levelset != None: if mesh == None: raise Exception("need mesh") self.lset_p1 = GridFunction(H1(mesh, order=1)) InterpolateToP1(levelset, self.lset_p1)
def DrawBasisFunction(N=4, i=0, order=1): mesh1D = Mesh1D(N) fes = H1(mesh1D, order=order) gf = GridFunction(fes) gf.vec[:] = 0 if i >= fes.ndof: print(" i is too large. Setting it to", fes.ndof - 1) i = fes.ndof - 1 gf.vec[i] = 1 Draw1D(mesh1D, [(gf, "Basisfunktion " + str(i))], n_p=2 * order**2)
def IndicatorCF(mesh, ba, facets=False): """ Returns a CoefficientFunction that evaluates a BitArray. On elements/facets with an index i where the BitArray evaluates to true the CoefficientFunction will evaluate as 1, otherwise as 0. Similar functionality (only on elements) can be obtained with BitArrayCF. """ if facets: ret = GridFunction(FESpace("facet", mesh, order=0)) for i in range(len(ba)): if ba[i]: ret.vec[i] = 1.0 else: ret.vec[i] = 0.0 return ret else: return BitArrayCF(BitArray(ba))
def update_gridfunction_from_files( gfu: GridFunction, file_dict: Dict[Optional[int], str]) -> None: """ Function to take an existing gridfunction and load data into it from one or more files. NOTE: It is assumed that the save data in the files is from the same FES and mesh as the existing grid function. There is no way to check this in code. NOTE: If the FES and mesh are not the same, NGSolve will not necesarily crash, it will instead silently return garbage. Args: file_dict: Dict containing the paths to the files gfu: The gridfunction to load the values into """ for key, val in file_dict.items(): # Check that the file exists. if not os.path.isfile(val): raise FileNotFoundError('The given file does not exist.') if key is None: # A single gridfunction # Confirm that file_dict only has one value, otherwise the gfu values will be overwritten multiple times assert len(file_dict) == 1 gfu.Load(val) else: # The values for the various components of the gridfunction were saved separately. gfu.components[key].Load(val)
def CutRatioGF(cutinfo): """ Ratio between negative and full part of an element. Vector taken from CutInfo and put into a piecewise constant GridFunction. """ ret = GridFunction(L2(cutinfo.Mesh(), order=0)) ret.vec.data = cutinfo.GetCutRatios(VOL) return ret
def dxtref(mesh, order=None, time_order=-1, **kwargs): """ Differential symbol for the integration over all elements extruded by the reference interval [0,1] to space-time prisms. Parameters ---------- mesh : ngsolve.Mesh The spatial mesh. The domain type of interest. order : int Modify the order of the integration rule used. definedon : Region Domain description on where the integrator is defined. element_boundary : bool Integration on each element boundary. Default: False element_vb : {VOL, BND, BBND} Integration on each element or its (B)boundary. Default: VOL (is overwritten by element_boundary if element_boundary is True) skeleton : bool Integration over element-interface. Default: False. deformation : ngsolve.GridFunction Mesh deformation. Default: None. definedonelements : ngsolve.BitArray Allows integration only on elements or facets (if skeleton=True) that are marked True. Default: None. time_order : int Order in time that is used in the space-time integration. Default: time_order=-1 means that no space-time rule will be applied. This is only relevant for space-time discretizations. Return ------ CutDifferentialSymbol(VOL) """ gflset = GridFunction(H1(mesh)) gflset.vec[:] = 1 lsetdom = {"levelset": gflset, "domain_type": POS} if order is not None: lsetdom["order"] = order if time_order > -1: lsetdom["time_order"] = time_order return _dCut_raw(lsetdom, **kwargs)
def dmesh(mesh=None, *args, **kwargs): """ Differential symbol for the integration over all elements in the mesh. Parameters ---------- mesh : ngsolve.Mesh The spatial mesh. The domain type of interest. definedon : Region Domain description on where the integrator is defined. element_boundary : bool Integration on each element boundary. Default: False element_vb : {VOL, BND, BBND} Integration on each element or its (B)boundary. Default: VOL (is overwritten by element_boundary if element_boundary is True) skeleton : bool Integration over element-interface. Default: False. deformation : ngsolve.GridFunction Mesh deformation. Default: None. definedonelements : ngsolve.BitArray Allows integration only on elements or facets (if skeleton=True) that are marked True. Default: None. tref : float turns a spatial integral resulting in spatial integration rules into a space-time quadrature rule with fixed reference time tref Return ------ CutDifferentialSymbol(VOL) """ if "tref" in kwargs: if mesh == None: raise Exception("dx(..,tref..) needs mesh") gflset = GridFunction(H1(mesh)) gflset.vec[:] = 1 lsetdom = { "levelset": gflset, "domain_type": POS, "tref": kwargs["tref"] } del kwargs["tref"] return _dCut_raw(lsetdom, **kwargs) else: return dx(*args, **kwargs)
def _sol_to_vtu(gfu: GridFunction, sol_path_str: str, output_dir_path: str, save_names: str, delete_sol_file: bool, subdivision: int, mesh: Mesh) -> str: """ Function that gets parallelized and does the actual sol-to-vtu conversion. Args: gfu: The grid function into which to load the .sol file sol_path_str: The path to the solve file to load output_dir_path: The path to the directory to save the .vtu into save_names: The names of the variables to save delete_sol_file: Whether or not to detele the sol file after subdivision: Number of subdivisions on each mesh element mesh: The mesh on which the gfu was solved. Returns: ~: A string containing the entry for the .pvd file for this .vtu file. """ # Get the timestep for this .sol file from its name sol_name = sol_path_str.split('/')[-1][:-4] time_str = sol_name.split('_')[-1] # Name for the .vtu filename = output_dir_path + 'vtu/' + sol_name # Load data into gfu gfu.Load(sol_path_str) # Convert gfu components into form needed for VTKOutput if len(gfu.components) > 0: coefs = [component for component in gfu.components] else: coefs = [gfu] # Write to .vtk VTKOutput(ma=mesh, coefs=coefs, names=save_names, filename=filename, subdivision=subdivision).Do() # Convert .vtk to .vtu meshio.read(filename + '.vtk').write(filename + '.vtu') # Remove the .vtk remove(filename + '.vtk') # Delete .sol if delete_sol_file: remove(sol_path_str) # Write timestep in .pvd return '<DataSet timestep=\"%e\" group=\"\" part=\"0\" file=\"%s\"/>\n'\ % (float(time_str), 'vtu/' + filename.split('/')[-1] + '.vtu')
def ComputeMatrixEntry2(N=8, order=1, k=1, i=0, j=0): mesh1D = Mesh1D(N) fes = H1(mesh1D, order=order) #, dirichlet=) gf1 = GridFunction(fes) gf2 = GridFunction(fes) gf1.vec[:] = 0 gf1.vec[i] = 1. gf2.vec[:] = 0 gf2.vec[j] = 1. Draw1D(mesh1D, [(gf1, "phi_i"), (gf2, "phi_j")], n_p=5 * order**2, figsize=(12, 3.5)) Draw1D(mesh1D, [(grad(gf1)[0], "dphi_idx"), (grad(gf2)[0], "dphi_jdx")], n_p=5 * order**2, figsize=(12, 3.5)) print("A[i,j] = ", Integrate(gf1.Deriv() * gf2.Deriv(), mesh1D, order=2 * (order - 1)))
def solvewave(mesh, p, F, q_zero, mu_zero, cwave, exactu=None): a, f, X, sep = makeforms(mesh, p, F, q_zero, mu_zero, cwave) euz = GridFunction(X) c = Preconditioner(type="local", bf=a) a.Assemble() f.Assemble() BVP(bf=a, lf=f, gf=euz, pre=c, maxsteps=10000, prec=1.e-10).Do() er = compute_error(euz, sep, exactu) return (er, euz, sep, X)
def kappa(mesh,lset_approx, subdivlvl=0): """ Tuple of ratios between negative/positive and full part of an element (deprecated). """ print("kappa-function is deprecated - use CutRatioGF instead") kappa1 = GridFunction(L2(mesh,order=0)) lset_neg = { "levelset" : lset_approx, "domain_type" : NEG, "subdivlvl" : subdivlvl} kappa_f = LinearForm(kappa1.space) kappa_f += SymbolicLFI(levelset_domain = lset_neg, form = kappa1.space.TestFunction() ) kappa_f.Assemble(); kappa1.space.SolveM(CoefficientFunction(1.0),kappa_f.vec) kappa1.vec.data = kappa_f.vec kappa2 = 1.0 - kappa1 return (kappa1,kappa2)
def SpaceTimeSet(self, cf, *args, **kwargs): """ Overrides the NGSolve version of Set in case of a space-time FESpace. In this case the usual Set() is used on each nodal dof in time. """ if (isinstance(self.space, CSpaceTimeFESpace)): cf = CoefficientFunction(cf) gfs = GridFunction(self.space.spaceFES) ndof_node = len(gfs.vec) j = 0 for i, ti in enumerate(self.space.TimeFE_nodes()): if self.space.IsTimeNodeActive(i): ngsolveSet(gfs, fix_tref(cf, ti), *args, **kwargs) self.vec[j * ndof_node:(j + 1) * ndof_node].data = gfs.vec[:] j += 1 else: ngsolveSet(self, cf, *args, **kwargs)
def _facet_jumps(sol: GridFunction, mesh: Mesh) -> float: """ Function to check how continuous the solution is across mesh facets. This is mainly of interest when DG is used. Continuous Galerkin FEM solutions will always be perfectly continuous across facets. Args: sol: The solution GridFunction. mesh: The mesh that was solved on. Returns: mag_jumps: The L2 norm of the facet jumps. """ mag_jumps = ngs.sqrt( ngs.Integrate((sol - sol.Other())**2 * ngs.dx(element_boundary=True), mesh)) return mag_jumps
def load_coefficientfunction_into_gridfunction( gfu: ngs.GridFunction, coef_dict: Dict[Optional[int], ngs.CoefficientFunction]) -> None: """ Function to load a coefficientfunction(s) into a gridfunction. Coefficientfunction may have a different dimension than the gridfunction and need to be loaded into a specific component. Args: coef_dict: Dict containing the coefficientfunction(s) and which component they belong to (keys) gfu: The gridfunction to load the values into """ for key, val in coef_dict.items(): if key is None: # Confirm that coef_dict only has one value, otherwise the gfu values will be overwritten multiple times assert len(coef_dict) == 1 gfu.Set(val) else: gfu.components[key].Set(val)
def apply_dirichlet_bcs_to(self, gfu: ngs.GridFunction) -> None: """ Function to set the Dirichlet boundary conditions within the solution GridFunction. Args: gfu: The GridFunction to add the Dirichlet boundary condition values to """ # NOTE: DO NOT change from definedon=self.mesh.Boundaries(marker) to definedon=marker. if len(self.g_D) > 0: if len(gfu.components) == 0: # Single trial functions # TODO: IDE is complaining that we don't specify parameter VOL_OR_BND for .Set() gfu.Set(self.g_D['u'], definedon=self.mesh.Boundaries( self.dirichlet_names['u'])) else: # Multiple trial functions. for component_name in self.g_D.keys(): # Apply Dirichlet or pinned BCs. i = self.model_components[component_name] gfu.components[i].Set( self.g_D[component_name], definedon=self.mesh.Boundaries( self.dirichlet_names[component_name]))
def solvewavedirect(mesh, p, F, q_zero, mu_zero, cwave, exactu=None, epsil=1.e-9): a, f, X, sep = makeforms(mesh, p, F, q_zero, mu_zero, cwave, epsil=epsil) euz = GridFunction(X) a.Assemble() f.Assemble() f.vec.data += a.harmonic_extension_trans * f.vec euz.vec.data = a.mat.Inverse(X.FreeDofs(True)) * f.vec euz.vec.data += a.harmonic_extension * euz.vec euz.vec.data += a.inner_solve * f.vec er = compute_error(euz, sep, exactu) return (er, euz, sep, X)
p = 3 # polynomial degree h0 = 1 # coarse mesh size for unit square domain markprm = 0.5 # percentage of max total error for marking # SET UP: mesh = Mesh(unit_square.GenerateMesh(maxh=h0)) q_zero = 'bottom' # Mesh boundary parts where q and mu_zero = 'bottom|right|left' # mu has essential b.c u00 = exp(-1000 * ((x - 0.5) * (x - 0.5))) # Nonzero initial condition cwave = 1. # wave speed F = ngs.CoefficientFunction((0, 0)) # Zero source a, f, X, sep = makeforms(mesh, p, F, q_zero, mu_zero, cwave, epsil=1.e-10) euz = GridFunction(X) # Contains solution at each adaptive step q = euz.components[sep[0]] # Volume (L2) components mu = euz.components[sep[0]+1] zq = euz.components[sep[1]] # Interface components zmu = euz.components[sep[1]+1] zq.Set(u00, definedon='bottom') zmu.Set(-u00, definedon='bottom') ngs.Draw(mu, autoscale=False, # draw only one of the solution components min=-1.0, max=1.0) # ADAPTIVE LOOP: globalerr = 1 # estimated total error
# H1-conforming finite element space fes = H1(mesh, order=3, dirichlet=[1,2,3,4]) # define trial- and test-functions u = fes.TrialFunction() v = fes.TestFunction() # the right hand side f = LinearForm(fes) f += 32 * (y*(1-y)+x*(1-x)) * v * dx # the bilinear-form a = BilinearForm(fes, symmetric=True) a += grad(u)*grad(v)*dx a.Assemble() f.Assemble() # the solution field gfu = GridFunction(fes) gfu.vec.data = a.mat.Inverse(fes.FreeDofs(), inverse="sparsecholesky") * f.vec # print (u.vec) # plot the solution (netgen-gui only) Draw (gfu) Draw (-grad(gfu), mesh, "Flux") exact = 16*x*(1-x)*y*(1-y) print ("L2-error:", sqrt (Integrate ( (gfu-exact)*(gfu-exact), mesh)))
def Heat1DFEM(N=8, order=1, k1=1, k2=1, Q1=0, Q2=10, boundary_condition_left="Robin", boundary_condition_right="Dirichlet", value_left=0, value_right=1, q_value_left=0, q_value_right=1, r_value_left=0, r_value_right=1, intervalsize=0.14): if (boundary_condition_left == "Neumann" or (boundary_condition_left == "Robin" and r_value_left == 0)) and ( boundary_condition_right == "Neumann" or (boundary_condition_right == "Robin" and r_value_right == 0)): print("Temperatur ist nicht eindeutig bestimmt.") #return mesh1D = Mesh1D(N, interval=(0, intervalsize)) dbnds = [] if boundary_condition_left == "Dirichlet": dbnds.append(1) # print(True) if boundary_condition_right == "Dirichlet": dbnds.append(2) fes = H1(mesh1D, order=order, dirichlet=dbnds) gf = GridFunction(fes) if boundary_condition_left == "Dirichlet": gf.vec[0] = value_left if boundary_condition_right == "Dirichlet": gf.vec[N] = value_right Q = IfPos(X - 0.5 * intervalsize, Q2, Q1) k = IfPos(X - 0.5 * intervalsize, k2, k1) u, v = fes.TnT() a = BilinearForm(fes) a += SymbolicBFI(k * grad(u) * grad(v)) if boundary_condition_left == "Robin": a += SymbolicBFI(r_value_left * u * v, definedon=mesh1D.Boundaries("left")) if boundary_condition_right == "Robin": a += SymbolicBFI(r_value_right * u * v, definedon=mesh1D.Boundaries("right")) a.Assemble() f = LinearForm(fes) f += SymbolicLFI(Q * v) f.Assemble() if boundary_condition_left == "Neumann": f.vec[0] += q_value_left elif boundary_condition_left == "Robin": f.vec[0] += r_value_left * value_left if boundary_condition_right == "Neumann": f.vec[N] += q_value_right elif boundary_condition_right == "Robin": f.vec[N] += r_value_right * value_right f.vec.data -= a.mat * gf.vec gf.vec.data += a.mat.Inverse(fes.FreeDofs()) * f.vec Draw1D(mesh1D, [(gf, "u_h")], n_p=5 * order**2)