def _resid(self, dtau, x): comm, rank, root = get_comm_rank_root() # Get an errest kern to compute the square of the maximum residual errest = self._get_errest_kerns() # Prepare and run the kernel self._prepare_reg_banks(x, x, x) self._queue % errest(dtau, 0.0) # L2 norm if self._pseudo_norm == 'l2': # Reduce locally (element types) and globally (MPI ranks) res = np.array([sum(ev) for ev in zip(*errest.retval)]) comm.Allreduce(get_mpi('in_place'), res, op=get_mpi('sum')) # Normalise and return return np.sqrt(res / self._gndofs) # L^∞ norm else: # Reduce locally (element types) and globally (MPI ranks) res = np.array([max(ev) for ev in zip(*errest.retval)]) comm.Allreduce(get_mpi('in_place'), res, op=get_mpi('max')) # Normalise and return return np.sqrt(res)
def _errest(self, rcurr, rprev, rerr): comm, rank, root = get_comm_rank_root() errest = self._get_reduction_kerns(rcurr, rprev, rerr, method='errest', norm=self._norm) # Obtain an estimate for the squared error self._queue.enqueue_and_run(errest, self._atol, self._rtol) # L2 norm if self._norm == 'l2': # Reduce locally (element types + field variables) err = np.array([sum(v for e in errest for v in e.retval)]) # Reduce globally (MPI ranks) comm.Allreduce(get_mpi('in_place'), err, op=get_mpi('sum')) # Normalise err = math.sqrt(float(err) / self._gndofs) # L^∞ norm else: # Reduce locally (element types + field variables) err = np.array([max(v for e in errest for v in e.retval)]) # Reduce globally (MPI ranks) comm.Allreduce(get_mpi('in_place'), err, op=get_mpi('max')) # Normalise err = math.sqrt(float(err)) return err if not math.isnan(err) else 100
def _errest(self, x, y, z): comm, rank, root = get_comm_rank_root() errest = self._get_errest_kerns() # Obtain an estimate for the squared error self._prepare_reg_banks(x, y, z) self._queue % errest(self._atol, self._rtol) # L2 norm if self._norm == 'l2': # Reduce locally (element types) and globally (MPI ranks) rl = sum(errest.retval) rg = comm.allreduce(rl, op=get_mpi('sum')) # Normalise err = math.sqrt(rg / self._gndofs) # Uniform norm else: # Reduce locally (element types) and globally (MPI ranks) rl = max(errest.retval) rg = comm.allreduce(rl, op=get_mpi('max')) # Normalise err = math.sqrt(rg) return err if not math.isnan(err) else 100
def _resid(self, rcurr, rold, dt_fac): comm, rank, root = get_comm_rank_root() # Get a reduction kern to compute the square of the maximum residual resid = self._get_reduction_kerns(rcurr, rold, method='resid', norm=self._pseudo_norm) # Run the kernel self._queue.enqueue_and_run(resid, dt_fac) # L2 norm if self._pseudo_norm == 'l2': # Reduce locally (element types) and globally (MPI ranks) res = np.array([sum(ev) for ev in zip(*[r.retval for r in resid])]) comm.Allreduce(get_mpi('in_place'), res, op=get_mpi('sum')) # Normalise and return return tuple(np.sqrt(res / self._gndofs)) # L^∞ norm else: # Reduce locally (element types) and globally (MPI ranks) res = np.array([max(ev) for ev in zip(*[r.retval for r in resid])]) comm.Allreduce(get_mpi('in_place'), res, op=get_mpi('max')) # Normalise and return return tuple(np.sqrt(res))
def _errest(self, x, y, z): comm, rank, root = get_comm_rank_root() errest = self._get_errest_kerns() # Obtain an estimate for the squared error self._prepare_reg_banks(x, y, z) self._queue % errest(self._atol, self._rtol) # L2 norm if self._norm == 'l2': # Reduce locally (element types + field variables) err = np.array([sum(v for e in errest.retval for v in e)]) # Reduce globally (MPI ranks) comm.Allreduce(get_mpi('in_place'), err, op=get_mpi('sum')) # Normalise err = math.sqrt(float(err) / self._gndofs) # L^∞ norm else: # Reduce locally (element types + field variables) err = np.array([max(v for e in errest.retval for v in e)]) # Reduce globally (MPI ranks) comm.Allreduce(get_mpi('in_place'), err, op=get_mpi('max')) # Normalise err = math.sqrt(float(err)) return err if not math.isnan(err) else 100
def __call__(self, intg): # Return if no output is due if intg.nsteps % self.nsteps: return # MPI info comm, rank, root = get_comm_rank_root() # Solution matrices indexed by element type solns = dict(zip(intg.system.ele_types, intg.soln)) # Force vector f = np.zeros(self.ndims) for etype, fidx in self._m0: # Get the interpolation operator m0 = self._m0[etype, fidx] nfpts, nupts = m0.shape # Extract the relevant elements from the solution uupts = solns[etype][..., self._eidxs[etype, fidx]] # Interpolate to the face ufpts = np.dot(m0, uupts.reshape(nupts, -1)) ufpts = ufpts.reshape(nfpts, self.nvars, -1) ufpts = ufpts.swapaxes(0, 1) # Compute the pressure p = self.elementscls.conv_to_pri(ufpts, self.cfg)[-1] # Get the quadrature weights and normal vectors qwts = self._qwts[etype, fidx] norms = self._norms[etype, fidx] # Do the quadrature f += np.einsum('i...,ij,jik', qwts, p, norms) # Reduce and output if we're the root rank if rank != root: comm.Reduce(f, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), f, op=get_mpi('sum'), root=root) # Build the row row = [intg.tcurr] + f.tolist() # Write print(','.join(str(r) for r in row), file=self.outf) # Flush to disk self.outf.flush()
def _get_gndofs(self): comm, rank, root = get_comm_rank_root() # Get the number of degrees of freedom in this partition ndofs = sum(self.system.ele_ndofs) # Sum to get the global number over all partitions return comm.allreduce(ndofs, op=get_mpi('sum'))
def __call__(self, intg): # If an output is due this step if intg.nacptsteps % self.nsteps == 0 and intg.nacptsteps: # MPI info comm, rank, root = get_comm_rank_root() # Previous and current solution prev = self._prev curr = intg.soln # Square of the residual vector for each variable resid = sum( np.linalg.norm(p - c, axis=(0, 2))**2 for p, c in zip(prev, curr)) # Reduce and, if we are the root rank, output if rank != root: comm.Reduce(resid, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), resid, op=get_mpi('sum'), root=root) # Normalise resid = np.sqrt(resid) / (intg.tcurr - self._tprev) # Build the row row = [intg.tcurr] + resid.tolist() # Write print(','.join(str(r) for r in row), file=self.outf) # Flush to disk self.outf.flush() del self._prev, self._tprev # If an output is due next step if (intg.nacptsteps + 1) % self.nsteps == 0: self._prev = [s.copy() for s in intg.soln] self._tprev = intg.tcurr
def __call__(self, intg): # If an output is due this step if intg.nacptsteps % self.nsteps == 0 and intg.nacptsteps: # MPI info comm, rank, root = get_comm_rank_root() # Previous and current solution prev = self._prev curr = intg.soln # Square of the residual vector for each variable resid = sum(np.linalg.norm(p - c, axis=(0, 2))**2 for p, c in zip(prev, curr)) # Reduce and, if we are the root rank, output if rank != root: comm.Reduce(resid, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), resid, op=get_mpi('sum'), root=root) # Normalise resid = np.sqrt(resid) / (intg.tcurr - self._tprev) # Build the row row = [intg.tcurr] + resid.tolist() # Write print(','.join(str(r) for r in row), file=self.outf) # Flush to disk self.outf.flush() del self._prev, self._tprev # If an output is due next step if (intg.nacptsteps + 1) % self.nsteps == 0: self._prev = [s.copy() for s in intg.soln] self._tprev = intg.tcurr
def __call__(self, intg): if intg.nacptsteps % self.nsteps == 0: # MPI info comm, rank, root = get_comm_rank_root() # Evaluate the integation expressions iintex = self._eval_exprs(intg) # Reduce and output if we're the root rank if rank != root: comm.Reduce(iintex, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), iintex, op=get_mpi('sum'), root=root) # Write print(intg.tcurr, *iintex, sep=',', file=self.outf) # Flush to disk self.outf.flush()
def __init__(self, intg, cfgsect, suffix): super().__init__(intg, cfgsect, suffix) # Underlying elements class self.elementscls = intg.system.elementscls # Output frequency self.nsteps = self.cfg.getint(cfgsect, 'nsteps') # List of points to be sampled and format self.pts = ast.literal_eval(self.cfg.get(cfgsect, 'samp-pts')) self.fmt = self.cfg.get(cfgsect, 'format', 'primitive') # MPI info comm, rank, root = get_comm_rank_root() # MPI rank responsible for each point and rank-indexed info self._ptsrank = ptsrank = [] self._ptsinfo = ptsinfo = [[] for i in range(comm.size)] # Physical location of the solution points plocs = [p.swapaxes(1, 2) for p in intg.system.ele_ploc_upts] for p in self.pts: # Find the nearest point in our partition cp = _closest_upt(intg.system.ele_types, plocs, p) # Reduce over all partitions mcp, mrank = comm.allreduce(cp, op=get_mpi('minloc')) # Store the rank responsible along with the info ptsrank.append(mrank) ptsinfo[mrank].append(mcp[1:]) # If we're the root rank then open the output file if rank == root: # Determine the file path fname = self.cfg.get(cfgsect, 'file') # Append the '.csv' extension if not fname.endswith('.csv'): fname += '.csv' # Open for appending self.outf = open(fname, 'a') # Output a header if required if (os.path.getsize(fname) == 0 and self.cfg.getbool(cfgsect, 'header', True)): print(self._header, file=self.outf)
def __call__(self, intg): # If an output is due this step if intg.nacptsteps % self.nsteps == 0 and intg.nacptsteps: # MPI info comm, rank, root = get_comm_rank_root() # Previous and current solution prev = self._prev curr = intg.soln # Square of the residual vector for each variable resid = sum( np.linalg.norm(p - c, axis=(0, 2))**2 for p, c in zip(prev, curr)) # Reduce and, if we are the root rank, output if rank != root: comm.Reduce(resid, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), resid, op=get_mpi('sum'), root=root) # Normalise resid = np.sqrt(resid) / (intg.tcurr - self._tprev) # Write print(intg.tcurr, *resid, sep=',', file=self.outf) # Flush to disk self.outf.flush() del self._prev, self._tprev # Prep work if an output is due next step self._prep_next_output(intg)
def setup_dataframe(self): self.elementscls = self.solver.system.elementscls # List of points to be sampled and format file = open(self.base_dir + '/samp_pts.txt', 'r') self.pts = eval(file.read()) self.fmt = 'not primitive' # all the configs had this as primitive but i dont think it was used # Define directory where solution snapshots should be saved self.save_dir = 'sol_data' if self.baseline_file is not None: f = h5py.File(self.baseline_file, 'r') self.goal_state = np.array(f['sol_data']) else: self.goal_state = None # Initial omega self.solver.system.omega = 0 # MPI info comm, rank, root = get_comm_rank_root() # MPI rank responsible for each point and rank-indexed info self._ptsrank = ptsrank = [] self._ptsinfo = ptsinfo = [[] for i in range(comm.size)] # Physical location of the solution points plocs = [p.swapaxes(1, 2) for p in self.solver.system.ele_ploc_upts] # Load map from point to index with open(self.base_dir + '/loc_to_idx.json') as loc_to_idx: loc_to_idx_str = json.load(loc_to_idx, ) self.loc_to_idx = dict() for key in loc_to_idx_str: self.loc_to_idx[int(key)] = loc_to_idx_str[key] # Locate the closest solution points in our partition closest = _closest_upts(self.solver.system.ele_types, plocs, self.pts) # Process these points for cp in closest: # Reduce over the distance _, mrank = comm.allreduce((cp[0], rank), op=get_mpi('minloc')) # Store the rank responsible along with its info ptsrank.append(mrank) ptsinfo[mrank].append( comm.bcast(cp[1:] if rank == mrank else None, root=mrank))
def __init__(self, backend, systemcls, rallocs, mesh, initsoln, cfg): self.backend = backend self.rallocs = rallocs self.cfg = cfg # Sanity checks if self._controller_needs_errest and not self._stepper_has_errest: raise TypeError('Incompatible stepper/controller combination') # Start time self.tstart = cfg.getfloat('solver-time-integrator', 't0', 0.0) # Output times self.tout = sorted(range_eval(cfg.get('soln-output', 'times'))) self.tend = self.tout[-1] # Current time; defaults to tstart unless resuming a simulation if initsoln is None or 'stats' not in initsoln: self.tcurr = self.tstart else: stats = Inifile(initsoln['stats']) self.tcurr = stats.getfloat('solver-time-integrator', 'tcurr') # Cull already written output times self.tout = [t for t in self.tout if t > self.tcurr] # Ensure no time steps are in the past if self.tout[0] < self.tcurr: raise ValueError('Output times must be in the future') # Determine the amount of temp storage required by thus method nreg = self._stepper_nregs # Construct the relevant mesh partition self.system = systemcls(backend, rallocs, mesh, initsoln, nreg, cfg) # Extract the UUID of the mesh (to be saved with solutions) self._mesh_uuid = mesh['mesh_uuid'] # Get a queue for subclasses to use self._queue = backend.queue() # Get the number of degrees of freedom in this partition ndofs = sum(self.system.ele_ndofs) comm, rank, root = get_comm_rank_root() # Sum to get the global number over all partitions self._gndofs = comm.allreduce(ndofs, op=get_mpi('sum'))
def _resid(self, x, y): comm, rank, root = get_comm_rank_root() # Get an errest kern to compute the square of the maximum residual errest = self._get_errest_kerns() # Prepare and run the kernel self._prepare_reg_banks(x, y, y) self._queue % errest(self._pseudo_aresid, self._pseudo_rresid) # Reduce locally (element types) and globally (MPI ranks) rl = max(errest.retval) rg = comm.allreduce(rl, op=get_mpi('max')) # Normalise return math.sqrt(rg)
def __init__(self, backend, systemcls, rallocs, mesh, initsoln, cfg): self.backend = backend self.rallocs = rallocs self.cfg = cfg self.isrestart = initsoln is not None # Sanity checks if self._controller_needs_errest and not self._stepper_has_errest: raise TypeError('Incompatible stepper/controller combination') # Start time self.tstart = cfg.getfloat('solver-time-integrator', 'tstart', 0.0) self.tend = cfg.getfloat('solver-time-integrator', 'tend') # Current time; defaults to tstart unless restarting if self.isrestart: stats = Inifile(initsoln['stats']) self.tcurr = stats.getfloat('solver-time-integrator', 'tcurr') else: self.tcurr = self.tstart self.tlist = deque([self.tend]) # Determine the amount of temp storage required by thus method nreg = self._stepper_nregs # Construct the relevant mesh partition self.system = systemcls(backend, rallocs, mesh, initsoln, nreg, cfg) # Extract the UUID of the mesh (to be saved with solutions) self.mesh_uuid = mesh['mesh_uuid'] # Get a queue for subclasses to use self._queue = backend.queue() # Get the number of degrees of freedom in this partition ndofs = sum(self.system.ele_ndofs) comm, rank, root = get_comm_rank_root() # Sum to get the global number over all partitions self._gndofs = comm.allreduce(ndofs, op=get_mpi('sum'))
def __init__(self, intg, cfgsect, suffix): super().__init__(intg, cfgsect, suffix) # Underlying elements class self.elementscls = intg.system.elementscls # Output frequency self.nsteps = self.cfg.getint(cfgsect, 'nsteps') # List of points to be sampled and format self.pts = self.cfg.getliteral(cfgsect, 'samp-pts') self.fmt = self.cfg.get(cfgsect, 'format', 'primitive') # MPI info comm, rank, root = get_comm_rank_root() # MPI rank responsible for each point and rank-indexed info self._ptsrank = ptsrank = [] self._ptsinfo = ptsinfo = [[] for i in range(comm.size)] # Physical location of the solution points plocs = [p.swapaxes(1, 2) for p in intg.system.ele_ploc_upts] # Locate the closest solution points in our partition closest = _closest_upts(intg.system.ele_types, plocs, self.pts) # Process these points for cp in closest: # Reduce over the distance _, mrank = comm.allreduce((cp[0], rank), op=get_mpi('minloc')) # Store the rank responsible along with its info ptsrank.append(mrank) ptsinfo[mrank].append( comm.bcast(cp[1:] if rank == mrank else None, root=mrank) ) # If we're the root rank then open the output file if rank == root: self.outf = init_csv(self.cfg, cfgsect, self._header)
def __init__(self, intg, cfgsect, suffix): super().__init__(intg, cfgsect, suffix) # Underlying elements class self.elementscls = intg.system.elementscls # Output frequency self.nsteps = self.cfg.getint(cfgsect, 'nsteps') # List of points to be sampled and format self.pts = self.cfg.getliteral(cfgsect, 'samp-pts') self.fmt = self.cfg.get(cfgsect, 'format', 'primitive') # MPI info comm, rank, root = get_comm_rank_root() # MPI rank responsible for each point and rank-indexed info self._ptsrank = ptsrank = [] self._ptsinfo = ptsinfo = [[] for i in range(comm.size)] # Physical location of the solution points plocs = [p.swapaxes(1, 2) for p in intg.system.ele_ploc_upts] # Locate the closest solution points in our partition closest = _closest_upts(intg.system.ele_types, plocs, self.pts) # Process these points for cp in closest: # Reduce over the distance _, mrank = comm.allreduce((cp[0], rank), op=get_mpi('minloc')) # Store the rank responsible along with its info ptsrank.append(mrank) ptsinfo[mrank].append( comm.bcast(cp[1:] if rank == mrank else None, root=mrank)) # If we're the root rank then open the output file if rank == root: self.outf = init_csv(self.cfg, cfgsect, self._header)
def __init__(self, intg, cfgsect, suffix): super().__init__(intg, cfgsect, suffix) # Underlying elements class self.elementscls = intg.system.elementscls # Output frequency self.nsteps = self.cfg.getint(cfgsect, 'nsteps') # List of points to be sampled and format self.pts = ast.literal_eval(self.cfg.get(cfgsect, 'samp-pts')) self.fmt = self.cfg.get(cfgsect, 'format', 'primitive') # MPI info comm, rank, root = get_comm_rank_root() # MPI rank responsible for each point and rank-indexed info self._ptsrank = ptsrank = [] self._ptsinfo = ptsinfo = [[] for i in range(comm.size)] # Physical location of the solution points plocs = [p.swapaxes(1, 2) for p in intg.system.ele_ploc_upts] for p in self.pts: # Find the nearest point in our partition cp = _closest_upt(intg.system.ele_types, plocs, p) # Reduce over all partitions mcp, mrank = comm.allreduce(cp, op=get_mpi('minloc')) # Store the rank responsible along with the info ptsrank.append(mrank) ptsinfo[mrank].append(mcp[1:]) # If we're the root rank then open the output file if rank == root: self.outf = init_csv(self.cfg, cfgsect, self._header)
def __call__(self, intg): # Return if no output is due if intg.nacptsteps % self.nsteps: return # MPI info comm, rank, root = get_comm_rank_root() # Solution matrices indexed by element type solns = dict(zip(intg.system.ele_types, intg.soln)) ndims, nvars = self.ndims, self.nvars # Force vector f = np.zeros(2*ndims if self._viscous else ndims) for etype, fidx in self._m0: # Get the interpolation operator m0 = self._m0[etype, fidx] nfpts, nupts = m0.shape # Extract the relevant elements from the solution uupts = solns[etype][..., self._eidxs[etype, fidx]] # Interpolate to the face ufpts = np.dot(m0, uupts.reshape(nupts, -1)) ufpts = ufpts.reshape(nfpts, nvars, -1) ufpts = ufpts.swapaxes(0, 1) # Compute the pressure p = self.elementscls.conv_to_pri(ufpts, self.cfg)[-1] # Get the quadrature weights and normal vectors qwts = self._qwts[etype, fidx] norms = self._norms[etype, fidx] # Do the quadrature f[:ndims] += np.einsum('i...,ij,jik', qwts, p, norms) if self._viscous: # Get operator and J^-T matrix m4 = self._m4[etype] rcpjact = self._rcpjact[etype, fidx] # Transformed gradient at solution points tduupts = np.dot(m4, uupts.reshape(nupts, -1)) tduupts = tduupts.reshape(ndims, nupts, nvars, -1) # Physical gradient at solution points duupts = np.einsum('ijkl,jkml->ikml', rcpjact, tduupts) duupts = duupts.reshape(ndims, nupts, -1) # Interpolate gradient to flux points dufpts = np.array([np.dot(m0, duupts[i]) for i in range(ndims)]) dufpts = dufpts.reshape(ndims, nfpts, nvars, -1).swapaxes(1, 2) # Viscous stress vis = self.stress_tensor(ufpts, dufpts) # Do the quadrature f[ndims:] += np.einsum('i...,klij,jil', qwts, vis, norms) # Reduce and output if we're the root rank if rank != root: comm.Reduce(f, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), f, op=get_mpi('sum'), root=root) # Build the row row = [intg.tcurr] + f.tolist() # Write print(','.join(str(r) for r in row), file=self.outf) # Flush to disk self.outf.flush()
def __call__(self, intg): # MPI info comm, rank, root = get_comm_rank_root() if not self.hasinlet: intg.system.mdot = comm.allreduce(0.0, op=get_mpi('sum')) else: # Solution matrices indexed by element type solns = dict(zip(intg.system.ele_types, intg.soln)) ndims, nvars = self.ndims, self.nvars # Force vector rhou = np.zeros(ndims) area = np.zeros(ndims) for etype, fidx in self._m0: # Get the interpolation operator m0 = self._m0[etype, fidx] nfpts, nupts = m0.shape # Extract the relevant elements from the solution uupts = solns[etype][..., self._eidxs[etype, fidx]] # Interpolate to the face ufpts = np.dot(m0, uupts.reshape(nupts, -1)) ufpts = ufpts.reshape(nfpts, nvars, -1) ufpts = ufpts.swapaxes(0, 1) # Compute the U-momentum ruidx = 1 ru = ufpts[ruidx] ones = np.ones(np.shape(ru)) # Get the quadrature weights and normal vectors qwts = self._qwts[etype, fidx] norms = self._norms[etype, fidx] # Do the quadrature rhou[:ndims] += np.einsum('i...,ij,jik', qwts, ru, norms) area[:ndims] += -np.einsum('i...,ij,jik', qwts, ones, norms) self.mdot = abs( (area[0] / self.area) * rhou[0] / area[0] ) # Negative since rhou_in normal points outwards, normalized by portion of total inlet area intg.system.mdot = comm.allreduce(self.mdot, op=get_mpi('sum')) if rank == root: # Body forcing term added to maintain constant mass inflow -> weight by portion of total area for parallel runs ruf = intg.system.rhouforce + (1.0 / intg._dt) * ( self.mdotstar - 2. * intg.system.mdot + intg.system.mdotold) if (intg.system.mdot / self.mdotstar > 1.1 or intg.system.mdot / self.mdotstar < 0.9): print('Mass flow rate exceeds 10%% error: ', intg.system.mdot / self.mdotstar) # Broadcast to all ranks intg.system.rhouforce = float(comm.bcast(ruf, root=root)) intg.system.mdotold = float(comm.bcast(intg.system.mdot, root=root)) else: intg.system.rhouforce = float(comm.bcast(None, root=root)) intg.system.mdotold = float(comm.bcast(None, root=root))
def __init__(self, intg, cfgsect, suffix): super().__init__(intg, cfgsect, suffix) # Underlying elements class self.elementscls = intg.system.elementscls # Output frequency self.nsteps = self.cfg.getint(cfgsect, 'nsteps') # List of points to be sampled and format self.pts = self.cfg.getliteral(cfgsect, 'samp-pts') self.fmt = self.cfg.get(cfgsect, 'format', 'primitive') # MPI info comm, rank, root = get_comm_rank_root() # MPI rank responsible for each sample point if rank == root: ptsrank = [] # Sample points we're responsible for, grouped by element type elepts = [[] for i in range(len(intg.system.ele_map))] # Search locations in transformed and physical space tlocs, plocs = self._search_pts(intg) # For each sample point find our nearest search location closest = _closest_pts(plocs, self.pts) # Process these points for i, (dist, etype, (uidx, eidx)) in enumerate(closest): # Reduce over the distance _, mrank = comm.allreduce((dist, rank), op=get_mpi('minloc')) # If we have the closest point then save the relevant info if rank == mrank: elepts[etype].append((i, eidx, tlocs[etype][uidx])) # Note what rank is responsible for the point if rank == root: ptsrank.append(mrank) # Refine self._ourpts = ourpts = self._refine_pts(intg, elepts) # Send the refined sample locations to the root rank ptsplocs = comm.gather([pl for et, ei, pl, op in ourpts], root=root) if rank == root: nvars = self.nvars # Allocate a buffer to store the sampled points self._ptsbuf = ptsbuf = np.empty((len(self.pts), self.nvars)) # Tally up how many points each rank is responsible for nptsrank = [len(ploc) for ploc in ptsplocs] # Compute the counts and displacements, sans nvars ptscounts = np.array(nptsrank, dtype=np.int32) ptsdisps = np.cumsum([0] + nptsrank[:-1], dtype=np.int32) # Apply the displacements to each ranks points miters = [ enumerate(ploc, start=pdisp) for ploc, pdisp in zip(ptsplocs, ptsdisps) ] # With this form the final point (offset, location) list self._ptsinfo = [next(miters[pr]) for pr in ptsrank] # Form the MPI Gatherv receive buffer tuple self._ptsrecv = (ptsbuf, (nvars * ptscounts, nvars * ptsdisps)) # Open the output file self.outf = init_csv(self.cfg, cfgsect, self._header) else: self._ptsrecv = None
def __call__(self, intg): # Return if no output is due if intg.nacptsteps % self.nsteps: return # MPI info comm, rank, root = get_comm_rank_root() # Solution matrices indexed by element type solns = dict(zip(intg.system.ele_types, intg.soln)) ndims, nvars = self.ndims, self.nvars # Force vector f = np.zeros(2*ndims if self._viscous else ndims) for etype, fidx in self._m0: # Get the interpolation operator m0 = self._m0[etype, fidx] nfpts, nupts = m0.shape # Extract the relevant elements from the solution uupts = solns[etype][..., self._eidxs[etype, fidx]] # Interpolate to the face ufpts = np.dot(m0, uupts.reshape(nupts, -1)) ufpts = ufpts.reshape(nfpts, nvars, -1) ufpts = ufpts.swapaxes(0, 1) # Compute the pressure p = self.elementscls.con_to_pri(ufpts, self.cfg)[-1] # Get the quadrature weights and normal vectors qwts = self._qwts[etype, fidx] norms = self._norms[etype, fidx] # Do the quadrature f[:ndims] += np.einsum('i...,ij,jik', qwts, p, norms) if self._viscous: # Get operator and J^-T matrix m4 = self._m4[etype] rcpjact = self._rcpjact[etype, fidx] # Transformed gradient at solution points tduupts = np.dot(m4, uupts.reshape(nupts, -1)) tduupts = tduupts.reshape(ndims, nupts, nvars, -1) # Physical gradient at solution points duupts = np.einsum('ijkl,jkml->ikml', rcpjact, tduupts) duupts = duupts.reshape(ndims, nupts, -1) # Interpolate gradient to flux points dufpts = np.array([np.dot(m0, du) for du in duupts]) dufpts = dufpts.reshape(ndims, nfpts, nvars, -1) dufpts = dufpts.swapaxes(1, 2) # Viscous stress vis = self.stress_tensor(ufpts, dufpts) # Do the quadrature f[ndims:] += np.einsum('i...,klij,jil', qwts, vis, norms) # Reduce and output if we're the root rank if rank != root: comm.Reduce(f, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), f, op=get_mpi('sum'), root=root) # Build the row row = [intg.tcurr] + f.tolist() # Write print(','.join(str(r) for r in row), file=self.outf) # Flush to disk self.outf.flush()
def _check_abort(self): comm, rank, root = get_comm_rank_root() if comm.allreduce(self.abort, op=get_mpi('lor')): # Ensure that the callbacks registered in atexit # are called only once if stopping the computation sys.exit(1)
def __call__(self, intg): # Return if no output is due if intg.nacptsteps % self.nsteps: return # MPI info comm, rank, root = get_comm_rank_root() # Solution matrices indexed by element type solns = dict(zip(intg.system.ele_types, intg.soln)) ndims, nvars, mcomp = self.ndims, self.nvars, self._mcomp # Force and moment vectors fm = np.zeros((2 if self._viscous else 1, ndims + mcomp)) for etype, fidx in self._m0: # Get the interpolation operator m0 = self._m0[etype, fidx] nfpts, nupts = m0.shape # Extract the relevant elements from the solution uupts = solns[etype][..., self._eidxs[etype, fidx]] # Interpolate to the face ufpts = m0 @ uupts.reshape(nupts, -1) ufpts = ufpts.reshape(nfpts, nvars, -1) ufpts = ufpts.swapaxes(0, 1) # Compute the pressure pidx = 0 if self._ac else -1 p = self.elementscls.con_to_pri(ufpts, self.cfg)[pidx] # Get the quadrature weights and normal vectors qwts = self._qwts[etype, fidx] norms = self._norms[etype, fidx] # Do the quadrature fm[0, :ndims] += np.einsum('i...,ij,jik', qwts, p, norms) if self._viscous: # Get operator and J^-T matrix m4 = self._m4[etype] rcpjact = self._rcpjact[etype, fidx] # Transformed gradient at solution points tduupts = m4 @ uupts.reshape(nupts, -1) tduupts = tduupts.reshape(ndims, nupts, nvars, -1) # Physical gradient at solution points duupts = np.einsum('ijkl,jkml->ikml', rcpjact, tduupts) duupts = duupts.reshape(ndims, nupts, -1) # Interpolate gradient to flux points dufpts = np.array([m0 @ du for du in duupts]) dufpts = dufpts.reshape(ndims, nfpts, nvars, -1) dufpts = dufpts.swapaxes(1, 2) # Viscous stress if self._ac: vis = self.ac_stress_tensor(dufpts) else: vis = self.stress_tensor(ufpts, dufpts) # Do the quadrature fm[1, :ndims] += np.einsum('i...,klij,jil', qwts, vis, norms) if self._mcomp: # Get the flux points positions relative to the moment origin rfpts = self._rfpts[etype, fidx] # Do the cross product with the normal vectors rcn = np.atleast_3d(np.cross(rfpts, norms)) # Pressure force moments fm[0, ndims:] += np.einsum('i...,ij,jik->k', qwts, p, rcn) if self._viscous: # Normal viscous force at each flux point viscf = np.einsum('ijkl,lkj->lki', vis, norms) # Normal viscous force moments at each flux point rcf = np.atleast_3d(np.cross(rfpts, viscf)) # Do the quadrature fm[1, ndims:] += np.einsum('i,jik->k', qwts, rcf) # Reduce and output if we're the root rank if rank != root: comm.Reduce(fm, None, op=get_mpi('sum'), root=root) else: comm.Reduce(get_mpi('in_place'), fm, op=get_mpi('sum'), root=root) # Write print(intg.tcurr, *fm.ravel(), sep=',', file=self.outf) # Flush to disk self.outf.flush()