def get_integrator(backend, systemcls, rallocs, mesh, initsoln, cfg): form = cfg.get('solver-time-integrator', 'formulation', 'std') if form == 'std': cn = cfg.get('solver-time-integrator', 'controller') sn = cfg.get('solver-time-integrator', 'scheme') cc = subclass_where(BaseStdController, controller_name=cn) sc = subclass_where(BaseStdStepper, stepper_name=sn) elif form == 'dual': cn = cfg.get('solver-time-integrator', 'controller') sn = cfg.get('solver-time-integrator', 'scheme') cc = subclass_where(BaseDualController, controller_name=cn) sc = subclass_where(BaseDualStepper, stepper_name=sn) else: raise ValueError('Invalid integrator formulation') # Determine the integrator name name = '_'.join([form, cn, sn, 'integrator']) name = re.sub('(?:^|_|-)([a-z])', lambda m: m.group(1).upper(), name) # Composite the base classes together to form a new type integrator = type(name, (cc, sc), dict(name=name)) # Construct and return an instance of this new integrator class return integrator(backend, systemcls, rallocs, mesh, initsoln, cfg)
def _write_data(self, vtuf, mk, sk): name = self.mesh_inf[mk][0] mesh = self.mesh[mk] soln = self.soln[sk] # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=name) subdvcls = subclass_where(BaseShapeSubDiv, name=name) # Dimensions nspts, neles = mesh.shape[:2] # Sub divison points inside of a standard element svpts = shapecls.std_ele(self.divisor) nsvpts = len(svpts) # Shape soln_b = shapecls(nspts, self.cfg) # Generate the operator matrices mesh_vtu_op = soln_b.sbasis.nodal_basis_at(svpts) soln_vtu_op = soln_b.ubasis.nodal_basis_at(svpts) # Calculate node locations of vtu elements vpts = np.dot(mesh_vtu_op, mesh.reshape(nspts, -1)) vpts = vpts.reshape(nsvpts, -1, self.ndims) # Calculate solution at node locations of vtu elements vsol = np.dot(soln_vtu_op, soln.reshape(-1, self.nvars*neles)) vsol = vsol.reshape(nsvpts, self.nvars, -1).swapaxes(0, 1) # Append dummy z dimension for points in 2D if self.ndims == 2: vpts = np.pad(vpts, [(0, 0), (0, 0), (0, 1)], 'constant') # Write element node locations to file self._write_darray(vpts.swapaxes(0, 1), vtuf, self.dtype) # Perform the sub division nodes = subdvcls.subnodes(self.divisor) # Prepare vtu cell arrays vtu_con = np.tile(nodes, (neles, 1)) vtu_con += (np.arange(neles)*nsvpts)[:, None] # Generate offset into the connectivity array vtu_off = np.tile(subdvcls.subcelloffs(self.divisor), (neles, 1)) vtu_off += (np.arange(neles)*len(nodes))[:, None] # Tile vtu cell type numbers vtu_typ = np.tile(subdvcls.subcelltypes(self.divisor), neles) # Write vtu node connectivity, connectivity offsets and cell types self._write_darray(vtu_con, vtuf, np.int32) self._write_darray(vtu_off, vtuf, np.int32) self._write_darray(vtu_typ, vtuf, np.uint8) # Process and write out the various fields for arr in self._proc_fields(vsol): self._write_darray(arr.T, vtuf, self.dtype)
def _write_vtu_header(args, vtuf, m_inf, s_inf, off): # Set vtk name for float, set size in bytes if args.precision == 'single': flt = ['Float32', 4] else: flt = ['Float64', 8] # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=m_inf[0]) subdvcls = subclass_where(BaseShapeSubDiv, name=m_inf[0]) npts = shapecls.nspts_from_order(args.divisor + 1) * m_inf[1][1] cells = subdvcls.subcells(args.divisor) nodes = subdvcls.subnodes(args.divisor) ncells = len(cells) * m_inf[1][1] nnodes = len(nodes) * m_inf[1][1] # Standard template for vtk DataArray string darray = '<DataArray Name="%s" type="%s" NumberOfComponents="%s" '\ 'format="appended" offset="%d"/>\n' # Write headers for vtu elements vtuf.write('<Piece NumberOfPoints="%s" NumberOfCells="%s">\n<Points>\n' % (npts, ncells)) # Lists of DataArray "names", "types" and "NumberOfComponents" nams = [ '', 'connectivity', 'offsets', 'types', 'Density', 'Velocity', 'Pressure' ] typs = [flt[0], 'Int32', 'Int32', 'UInt8'] + [flt[0]] * 3 ncom = ['3', '', '', '', '1', str(m_inf[1][2]), '1'] # Calculate size of described DataArrays (in bytes) offs = np.array([0, 3, 4 * nnodes, 4 * ncells, ncells, 1, m_inf[1][2], 1]) offs[[1, 5, 6, 7]] *= flt[1] * npts # Write vtk DaraArray headers for i in xrange(len(nams)): vtuf.write( darray % (nams[i], typs[i], ncom[i], sum(offs[:i + 1]) + i * 4 + off)) # Write ends/starts of vtk file objects if i == 0: vtuf.write('</Points>\n<Cells>\n') elif i == 3: vtuf.write('</Cells>\n<PointData>\n') # Write end of vtk element data vtuf.write('</PointData>\n</Piece>\n') # Return the number of bytes appended return sum(offs) + 4 * len(nams)
def get_pseudo_stepper_cls(name, porder): for p in range(porder, -1, -1): try: return subclass_where(BaseDualPseudoStepper, pseudo_stepper_name=name, pseudo_stepper_porder=p) except KeyError: pass return subclass_where(BaseDualPseudoStepper, pseudo_stepper_name=name)
def _get_npts_ncells_nnodes(self, sk): etype, neles = self.soln_inf[sk][0], self.soln_inf[sk][1][2] # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=etype) subdvcls = subclass_where(BaseShapeSubDiv, name=etype) # Number of vis points npts = shapecls.nspts_from_order(self.divisor + 1)*neles # Number of sub cells and nodes ncells = len(subdvcls.subcells(self.divisor))*neles nnodes = len(subdvcls.subnodes(self.divisor))*neles return npts, ncells, nnodes
def _get_npts_ncells_nnodes(self, mk): m_inf = self.mesh_inf[mk] # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=m_inf[0]) subdvcls = subclass_where(BaseShapeSubDiv, name=m_inf[0]) # Number of vis points npts = shapecls.nspts_from_order(self.divisor + 1) * m_inf[1][1] # Number of sub cells and nodes ncells = len(subdvcls.subcells(self.divisor)) * m_inf[1][1] nnodes = len(subdvcls.subnodes(self.divisor)) * m_inf[1][1] return npts, ncells, nnodes
def _get_npts_ncells_nnodes(self, mk): m_inf = self.mesh_inf[mk] # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=m_inf[0]) subdvcls = subclass_where(BaseShapeSubDiv, name=m_inf[0]) # Number of vis points npts = shapecls.nspts_from_order(self.divisor + 1)*m_inf[1][1] # Number of sub cells and nodes ncells = len(subdvcls.subcells(self.divisor))*m_inf[1][1] nnodes = len(subdvcls.subnodes(self.divisor))*m_inf[1][1] return npts, ncells, nnodes
def __init__(self, args): from pyfr.solvers.base import BaseSystem self.outf = args.outf # Load the mesh and solution files self.soln = NativeReader(args.solnf) self.mesh = NativeReader(args.meshf) # Check solution and mesh are compatible if self.mesh['mesh_uuid'] != self.soln['mesh_uuid']: raise RuntimeError('Solution "%s" was not computed on mesh "%s"' % (args.solnf, args.meshf)) # Load the configuration and stats files self.cfg = Inifile(self.soln['config']) self.stats = Inifile(self.soln['stats']) # Data file prefix (defaults to soln for backwards compatibility) self.dataprefix = self.stats.get('data', 'prefix', 'soln') # Get element types and array shapes self.mesh_inf = self.mesh.array_info('spt') self.soln_inf = self.soln.array_info(self.dataprefix) # Dimensions self.ndims = next(iter(self.mesh_inf.values()))[1][2] self.nvars = next(iter(self.soln_inf.values()))[1][1] # System and elements classes self.systemscls = subclass_where(BaseSystem, name=self.cfg.get('solver', 'system')) self.elementscls = self.systemscls.elementscls
def _pre_proc_fields_grad(self, name, mesh, soln): # Call the reference pre-processor soln = self._pre_proc_fields_ref(name, mesh, soln) # Dimensions nvars, nupts = soln.shape[:2] # Get the shape class basiscls = subclass_where(BaseShape, name=name) # Construct an instance of the relevant elements class eles = self.elementscls(basiscls, mesh, self.cfg) # Get the smats and |J|^-1 to untransform the gradient smat = eles.smat_at_np('upts').transpose(2, 0, 1, 3) rcpdjac = eles.rcpdjac_at_np('upts') # Gradient operator gradop = eles.basis.m4.astype(self.dtype) # Evaluate the transformed gradient of the solution gradsoln = np.dot(gradop, soln.swapaxes(0, 1).reshape(nupts, -1)) gradsoln = gradsoln.reshape(self.ndims, nupts, nvars, -1) # Untransform gradsoln = np.einsum('ijkl,jkml->mikl', smat * rcpdjac, gradsoln, dtype=self.dtype, casting='same_kind') gradsoln = gradsoln.reshape(nvars * self.ndims, nupts, -1) return np.vstack([soln, gradsoln])
def _pre_proc_fields_grad(self, name, mesh, soln): # Call the reference pre-processor soln = self._pre_proc_fields_ref(name, mesh, soln) # Dimensions nvars, nupts = soln.shape[:2] # Get the shape class basiscls = subclass_where(BaseShape, name=name) # Construct an instance of the relevant elements class eles = self.elementscls(basiscls, mesh, self.cfg) # Get the smats and |J|^-1 to untransform the gradient smat = eles.smat_at_np('upts').transpose(2, 0, 1, 3) rcpdjac = eles.rcpdjac_at_np('upts') # Gradient operator gradop = eles.basis.m4.astype(self.dtype) # Evaluate the transformed gradient of the solution gradsoln = np.dot(gradop, soln.swapaxes(0, 1).reshape(nupts, -1)) gradsoln = gradsoln.reshape(self.ndims, nupts, nvars, -1) # Untransform gradsoln = np.einsum('ijkl,jkml->mikl', smat*rcpdjac, gradsoln, dtype=self.dtype, casting='same_kind') gradsoln = gradsoln.reshape(nvars*self.ndims, nupts, -1) return np.vstack([soln, gradsoln])
def __init__(self, args): from pyfr.solvers.base import BaseSystem self.outf = args.outf # Load the mesh and solution files self.soln = NativeReader(args.solnf) self.mesh = NativeReader(args.meshf) # Check solution and mesh are compatible if self.mesh['mesh_uuid'] != self.soln['mesh_uuid']: raise RuntimeError('Solution "%s" was not computed on mesh "%s"' % (args.solnf, args.meshf)) # Load the configuration and stats files self.cfg = Inifile(self.soln['config']) self.stats = Inifile(self.soln['stats']) # Data file prefix (defaults to soln for backwards compatibility) self.dataprefix = self.stats.get('data', 'prefix', 'soln') # Get element types and array shapes self.mesh_inf = self.mesh.array_info('spt') self.soln_inf = self.soln.array_info(self.dataprefix) # Dimensions self.ndims = next(iter(self.mesh_inf.values()))[1][2] self.nvars = next(iter(self.soln_inf.values()))[1][1] # System and elements classes self.systemscls = subclass_where( BaseSystem, name=self.cfg.get('solver', 'system') ) self.elementscls = self.systemscls.elementscls
def get_pseudo_integrator(backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt): register_tabulated_pseudo_steppers() # A new type of integrator allowing multip convergence acceleration if 'solver-dual-time-integrator-multip' in cfg.sections(): return DualMultiPIntegrator(backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt) else: cn = cfg.get('solver-time-integrator', 'pseudo-controller') pn = cfg.get('solver-time-integrator', 'pseudo-scheme') porder = cfg.getint('solver', 'order') cc = subclass_where(BaseDualPseudoController, pseudo_controller_name=cn) pc = get_pseudo_stepper_cls(pn, porder) # Determine the integrator name name = '_'.join(['dual', cn, pn, 'pseudointegrator']) name = re.sub('(?:^|_|-)([a-z])', lambda m: m.group(1).upper(), name) pseudointegrator = type(name, (cc, pc), dict(name=name)) # Construct and return an instance of this new integrator class return pseudointegrator(backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt)
def get_quadrule(eletype, rule, npts): eletype_map = { 'line': BaseLineQuadRule, 'hex': BaseHexQuadRule, 'pri': BasePriQuadRule, 'quad': BaseQuadQuadRule, 'tet': BaseTetQuadRule, 'tri': BaseTriQuadRule } # Obtain the base quadrature rule class for this element type basecls = eletype_map[eletype] # See if rule looks like the name of a scheme if re.match(r'[a-zA-Z0-9\-+*_]+$', rule): return subclass_where(basecls, name=rule)(npts) # Otherwise see if it looks like a tabulation elif 'PTS' in rule.upper(): # Create a suitable subclass rulecls = type(eletype, (basecls, BaseTabulatedQuadRule), {}) # Instantiate and validate r = rulecls(rule) if len(r.points) != npts: raise ValueError('Invalid number of points for quad rule') return r # Invalid else: raise ValueError('Invalid quadrature rule')
def get_integrator(backend, systemcls, rallocs, mesh, initsoln, cfg): # Look-up the controller, stepper and writer names c = cfg.get('solver-time-integrator', 'controller') s = cfg.get('solver-time-integrator', 'scheme') controller = subclass_where(BaseController, controller_name=c) stepper = subclass_where(BaseStepper, stepper_name=s) # Determine the integrator name name = '_'.join([c, s]) name = re.sub('(?:^|_|-)([a-z])', lambda m: m.group(1).upper(), name) # Composite the classes together to form a new type integrator = type(name, (controller, stepper), dict(name=name)) # Construct and return an instance of this new integrator class return integrator(backend, systemcls, rallocs, mesh, initsoln, cfg)
def _write_data(self, vtuf, mk, sk): name = self.mesh_inf[mk][0] mesh = self.mesh[mk].astype(self.dtype) soln = self.soln[sk].swapaxes(0, 1).astype(self.dtype) # Dimensions nspts, neles = mesh.shape[:2] # Sub divison points inside of a standard element svpts = self._get_std_ele(name, nspts) nsvpts = len(svpts) # Generate the operator matrices mesh_vtu_op = self._get_mesh_op(name, nspts, svpts) soln_vtu_op = self._get_soln_op(name, nspts, svpts) # Calculate node locations of VTU elements vpts = np.dot(mesh_vtu_op, mesh.reshape(nspts, -1)) vpts = vpts.reshape(nsvpts, -1, self.ndims) # Pre-process the solution soln = self._pre_proc_fields(name, mesh, soln).swapaxes(0, 1) # Interpolate the solution to the vis points vsoln = np.dot(soln_vtu_op, soln.reshape(len(soln), -1)) vsoln = vsoln.reshape(nsvpts, -1, neles).swapaxes(0, 1) # Append dummy z dimension for points in 2D if self.ndims == 2: vpts = np.pad(vpts, [(0, 0), (0, 0), (0, 1)], 'constant') # Write element node locations to file self._write_darray(vpts.swapaxes(0, 1), vtuf, self.dtype) # Perform the sub division subdvcls = subclass_where(BaseShapeSubDiv, name=name) nodes = subdvcls.subnodes(self.divisor) # Prepare VTU cell arrays vtu_con = np.tile(nodes, (neles, 1)) vtu_con += (np.arange(neles)*nsvpts)[:, None] # Generate offset into the connectivity array vtu_off = np.tile(subdvcls.subcelloffs(self.divisor), (neles, 1)) vtu_off += (np.arange(neles)*len(nodes))[:, None] # Tile VTU cell type numbers vtu_typ = np.tile(subdvcls.subcelltypes(self.divisor), neles) # Write VTU node connectivity, connectivity offsets and cell types self._write_darray(vtu_con, vtuf, np.int32) self._write_darray(vtu_off, vtuf, np.int32) self._write_darray(vtu_typ, vtuf, np.uint8) # Process and write out the various fields for arr in self._post_proc_fields(vsoln): self._write_darray(arr.T, vtuf, self.dtype)
def _write_data(self, vtuf, mk, sk): name = self.mesh_inf[mk][0] mesh = self.mesh[mk].astype(self.dtype) soln = self.soln[sk].swapaxes(0, 1).astype(self.dtype) # Dimensions nspts, neles = mesh.shape[:2] # Sub divison points inside of a standard element svpts = self._get_std_ele(name, nspts) nsvpts = len(svpts) # Generate the operator matrices mesh_vtu_op = self._get_mesh_op(name, nspts, svpts) soln_vtu_op = self._get_soln_op(name, nspts, svpts) # Calculate node locations of VTU elements vpts = np.dot(mesh_vtu_op, mesh.reshape(nspts, -1)) vpts = vpts.reshape(nsvpts, -1, self.ndims) # Pre-process the solution soln = self._pre_proc_fields(name, mesh, soln).swapaxes(0, 1) # Interpolate the solution to the vis points vsoln = np.dot(soln_vtu_op, soln.reshape(len(soln), -1)) vsoln = vsoln.reshape(nsvpts, -1, neles).swapaxes(0, 1) # Append dummy z dimension for points in 2D if self.ndims == 2: vpts = np.pad(vpts, [(0, 0), (0, 0), (0, 1)], 'constant') # Write element node locations to file self._write_darray(vpts.swapaxes(0, 1), vtuf, self.dtype) # Perform the sub division subdvcls = subclass_where(BaseShapeSubDiv, name=name) nodes = subdvcls.subnodes(self.divisor) # Prepare VTU cell arrays vtu_con = np.tile(nodes, (neles, 1)) vtu_con += (np.arange(neles) * nsvpts)[:, None] # Generate offset into the connectivity array vtu_off = np.tile(subdvcls.subcelloffs(self.divisor), (neles, 1)) vtu_off += (np.arange(neles) * len(nodes))[:, None] # Tile VTU cell type numbers vtu_typ = np.tile(subdvcls.subcelltypes(self.divisor), neles) # Write VTU node connectivity, connectivity offsets and cell types self._write_darray(vtu_con, vtuf, np.int32) self._write_darray(vtu_off, vtuf, np.int32) self._write_darray(vtu_typ, vtuf, np.uint8) # Process and write out the various fields for arr in self._post_proc_fields(vsoln): self._write_darray(arr.T, vtuf, self.dtype)
def __init__(self, npts): roots = mp.polyroots([1, 1, 0, -2*npts]) roots = [int(x) for x in roots if mp.isint(x) and x > 0] if not roots: raise ValueError('Invalid number of points for quad rule') tname, lname = self.name.split('*') trulecls = subclass_where(BaseTriQuadRule, name=tname) trule = trulecls(roots[0]*(roots[0] + 1) // 2) lrulecls = subclass_where(BaseLineQuadRule, name=lname) lrule = lrulecls[lname](roots[0]) self.points = [(t[0], t[1], l) for l in lrule.points for t in trule.points] if hasattr(trule, 'weights') and hasattr(lrule, 'weights'): self.weights = [i*j for j in lrule.weights for i in trule.weights]
def __init__(self, npts): if not mp.isint(mp.sqrt(npts)): raise ValueError('Invalid number of points for quad rule') rulecls = subclass_where(BaseLineQuadRule, name=self.name) rule = rulecls(int(mp.sqrt(npts))) self.points = [(i, j) for j in rule.points for i in rule.points] if hasattr(rule, 'weights'): self.weights = [i*j for j in rule.weights for i in rule.weights]
def get_std_ele_by_name(name, order): """ Gets the shape points of a standard element of name and order :param name: Name of PyFR element type :param order: Shape order of element :type name: string :type order: integer :rtype: np.ndarray """ return subclass_where(BaseBasis, name=name).std_ele(order)
def get_pseudo_integrator(backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt): # A new type of integrator allowing multip convergence acceleration if 'solver-dual-time-integrator-multip' in cfg.sections(): return DualMultiPIntegrator(backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt) else: cn = cfg.get('solver-time-integrator', 'pseudo-controller') pn = cfg.get('solver-time-integrator', 'pseudo-scheme') cc = subclass_where(BaseDualPseudoController, pseudo_controller_name=cn) pc = subclass_where(BaseDualPseudoStepper, pseudo_stepper_name=pn) # Determine the integrator name name = '_'.join(['dual', cn, pn, 'pseudointegrator']) name = re.sub('(?:^|_|-)([a-z])', lambda m: m.group(1).upper(), name) pseudointegrator = type(name, (cc, pc), dict(name=name)) # Construct and return an instance of this new integrator class return pseudointegrator(backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt)
def _get_npts_ncells_nnodes_ho(self, sk): etype, neles = self.soln_inf[sk][0], self.soln_inf[sk][1][2] # Fallback to subdivision for pyramids if etype == 'pyr': return self._get_npts_ncells_nnodes_lin(sk) # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=etype) # Total number of vis points npts = neles*shapecls.nspts_from_order(self.etypes_div[etype] + 1) return npts, neles, npts
def _find_curved_eles(etype, eles, tol=1e-5): nspts, neles, ndims = eles.shape shape = subclass_where(BaseShape, name=etype) order = shape.order_from_nspts(nspts) basis = get_polybasis(etype, order, shape.std_ele(order - 1)) lmodes = get_polybasis(etype, 2).degrees hmodes = [i for i, j in enumerate(basis.degrees) if j not in lmodes] ehmodes = basis.invvdm.T[hmodes] @ eles.reshape(nspts, -1) ehmodes = ehmodes.reshape(-1, neles, ndims) return np.any(np.abs(ehmodes) > tol, axis=(0, 2))
def get_integrator(backend, systemcls, rallocs, mesh, initsoln, cfg): form = cfg.get('solver-time-integrator', 'formulation', 'std') if form == 'std': cn = cfg.get('solver-time-integrator', 'controller') sn = cfg.get('solver-time-integrator', 'scheme') cc = subclass_where(BaseStdController, controller_name=cn) sc = subclass_where(BaseStdStepper, stepper_name=sn) bases = [(cn, cc), (sn, sc)] elif form == 'dual': cn = cfg.get('solver-time-integrator', 'controller') pn = cfg.get('solver-time-integrator', 'pseudo-scheme') sn = cfg.get('solver-time-integrator', 'scheme') cc = subclass_where(BaseDualController, controller_name=cn) pc = subclass_where(BaseDualPseudoStepper, pseudo_stepper_name=pn) sc = subclass_where(BaseDualStepper, stepper_name=sn) bases = [(cn, cc), (pn, pc), (sn, sc)] if 'solver-dual-time-integrator-multip' in cfg.sections(): bases.insert(0, ('multip', DualMultiPIntegrator)) else: raise ValueError('Invalid integrator formulation') # Determine the integrator name name = '_'.join([form] + list(bn for bn, bc in bases) + ['integrator']) name = re.sub('(?:^|_|-)([a-z])', lambda m: m.group(1).upper(), name) # Composite the base classes together to form a new type integrator = type(name, tuple(bc for bn, bc in bases), dict(name=name)) # Construct and return an instance of this new integrator class return integrator(backend, systemcls, rallocs, mesh, initsoln, cfg)
def _get_npts_ncells_nnodes_ho(self, sk): etype, neles = self.soln_inf[sk][0], self.soln_inf[sk][1][2] if etype == 'pyr': # No Lagrange pyr cells in VTK # Therefore, rely on sub-division with # linear cells return self._get_npts_ncells_nnodes_lin(sk) # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=etype) # Number of vis points # which coincides with the number of # nodes of the vtkLagrange* correspondent # objects npts = shapecls.nspts_from_order(self.etypes_div[etype] + 1) * neles return npts, neles, npts
def _linearise_eles(self, lintol): lidx = {} for etype, pent in self._elenodes: if pent != self._felespent: continue # Elements and type information elesix = self._elenodes[etype, pent] petype, nnodes = self._etype_map[etype] # Obtain the dimensionality of the element type ndim = self._petype_ndim[petype] # Node maps between input and PyFR orderings itop = self._nodemaps[petype, nnodes] ptoi = np.argsort(itop) # Construct the element array eles = self._nodepts[elesix[:, itop], :ndim].swapaxes(0, 1) # Generate the associated polynomial bases shape = subclass_where(BaseShape, name=petype) order = shape.order_from_nspts(nnodes) hbasis = get_polybasis(petype, order, shape.std_ele(order - 1)) lbasis = get_polybasis(petype, 2, shape.std_ele(1)) htol = hbasis.nodal_basis_at(lbasis.pts) ltoh = lbasis.nodal_basis_at(hbasis.pts) leles = (ltoh @ htol) @ eles.reshape(nnodes, -1) leles = leles.reshape(nnodes, -1, ndim) # Use this to determine which elements are linear num = np.max(np.abs(eles - leles), axis=0) den = np.max(eles, axis=0) - np.min(eles, axis=0) lin = lidx[petype] = np.any(num / den < lintol, axis=1) for ix in np.nonzero(lin)[0]: self._nodepts[elesix[ix], :ndim] = leles[ptoi, ix] return lidx
def __init__(self, args): """Loads PyFR mesh and solution files A check is made to ensure the solution was computed on the mesh. :param args: Command line arguments passed from scripts/postp.py :type args: class 'argparse.Namespace' """ self.outf = args.outf # Load mesh and solution files self.soln = read_pyfr_data(args.solnf) self.mesh = read_pyfr_data(args.meshf) # Get element types and array shapes self.mesh_inf = self.mesh.array_info self.soln_inf = self.soln.array_info # Dimensions self.ndims = next(iter(self.mesh_inf.values()))[1][2] self.nvars = next(iter(self.soln_inf.values()))[1][1] # Check solution and mesh are compatible if self.mesh['mesh_uuid'] != self.soln['mesh_uuid']: raise RuntimeError('Solution "%s" was not computed on mesh "%s"' % (args.solnf, args.meshf)) # Load the config file self.cfg = Inifile(self.soln['config']) # System and elements classs self.systemscls = subclass_where( BaseSystem, name=self.cfg.get('solver', 'system') ) self.elementscls = self.systemscls.elementscls
def _write_vtu_data(args, vtuf, cfg, mesh, m_inf, soln, s_inf): """ Writes mesh and solution data for appended (binary) data .vtu files :param args: pyfr-postp command line arguments from argparse :param vtuf: .vtu output file :param cfg: PyFR config file used in the respective simulation :param mesh: Single PyFR mesh array (corresponding to soln) :param m_inf: Tuple of element type and array shape of mesh :param soln: Single PyFR solution array (corresponding to mesh) :param s_inf: Tuple of element type and array shape of soln :type args: class 'argparse.Namespace' :type vtuf: file :type cfg: class 'pyfr.inifile.Inifile' :type mesh: numpy.ndarray :type m_inf: tuple :type soln: numpy.ndrayy :type s_inf: tuple """ # Set numpy name for float; set size in bytes if args.precision == 'single': flt = ['float32', 4] else: flt = ['float64', 8] # Get the basis class basiscls = subclass_where(BaseShape, name=m_inf[0]) ndims = m_inf[1][2] # Set npts for divide/append cases if args.divisor is not None: npts = _npts_from_order(args.divisor, m_inf, total=False) else: npts = ParaviewWriter.vtk_to_pyfr[m_inf[0]][2] # Generate basis objects for solution and vtu output soln_b = basiscls(m_inf[1][0], cfg) vtu_b = basiscls(npts, cfg) # Generate operator matrices to move points and solutions to vtu nodes mesh_vtu_op = soln_b.sbasis.nodal_basis_at(vtu_b.spts) soln_vtu_op = soln_b.ubasis.nodal_basis_at(vtu_b.spts) # Calculate node locations of vtu elements pts = np.dot(mesh_vtu_op, mesh.reshape(m_inf[1][0], -1)).reshape(npts, -1, ndims) # Calculate solution at node locations of vtu elements sol = np.dot(soln_vtu_op, soln.reshape(s_inf[1][0], -1)).reshape(npts, s_inf[1][1], -1) # Append dummy z dimension for points in 2-d (required by Paraview) if ndims == 2: pts = np.append(pts, np.zeros(pts.shape[:-1])[...,None], axis=2) # Write element node locations to file _write_vtk_darray(pts.swapaxes(0,1), vtuf, flt[0]) # Prepare vtu cell arrays (connectivity, offsets, types): # Generate and extend vtu sub-cell node connectivity across all elements vtu_con = np.tile(_base_con(m_inf[0], args.divisor), (m_inf[1][1], 1)) vtu_con += (np.arange(m_inf[1][1]) * npts)[:, None] # Generate offset into the connectivity array for the end of each element vtu_off = np.arange(_ncells_after_subdiv(m_inf, args.divisor)) + 1 vtu_off *= ParaviewWriter.vtk_to_pyfr[m_inf[0]][2] # Tile vtu cell type numbers vtu_typ = np.tile(ParaviewWriter.vtk_to_pyfr[m_inf[0]][0], _ncells_after_subdiv(m_inf, args.divisor)) # Write vtu node connectivity, connectivity offsets and cell types _write_vtk_darray(vtu_con, vtuf, 'int32') _write_vtk_darray(vtu_off, vtuf, 'int32') _write_vtk_darray(vtu_typ, vtuf, 'uint8') # Convert rhou, rhov, [rhow] to u, v, [w] and energy to pressure _component_to_physical_soln(sol, cfg.getfloat('constants', 'gamma')) # Write Density, Velocity and Pressure _write_vtk_darray(sol[:,0].T, vtuf, flt[0]) _write_vtk_darray(sol[:,1:-1].transpose(2, 0, 1), vtuf, flt[0]) _write_vtk_darray(sol[:,-1].T, vtuf, flt[0]) # Append high-order data as CellData if not dividing cells if args.divisor is None: # Calculate number of points written as low-order, and left to append nlpts = ParaviewWriter.vtk_to_pyfr[s_inf[0]][2] nhpts = s_inf[1][0] - nlpts # Generate basis objects for mesh, solution and vtu output mesh_b = basiscls(m_inf[1][0], cfg) # Get location of spts in standard element of solution order uord = cfg.getint('solver', 'order') ele_spts = subclass_where(BaseShape, name=m_inf[0]).std_ele(uord) # Generate operator matrices to move points and solutions to vtu nodes mesh_hpts_op = mesh_b.sbasis.nodal_basis_at(ele_spts) soln_hpts_op = mesh_b.ubasis.nodal_basis_at(ele_spts) # Calculate node locations of vtu elements pts = np.dot(mesh_hpts_op, mesh.reshape(m_inf[1][0], -1)) pts = pts.reshape((-1,) + m_inf[1][1:]) # Append dummy z dimension to 2-d points (required by Paraview) if ndims == 2: pts = np.append(pts, np.zeros(pts.shape[:-1])[...,None], axis=2) # Calculate solution at node locations sol = np.dot(soln_hpts_op, soln.reshape(s_inf[1][0], -1)) sol = sol.reshape((-1,) + s_inf[1][1:]) # Convert rhou, rhov, [rhow] to u, v, [w] and energy to pressure _component_to_physical_soln(sol, cfg.getfloat('constants', 'gamma')) # Write data arrays, one set of high order points at a time for gmshpt in xrange(nhpts): # Convert Gmsh node number to equivalent in PyFR pt = GmshNodeMaps.to_pyfr[s_inf[0], s_inf[1][0]][gmshpt + nlpts] # Write node locations, density, velocity and pressure _write_vtk_darray(pts[pt], vtuf, flt[0]) _write_vtk_darray(sol[pt,0], vtuf, flt[0]) _write_vtk_darray(sol[pt,1:-1].T, vtuf, flt[0]) _write_vtk_darray(sol[pt,-1], vtuf, flt[0])
def _write_data(self, vtuf, mk, sk): name = self.mesh_inf[mk][0] mesh = self.mesh[mk].astype(self.dtype) soln = self.soln[sk].swapaxes(0, 1).astype(self.dtype) # Handle the case of partial solution files if soln.shape[2] != mesh.shape[1]: skpre, skpost = sk.rsplit('_', 1) mesh = mesh[:, self.soln[f'{skpre}_idxs_{skpost}'], :] # Dimensions nspts, neles = mesh.shape[:2] # Sub divison points inside of a standard element svpts = self._get_std_ele(name, nspts) nsvpts = len(svpts) if name != 'pyr' and self.ho_output: # Transform PyFR to VTK9 points # See nodemaps.py for more information on # why VTK9 maps are chosen over those of VTK8 # High order `pyr` elements are not currently # supported in VTK svpts = np.array(svpts)[VTKHONodeMaps.to_pyfr[name, nsvpts]] # Generate the operator matrices mesh_vtu_op = self._get_mesh_op(name, nspts, svpts) soln_vtu_op = self._get_soln_op(name, nspts, svpts) # Calculate node locations of VTU elements vpts = mesh_vtu_op @ mesh.reshape(nspts, -1) vpts = vpts.reshape(nsvpts, -1, self.ndims) # Pre-process the solution soln = self._pre_proc_fields(name, mesh, soln).swapaxes(0, 1) # Interpolate the solution to the vis points vsoln = soln_vtu_op @ soln.reshape(len(soln), -1) vsoln = vsoln.reshape(nsvpts, -1, neles).swapaxes(0, 1) # Append dummy z dimension for points in 2D if self.ndims == 2: vpts = np.pad(vpts, [(0, 0), (0, 0), (0, 1)], 'constant') # Write element node locations to file self._write_darray(vpts.swapaxes(0, 1), vtuf, self.dtype) # Perform the sub division if name != 'pyr' and self.ho_output: nodes = np.arange(nsvpts) subcellsoff = nsvpts types = self.vtk_types_ho[name] else: subdvcls = subclass_where(BaseShapeSubDiv, name=name) nodes = subdvcls.subnodes(self.etypes_div[name]) subcellsoff = subdvcls.subcelloffs(self.etypes_div[name]) types = subdvcls.subcelltypes(self.etypes_div[name]) # Prepare VTU cell arrays vtu_con = np.tile(nodes, (neles, 1)) vtu_con += (np.arange(neles) * nsvpts)[:, None] # Generate offset into the connectivity array vtu_off = np.tile(subcellsoff, (neles, 1)) vtu_off += (np.arange(neles) * len(nodes))[:, None] # Tile VTU cell type numbers vtu_typ = np.tile(types, neles) # Write VTU node connectivity, connectivity offsets and cell types self._write_darray(vtu_con, vtuf, np.int32) self._write_darray(vtu_off, vtuf, np.int32) self._write_darray(vtu_typ, vtuf, np.uint8) # Process and write out the various fields for arr in self._post_proc_fields(vsoln): self._write_darray(arr.T, vtuf, self.dtype)
def _get_shape(self, name, nspts): shapecls = subclass_where(BaseShape, name=name) return shapecls(nspts, self.cfg)
def get_rank_allocation(mesh, cfg): name = cfg.get('backend', 'rank-allocator', 'linear') return subclass_where(BaseRankAllocator, name=name)(mesh, cfg)
def __init__(self, backend, systemcls, rallocs, mesh, initsoln, cfg, tcoeffs, dt): self.backend = backend sect = 'solver-time-integrator' mgsect = 'solver-dual-time-integrator-multip' # Get the solver order and set the initial multigrid level self._order = self.level = order = cfg.getint('solver', 'order') # Get the multigrid cycle self.cycle, self.csteps = zip(*cfg.getliteral(mgsect, 'cycle')) self.levels = sorted(set(self.cycle), reverse=True) if max(self.cycle) > self._order: raise ValueError('The multigrid level orders cannot exceed ' 'the solution order') if any(abs(i - j) > 1 for i, j in zip(self.cycle, self.cycle[1:])): raise ValueError('The orders of consecutive multigrid levels can ' 'only change by one') if self.cycle[0] != self._order or self.cycle[-1] != self._order: raise ValueError('The multigrid cycle needs to start end with the ' 'highest (solution) order ') # Initialise the number of cycles self.npmgcycles = 0 # Multigrid pseudo-time steps dtau = cfg.getfloat(sect, 'pseudo-dt') self.dtauf = cfg.getfloat(mgsect, 'pseudo-dt-fact', 1.0) self._maxniters = cfg.getint(sect, 'pseudo-niters-max', 0) self._minniters = cfg.getint(sect, 'pseudo-niters-min', 0) # Get the multigrid pseudostepper and pseudocontroller classes pn = cfg.get(sect, 'pseudo-scheme') cn = cfg.get(sect, 'pseudo-controller') cc = subclass_where(BaseDualPseudoController, pseudo_controller_name=cn) cc_none = subclass_where(BaseDualPseudoController, pseudo_controller_name='none') # Construct a pseudo-integrator for each level from pyfr.integrators.dual.pseudo import get_pseudo_stepper_cls self.pintgs = {} for l in self.levels: pc = get_pseudo_stepper_cls(pn, l) if l == order: bases = [cc, pc] mcfg = cfg else: bases = [cc_none, pc] mcfg = Inifile(cfg.tostr()) mcfg.set('solver', 'order', l) mcfg.set(sect, 'pseudo-dt', dtau * self.dtauf**(order - l)) for s in cfg.sections(): m = re.match(f'solver-(.*)-mg-p{l}$', s) if m: mcfg.rename_section(s, f'solver-{m.group(1)}') # A class that bypasses pseudo-controller methods within a cycle class lpsint(*bases): name = 'MultiPPseudoIntegrator' + str(l) aux_nregs = 2 if l != self._order else 0 @property def _aux_regidx(iself): if iself.aux_nregs != 0: return iself._regidx[-2:] @property def ntotiters(iself): return self.npmgcycles def convmon(iself, *args, **kwargs): pass def finalise_pseudo_advance(iself, *args, **kwargs): pass def _rhs_with_dts(iself, t, uin, fout): # Compute -∇·f iself.system.rhs(t, uin, fout) # Coefficients for the physical stepper svals = [sc / iself._dt for sc in iself._stepper_coeffs] # Physical stepper source addition -∇·f - dQ/dt axnpby = iself._get_axnpby_kerns(len(svals) + 1, subdims=iself._subdims) iself._prepare_reg_banks(fout, iself._idxcurr, *iself._stepper_regidx) iself._queue.enqueue_and_run(axnpby, 1, *svals) # Multigrid r addition if iself._aux_regidx: axnpby = iself._get_axnpby_kerns(2) iself._prepare_reg_banks(fout, iself._aux_regidx[0]) iself._queue.enqueue_and_run(axnpby, 1, -1) self.pintgs[l] = lpsint(backend, systemcls, rallocs, mesh, initsoln, mcfg, tcoeffs, dt) # Get the highest p system from plugins self.system = self.pintgs[self._order].system # Get the convergence monitoring method self.mg_convmon = cc.convmon # Initialise the restriction and prolongation matrices self._init_proj_mats() # Delete remaining elements maps from multigrid systems for l in self.levels[1:]: del self.pintgs[l].system.ele_map
def get_writer_by_name(name, *args, **kwargs): return subclass_where(BaseWriter, name=name)(*args, **kwargs)
def _get_shape(self, name, nspts): shapecls = subclass_where(BaseShape, name=name) return shapecls(nspts, self.cfg)
def get_writer_by_name(name, *args, **kwargs): return subclass_where(BaseWriter, name=name)(*args, **kwargs)
def get_plugin(name, *args, **kwargs): return subclass_where(BasePlugin, name=name)(*args, **kwargs)
def _write_vtu_data(args, vtuf, cfg, mesh, m_inf, soln, s_inf): dtype = 'float32' if args.precision == 'single' else 'float64' # Get the shape and sub division classes shapecls = subclass_where(BaseShape, name=m_inf[0]) subdvcls = subclass_where(BaseShapeSubDiv, name=m_inf[0]) nspts, neles, ndims = m_inf[1] nvpts = shapecls.nspts_from_order(args.divisor + 1) # Generate basis objects for solution and vtu output soln_b = shapecls(nspts, cfg) vtu_b = shapecls(nvpts, cfg) # Generate operator matrices to move points and solutions to vtu nodes mesh_vtu_op = soln_b.sbasis.nodal_basis_at(vtu_b.spts) soln_vtu_op = soln_b.ubasis.nodal_basis_at(vtu_b.spts) # Calculate node locations of vtu elements pts = np.dot(mesh_vtu_op, mesh.reshape(nspts, -1)) pts = pts.reshape(nvpts, -1, ndims) # Calculate solution at node locations of vtu elements sol = np.dot(soln_vtu_op, soln.reshape(s_inf[1][0], -1)) sol = sol.reshape(nvpts, s_inf[1][1], -1) # Append dummy z dimension for points in 2-d (required by Paraview) if ndims == 2: pts = np.append(pts, np.zeros(pts.shape[:-1])[..., None], axis=2) # Write element node locations to file _write_vtk_darray(pts.swapaxes(0, 1), vtuf, dtype) # Perform the sub division cells = subdvcls.subcells(args.divisor) nodes = subdvcls.subnodes(args.divisor) # Prepare vtu cell arrays (connectivity, offsets, types): # Generate and extend vtu sub-cell node connectivity across all elements vtu_con = np.tile(nodes, (neles, 1)) vtu_con += (np.arange(neles) * nvpts)[:, None] # Generate offset into the connectivity array for the end of each element vtu_off = np.tile(subdvcls.subcelloffs(args.divisor), (neles, 1)) vtu_off += (np.arange(neles) * len(nodes))[:, None] # Tile vtu cell type numbers vtu_typ = np.tile(subdvcls.subcelltypes(args.divisor), neles) # Write vtu node connectivity, connectivity offsets and cell types _write_vtk_darray(vtu_con, vtuf, 'int32') _write_vtk_darray(vtu_off, vtuf, 'int32') _write_vtk_darray(vtu_typ, vtuf, 'uint8') # Convert rhou, rhov, [rhow] to u, v, [w] and energy to pressure _component_to_physical_soln(sol, cfg.getfloat('constants', 'gamma')) # Write Density, Velocity and Pressure _write_vtk_darray(sol[:, 0].T, vtuf, dtype) _write_vtk_darray(sol[:, 1:-1].transpose(2, 0, 1), vtuf, dtype) _write_vtk_darray(sol[:, -1].T, vtuf, dtype)
def get_polybasis(name, order, pts=[]): return subclass_where(BasePolyBasis, name=name)(order, pts)
def get_backend(name, cfg): return subclass_where(BaseBackend, name=name.lower())(cfg)
def get_solver(backend, rallocs, mesh, initsoln, cfg): systemcls = subclass_where(BaseSystem, name=cfg.get('solver', 'system')) # Combine with an integrator to yield the solver return get_integrator(backend, systemcls, rallocs, mesh, initsoln, cfg)
def __init__(self, backend, systemcls, rallocs, mesh, initsoln, cfg, stp_nregs, stg_nregs, dt): self.backend = backend sect = 'solver-time-integrator' mgsect = 'solver-dual-time-integrator-multip' # Get the solver order and set the initial multigrid level self._order = self.level = order = cfg.getint('solver', 'order') # Get the multigrid cycle self.cycle, self.csteps = zip(*cfg.getliteral(mgsect, 'cycle')) self.levels = sorted(set(self.cycle), reverse=True) if max(self.cycle) > self._order: raise ValueError('The multigrid level orders cannot exceed ' 'the solution order') if any(abs(i - j) > 1 for i, j in zip(self.cycle, self.cycle[1:])): raise ValueError('The orders of consecutive multigrid levels can ' 'only change by one') if self.cycle[0] != self._order or self.cycle[-1] != self._order: raise ValueError('The multigrid cycle needs to start end with the ' 'highest (solution) order ') # Initialise the number of cycles self.npmgcycles = 0 # Multigrid pseudo-time steps dtau = cfg.getfloat(sect, 'pseudo-dt') self.dtauf = cfg.getfloat(mgsect, 'pseudo-dt-fact', 1.0) self._maxniters = cfg.getint(sect, 'pseudo-niters-max', 0) self._minniters = cfg.getint(sect, 'pseudo-niters-min', 0) # Get the multigrid pseudostepper and pseudocontroller classes pn = cfg.get(sect, 'pseudo-scheme') cn = cfg.get(sect, 'pseudo-controller') cc = subclass_where(BaseDualPseudoController, pseudo_controller_name=cn) cc_none = subclass_where(BaseDualPseudoController, pseudo_controller_name='none') # Construct a pseudo-integrator for each level from pyfr.integrators.dual.pseudo import get_pseudo_stepper_cls self.pintgs = {} for l in self.levels: pc = get_pseudo_stepper_cls(pn, l) if l == order: bases = [cc, pc] mcfg = cfg else: bases = [cc_none, pc] mcfg = Inifile(cfg.tostr()) mcfg.set('solver', 'order', l) mcfg.set(sect, 'pseudo-dt', dtau * self.dtauf**(order - l)) for s in cfg.sections(): if (m := re.match(f'solver-(.*)-mg-p{l}$', s)): mcfg.rename_section(s, f'solver-{m.group(1)}') # A class that bypasses pseudo-controller methods within a cycle class lpsint(*bases): name = 'MultiPPseudoIntegrator' + str(l) aux_nregs = 2 if l != self._order else 0 stepper_nregs = stp_nregs if l == self._order else 0 stage_nregs = stg_nregs if l == self._order else 0 @property def _aux_regidx(iself): if iself.aux_nregs != 0: return iself._regidx[-2:] @property def ntotiters(iself): return self.npmgcycles def convmon(iself, *args, **kwargs): pass def _rhs_with_dts(iself, t, uin, fout, mg_add=True): # Compute -∇·f iself.system.rhs(t, uin, fout) if iself.stage_nregs > 1: iself._add(0, self._stage_regidx[iself.currstg], 1, fout) # Registers vals = iself.stepper_coeffs[:2] + [1] regs = [fout, iself._idxcurr, iself._source_regidx] # Physical stepper source addition -∇·f - dQ/dt iself._addv(vals, regs, subdims=iself._subdims) # Multigrid r addition if mg_add and iself._aux_regidx: iself._add(1, fout, -1, iself._aux_regidx[0]) self.pintgs[l] = lpsint(backend, systemcls, rallocs, mesh, initsoln, mcfg, stp_nregs, stg_nregs, dt)
def get_reader_by_name(name, *args, **kwargs): return subclass_where(BaseReader, name=name)(*args, **kwargs)
def get_rank_allocation(mesh, cfg): name = cfg.get("backend", "rank-allocator", "linear") return subclass_where(BaseRankAllocator, name=name)(mesh, cfg)
def get_polybasis(name, order, pts=[]): return subclass_where(BasePolyBasis, name=name)(order, pts)
def get_partitioner(name, *args, **kwargs): return subclass_where(BasePartitioner, name=name)(*args, **kwargs)
def get_reader_by_name(name, *args, **kwargs): return subclass_where(BaseReader, name=name)(*args, **kwargs)
def _write_vtu_data(args, vtuf, cfg, mesh, m_inf, soln, s_inf): """ Writes mesh and solution data for appended (binary) data .vtu files :param args: pyfr-postp command line arguments from argparse :param vtuf: .vtu output file :param cfg: PyFR config file used in the respective simulation :param mesh: Single PyFR mesh array (corresponding to soln) :param m_inf: Tuple of element type and array shape of mesh :param soln: Single PyFR solution array (corresponding to mesh) :param s_inf: Tuple of element type and array shape of soln :type args: class 'argparse.Namespace' :type vtuf: file :type cfg: class 'pyfr.inifile.Inifile' :type mesh: numpy.ndarray :type m_inf: tuple :type soln: numpy.ndrayy :type s_inf: tuple """ # Set numpy name for float; set size in bytes if args.precision == 'single': flt = ['float32', 4] else: flt = ['float64', 8] # Get the basis class basiscls = subclass_where(BaseBasis, name=m_inf[0]) ndims = m_inf[1][2] # Set npts for divide/append cases if args.divisor is not None: npts = _npts_from_order(args.divisor, m_inf, total=False) else: npts = ParaviewWriter.vtk_to_pyfr[m_inf[0]][2] # Generate basis objects for solution and vtu output soln_b = basiscls(m_inf[1][0], cfg) vtu_b = basiscls(npts, cfg) # Generate operator matrices to move points and solutions to vtu nodes mesh_vtu_op = np.array(soln_b.sbasis_at(vtu_b.spts), dtype=float) soln_vtu_op = np.array(soln_b.ubasis_at(vtu_b.spts), dtype=float) # Calculate node locations of vtu elements pts = np.dot(mesh_vtu_op, mesh.reshape(m_inf[1][0], -1)).reshape(npts, -1, ndims) # Calculate solution at node locations of vtu elements sol = np.dot(soln_vtu_op, soln.reshape(s_inf[1][0], -1)).reshape(npts, s_inf[1][1], -1) # Append dummy z dimension for points in 2-d (required by Paraview) if ndims == 2: pts = np.append(pts, np.zeros(pts.shape[:-1])[...,None], axis=2) # Write element node locations to file _write_vtk_darray(pts.swapaxes(0,1), vtuf, flt[0]) # Prepare vtu cell arrays (connectivity, offsets, types): # Generate and extend vtu sub-cell node connectivity across all elements vtu_con = np.tile(_base_con(m_inf[0], args.divisor), (m_inf[1][1], 1)) vtu_con += (np.arange(m_inf[1][1]) * npts)[:, None] # Generate offset into the connectivity array for the end of each element vtu_off = np.arange(_ncells_after_subdiv(m_inf, args.divisor)) + 1 vtu_off *= ParaviewWriter.vtk_to_pyfr[m_inf[0]][2] # Tile vtu cell type numbers vtu_typ = np.tile(ParaviewWriter.vtk_to_pyfr[m_inf[0]][0], _ncells_after_subdiv(m_inf, args.divisor)) # Write vtu node connectivity, connectivity offsets and cell types _write_vtk_darray(vtu_con, vtuf, 'int32') _write_vtk_darray(vtu_off, vtuf, 'int32') _write_vtk_darray(vtu_typ, vtuf, 'uint8') # Convert rhou, rhov, [rhow] to u, v, [w] and energy to pressure _component_to_physical_soln(sol, cfg.getfloat('constants', 'gamma')) # Write Density, Velocity and Pressure _write_vtk_darray(sol[:,0].T, vtuf, flt[0]) _write_vtk_darray(sol[:,1:-1].transpose(2, 0, 1), vtuf, flt[0]) _write_vtk_darray(sol[:,-1].T, vtuf, flt[0]) # Append high-order data as CellData if not dividing cells if args.divisor is None: # Calculate number of points written as low-order, and left to append nlpts = ParaviewWriter.vtk_to_pyfr[s_inf[0]][2] nhpts = s_inf[1][0] - nlpts # Generate basis objects for mesh, solution and vtu output mesh_b = basiscls(m_inf[1][0], cfg) # Get location of spts in standard element of solution order uord = cfg.getint('solver', 'order') ele_spts = get_std_ele_by_name(m_inf[0], uord) # Generate operator matrices to move points and solutions to vtu nodes mesh_hpts_op = np.array(mesh_b.sbasis_at(ele_spts), dtype=float) soln_hpts_op = np.array(mesh_b.ubasis_at(ele_spts), dtype=float) # Calculate node locations of vtu elements pts = np.dot(mesh_hpts_op, mesh.reshape(m_inf[1][0], -1)) pts = pts.reshape((-1,) + m_inf[1][1:]) # Append dummy z dimension to 2-d points (required by Paraview) if ndims == 2: pts = np.append(pts, np.zeros(pts.shape[:-1])[...,None], axis=2) # Calculate solution at node locations sol = np.dot(soln_hpts_op, soln.reshape(s_inf[1][0], -1)) sol = sol.reshape((-1,) + s_inf[1][1:]) # Convert rhou, rhov, [rhow] to u, v, [w] and energy to pressure _component_to_physical_soln(sol, cfg.getfloat('constants', 'gamma')) # Write data arrays, one set of high order points at a time for gmshpt in xrange(nhpts): # Convert Gmsh node number to equivalent in PyFR pt = GmshNodeMaps.to_pyfr[s_inf[0], s_inf[1][0]][gmshpt + nlpts] # Write node locations, density, velocity and pressure _write_vtk_darray(pts[pt], vtuf, flt[0]) _write_vtk_darray(sol[pt,0], vtuf, flt[0]) _write_vtk_darray(sol[pt,1:-1].T, vtuf, flt[0]) _write_vtk_darray(sol[pt,-1], vtuf, flt[0])