From c9709ee778f83c256589c10c5f057ed6a809eef4 Mon Sep 17 00:00:00 2001 From: Homer Reid Date: Thu, 28 Sep 2017 23:03:01 -0400 Subject: [PATCH] Arrayslice (#96) * trying mkdocs stuff on master branch as readthedocs seems to have trouble with other branches * synced with stevengj/master * sync with stevenj master * updates * added src/array_slice.cpp and tests/array_slice_test.cpp * added src/array_slice.cpp and tests/array_slice_test.cpp * updates * updates * added real_valued and complex_valued array_slice entry points and added sum_to_all processing * revamped tests/array_slice_test to eliminate reference to h5 files * updates * updates * added libmeepgeom/cavity-arrayslice_ll.cpp * added libmeepgeom/cavity-arrayslice_ll.cpp * updates to libmeepgeom/cavity-arrayslice.cpp * updates to libmeepgeom/array_slice_test.cpp * added python/examples/cavity_arrayslice.py * finalized python/examples/cavity_arrayslice.py and typemaps in python/meep.i * updates to tests/array_slice_test.cpp * added libmeepgeom/array-slice-ll.cpp test * updates to libmeepgeom/cavity-arrayslice.cpp * updates to Makefile.am * updates to libmeepgeom/array-slice-ll.cpp test code * added libmeepgeom/array-slice-ll.h5 binary reference data file for array-slice-ll unit test * update .travis.yml * try revising .travis.yml to avoid annoying link error that has reappeared * fixed missing file * fixed missing file * update .travis.yml in attempt to decipher inscrutable travis test failures * added array-slice-ll test to libmeepgeom tests * removed array-slice-ll test from libmeepgeom * removed array-slice-ll from libmeepgeom tests * deleted .travis.yml and replaced it with version from master branch * added support for derived_components in array_slice * rebased; restored array-slice-ll test in libmeepgeom * added array-slice-ll to CHECKS in libmeepgeom/Makefile.am * pulled updated .travis.yml * updated libmeepgeom/array-slice-ll to find data file correctly in out-of-tree builds --- libmeepgeom/Makefile.am | 12 +- libmeepgeom/array-slice-ll.cpp | 249 ++++++++++++++ libmeepgeom/array-slice-ll.h5 | Bin 0 -> 42464 bytes python/examples/cavity_arrayslice.py | 92 +++++ python/meep.i | 6 + src/Makefile.am | 18 +- src/array_slice.cpp | 482 +++++++++++++++++++++++++++ src/h5file.cpp | 2 +- src/meep.hpp | 49 ++- tests/Makefile.am | 2 +- tests/h5test.cpp | 6 +- 11 files changed, 899 insertions(+), 19 deletions(-) create mode 100644 libmeepgeom/array-slice-ll.cpp create mode 100644 libmeepgeom/array-slice-ll.h5 create mode 100644 python/examples/cavity_arrayslice.py create mode 100644 src/array_slice.cpp diff --git a/libmeepgeom/Makefile.am b/libmeepgeom/Makefile.am index 819a774ab..7a67b7485 100644 --- a/libmeepgeom/Makefile.am +++ b/libmeepgeom/Makefile.am @@ -11,9 +11,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/src -DDATADIR=\"$(srcdir)/\" libmeepgeom_la_SOURCES = \ meepgeom.cpp meepgeom.hpp material_data.hpp -libmeepgeom_la_LDFLAGS = -version-info @SHARED_VERSION_INFO@ +libmeepgeom_la_LDFLAGS = -version-info @SHARED_VERSION_INFO@ -check_PROGRAMS = pw-source-ll ring-ll cyl-ellipsoid-ll absorber-1d-ll +check_PROGRAMS = pw-source-ll ring-ll cyl-ellipsoid-ll absorber-1d-ll array-slice-ll absorber_1d_ll_SOURCES = absorber-1d-ll.cpp absorber_1d_ll_LDADD = libmeepgeom.la $(MEEPLIBS) @@ -27,10 +27,14 @@ pw_source_ll_LDADD = libmeepgeom.la $(MEEPLIBS) ring_ll_SOURCES = ring-ll.cpp ring_ll_LDADD = libmeepgeom.la $(MEEPLIBS) -TESTS = cyl-ellipsoid-ll +array_slice_ll_SOURCES = array-slice-ll.cpp +array_slice_ll_LDADD = libmeepgeom.la $(MEEPLIBS) + +TESTS = cyl-ellipsoid-ll array-slice-ll noinst_PROGRAMS = bend-flux-ll + bend_flux_ll_SOURCES = bend-flux-ll.cpp bend_flux_ll_LDADD = libmeepgeom.la $(MEEPLIBS) -noinst_DATA = cyl-ellipsoid-eps-ref.h5 +noinst_DATA = cyl-ellipsoid-eps-ref.h5 array-slice-ll.h5 diff --git a/libmeepgeom/array-slice-ll.cpp b/libmeepgeom/array-slice-ll.cpp new file mode 100644 index 000000000..0040669ea --- /dev/null +++ b/libmeepgeom/array-slice-ll.cpp @@ -0,0 +1,249 @@ +/***************************************************************/ +/***************************************************************/ +/***************************************************************/ +#include +#include +#include +#include + +#include "meep.hpp" + +#include "ctl-math.h" +#include "ctlgeom.h" + +#include "meepgeom.hpp" + +#ifndef DATADIR + #define DATADIR "./" +#endif + +using namespace meep; + +typedef std::complex cdouble; + +vector3 v3(double x, double y=0.0, double z=0.0) +{ + vector3 v; + v.x=x; v.y=y; v.z=z; + return v; +} + +// passthrough field function +cdouble default_field_function(const cdouble *fields, + const vec &loc, void *data_) +{ + (void) loc; // unused + (void) data_; // unused + return fields[0]; +} + +/***************************************************************/ +/***************************************************************/ +/***************************************************************/ +#define RELTOL 1.0e-6 +double Compare(double *d1, double *d2, int N, const char *Name) +{ + double Norm1=0.0, Norm2=0.0, NormDelta=0.0; + for(int n=0; n RELTOL) + abort("fail: rel error in %s data = %e\n",Name,RelErr); + return RelErr; +} + +double Compare(cdouble *d1, cdouble *d2, int N, const char *Name) +{ + double Norm1=0.0, Norm2=0.0, NormDelta=0.0; + for(int n=0; n RELTOL) + abort("fail: rel error in %s data = %e\n",Name,RelErr); + return RelErr; +} + +/***************************************************************/ +/* dummy material function needed to pass to structure( ) */ +/* constructor as a placeholder before we can call */ +/* set_materials_from_geometry */ +/***************************************************************/ +double dummy_eps(const vec &) { return 1.0; } + +/***************************************************************/ +/***************************************************************/ +/***************************************************************/ +void usage(char *progname) +{ master_printf("usage: %s [options]\n",progname); + master_printf("options: \n"); + master_printf(" --use-symmetry use geometric symmetries\n"); + master_printf(" --write-files write reference data files\n"); + abort(); +} + +/***************************************************************/ +/***************************************************************/ +/***************************************************************/ +int main(int argc, char *argv[], char **envp) +{ + initialize mpi(argc, argv); + + /***************************************************************/ + /* parse command-line options **********************************/ + /***************************************************************/ + bool use_symmetry=false; + bool write_files=false; + for(int narg=1; nargread("hz.r", &rank, dims1D, 1); + if (rank!=1 || dims1D[0]!=NX) + abort("failed to read 1D data(hz.r) from file %s.h5",H5FILENAME); + double *idata = file->read("hz.i", &rank, dims1D, 1); + if (rank!=1 || dims1D[0]!=NX) + abort("failed to read 1D data(hz.i) from file %s.h5",H5FILENAME); + file_slice1d = new cdouble[dims1D[0]]; + for(int n=0; nread("sy", &rank, dims2D, 2); + if (rank!=2 || dims2D[0]!=NX || dims2D[1]!=NY) + abort("failed to read 2D reference data from file %s.h5",H5FILENAME); + delete file; + + // + // generate 1D and 2D array slices and compare to + // data read from file + // + rank=f.get_array_slice_dimensions(v1d, dims1D); + if (rank!=1 || dims1D[0]!=NX) + abort("incorrect dimensions for 1D slice"); + cdouble *slice1d=f.get_complex_array_slice(v1d, Hz); + double RelErr1D=Compare(slice1d, file_slice1d, NX, "Hz_1d"); + master_printf("1D: rel error %e\n",RelErr1D); + + rank=f.get_array_slice_dimensions(v2d, dims2D); + if (rank!=2 || dims2D[0]!=NX || dims2D[1]!=NY) + abort("incorrect dimensions for 2D slice"); + double *slice2d=f.get_array_slice(v2d, Sy); + double RelErr2D=Compare(slice2d, file_slice2d, NX*NY, "Sy_2d"); + master_printf("2D: rel error %e\n",RelErr2D); + + }; // if (write_files) ... else ... + + return 0; +} diff --git a/libmeepgeom/array-slice-ll.h5 b/libmeepgeom/array-slice-ll.h5 new file mode 100644 index 0000000000000000000000000000000000000000..9d359db83c4e00f0f26c222cc99daff40af22f63 GIT binary patch literal 42464 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 literal 0 HcmV?d00001 diff --git a/python/examples/cavity_arrayslice.py b/python/examples/cavity_arrayslice.py new file mode 100644 index 000000000..bc97e9686 --- /dev/null +++ b/python/examples/cavity_arrayslice.py @@ -0,0 +1,92 @@ +import unittest +import meep as mp +import numpy as np +import matplotlib.pyplot as plt + +################################################## +# set up the geometry +################################################## +eps = 13 +w = 1.2 +r = 0.36 +d = 1.4 +N = 3 +sy = 6 +pad = 2 +dpml = 1 +sx = (2 * (pad + dpml + N)) + d - 1 +fcen = 0.25 +df = 0.2 +nfreq = 500 + +cell = mp.Vector3(sx, sy, 0) + +blk = mp.Block(size=mp.Vector3(1e20, w, 1e20), + material=mp.Medium(epsilon=eps)) + +geometry = [blk] + +for i in range(3): + geometry.append(mp.Cylinder(r, center=mp.Vector3(d / 2 + i))) + +for i in range(3): + geometry.append(mp.Cylinder(r, center=mp.Vector3(d / -2 - i))) + +sim = mp.Simulation(cell_size=cell, + geometry=geometry, + sources=[], + boundary_layers=[mp.pml(dpml)], + resolution=20) + +################################################## +# add sources +################################################## +sim.sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), + mp.Hz, mp.Vector3())] + +#sim.symmetries = [mp.Mirror(mp.Y, phase=-1), mp.Mirror(mp.X, phase=-1)] + +################################################## +# run until sources are finished (and no later) +################################################## +sim._run_sources_until(0,[]) + +################################################## +# get 1D and 2D array slices +################################################## +xMin = -0.25*sx; +xMax = +0.25*sx; +yMin = -0.15*sy; +yMax = +0.15*sy; + +# 1D slice of Hz data +dims1d=np.zeros(3,dtype=np.int32) +v1d=mp.volume( mp.vec(xMin, 0.0), mp.vec(xMax, 0.0) ) +rank1d = sim.fields.get_array_slice_dimensions(v1d, dims1d); +NX1 = dims1d[0]; +slice1d = np.zeros(NX1, dtype=np.double); +sim.fields.get_array_slice(v1d, mp.Hz, slice1d); + +# 2D slice of Hz data +dims2d=np.zeros(3,dtype=np.int32) +v2d=mp.volume( mp.vec(xMin, yMin), mp.vec(xMax, yMax) ) +rank2d = sim.fields.get_array_slice_dimensions(v2d, dims2d); +NX2 = dims2d[0]; +NY2 = dims2d[1]; +N2 = NX2*NY2; +slice2d = np.zeros(N2, dtype=np.double); +sim.fields.get_array_slice(v2d, mp.Hz, slice2d); + +# plot 1D slice +plt.subplot(1,2,1); +x1d=np.linspace(xMin, xMax, dims1d[0]); +plt.plot(x1d, slice1d); + +# plot 2D slice +plt.subplot(1,2,2); +dy = (yMax-yMin) / dims2d[1]; +dx = (xMax-xMin) / dims2d[0]; +(x2d,y2d)=np.mgrid[ slice(xMin, xMax, dx), slice(yMin, yMax, dy)]; +plt.contourf( x2d, y2d, np.reshape(slice2d, (dims2d[0], dims2d[1])) ) +plt.colorbar(); +plt.show() diff --git a/python/meep.i b/python/meep.i index 55627b0c8..a2dd0230d 100644 --- a/python/meep.i +++ b/python/meep.i @@ -261,6 +261,7 @@ PyObject *py_do_harminv(PyObject *vals, double dt, double f_min, double f_max, i SWIG_exception_fail(SWIG_ArgError(tmp_res), "Couldn't convert Python object to meep::src_time"); } $1 = reinterpret_cast(tmp_ptr); + } // Typemap suite for boundary_region @@ -295,6 +296,11 @@ PyObject *py_do_harminv(PyObject *vals, double dt, double f_min, double f_max, i } } +// Typemap suite for array_slice +// TODO: add (cdouble *, int) version +%apply (double* INPLACE_ARRAY1, int DIM1) {(double *slice, int slice_length)}; +%apply int INPLACE_ARRAY1[ANY] { int [3] }; + // Rename python builtins %rename(br_apply) meep::boundary_region::apply; %rename(_is) meep::dft_chunk::is; diff --git a/src/Makefile.am b/src/Makefile.am index 5be09c990..3f5deb098 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,15 +9,15 @@ BUILT_SOURCES = sphere-quad.h step_generic_stride1.cpp HDRS = meep.hpp meep_internals.hpp meep/mympi.hpp meep/vec.hpp \ bicgstab.hpp -libmeep@MEEP_SUFFIX@_la_SOURCES = anisotropic_averaging.cpp bands.cpp \ -boundaries.cpp bicgstab.cpp casimir.cpp control_c.cpp cw_fields.cpp \ -dft.cpp dft_ldos.cpp energy_and_flux.cpp fields.cpp loop_in_chunks.cpp \ -h5fields.cpp h5file.cpp initialize.cpp integrate.cpp \ -integrate2.cpp monitor.cpp mympi.cpp multilevel-atom.cpp near2far.cpp \ -output_directory.cpp random.cpp sources.cpp step.cpp step_db.cpp \ -stress.cpp structure.cpp susceptibility.cpp time.cpp update_eh.cpp \ -mpb.cpp update_pols.cpp vec.cpp step_generic.cpp $(HDRS) \ -$(BUILT_SOURCES) +libmeep@MEEP_SUFFIX@_la_SOURCES = array_slice.cpp anisotropic_averaging.cpp \ +bands.cpp boundaries.cpp bicgstab.cpp casimir.cpp \ +control_c.cpp cw_fields.cpp dft.cpp dft_ldos.cpp energy_and_flux.cpp \ +fields.cpp loop_in_chunks.cpp h5fields.cpp h5file.cpp \ +initialize.cpp integrate.cpp integrate2.cpp monitor.cpp mympi.cpp \ +multilevel-atom.cpp near2far.cpp output_directory.cpp random.cpp \ +sources.cpp step.cpp step_db.cpp stress.cpp structure.cpp \ +susceptibility.cpp time.cpp update_eh.cpp mpb.cpp update_pols.cpp \ +vec.cpp step_generic.cpp $(HDRS) $(BUILT_SOURCES) libmeep@MEEP_SUFFIX@_la_LDFLAGS = -version-info @SHARED_VERSION_INFO@ diff --git a/src/array_slice.cpp b/src/array_slice.cpp new file mode 100644 index 000000000..b2656ec2e --- /dev/null +++ b/src/array_slice.cpp @@ -0,0 +1,482 @@ +/* Copyright (C) 2005-2015 Massachusetts Institute of Technology +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2, or (at your option) +% any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software Foundation, +% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* HDF5 output of fields and arbitrary functions thereof. Works + very similarly to integrate.cpp (using fields::loop_in_chunks). */ + +#include +#include +#include + +#include "meep_internals.hpp" + +using namespace std; + +typedef complex cdouble; + +namespace meep { + +/***************************************************************************/ + +typedef struct { + + // information related to the volume covered by the + // array slice (its size, etcetera) + // these fields are filled in by get_array_slice_dimensions + // if the data parameter is non-null + ivec min_corner, max_corner; + int num_chunks; + int rank; + direction ds[3]; + int slice_size; + + // the function to output and related info (offsets for averaging, etc.) + // note: either fun *or* rfun should be non-NULL (not both) + field_function fun; + field_rfunction rfun; + void *fun_data; + std::vector components; + + void *vslice; + + // temporary internal storage buffers + component *cS; + cdouble *ph; + cdouble *fields; + int *offsets; + + int ninveps; + component inveps_cs[3]; + direction inveps_ds[3]; + + int ninvmu; + component invmu_cs[3]; + direction invmu_ds[3]; + +} array_slice_data; + +#define UNUSED(x) (void) x // silence compiler warnings + +/* passthrough field function equivalent to component_fun in h5fields.cpp */ +static cdouble default_field_func(const cdouble *fields, + const vec &loc, void *data_) +{ + (void) loc; // unused + (void) data_; // unused + return fields[0]; +} + +static double default_field_rfunc(const cdouble *fields, + const vec &loc, void *data_) +{ + (void) loc; // unused + (void) data_; // unused + return real(fields[0]); +} + +/***************************************************************/ +/* callback function passed to loop_in_chunks to compute */ +/* dimensions of array slice */ +/***************************************************************/ +static void get_array_slice_dimensions_chunkloop(fields_chunk *fc, int ichnk, component cgrid, + ivec is, ivec ie, + vec s0, vec s1, vec e0, vec e1, + double dV0, double dV1, + ivec shift, complex shift_phase, + const symmetry &S, int sn, + void *data_) +{ + UNUSED(ichnk);UNUSED(cgrid);UNUSED(s0);UNUSED(s1);UNUSED(e0);UNUSED(e1); + UNUSED(dV0);UNUSED(dV1);UNUSED(shift_phase); UNUSED(fc); + array_slice_data *data = (array_slice_data *) data_; + ivec isS = S.transform(is, sn) + shift; + ivec ieS = S.transform(ie, sn) + shift; + data->min_corner = min(data->min_corner, min(isS, ieS)); + data->max_corner = max(data->max_corner, max(isS, ieS)); + data->num_chunks++; + +} + +/***************************************************************/ +/* callback function passed to loop_in_chunks to fill array slice */ +/***************************************************************/ +static void get_array_slice_chunkloop(fields_chunk *fc, int ichnk, component cgrid, + ivec is, ivec ie, vec s0, vec s1, vec e0, vec e1, + double dV0, double dV1, + ivec shift, complex shift_phase, + const symmetry &S, int sn, void *data_) +{ + UNUSED(ichnk);UNUSED(cgrid);UNUSED(s0);UNUSED(s1);UNUSED(e0);UNUSED(e1); + UNUSED(dV0);UNUSED(dV1); + array_slice_data *data = (array_slice_data *) data_; + + //-----------------------------------------------------------------------// + // Find output chunk dimensions and strides, etc. + + int count[3]={1,1,1}, offset[3]={0,0,0}, stride[3]={1,1,1}; + + ivec isS = S.transform(is, sn) + shift; + ivec ieS = S.transform(ie, sn) + shift; + + // figure out what yucky_directions (in LOOP_OVER_IVECS) + // correspond to what directions in the transformed vectors (in output). + ivec permute(zero_ivec(fc->gv.dim)); + for (int i = 0; i < 3; ++i) + permute.set_direction(fc->gv.yucky_direction(i), i); + permute = S.transform_unshifted(permute, sn); + LOOP_OVER_DIRECTIONS(permute.dim, d) + permute.set_direction(d, abs(permute.in_direction(d))); + + // compute the size of the chunk to output, and its strides etc. + for (int i = 0; i < data->rank; ++i) { + direction d = data->ds[i]; + int isd = isS.in_direction(d), ied = ieS.in_direction(d); + count[i] = abs(ied - isd) / 2 + 1; + if (ied < isd) offset[permute.in_direction(d)] = count[i] - 1; + } + for (int i = 0; i < data->rank; ++i) { + direction d = data->ds[i]; + int j = permute.in_direction(d); + for (int k = i + 1; k < data->rank; ++k) stride[j] *= count[k]; + offset[j] *= stride[j]; + if (offset[j]) stride[j] *= -1; + } + + //-----------------------------------------------------------------------// + // Compute the function to output, exactly as in fields::integrate. + int *off = data->offsets; + component *cS = data->cS; + complex *fields = data->fields, *ph = data->ph; + const component *iecs = data->inveps_cs; + const direction *ieds = data->inveps_ds; + int ieos[6]; + const component *imcs = data->invmu_cs; + const direction *imds = data->invmu_ds; + int imos[6]; + int num_components=data->components.size(); + + double *slice=0; + cdouble *zslice=0; + bool complex_data = (data->rfun==0); + if (complex_data) + zslice = (cdouble *)data->vslice; + else + slice = (double *)data->vslice; + + for (int i = 0; i < num_components; ++i) { + cS[i] = S.transform(data->components[i], -sn); + if (cS[i] == Dielectric || cS[i] == Permeability) + ph[i] = 1.0; + else { + fc->gv.yee2cent_offsets(cS[i], off[2*i], off[2*i+1]); + ph[i] = shift_phase * S.phase_shift(cS[i], sn); + } + } + for (int k = 0; k < data->ninveps; ++k) + fc->gv.yee2cent_offsets(iecs[k], ieos[2*k], ieos[2*k+1]); + for (int k = 0; k < data->ninvmu; ++k) + fc->gv.yee2cent_offsets(imcs[k], imos[2*k], imos[2*k+1]); + + vec rshift(shift * (0.5*fc->gv.inva)); + LOOP_OVER_IVECS(fc->gv, is, ie, idx) { + IVEC_LOOP_LOC(fc->gv, loc); + loc = S.transform(loc, sn) + rshift; + + for (int i = 0; i < num_components; ++i) { + if (cS[i] == Dielectric) { + double tr = 0.0; + for (int k = 0; k < data->ninveps; ++k) { + const realnum *ie = fc->s->chi1inv[iecs[k]][ieds[k]]; + if (ie) tr += (ie[idx] + ie[idx+ieos[2*k]] + ie[idx+ieos[1+2*k]] + + ie[idx+ieos[2*k]+ieos[1+2*k]]); + else tr += 4; // default inveps == 1 + } + fields[i] = (4 * data->ninveps) / tr; + } + else if (cS[i] == Permeability) { + double tr = 0.0; + for (int k = 0; k < data->ninvmu; ++k) { + const realnum *im = fc->s->chi1inv[imcs[k]][imds[k]]; + if (im) tr += (im[idx] + im[idx+imos[2*k]] + im[idx+imos[1+2*k]] + + im[idx+imos[2*k]+imos[1+2*k]]); + else tr += 4; // default invmu == 1 + } + fields[i] = (4 * data->ninvmu) / tr; + } + else { + double f[2]; + for (int k = 0; k < 2; ++k) + if (fc->f[cS[i]][k]) + f[k] = 0.25 * (fc->f[cS[i]][k][idx] + + fc->f[cS[i]][k][idx+off[2*i]] + + fc->f[cS[i]][k][idx+off[2*i+1]] + + fc->f[cS[i]][k][idx+off[2*i]+off[2*i+1]]); + else + f[k] = 0; + fields[i] = complex(f[0], f[1]) * ph[i]; + } + } + int idx2 = ((((offset[0] + offset[1] + offset[2]) + + loop_i1 * stride[0]) + + loop_i2 * stride[1]) + + loop_i3 * stride[2]); + + if (complex_data) + zslice[idx2] = data->fun(fields, loc, data->fun_data); + else + slice[idx2] = data->rfun(fields, loc, data->fun_data); + + }; + +} + +/***************************************************************/ +/* given a volume, fill in the dims[] and directions[] arrays */ +/* describing the array slice needed to store field data for */ +/* all grid points in the volume. */ +/* */ +/* return value is rank of array slice. */ +/* */ +/* if caller_data is non-NULL, it should point to a */ +/* caller-allocated array_slice_data structure which will be */ +/* initialized appopriately for subsequent use in */ +/* get_array_slice. */ +/***************************************************************/ +int fields::get_array_slice_dimensions(const volume &where, int dims[3], void *caller_data) +{ + am_now_working_on(ArraySlice); + + // use a local data structure if the caller didn't provide one + array_slice_data local_data; + array_slice_data *data=(array_slice_data *)caller_data; + if (data==0) + data=&local_data; + + data->min_corner = gv.round_vec(where.get_max_corner()) + one_ivec(gv.dim); + data->max_corner = gv.round_vec(where.get_min_corner()) - one_ivec(gv.dim); + data->num_chunks = 0; + + loop_in_chunks(get_array_slice_dimensions_chunkloop, + (void *) data, where, Centered, true, true); + + data->max_corner = max_to_all(data->max_corner); + data->min_corner = -max_to_all(-data->min_corner); // i.e., min_to_all + data->num_chunks = sum_to_all(data->num_chunks); + if (data->num_chunks == 0 || !(data->min_corner <= data->max_corner)) + return 0; // no data to write; + + int rank=0, slice_size=1; + LOOP_OVER_DIRECTIONS(gv.dim, d) { + if (rank >= 3) abort("too many dimensions in array_slice"); + int n = (data->max_corner.in_direction(d) + - data->min_corner.in_direction(d)) / 2 + 1; + if (n > 1) { + data->ds[rank] = d; + dims[rank++] = n; + slice_size *= n; + } + } + data->rank=rank; + data->slice_size=slice_size; + finished_working(); + + return rank; +} + +/***************************************************************/ +/* precisely one of fun, rfun should be non-NULL */ +/***************************************************************/ +void *fields::do_get_array_slice(const volume &where, + std::vector components, + field_function fun, + field_rfunction rfun, + void *fun_data, + void *vslice) { + + am_now_working_on(ArraySlice); + + /***************************************************************/ + /* call get_array_slice_dimensions to get slice dimensions and */ + /* partially initialze an array_slice_data struct */ + /***************************************************************/ + int dims[3]; + array_slice_data data; + int rank=get_array_slice_dimensions(where, dims, &data); + int slice_size=data.slice_size; + if (rank==0 || slice_size==0) return 0; // no data to write + + bool complex_data = (rfun==0); + cdouble *zslice; + double *slice; + if (vslice==0) + { if (complex_data) + { zslice = new cdouble[slice_size]; + vslice = (void *)zslice; + } + else + { slice = new double[slice_size]; + vslice = (void *)slice; + }; + }; + + data.vslice = vslice; + data.fun = fun; + data.rfun = rfun; + data.fun_data = fun_data; + data.components = components; + + int num_components = components.size(); + + data.cS = new component[num_components]; + data.ph = new cdouble[num_components]; + data.fields = new cdouble[num_components]; + + data.offsets = new int[2 * num_components]; + for (int i = 0; i < 2 * num_components; ++i) + data.offsets[i] = 0; + + /* compute inverse-epsilon directions for computing Dielectric fields */ + data.ninveps = 0; + bool needs_dielectric = false; + for (int i = 0; i < num_components; ++i) + if (components[i] == Dielectric) { needs_dielectric = true; break; } + if (needs_dielectric) + FOR_ELECTRIC_COMPONENTS(c) if (gv.has_field(c)) { + if (data.ninveps == 3) abort("more than 3 field components??"); + data.inveps_cs[data.ninveps] = c; + data.inveps_ds[data.ninveps] = component_direction(c); + ++data.ninveps; + } + + /* compute inverse-mu directions for computing Permeability fields */ + data.ninvmu = 0; + bool needs_permeability = false; + for (int i = 0; i < num_components; ++i) + if (components[i] == Permeability) { needs_permeability = true; break; } + if (needs_permeability) + FOR_MAGNETIC_COMPONENTS(c) if (gv.has_field(c)) { + if (data.ninvmu == 3) abort("more than 3 field components??"); + data.invmu_cs[data.ninvmu] = c; + data.invmu_ds[data.ninvmu] = component_direction(c); + ++data.ninvmu; + } + + loop_in_chunks(get_array_slice_chunkloop, (void *) &data, + where, Centered, true, true); + + /***************************************************************/ + /* repeatedly call sum_to_all to consolidate full array slice */ + /* on all cores */ + /***************************************************************/ +#define BUFSIZE 1<<16 // use 64k buffer + if (complex_data) + { cdouble *buffer = new cdouble[BUFSIZE]; + cdouble *slice = (cdouble *)vslice; + int offset=0, remaining=slice_size; + while(remaining!=0) + { + int size = (remaining > BUFSIZE ? BUFSIZE : remaining); + sum_to_all(slice + offset, buffer, size); + memcpy(slice+offset, buffer, size*sizeof(cdouble)); + remaining-=size; + offset+=size; + }; + delete[] buffer; + } + else + { double *buffer = new double[BUFSIZE]; + double *slice = (double *)vslice; + int offset=0, remaining=slice_size; + while(remaining!=0) + { int size = (remaining > BUFSIZE ? BUFSIZE : remaining); + sum_to_all(slice + offset, buffer, size); + memcpy(slice+offset, buffer, size*sizeof(double)); + remaining-=size; + offset+=size; + }; + delete[] buffer; + }; + + delete[] data.offsets; + delete[] data.fields; + delete[] data.ph; + delete[] data.cS; + finished_working(); + + return vslice; +} + +/***************************************************************/ +/* entry points to get_array_slice */ +/***************************************************************/ +double *fields::get_array_slice(const volume &where, + std::vector components, + field_rfunction rfun, void *fun_data, + double *slice) +{ + return (double *)do_get_array_slice(where, components, + 0, rfun, fun_data, + (void *)slice); +} + +cdouble *fields::get_complex_array_slice(const volume &where, + std::vector components, + field_function fun, void *fun_data, + cdouble *slice) +{ + return (cdouble *)do_get_array_slice(where, components, + fun, 0, fun_data, + (void *)slice); +} + +double *fields::get_array_slice(const volume &where, component c, + double *slice, int slice_length) +{ + (void) slice_length; + std::vector components(1); + components[0]=c; + return (double *)do_get_array_slice(where, components, + 0, default_field_rfunc, 0, + (void *)slice); +} + +double *fields::get_array_slice(const volume &where, + derived_component c, + double *slice, int slice_length) +{ + (void) slice_length; + int nfields; + component carray[12]; + field_rfunction rfun = derived_component_func(c, gv, nfields, carray); + std::vector cs(carray, carray+nfields); + return (double *)do_get_array_slice(where, cs, + 0, rfun, &nfields, + (void *)slice); +} + +cdouble *fields::get_complex_array_slice(const volume &where, component c, + cdouble *slice, int slice_length) +{ + (void) slice_length; + std::vector components(1); + components[0]=c; + return (cdouble *)do_get_array_slice(where, components, + default_field_func, 0, 0, + (void *)slice); +} + +} // namespace meep diff --git a/src/h5file.cpp b/src/h5file.cpp index 4c3ac1dcd..5f7b89b9d 100644 --- a/src/h5file.cpp +++ b/src/h5file.cpp @@ -309,7 +309,7 @@ void h5file::read_size(const char *dataname, int *rank, int *dims, int maxrank) #define REALNUM_H5T (sizeof(realnum) == sizeof(double) ? H5T_NATIVE_DOUBLE : H5T_NATIVE_FLOAT) realnum *h5file::read(const char *dataname, - int *rank, int *dims, int maxrank) + int *rank, int *dims, int maxrank) { #ifdef HAVE_HDF5 realnum *data = 0; diff --git a/src/meep.hpp b/src/meep.hpp index 9c9d4da16..6774568b1 100644 --- a/src/meep.hpp +++ b/src/meep.hpp @@ -23,6 +23,8 @@ #include "meep/vec.hpp" #include "meep/mympi.hpp" +#include + namespace meep { /* We use the type realnum for large arrays, e.g. the fields. @@ -1165,7 +1167,8 @@ class fields_chunk { enum boundary_condition { Periodic=0, Metallic, Magnetic, None }; enum time_sink { Connecting, Stepping, Boundaries, MpiTime, - FieldOutput, FourierTransforming, Other }; + FieldOutput, FourierTransforming, + ArraySlice, Other }; typedef void (*field_chunkloop)(fields_chunk *fc, int ichunk, component cgrid, ivec is, ivec ie, @@ -1285,6 +1288,50 @@ class fields { const char *h5file_name(const char *name, const char *prefix = NULL, bool timestamp = false); + // array_slice.cpp methods + + // given a subvolume, compute the dimensions of the array slice + // needed to store field data for that subvolume. + // the data parameter is used internally in get_array_slice + // and should be ignored by external callers. + int get_array_slice_dimensions(const volume &where, int dims[3], void *data=0); + + // given a subvolume, return a column-major array containing + // the given function of the field components in that subvolume + // if slice is non-null, it must be a user-allocated buffer + // of the correct size. + // otherwise, a new buffer is allocated and returned; it + // must eventually be caller-deallocated via delete[]. + double *get_array_slice(const volume &where, + std::vector components, + field_rfunction rfun, void *fun_data, + double *slice=0); + + std::complex *get_complex_array_slice(const volume &where, + std::vector components, + field_function fun, + void *fun_data, + std::complex *slice=0); + + // alternative entry points for when you have no field + // function, i.e. you want just a single component or + // derived component.) (The slice_length parameter is only + // present to facilitate SWIG-generated python wrappers; + // it is ignored by the C code). + double *get_array_slice(const volume &where, component c, double *slice=0, int slice_length=0); + double *get_array_slice(const volume &where, derived_component c, double *slice=0, int slice_length=0); + std::complex *get_complex_array_slice(const volume &where, + component c, + std::complex *slice=0, int slice_length=0); + + // master routine for all above entry points + void *do_get_array_slice(const volume &where, + std::vector components, + field_function fun, + field_rfunction rfun, + void *fun_data, + void *vslice); + // step.cpp methods: double last_step_output_wall_time; int last_step_output_t; diff --git a/tests/Makefile.am b/tests/Makefile.am index e03ce8fb2..cbd1bcd74 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -74,7 +74,7 @@ h5test_LDADD = $(LIBMEEP) pml_SOURCES = pml.cpp pml_LDADD = $(LIBMEEP) -TESTS = aniso_disp bench bragg_transmission convergence_cyl_waveguide cylindrical flux harmonics integrate known_results near2far one_dimensional physical stress_tensor symmetry three_d two_dimensional 2D_convergence h5test pml +TESTS = aniso_disp bench bragg_transmission convergence_cyl_waveguide cylindrical flux harmonics integrate known_results near2far one_dimensional physical stress_tensor symmetry three_d two_dimensional 2D_convergence h5test pml LOG_COMPILER = $(RUNCODE) diff --git a/tests/h5test.cpp b/tests/h5test.cpp index 247f38d5b..00bb79c4a 100644 --- a/tests/h5test.cpp +++ b/tests/h5test.cpp @@ -185,7 +185,7 @@ bool check_2d(double eps(const vec &), double a, int splitting, symfunc Sf, delete[] h5data; } - file->remove(); + //file->remove(); delete file; master_printf("Passed %s (%g..%g), err=%g\n", name, @@ -300,7 +300,7 @@ bool check_3d(double eps(const vec &), double a, int splitting, symfunc Sf, delete[] h5data; } - file->remove(); + //file->remove(); delete file; master_printf("Passed %s (%g..%g), err=%g\n", name, @@ -384,7 +384,7 @@ bool check_2d_monitor(double eps(const vec &), delete[] mon; - file->remove(); + //file->remove(); delete file; master_printf("Passed %s (%g..%g), err=%g\n", name,