From c072e41677c125f7aca1d506e164a57656d026c6 Mon Sep 17 00:00:00 2001 From: Michael Krayer Date: Thu, 24 Mar 2022 21:11:44 +0100 Subject: [PATCH] first version of serial tool: can compute Q, but untested --- __init__.py | 1 + field.py | 46 +++++++++++- helper.py | 56 ++++++++++++++- serial.py | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 serial.py diff --git a/__init__.py b/__init__.py index 0f9973b..35adf69 100644 --- a/__init__.py +++ b/__init__.py @@ -4,3 +4,4 @@ from . import field from . import ucf from . import helper from . import parallel +from . import serial \ No newline at end of file diff --git a/field.py b/field.py index 2619abc..d4464f0 100644 --- a/field.py +++ b/field.py @@ -10,7 +10,7 @@ class Field3d: self.data = data self.origin = tuple([float(x) for x in origin]) self.spacing = tuple([float(x) for x in spacing]) - self.eps_collapse = 1e-8 + self.eps_collapse = 1e-4*np.min(spacing) self._dim = None return @@ -131,6 +131,48 @@ class Field3d: if not is_open: f.close() return cls(data,origin,spacing) + @classmethod + def from_snapshot(cls,snap,key,procslice=None,dtype=np.float64): + ''' + Construct a Field3d from a field saved in an UCF snapshot. + Use procslice = (ib,ie,jb,je,kb,je) to load only a block + of the full data. + ''' + from .helper import eval_procslice, key_grid, rank_from_position + proc = snap.procgrid() + origin = snap.origin() + spacing = snap.spacing() + periodicity = snap.periodicity() + bounds = snap.bounds() + keyg = key_grid(key) + nxp,nyp,nzp = [len(proc[keyg][2*ii]) for ii in range(3)] + # Get field dimensions + (ipb,ipe,jpb,jpe,kpb,kpe),ori,(nxl,nyl,nzl) = eval_procslice( + proc,origin,spacing,keyg,procslice) + # Create a Field3d: ghost cells will be included! + data = np.empty((nxl+2,nyl+2,nzl+2),dtype=dtype) + field_ = cls(data,[ori[ii]-spacing[ii] for ii in range(3)],spacing) + # Go through each chunk to be read and construct the field + for ip_ in range(ipb,ipe+1): + for jp_ in range(jpb,jpe+1): + for kp_ in range(kpb,kpe+1): + # Determine rank of the chunk to be read + rank_ = rank_from_position(ip_,jp_,kp_,nyp,nzp) + # Compute bounds of this chunk + ib_ = proc[keyg][0][ip_] + ie_ = proc[keyg][1][ip_] + jb_ = proc[keyg][2][jp_] + je_ = proc[keyg][3][jp_] + kb_ = proc[keyg][4][kp_] + ke_ = proc[keyg][5][kp_] + nxl_ = ie_-ib_+1 + nyl_ = je_-jb_+1 + nzl_ = ke_-kb_+1 + # Read data and insert it + subfield = snap.field_chunk(rank_,key) + field_.insert_subfield(subfield) + return field_ + @classmethod def allocate(cls,dim,origin,spacing,fill=None,dtype=np.float64): '''Allocates an empty field, or a field filled with 'fill'.''' @@ -359,7 +401,7 @@ class Field3d: assert axis<3, "'axis' must be one of 0,1,2." origin = list(self.origin) assert shift_origin in ('before','after'), "'shift_origin' must be one of {'before','after'}." - if shift_origin=='left': + if shift_origin=='before': corr_orig = 0 origin[axis] -= 0.5*self.spacing[axis] else: diff --git a/helper.py b/helper.py index 1ead405..c67a744 100644 --- a/helper.py +++ b/helper.py @@ -97,4 +97,58 @@ def position_from_rank(rank,nyp,nzp): ip = rank//(nyp*nzp) jp = (rank//nzp)%nyp kp = rank%nzp - return (ip,jp,kp) \ No newline at end of file + return (ip,jp,kp) + +def key_grid(key): + '''Get the key for the grid: scalar fields all share a common grid, + so this routine returns 's' if key is 'sX' with X being a (potentially + multi-digit) number. Otherwise the key itself is returned.''' + if key[0]=='s' and key[1:].isnumeric(): + return 's' + else: + return key + +def eval_procslice(proc,origin,spacing,key_grid,procslice): + # Get number of procs + nxp = len(proc[key_grid][0]) + nyp = len(proc[key_grid][2]) + nzp = len(proc[key_grid][4]) + nxyzp = (nxp,nyp,nzp) + # Get processor indices for slicing + if procslice is not None: + ipb = procslice[0] if procslice[0] is not None else 0 + ipe = procslice[1] if procslice[1] is not None else nxp-1 + jpb = procslice[2] if procslice[2] is not None else 0 + jpe = procslice[3] if procslice[3] is not None else nyp-1 + kpb = procslice[4] if procslice[4] is not None else 0 + kpe = procslice[5] if procslice[5] is not None else nzp-1 + else: + ipb = 0 + ipe = nxp-1 + jpb = 0 + jpe = nyp-1 + kpb = 0 + kpe = nzp-1 + idxproc = (ipb,ipe,jpb,jpe,kpb,kpe) + for ii_ in range(6): + assert idxproc[ii_]>=0, \ + "procslice[{:d}] must be greater than or equal to 0, but is {:d}.".format( + ii_,idxproc[ii_]) + assert idxproc[ii_]=self.bounds[2*axis+1]],self.bounds[2*axis+1]) + idx_ = self.field[key].dim(axis=axis)-len(xgh) + for x_ in xgh: + ii = self.field[key].nearest_gridpoint(x_,axis=axis) + assert math.fabs(x_-x[ii])