diff options
-rw-r--r-- | Python/pywarpx/fields.py | 447 |
1 files changed, 183 insertions, 264 deletions
diff --git a/Python/pywarpx/fields.py b/Python/pywarpx/fields.py index 2c9451cdd..612ea32c0 100644 --- a/Python/pywarpx/fields.py +++ b/Python/pywarpx/fields.py @@ -81,6 +81,7 @@ class _MultiFABWrapper(object): - direction: In 3d, one of 'x', 'y', or 'z'. In 2d, Cartesian, one of 'x', or 'z'. In RZ, one of 'r', or 'z' + In Z, 'z'. """ try: @@ -93,6 +94,9 @@ class _MultiFABWrapper(object): elif _libwarpx.geometry_dim == 'rz': idir = ['r', 'z'].index(direction) celldir = 2*idir + elif _libwarpx.geometry_dim == '1d': + idir = ['z'].index(direction) + celldir = idir except ValueError: raise Exception('Inappropriate direction given') @@ -118,33 +122,11 @@ class _MultiFABWrapper(object): return np.arange(nn)*dd + shift - def __getitem__(self, index): - """Returns slices of a decomposed array, The shape of - the object returned depends on the number of ix, iy and iz specified, which - can be from none to all three. Note that the values of ix, iy and iz are - relative to the fortran indexing, meaning that 0 is the lower boundary - of the whole domain. - """ - if index == Ellipsis: - index = tuple(self.dim*[slice(None)]) - - if len(index) < self.dim: - # --- Add extra dims to index if needed - index = list(index) - for i in range(len(index), self.dim): - index.append(slice(None)) - index = tuple(index) - - if self.dim == 2: - return self._getitem2d(index) - elif self.dim == 3: - return self._getitem3d(index) - def _find_start_stop(self, ii, imin, imax, d): """Given the input index, calculate the start and stop range of the indices. - ii: input index, either a slice object or an integer - - imin: the global lowest lovect value in the specified diretion - - imax: the global highest hivect value in the specified diretion + - imin: the global lowest lovect value in the specified direction + - imax: the global highest hivect value in the specified direction - d: the direction, an integer, 0, 1, or 2 If ii is a slice, the start and stop values are used directly, unless they are None, then the lower or upper bound is used. @@ -166,114 +148,101 @@ class _MultiFABWrapper(object): assert imin <= iistop <= imax + self.overlaps[d], Exception(f'Dimension {d} upper index is out of bounds') return iistart, iistop - def _getitem3d(self, index): - """Returns slices of a 3D decomposed array, - """ - - lovects, ngrow = self._getlovects() - hivects, ngrow = self._gethivects() - fields = self._getfields() - - ix = index[0] - iy = index[1] - iz = index[2] - - if len(fields[0].shape) > self.dim: - ncomps = fields[0].shape[-1] - else: - ncomps = 1 - - if len(index) > self.dim: - if ncomps > 1: - ic = index[-1] - else: - raise Exception('Too many indices given') - else: - ic = None - - ixmin = lovects[0,:].min() - ixmax = hivects[0,:].max() - iymin = lovects[1,:].min() - iymax = hivects[1,:].max() - izmin = lovects[2,:].min() - izmax = hivects[2,:].max() - - if npes > 1: - ixmin = comm_world.allreduce(ixmin, op=mpi.MIN) - ixmax = comm_world.allreduce(ixmax, op=mpi.MAX) - iymin = comm_world.allreduce(iymin, op=mpi.MIN) - iymax = comm_world.allreduce(iymax, op=mpi.MAX) - izmin = comm_world.allreduce(izmin, op=mpi.MIN) - izmax = comm_world.allreduce(izmax, op=mpi.MAX) - - ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) - iystart, iystop = self._find_start_stop(iy, iymin, iymax, 1) - izstart, izstop = self._find_start_stop(iz, izmin, izmax, 2) - - # --- Setup the size of the array to be returned and create it. - # --- Space is added for multiple components if needed. - sss = (max(0, ixstop - ixstart), - max(0, iystop - iystart), - max(0, izstop - izstart)) - if ncomps > 1 and ic is None: - sss = tuple(list(sss) + [ncomps]) - resultglobal = np.zeros(sss, dtype=_libwarpx._numpy_real_dtype) - - datalist = [] - for i in range(len(fields)): - - # --- The ix1, 2 etc are relative to global indexing - ix1 = max(ixstart, lovects[0,i]) - ix2 = min(ixstop, lovects[0,i] + fields[i].shape[0]) - iy1 = max(iystart, lovects[1,i]) - iy2 = min(iystop, lovects[1,i] + fields[i].shape[1]) - iz1 = max(izstart, lovects[2,i]) - iz2 = min(izstop, lovects[2,i] + fields[i].shape[2]) + def _get_indices(self, index): + if self.dim == 1: + return None, None, index[0] + elif self.dim == 2: + return index[0], None, index[1] + elif self.dim == 3: + return index[0], index[1], index[2] - if ix1 < ix2 and iy1 < iy2 and iz1 < iz2: + def _get_min_indices(self, lovects): + if self.dim == 1: + return 0, 0, lovects[0,:].min() + elif self.dim == 2: + return lovects[0,:].min(), 0, lovects[1,:].min() + elif self.dim == 3: + return lovects[0,:].min(), lovects[1,:].min(), lovects[2,:].min() - sss = (slice(ix1 - lovects[0,i], ix2 - lovects[0,i]), - slice(iy1 - lovects[1,i], iy2 - lovects[1,i]), - slice(iz1 - lovects[2,i], iz2 - lovects[2,i])) - if ic is not None: - sss = tuple(list(sss) + [ic]) + def _get_max_indices(self, hivects): + if self.dim == 1: + return 0, 0, hivects[0,:].max() + elif self.dim == 2: + return hivects[0,:].max(), 0, hivects[1,:].max() + elif self.dim == 3: + return hivects[0,:].max(), hivects[1,:].max(), hivects[2,:].max() + + def _get_vslice(self, lovects, fields_shape, ixstart, ixstop, iystart, + iystop, izstart, izstop, ic): + # --- The ix1, 2 etc are relative to global indexing + if self.dim == 1: + ix1, ix2 = 0, 1 + iy1, iy2 = 0, 1 + iz1 = max(izstart, lovects[0]) + iz2 = min(izstop, lovects[0] + fields_shape[0]) + elif self.dim == 2: + ix1 = max(ixstart, lovects[0]) + ix2 = min(ixstop, lovects[0] + fields_shape[0]) + iy1, iy2 = 0, 1 + iz1 = max(izstart, lovects[1]) + iz2 = min(izstop, lovects[1] + fields_shape[1]) + elif self.dim == 3: + ix1 = max(ixstart, lovects[0]) + ix2 = min(ixstop, lovects[0] + fields_shape[0]) + iy1 = max(iystart, lovects[1]) + iy2 = min(iystop, lovects[1] + fields_shape[1]) + iz1 = max(izstart, lovects[2]) + iz2 = min(izstop, lovects[2] + fields_shape[2]) + + if ix1 < ix2 and iy1 < iy2 and iz1 < iz2: + + if self.dim == 1: + sss = (slice(iz1 - lovects[0], iz2 - lovects[0])) + vslice = (slice(iz1 - izstart, iz2 - izstart)) + + elif self.dim == 2: + sss = (slice(ix1 - lovects[0], ix2 - lovects[0]), + slice(iz1 - lovects[1], iz2 - lovects[1])) + vslice = (slice(ix1 - ixstart, ix2 - ixstart), + slice(iz1 - izstart, iz2 - izstart)) + elif self.dim == 3: + sss = (slice(ix1 - lovects[0], ix2 - lovects[0]), + slice(iy1 - lovects[1], iy2 - lovects[1]), + slice(iz1 - lovects[2], iz2 - lovects[2])) vslice = (slice(ix1 - ixstart, ix2 - ixstart), slice(iy1 - iystart, iy2 - iystart), slice(iz1 - izstart, iz2 - izstart)) - datalist.append((vslice, fields[i][sss])) + if ic is not None: + sss = tuple(list(sss) + [ic]) - if npes == 1: - all_datalist = [datalist] + return sss, vslice else: - all_datalist = comm_world.allgather(datalist) - - for datalist in all_datalist: - for vslice, ff in datalist: - resultglobal[vslice] = ff + return None, None - # --- Now remove any of the reduced dimensions. - sss = [slice(None), slice(None), slice(None)] - if not isinstance(ix, slice): - sss[0] = 0 - if not isinstance(iy, slice): - sss[1] = 0 - if not isinstance(iz, slice): - sss[2] = 0 - - return resultglobal[tuple(sss)] - - def _getitem2d(self, index): - """Returns slices of a 2D decomposed array, + def __getitem__(self, index): + """Returns slices of a decomposed array, The shape of + the object returned depends on the number of ix, iy and iz specified, which + can be from none to all three. Note that the values of ix, iy and iz are + relative to the fortran indexing, meaning that 0 is the lower boundary + of the whole domain. """ + if index == Ellipsis: + index = tuple(self.dim*[slice(None)]) + + if len(index) < self.dim: + # --- Add extra dims to index if needed + index = list(index) + for i in range(len(index), self.dim): + index.append(slice(None)) + index = tuple(index) lovects, ngrow = self._getlovects() hivects, ngrow = self._gethivects() fields = self._getfields() - ix = index[0] - iz = index[1] + ix, iy, iz = self._get_indices(index) if len(fields[0].shape) > self.dim: ncomps = fields[0].shape[-1] @@ -282,53 +251,63 @@ class _MultiFABWrapper(object): if len(index) > self.dim: if ncomps > 1: - ic = index[2] + ic = index[self.dim] else: raise Exception('Too many indices given') else: ic = None - ixmin = lovects[0,:].min() - ixmax = hivects[0,:].max() - izmin = lovects[1,:].min() - izmax = hivects[1,:].max() + ixmin, iymin, izmin = self._get_min_indices(lovects) + ixmax, iymax, izmax = self._get_max_indices(hivects) if npes > 1: - ixmin = comm_world.allreduce(ixmin, op=mpi.MIN) - ixmax = comm_world.allreduce(ixmax, op=mpi.MAX) izmin = comm_world.allreduce(izmin, op=mpi.MIN) izmax = comm_world.allreduce(izmax, op=mpi.MAX) + if self.dim > 1: + ixmin = comm_world.allreduce(ixmin, op=mpi.MIN) + ixmax = comm_world.allreduce(ixmax, op=mpi.MAX) + if self.dim == 3: + iymin = comm_world.allreduce(iymin, op=mpi.MIN) + iymax = comm_world.allreduce(iymax, op=mpi.MAX) + + # --- Setup the size of the array to be returned. + if self.dim == 1: + ixstart, ixstop = None, None + iystart, iystop = None, None + izstart, izstop = self._find_start_stop(iz, izmin, izmax, 0) + + sss = [max(0, izstop - izstart)] + + elif self.dim == 2: + ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) + iystart, iystop = None, None + izstart, izstop = self._find_start_stop(iz, izmin, izmax, 1) - ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) - izstart, izstop = self._find_start_stop(iz, izmin, izmax, 1) + sss = (max(0, ixstop - ixstart), + max(0, izstop - izstart)) + + elif self.dim == 3: + ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) + iystart, iystop = self._find_start_stop(iy, iymin, iymax, 1) + izstart, izstop = self._find_start_stop(iz, izmin, izmax, 2) + + sss = (max(0, ixstop - ixstart), + max(0, iystop - iystart), + max(0, izstop - izstart)) - # --- Setup the size of the array to be returned and create it. # --- Space is added for multiple components if needed. - sss = (max(0, ixstop - ixstart), - max(0, izstop - izstart)) if ncomps > 1 and ic is None: sss = tuple(list(sss) + [ncomps]) + # --- Create the array to be returned. resultglobal = np.zeros(sss, dtype=_libwarpx._numpy_real_dtype) datalist = [] for i in range(len(fields)): - - # --- The ix1, 2 etc are relative to global indexing - ix1 = max(ixstart, lovects[0,i]) - ix2 = min(ixstop, lovects[0,i] + fields[i].shape[0]) - iz1 = max(izstart, lovects[1,i]) - iz2 = min(izstop, lovects[1,i] + fields[i].shape[1]) - - if ix1 < ix2 and iz1 < iz2: - - sss = (slice(ix1 - lovects[0,i], ix2 - lovects[0,i]), - slice(iz1 - lovects[1,i], iz2 - lovects[1,i])) - if ic is not None: - sss = tuple(list(sss) + [ic]) - - vslice = (slice(ix1 - ixstart, ix2 - ixstart), - slice(iz1 - izstart, iz2 - izstart)) - + sss, vslice = self._get_vslice( + lovects[:,i], fields[i].shape, ixstart, ixstop, iystart, + iystop, izstart, izstop, ic + ) + if vslice is not None: datalist.append((vslice, fields[i][sss])) if npes == 1: @@ -341,18 +320,31 @@ class _MultiFABWrapper(object): resultglobal[vslice] = ff # --- Now remove any of the reduced dimensions. - sss = [slice(None), slice(None)] - if not isinstance(ix, slice): - sss[0] = 0 - if not isinstance(iz, slice): - sss[1] = 0 + if self.dim == 1: + sss = [slice(None)] + if not isinstance(iz, slice): + sss[0] = 0 + elif self.dim == 2: + sss = [slice(None), slice(None)] + if not isinstance(ix, slice): + sss[0] = 0 + if not isinstance(iz, slice): + sss[1] = 0 + elif self.dim == 3: + sss = [slice(None), slice(None), slice(None)] + if not isinstance(ix, slice): + sss[0] = 0 + if not isinstance(iy, slice): + sss[1] = 0 + if not isinstance(iz, slice): + sss[2] = 0 return resultglobal[tuple(sss)] def __setitem__(self, index, value): """Sets slices of a decomposed array. The shape of - the input object depends on the number of arguments specified, which can - be from none to all three. + the input object depends on the number of arguments specified, which can + be from none to all three. - value: input array (must be supplied) """ if index == Ellipsis: @@ -365,94 +357,11 @@ class _MultiFABWrapper(object): index.append(slice(None)) index = tuple(index) - if self.dim == 2: - return self._setitem2d(index, value) - elif self.dim == 3: - return self._setitem3d(index, value) - - def _setitem3d(self, index, value): - """Sets slices of a decomposed 3D array. - """ - ix = index[0] - iy = index[1] - iz = index[2] - lovects, ngrow = self._getlovects() hivects, ngrow = self._gethivects() fields = self._getfields() - if len(index) > self.dim: - if ncomps > 1: - ic = index[-1] - else: - raise Exception('Too many indices given') - else: - ic = None - - ixmin = lovects[0,:].min() - ixmax = hivects[0,:].max() - iymin = lovects[1,:].min() - iymax = hivects[1,:].max() - izmin = lovects[2,:].min() - izmax = hivects[2,:].max() - - if npes > 1: - ixmin = comm_world.allreduce(ixmin, op=mpi.MIN) - ixmax = comm_world.allreduce(ixmax, op=mpi.MAX) - iymin = comm_world.allreduce(iymin, op=mpi.MIN) - iymax = comm_world.allreduce(iymax, op=mpi.MAX) - izmin = comm_world.allreduce(izmin, op=mpi.MIN) - izmax = comm_world.allreduce(izmax, op=mpi.MAX) - - ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) - iystart, iystop = self._find_start_stop(iy, iymin, iymax, 1) - izstart, izstop = self._find_start_stop(iz, izmin, izmax, 2) - - # --- Add extra dimensions so that the input has the same number of - # --- dimensions as array. - if isinstance(value, np.ndarray): - value3d = np.array(value, copy=False) - sss = list(value3d.shape) - if not isinstance(ix, slice): sss[0:0] = [1] - if not isinstance(iy, slice): sss[1:1] = [1] - if not isinstance(iz, slice): sss[2:2] = [1] - value3d.shape = sss - - for i in range(len(fields)): - - # --- The ix1, 2 etc are relative to global indexing - ix1 = max(ixstart, lovects[0,i]) - ix2 = min(ixstop, lovects[0,i] + fields[i].shape[0]) - iy1 = max(iystart, lovects[1,i]) - iy2 = min(iystop, lovects[1,i] + fields[i].shape[1]) - iz1 = max(izstart, lovects[2,i]) - iz2 = min(izstop, lovects[2,i] + fields[i].shape[2]) - - if ix1 < ix2 and iy1 < iy2 and iz1 < iz2: - - sss = (slice(ix1 - lovects[0,i], ix2 - lovects[0,i]), - slice(iy1 - lovects[1,i], iy2 - lovects[1,i]), - slice(iz1 - lovects[2,i], iz2 - lovects[2,i])) - if ic is not None: - sss = tuple(list(sss) + [ic]) - - if isinstance(value, np.ndarray): - vslice = (slice(ix1 - ixstart, ix2 - ixstart), - slice(iy1 - iystart, iy2 - iystart), - slice(iz1 - izstart, iz2 - izstart)) - fields[i][sss] = value3d[vslice] - else: - fields[i][sss] = value - - def _setitem2d(self, index, value): - """Sets slices of a decomposed 2D array. - """ - ix = index[0] - iz = index[1] - - lovects, ngrow = self._getlovects() - hivects, ngrow = self._gethivects() - fields = self._getfields() + ix, iy, iz = self._get_indices(index) if len(fields[0].shape) > self.dim: ncomps = fields[0].shape[-1] @@ -461,53 +370,63 @@ class _MultiFABWrapper(object): if len(index) > self.dim: if ncomps > 1: - ic = index[2] + ic = index[self.dim] else: raise Exception('Too many indices given') else: ic = None - ixmin = lovects[0,:].min() - ixmax = hivects[0,:].max() - izmin = lovects[1,:].min() - izmax = hivects[1,:].max() + ixmin, iymin, izmin = self._get_min_indices(lovects) + ixmax, iymax, izmax = self._get_max_indices(hivects) if npes > 1: - ixmin = comm_world.allreduce(ixmin, op=mpi.MIN) - ixmax = comm_world.allreduce(ixmax, op=mpi.MAX) izmin = comm_world.allreduce(izmin, op=mpi.MIN) izmax = comm_world.allreduce(izmax, op=mpi.MAX) - - ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) - izstart, izstop = self._find_start_stop(iz, izmin, izmax, 1) + if self.dim > 1: + ixmin = comm_world.allreduce(ixmin, op=mpi.MIN) + ixmax = comm_world.allreduce(ixmax, op=mpi.MAX) + if self.dim == 3: + iymin = comm_world.allreduce(iymin, op=mpi.MIN) + iymax = comm_world.allreduce(iymax, op=mpi.MAX) # --- Add extra dimensions so that the input has the same number of # --- dimensions as array. + if self.dim == 1: + ixstart, ixstop = None, None + iystart, iystop = None, None + izstart, izstop = self._find_start_stop(iz, izmin, izmax, 0) + + elif self.dim == 2: + ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) + iystart, iystop = None, None + izstart, izstop = self._find_start_stop(iz, izmin, izmax, 1) + + elif self.dim == 3: + ixstart, ixstop = self._find_start_stop(ix, ixmin, ixmax, 0) + iystart, iystop = self._find_start_stop(iy, iymin, iymax, 1) + izstart, izstop = self._find_start_stop(iz, izmin, izmax, 2) + if isinstance(value, np.ndarray): value3d = np.array(value, copy=False) sss = list(value3d.shape) - if not isinstance(ix, slice): sss[0:0] = [1] - if not isinstance(iz, slice): sss[1:1] = [1] + if self.dim == 1: + if not isinstance(iz, slice): sss[0:0] = [1] + elif self.dim == 2: + if not isinstance(ix, slice): sss[0:0] = [1] + if not isinstance(iz, slice): sss[1:1] = [1] + elif self.dim == 3: + if not isinstance(ix, slice): sss[0:0] = [1] + if not isinstance(iy, slice): sss[1:1] = [1] + if not isinstance(iz, slice): sss[2:2] = [1] value3d.shape = sss for i in range(len(fields)): - - # --- The ix1, 2 etc are relative to global indexing - ix1 = max(ixstart, lovects[0,i]) - ix2 = min(ixstop, lovects[0,i] + fields[i].shape[0]) - iz1 = max(izstart, lovects[1,i]) - iz2 = min(izstop, lovects[1,i] + fields[i].shape[1]) - - if ix1 < ix2 and iz1 < iz2: - - sss = (slice(ix1 - lovects[0,i], ix2 - lovects[0,i]), - slice(iz1 - lovects[1,i], iz2 - lovects[1,i])) - if ic is not None: - sss = tuple(list(sss) + [ic]) - + sss, vslice = self._get_vslice( + lovects[:,i], fields[i].shape, ixstart, ixstop, iystart, + iystop, izstart, izstop, ic + ) + if vslice is not None: if isinstance(value, np.ndarray): - vslice = (slice(ix1 - ixstart, ix2 - ixstart), - slice(iz1 - izstart, iz2 - izstart)) fields[i][sss] = value3d[vslice] else: fields[i][sss] = value |