📄 loop_in_chunks.cpp
字号:
/* Copyright (C) 2006 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 of the License, 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 */#include <stdio.h>#include <stdlib.h>#include <math.h>#include "meep.hpp"#include "meep_internals.hpp"/* This file contains a generic function for looping over all of the points in all of the chunks that intersect some given volume. This is used for everything from HDF5 output to applying source volumes to integrating energy and flux. It's fairly tricky because of the parallelization, arbitrary chunk divisions, symmetries, and periodic boundary conditions, but at least all of the trickiness is in one place. It is designed so that the inner loops over the actual grid points can be tight and fast (using the LOOP_OVER_IVECS macro). Many of the loops over chunks involve some sort of integration-like computation, and so we also perform the additional task of calculating the integration weights for each point -- mainly, this involves weighting the boundary points appropriately so that the sum approximates (via linear interpolation) a continuous integral over the supplied volume. *//**************************************************************************** Integration WeightsWe want the integral from a to b, assuming linear interpolation of fn(function values on grid points n). Most interior points have weight1, but the points just inside and outside the boundaries havedifferent weights. Call the weights for the points just *outside* thestarting and ending boundaries s0 and e0, respectively, and weightsfor the points just *inside* the boundaries s1 and e1. Then we haveto handle the following cases:1) a and b separated by at least 2 grid points, e.g.:x | x x x | x0 a 1 2 3 b 4first segment: f(x) = f0 (1 - x) + f1 x -- \int_a^1 f(x) dx = f0 (1 - a)^2/2 + f1 (1 - a^2) / 2last segment: f(x) = f3 (4 - x) + f4 (x - 3) -- \int_3^b f(x) dx = f3 [1 - (4-b)^2] / 2 + f4 (b - 3)^2 / 2integral = f0 (1 - a)^2/2 <---- f0 s0 + f1 (1 - a^2/2) <---- f1 s1 + f2 + f3 (1 - (4-b)^2 / 2) <---- f3 e1 + f4 (b - 3)^2 / 2 <---- f4 e0In terms of starting and ending weights: w0 = 1 - a w1 = b - 3 s0 = w0^2 / 2 s1 = 1 - (1 - w0)^2 / 2 e0 = w1^2 / 2 e1 = 1 - (1 - w1)^2 / 22) one grid point between a and b.x | x | x0 a 1 b 2integral = f0 (1 - a)^2 / 2 + f1 [(1 - a^2) + 1 - (2 - b)^2] / 2 + f3 (b - 1)^2 / 2s0 = w0^2 / 2e0 = w1^2 / 2s1 = e1 = 1 - (1 - w0)^2 / 2 - (1 - w1)^2 / 23) no grid points between a and b.x | | x0 a b 1integral = f0 [ (1-a)^2 - (1-b)^2 ] / 2 + f1 [ b^2 - a^2 ] / 2 = f0 [ w0^2 - (1-w1)^2 ] / 2 + f1 [ w1^2 - (1-w0)^2 ] / 2s0 = e1 = w0^2/2 - (1-w1)^2/2e0 = s1 = w1^2/2 - (1-w0)^2/24) as (3), but a = b: interpolation, not integration: -- want: f0 * w0 + f1 * w1s0 = w0e0 = w1 = 1 - w0 -------------- Integration Weights in Cylindrical Coordinates FIXME: implement this below?Ideally, we should have different weights for the R direction ofcylindrical coordinates, i.e. for integrating f(r) r dr, because againwe want to perfectly integrate any linear f(r). Thus, the integrationweights will depend upon r. Note, however, that we also have an r inthe dV, so we will have to divide the weights by this factor.1) a and b separated by at least 2 grid points, e.g.:x | x x x | xi a i+1 i+2 i+3 b i+4(where r = i * inva).linear interpolation in [i,i+1): f(x) = f_i (i+1 - x) + f_{i+1} (x-i) want: \int_a^b f(x) x dxin terms of starting and ending weights: w0 = (i+1) - a w1 = b - (i+3)integral = f_i [-w0^3 / 3 + (i+1) w0^2 / 2] <- s0 i + f_{j=i+1} [w0^3 / 3 - (j+1) w0^2 / 2 + j w0 + j/2 + 1/6] <- s1 (i+1) + f_{j=i+2} j <- 1 (i+2) + f_{j=i+3} [-w1^3 / 3 - (j-1) w1^2 / 2 + j w1 + j/2 - 1/6] <- e1 (i+3) + f_{j=i+4} [w1^3 / 3 + (j-1) w1^2 / 2] <- e0 (i+4) (thanks to Maple for doing the annoying algebra) (yes, I have tested that it correctly integrates linear f(r))Note that the coefficients need to be divided by i, i+1, etcetera toget s0, s1, etcetera; this gives an interior-point weight of 1 asbefore. For i->infinity, this should converge to the weights frombefore. Avoiding division by zero is more tricky, because the weightat j=0 is not necessarily zero, due to the interpolation. It might bebetter to pre-include the dV in the weight for edge elements, withappropriate logic in the IVEC_LOOP_WEIGHT macro. Tricky.The above is also not correct for integrals that cross x=0, becauseit should really be the integral of f(x) |x|. Even interior pointsprobably need special handling in that case. For sanity, we wouldjust divide the integration region into positive and negative r andintegrate them separately somehow. Grrr.2) one grid point between a and b.x | x | xi a i+1 b i+2integral = f_i [-w0^3 / 3 + (i+1) w0^2 / 2] <- s0 i + f_{j=i+1} [w0^3 / 3 - (j+1) w0^2 / 2 + j w0 + -w1^3 / 3 - (j-1) w1^2 / 2 + j w1] <- {s1,e1} (i+1) + f_{j=i+2} [w1^3 / 3 + (j-1) w1^2 / 2] <- e0 (i+2)3) no grid points between a and b.x | | xi a b i+1integral = f_i [-w0^3/3 + (i+1) w0^2/2 + -w1^3/3 - (i-1) w1^2/2 + i w1 - i/2 - 1/6] <- s0 i + f_{j=i+1} [ w0^3/3 - (j+1) w0^2/2 + j w0 + w1^3/3 + (j-1) w1^2/2 - j/2 + 1/6] <- e0 (i+1)4) as (3), but a = b: interpolation, not integration: same as above****************************************************************************/namespace meep {/* The following two functions convert a vec to the nearest ivec in the dielectric (odd-coordinate) grid, either rounding down (floor) or up (ceil). In the special case where a component of the vec is *exactly* on a component of the ivec, we add the corresponding component of equal_shift (which should be either -2, 0, or +2). (equal_shift is there to prevent us from counting edge points twice.) */ static ivec vec2diel_floor(const vec &v, double a, const ivec &equal_shift) { ivec iv(v.dim); LOOP_OVER_DIRECTIONS(v.dim, d) { iv.set_direction(d, 1+2*int(floor(v.in_direction(d)*a-.5))); if (iv.in_direction(d) == v.in_direction(d)) iv.set_direction(d, iv.in_direction(d) + equal_shift.in_direction(d)); } return iv;}static ivec vec2diel_ceil(const vec &v, double a, const ivec &equal_shift) { ivec iv(v.dim); LOOP_OVER_DIRECTIONS(v.dim, d) { iv.set_direction(d, 1+2*int(ceil(v.in_direction(d)*a-.5))); if (iv.in_direction(d) == v.in_direction(d)) iv.set_direction(d, iv.in_direction(d) + equal_shift.in_direction(d)); } return iv;}static inline int iabs(int i) { return (i < 0 ? -i : i); }/* Generic function for computing loops within the chunks, often integral-like things, over a volume WHERE. The job of this function is to call CHUNKLOOP() for each chunk that intersects WHERE, passing it the chunk, the range of integer coordinates to loop over, the integration weights for the boundary points, and the bloch phase shift, translational shift, and symmetry operation to transform the chunk to the actual integration location. (N.B. we apply the symmetry first to the chunk, *then* the shift.) We also pass CHUNKLOOP() dV0 and dV1, such that the integration "volume" dV is dV0 + dV1 * iloopR, where iloopR is the loop variable (starting from 0 at the starting integer coord and incrementing by 1) corresponding to the direction R. Note that, in the LOOP_OVER_IVECS macro, iloopR corresponds to the loop variable loop_i2 in Dcyl (cylindrical coordinates). In other coordinates, dV1 is 0. Note also that by "volume" dV we mean the integration unit corresponding to the dimensionality of WHERE (e.g. an area if WHERE is 2d, etc.) In particular, the loop's point coordinates are calculated on the Yee grid for component cgrid. cgrid == Dielectric is a good choice if you want to work with a combination of multiple field components, because all of the field components can be interpolated onto this grid without communication between chunks. The integration weights are chosen to correspond to integrating the linear interpolation of the function values from these grid points. For a simple example of an chunkloop routine, see the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -