Removed the "size" field from the default arithmetic array class.

This field was mostly unused, the only real use being in the initialize method
where it computed the value for size, set the field, then used the field.
Changed it into a variable local to the initialize method.

The size field was also not always set correctly, for example array slices
and rank changing slices maintained the size of the original array.

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@17312 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

use DSIUtil;
config param debugDefaultDist = false;
config param debugDataPar = false;

class DefaultDist: BaseDist {
  def dsiNewArithmeticDom(param rank: int, type idxType, param stridable: bool)
    return new DefaultArithmeticDom(rank, idxType, stridable, this);

  def dsiNewAssociativeDom(type idxType)
    return new DefaultAssociativeDom(idxType, this);

  def dsiNewOpaqueDom(type idxType)
    return new DefaultOpaqueDom(this);

  def dsiNewSparseDom(param rank: int, type idxType, dom: domain)
    return new DefaultSparseDom(rank, idxType, this, dom);

  def dsiClone() return this;

  def dsiAssign(other: this.type) { }

  def dsiCreateReindexDist(newSpace, oldSpace) return this;
  def dsiCreateRankChangeDist(param newRank, args) return this;
}

//
// Note that the replicated copies are set up in ChapelLocale on the
// other locales.  This just sets it up on this locale.
//
pragma "private" var defaultDist = new dmap(new DefaultDist());

class DefaultArithmeticDom: BaseArithmeticDom {
  param rank : int;
  type idxType;
  param stridable: bool;
  var dist: DefaultDist;
  var ranges : rank*range(idxType,BoundedRangeType.bounded,stridable);

  def linksDistribution() param return false;

  def DefaultArithmeticDom(param rank, type idxType, param stridable, dist) {
    this.dist = dist;
  }

  def dsiClear() {
    var emptyRange: range(idxType, BoundedRangeType.bounded, stridable);
    for param i in 1..rank do
      ranges(i) = emptyRange;
  }

  // function and iterator versions, also for setIndices
  def dsiGetIndices() return ranges;

  def dsiSetIndices(x) {
    if ranges.size != x.size then
      compilerError("rank mismatch in domain assignment");
    if ranges(1).idxType != x(1).idxType then
      compilerError("index type mismatch in domain assignment");
    ranges = x;
  }

  def these_help(param d: int) {
    if d == rank - 1 {
      for i in ranges(d) do
        for j in ranges(rank) do
          yield (i, j);
    } else {
      for i in ranges(d) do
        for j in these_help(d+1) do
          yield (i, (...j));
    }
  }

  def these_help(param d: int, block) {
    if d == block.size - 1 {
      for i in block(d) do
        for j in block(block.size) do
          yield (i, j);
    } else {
      for i in block(d) do
        for j in these_help(d+1, block) do
          yield (i, (...j));
    }
  }

  def these() {
    if rank == 1 {
      for i in ranges(1) do
        yield i;
    } else {
      for i in these_help(1) do
        yield i;
    }
  }

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In domain leader code:"); // this = ", this);
    const numTasks = if dataParTasksPerLocale==0 then here.numCores
                     else dataParTasksPerLocale;
    const ignoreRunning = dataParIgnoreRunningTasks;
    const minIndicesPerTask = dataParMinGranularity;
    if debugDataPar {
      writeln("### numTasks = ", numTasks);
      writeln("### ignoreRunning = ", ignoreRunning);
      writeln("### minIndicesPerTask = ", minIndicesPerTask);
    }

    if debugDefaultDist then
      writeln("    numTasks=", numTasks, " (", ignoreRunning,
              "), minIndicesPerTask=", minIndicesPerTask);

    var (numChunks, parDim) = _computeChunkStuff(numTasks, ignoreRunning,
                                                 minIndicesPerTask,
                                                 ranges, rank);
    if debugDefaultDist then
      writeln("    numChunks=", numChunks, " parDim=", parDim,
              " ranges(", parDim, ").length=", ranges(parDim).length);

    if debugDataPar then writeln("### numChunks=", numChunks, " (parDim=", parDim, ")");

    if numChunks == 1 {
      if rank == 1 {
	yield tuple(0..ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:ranges(i).low.type..#(ranges(i).length);
      if debugDefaultDist then
	writeln("*** DI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeBlock(locBlock(parDim).length,
                                      numChunks, chunk,
                                      locBlock(parDim).high);
	tuple(parDim) = lo..hi;
        if debugDefaultDist then
          writeln("*** DI[", chunk, "]: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    def anyStridable(rangeTuple, param i: int = 1) param
      return if i == rangeTuple.size then rangeTuple(i).stridable
             else rangeTuple(i).stridable || anyStridable(rangeTuple, i+1);

    chpl__testPar("default arithmetic domain follower invoked on ", follower);
    if debugDefaultDist then
      writeln("In domain follower code: Following ", follower);
    param stridable = this.stridable || anyStridable(follower);
    var block: rank*range(stridable=stridable, idxType=idxType);
    if stridable {
      for param i in 1..rank {
        const rStride = ranges(i).stride:idxType,
              fStride = follower(i).stride:idxType;
        if ranges(i).stride > 0 {
          const low = ranges(i).low + follower(i).low*rStride,
                high = ranges(i).low + follower(i).high*rStride,
                stride = (rStride * fStride):int;
          block(i) = low..high by stride;
        } else {
          const low = ranges(i).high + follower(i).high*rStride,
                high = ranges(i).high + follower(i).low*rStride,
                stride = (rStride * fStride): int;
          block(i) = low..high by stride;
        }
      }
    } else {
      for  param i in 1..rank do
        block(i) = ranges(i).low+follower(i).low:idxType..ranges(i).low+follower(i).high:idxType;
    }
    if rank == 1 {
      for i in block do
        yield i;
    } else {
      for i in these_help(1, block) do
        yield i;
    }
  }

  def dsiMember(ind: rank*idxType) {
    for param i in 1..rank do
      if !ranges(i).member(ind(i)) then
        return false;
    return true;
  }

  def dsiIndexOrder(ind: rank*idxType) {
    var totOrder: idxType;
    var blk: idxType = 1;
    for param d in 1..rank by -1 {
      const orderD = ranges(d).indexOrder(ind(d));
      if (orderD == -1) then return orderD;
      totOrder += orderD * blk;
      blk *= ranges(d).length;
    }
    return totOrder;
  }

  def dsiDims()
    return ranges;

  def dsiDim(d : int)
    return ranges(d);

  // optional, is this necesary? probably not now that
  // homogeneous tuples are implemented as C vectors.
  def dsiDim(param d : int)
    return ranges(d);

  def dsiNumIndices {
    var sum = 1:idxType;
    for param i in 1..rank do
      sum *= ranges(i).length;
    return sum;
    // WANT: return * reduce (this(1..rank).length);
  }

  def dsiLow {
    if rank == 1 {
      return ranges(1)._low;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._low;
      return result;
    }
  }

  def dsiHigh {
    if rank == 1 {
      return ranges(1)._high;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._high;
      return result;
    }
  }

  def dsiBuildArray(type eltType) {
    return new DefaultArithmeticArr(eltType=eltType, rank=rank, idxType=idxType,
                                    stridable=stridable, dom=this);
  }

  def dsiBuildArithmeticDom(param rank: int, type idxType, param stridable: bool,
                            ranges: rank*range(idxType,
                                               BoundedRangeType.bounded,
                                               stridable)) {
    var dom = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      dom.ranges(i) = ranges(i);
    return dom;
  }
}

class DefaultArithmeticArr: BaseArr {
  type eltType;
  param rank : int;
  type idxType;
  param stridable: bool;

  var dom : DefaultArithmeticDom(rank=rank, idxType=idxType,
                                         stridable=stridable);
  var off: rank*idxType;
  var blk: rank*idxType;
  var str: rank*chpl__idxTypeToStrType(idxType);
  var origin: idxType;
  var factoredOffs: idxType;
  var data : _ddata(eltType);
  var noinit: bool = false;

  def canCopyFromDevice param return true;

  // end class definition here, then defined secondary methods below

  // can the compiler create this automatically?
  def dsiGetBaseDom() return dom;

  def dsiDestroyData() {
    if dom.dsiNumIndices > 0 {
      pragma "no copy" pragma "no auto destroy" var dr = data;
      pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
      pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, 0);
      pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
      if (chpl__maybeAutoDestroyed(ev)) {
        for i in 0..dom.dsiNumIndices-1 {
          pragma "no copy" pragma "no auto destroy" var dr = data;
          pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
          pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, i);
          pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
          chpl__autoDestroy(ev);
        }
      }
    }
    delete data;
  }

  def these() var {
    if rank == 1 {
      const stride = dom.ranges(1).stride: idxType,
            start  = if stride > 0 then dom.dsiLow else dom.dsiHigh,
            first  = getDataIndex(start),
            second = getDataIndex(start + stride),
            step   = (second-first):chpl__idxTypeToStrType(idxType),
            last   = first + (dom.dsiNumIndices-1) * step:idxType;
      if step > 0 then
        for i in first..last by step do
          yield data(i);
      else
        for i in last..first by step do
          yield data(i);
    } else {
      for i in dom do
        yield dsiAccess(i);
    }
  }

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In array leader code:");// [\n", this, "]");
    const numTasks = if dataParTasksPerLocale==0 then here.numCores
                     else dataParTasksPerLocale;
    const ignoreRunning = dataParIgnoreRunningTasks;
    const minElemsPerTask = dataParMinGranularity;
    if debugDataPar {
      writeln("### numTasks = ", numTasks);
      writeln("### ignoreRunning = ", ignoreRunning);
      writeln("### minElemsPerTask = ", minElemsPerTask);
    }

    if debugDefaultDist then
      writeln("    numTasks=", numTasks, " (", ignoreRunning,
              "), minElemsPerTask=", minElemsPerTask);

    var (numChunks, parDim) = _computeChunkStuff(numTasks, ignoreRunning,
                                                 minElemsPerTask,
                                                 dom.ranges, rank);
    if debugDefaultDist then
      writeln("    numChunks=", numChunks, " parDim=", parDim,
              " ranges(", parDim, ").length=", dom.ranges(parDim).length);

    if debugDataPar then writeln("### numChunks=", numChunks, " (parDim=", parDim, ")");

    if numChunks == 1 {
      if rank == 1 {
	yield tuple(0..dom.ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..dom.ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:dom.ranges(i).low.type..#(dom.ranges(i).length);
      if debugDefaultDist then
	writeln("*** AI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeBlock(locBlock(parDim).length,
                                      numChunks, chunk,
                                      locBlock(parDim).high);
	tuple(parDim) = lo..hi;
	if debugDefaultDist then
          writeln("*** AI[", chunk, "]: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) var where tag == iterator.follower {
    if debugDefaultDist then
      writeln("*** In array follower code:"); // [\n", this, "]");
    for i in dom.these(tag=iterator.follower, follower) do
      yield dsiAccess(i);
  }

  def computeFactoredOffs() {
    factoredOffs = 0:idxType;
    for i in 1..rank do {
      factoredOffs = factoredOffs + blk(i) * off(i);
    }
  }

  // change name to setup and call after constructor call sites
  // we want to get rid of all initialize functions everywhere
  def initialize() {
    if noinit == true then return;
    for param dim in 1..rank {
      off(dim) = dom.dsiDim(dim)._low;
      str(dim) = dom.dsiDim(dim)._stride;
    }
    blk(rank) = 1:idxType;
    for param dim in 1..rank-1 by -1 do
      blk(dim) = blk(dim+1) * dom.dsiDim(dim+1).length;
    computeFactoredOffs();
    var size = blk(1) * dom.dsiDim(1).length;
    data = new _ddata(eltType);
    data.init(size);
  }

  pragma "inline"
  def getDataIndex(ind: idxType ...1) where rank == 1
    return getDataIndex(ind);

  pragma "inline"
  def getDataIndex(ind: rank* idxType) {
    var sum = origin;
    if stridable {
      for param i in 1..rank do
        sum += (ind(i) - off(i)) * blk(i) / abs(str(i)):idxType;
    } else {
      for param i in 1..rank do
        sum += ind(i) * blk(i);
      sum -= factoredOffs;
    }
    return sum;
  }

  // only need second version because wrapper record can pass a 1-tuple
  pragma "inline"
  def dsiAccess(ind: idxType ...1) var where rank == 1
    return dsiAccess(ind);

  pragma "inline"
  def dsiAccess(ind : rank*idxType) var {
    if boundsChecking then
      if !dom.dsiMember(ind) then
        halt("array index out of bounds: ", ind);
    return data(getDataIndex(ind));
  }

  def dsiReindex(d: DefaultArithmeticDom) {
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=d.rank,
                                         idxType=d.idxType,
                                         stridable=d.stridable,
                                         dom=d, noinit=true);
    alias.data = data;
    for param i in 1..rank {
      alias.off(i) = d.dsiDim(i)._low;
      alias.blk(i) = (blk(i) * dom.dsiDim(i)._stride / str(i)) : d.idxType;
      alias.str(i) = d.dsiDim(i)._stride;
    }
    alias.origin = origin:d.idxType;
    alias.computeFactoredOffs();
    return alias;
  }

  def dsiSlice(d: DefaultArithmeticDom) {
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                         idxType=idxType,
                                         stridable=d.stridable,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.blk = blk;
    alias.str = str;
    alias.origin = origin;
    for param i in 1..rank {
      alias.off(i) = d.dsiDim(i)._low;
      alias.origin += blk(i) * (d.dsiDim(i)._low - off(i)) / str(i);
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def dsiRankChange(d, param newRank: int, param newStridable: bool, args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    var alias = new DefaultArithmeticArr(eltType=eltType, rank=newRank,
                                         idxType=idxType,
                                         stridable=newStridable,
                                         dom=d, noinit=true);
    alias.data = data;
    var i = 1;
    alias.origin = origin;
    for param j in 1..args.size {
      if isRange(args(j)) {
        alias.off(i) = d.dsiDim(i)._low;
        alias.origin += blk(j) * (d.dsiDim(i)._low - off(j)) / str(j);
        alias.blk(i) = blk(j);
        alias.str(i) = str(j);
        i += 1;
      } else {
        alias.origin += blk(j) * (args(j) - off(j)) / str(j);
      }
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def dsiReallocate(d: domain) {
    if (d._value.type == dom.type) {
      var copy = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                          idxType=idxType,
                                          stridable=d._value.stridable,
                                          dom=d._value);
      for i in d((...dom.ranges)) do
        copy.dsiAccess(i) = dsiAccess(i);
      off = copy.off;
      blk = copy.blk;
      str = copy.str;
      origin = copy.origin;
      factoredOffs = copy.factoredOffs;
      dsiDestroyData();
      data = copy.data;
      delete copy;
    } else {
      halt("illegal reallocation");
    }
  }
}

def DefaultArithmeticDom.dsiSerialWrite(f: Writer) {
  f.write("[", dsiDim(1));
  for i in 2..rank do
    f.write(", ", dsiDim(i));
  f.write("]");
}

def DefaultArithmeticArr.dsiSerialWrite(f: Writer) {
  def recursiveArrayWriter(in idx: rank*idxType, dim=1, in last=false) {
    var makeStridePositive = if dom.ranges(dim).stride > 0 then 1 else -1;
    if dim == rank {
      var first = true;
      for j in dom.ranges(dim) by makeStridePositive {
        if first then first = false; else f.write(" ");
        idx(dim) = j;
        f.write(dsiAccess(idx));
      }
    } else {
      for j in dom.ranges(dim) by makeStridePositive {
        var lastIdx = if dom.ranges(dim).stride > 0 then dom.ranges(dim).high
                                                    else dom.ranges(dim).low;
        idx(dim) = j;
        recursiveArrayWriter(idx, dim=dim+1,
                             last=(last || dim == 1) && (j == lastIdx));
      }
    }
    if !last && dim != 1 then
      f.writeln();
  }
  const zeroTup: rank*idxType;
  recursiveArrayWriter(zeroTup);
}

use DSIUtil;
config param debugDefaultDist = false;
config param debugDataPar = false;

class DefaultDist: BaseDist {
  def dsiNewArithmeticDom(param rank: int, type idxType, param stridable: bool)
    return new DefaultArithmeticDom(rank, idxType, stridable, this);

  def dsiNewAssociativeDom(type idxType)
    return new DefaultAssociativeDom(idxType, this);

  def dsiNewOpaqueDom(type idxType)
    return new DefaultOpaqueDom(this);

  def dsiNewSparseDom(param rank: int, type idxType, dom: domain)
    return new DefaultSparseDom(rank, idxType, this, dom);

  def dsiClone() return this;

  def dsiAssign(other: this.type) { }

  def dsiCreateReindexDist(newSpace, oldSpace) return this;
  def dsiCreateRankChangeDist(param newRank, args) return this;
}

//
// Note that the replicated copies are set up in ChapelLocale on the
// other locales.  This just sets it up on this locale.
//
pragma "private" var defaultDist = new dmap(new DefaultDist());

class DefaultArithmeticDom: BaseArithmeticDom {
  param rank : int;
  type idxType;
  param stridable: bool;
  var dist: DefaultDist;
  var ranges : rank*range(idxType,BoundedRangeType.bounded,stridable);

  def linksDistribution() param return false;

  def DefaultArithmeticDom(param rank, type idxType, param stridable, dist) {
    this.dist = dist;
  }

  def dsiClear() {
    var emptyRange: range(idxType, BoundedRangeType.bounded, stridable);
    for param i in 1..rank do
      ranges(i) = emptyRange;
  }

  // function and iterator versions, also for setIndices
  def dsiGetIndices() return ranges;

  def dsiSetIndices(x) {
    if ranges.size != x.size then
      compilerError("rank mismatch in domain assignment");
    if ranges(1).idxType != x(1).idxType then
      compilerError("index type mismatch in domain assignment");
    ranges = x;
  }

  def these_help(param d: int) {
    if d == rank - 1 {
      for i in ranges(d) do
        for j in ranges(rank) do
          yield (i, j);
    } else {
      for i in ranges(d) do
        for j in these_help(d+1) do
          yield (i, (...j));
    }
  }

  def these_help(param d: int, block) {
    if d == block.size - 1 {
      for i in block(d) do
        for j in block(block.size) do
          yield (i, j);
    } else {
      for i in block(d) do
        for j in these_help(d+1, block) do
          yield (i, (...j));
    }
  }

  def these() {
    if rank == 1 {
      for i in ranges(1) do
        yield i;
    } else {
      for i in these_help(1) do
        yield i;
    }
  }

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In domain leader code:"); // this = ", this);
    const numTasks = if dataParTasksPerLocale==0 then here.numCores
                     else dataParTasksPerLocale;
    const ignoreRunning = dataParIgnoreRunningTasks;
    const minIndicesPerTask = dataParMinGranularity;
    if debugDataPar {
      writeln("### numTasks = ", numTasks);
      writeln("### ignoreRunning = ", ignoreRunning);
      writeln("### minIndicesPerTask = ", minIndicesPerTask);
    }

    if debugDefaultDist then
      writeln("    numTasks=", numTasks, " (", ignoreRunning,
              "), minIndicesPerTask=", minIndicesPerTask);

    var (numChunks, parDim) = _computeChunkStuff(numTasks, ignoreRunning,
                                                 minIndicesPerTask,
                                                 ranges, rank);
    if debugDefaultDist then
      writeln("    numChunks=", numChunks, " parDim=", parDim,
              " ranges(", parDim, ").length=", ranges(parDim).length);

    if debugDataPar then writeln("### numChunks=", numChunks, " (parDim=", parDim, ")");

    if numChunks == 1 {
      if rank == 1 {
	yield tuple(0..ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:ranges(i).low.type..#(ranges(i).length);
      if debugDefaultDist then
	writeln("*** DI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeBlock(locBlock(parDim).length,
                                      numChunks, chunk,
                                      locBlock(parDim).high);
	tuple(parDim) = lo..hi;
        if debugDefaultDist then
          writeln("*** DI[", chunk, "]: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) where tag == iterator.follower {
    def anyStridable(rangeTuple, param i: int = 1) param
      return if i == rangeTuple.size then rangeTuple(i).stridable
             else rangeTuple(i).stridable || anyStridable(rangeTuple, i+1);

    chpl__testPar("default arithmetic domain follower invoked on ", follower);
    if debugDefaultDist then
      writeln("In domain follower code: Following ", follower);
    param stridable = this.stridable || anyStridable(follower);
    var block: rank*range(stridable=stridable, idxType=idxType);
    if stridable {
      for param i in 1..rank {
        const rStride = ranges(i).stride:idxType,
              fStride = follower(i).stride:idxType;
        if ranges(i).stride > 0 {
          const low = ranges(i).low + follower(i).low*rStride,
                high = ranges(i).low + follower(i).high*rStride,
                stride = (rStride * fStride):int;
          block(i) = low..high by stride;
        } else {
          const low = ranges(i).high + follower(i).high*rStride,
                high = ranges(i).high + follower(i).low*rStride,
                stride = (rStride * fStride): int;
          block(i) = low..high by stride;
        }
      }
    } else {
      for  param i in 1..rank do
        block(i) = ranges(i).low+follower(i).low:idxType..ranges(i).low+follower(i).high:idxType;
    }
    if rank == 1 {
      for i in block do
        yield i;
    } else {
      for i in these_help(1, block) do
        yield i;
    }
  }

  def dsiMember(ind: rank*idxType) {
    for param i in 1..rank do
      if !ranges(i).member(ind(i)) then
        return false;
    return true;
  }

  def dsiIndexOrder(ind: rank*idxType) {
    var totOrder: idxType;
    var blk: idxType = 1;
    for param d in 1..rank by -1 {
      const orderD = ranges(d).indexOrder(ind(d));
      if (orderD == -1) then return orderD;
      totOrder += orderD * blk;
      blk *= ranges(d).length;
    }
    return totOrder;
  }

  def dsiDims()
    return ranges;

  def dsiDim(d : int)
    return ranges(d);

  // optional, is this necesary? probably not now that
  // homogeneous tuples are implemented as C vectors.
  def dsiDim(param d : int)
    return ranges(d);

  def dsiNumIndices {
    var sum = 1:idxType;
    for param i in 1..rank do
      sum *= ranges(i).length;
    return sum;
    // WANT: return * reduce (this(1..rank).length);
  }

  def dsiLow {
    if rank == 1 {
      return ranges(1)._low;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._low;
      return result;
    }
  }

  def dsiHigh {
    if rank == 1 {
      return ranges(1)._high;
    } else {
      var result: rank*idxType;
      for param i in 1..rank do
        result(i) = ranges(i)._high;
      return result;
    }
  }

  def dsiBuildArray(type eltType) {
    return new DefaultArithmeticArr(eltType=eltType, rank=rank, idxType=idxType,
                                    stridable=stridable, dom=this);
  }

  def dsiBuildArithmeticDom(param rank: int, type idxType, param stridable: bool,
                            ranges: rank*range(idxType,
                                               BoundedRangeType.bounded,
                                               stridable)) {
    var dom = new DefaultArithmeticDom(rank, idxType, stridable, dist);
    for i in 1..rank do
      dom.ranges(i) = ranges(i);
    return dom;
  }
}

class DefaultArithmeticArr: BaseArr {
  type eltType;
  param rank : int;
  type idxType;
  param stridable: bool;

  var dom : DefaultArithmeticDom(rank=rank, idxType=idxType,
                                         stridable=stridable);
  var off: rank*idxType;
  var blk: rank*idxType;
  var str: rank*chpl__idxTypeToStrType(idxType);
  var origin: idxType;
  var factoredOffs: idxType;
  var size : idxType;
  var data : _ddata(eltType);
  var noinit: bool = false;

  def canCopyFromDevice param return true;

  // end class definition here, then defined secondary methods below

  // can the compiler create this automatically?
  def dsiGetBaseDom() return dom;

  def dsiDestroyData() {
    if dom.dsiNumIndices > 0 {
      pragma "no copy" pragma "no auto destroy" var dr = data;
      pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
      pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, 0);
      pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
      if (chpl__maybeAutoDestroyed(ev)) {
        for i in 0..dom.dsiNumIndices-1 {
          pragma "no copy" pragma "no auto destroy" var dr = data;
          pragma "no copy" pragma "no auto destroy" var dv = __primitive("get ref", dr);
          pragma "no copy" pragma "no auto destroy" var er = __primitive("array_get", dv, i);
          pragma "no copy" pragma "no auto destroy" var ev = __primitive("get ref", er);
          chpl__autoDestroy(ev);
        }
      }
    }
    delete data;
  }

  def these() var {
    if rank == 1 {
      const stride = dom.ranges(1).stride: idxType,
            start  = if stride > 0 then dom.dsiLow else dom.dsiHigh,
            first  = getDataIndex(start),
            second = getDataIndex(start + stride),
            step   = (second-first):chpl__idxTypeToStrType(idxType),
            last   = first + (dom.dsiNumIndices-1) * step:idxType;
      if step > 0 then
        for i in first..last by step do
          yield data(i);
      else
        for i in last..first by step do
          yield data(i);
    } else {
      for i in dom do
        yield dsiAccess(i);
    }
  }

  def these(param tag: iterator) where tag == iterator.leader {
    if debugDefaultDist then
      writeln("*** In array leader code:");// [\n", this, "]");
    const numTasks = if dataParTasksPerLocale==0 then here.numCores
                     else dataParTasksPerLocale;
    const ignoreRunning = dataParIgnoreRunningTasks;
    const minElemsPerTask = dataParMinGranularity;
    if debugDataPar {
      writeln("### numTasks = ", numTasks);
      writeln("### ignoreRunning = ", ignoreRunning);
      writeln("### minElemsPerTask = ", minElemsPerTask);
    }

    if debugDefaultDist then
      writeln("    numTasks=", numTasks, " (", ignoreRunning,
              "), minElemsPerTask=", minElemsPerTask);

    var (numChunks, parDim) = _computeChunkStuff(numTasks, ignoreRunning,
                                                 minElemsPerTask,
                                                 dom.ranges, rank);
    if debugDefaultDist then
      writeln("    numChunks=", numChunks, " parDim=", parDim,
              " ranges(", parDim, ").length=", dom.ranges(parDim).length);

    if debugDataPar then writeln("### numChunks=", numChunks, " (parDim=", parDim, ")");

    if numChunks == 1 {
      if rank == 1 {
	yield tuple(0..dom.ranges(1).length-1);
      } else {
	var block: rank*range(idxType);
	for param i in 1..rank do
	  block(i) = 0..dom.ranges(i).length-1;
	yield block;
      }
    } else {
      var locBlock: rank*range(idxType);
      for param i in 1..rank do
	locBlock(i) = 0:dom.ranges(i).low.type..#(dom.ranges(i).length);
      if debugDefaultDist then
	writeln("*** AI: locBlock = ", locBlock);
      coforall chunk in 0..numChunks-1 {
	var tuple: rank*range(idxType) = locBlock;
	const (lo,hi) = _computeBlock(locBlock(parDim).length,
                                      numChunks, chunk,
                                      locBlock(parDim).high);
	tuple(parDim) = lo..hi;
	if debugDefaultDist then
          writeln("*** AI[", chunk, "]: tuple = ", tuple);
	yield tuple;
      }
    }
  }

  def these(param tag: iterator, follower) var where tag == iterator.follower {
    if debugDefaultDist then
      writeln("*** In array follower code:"); // [\n", this, "]");
    for i in dom.these(tag=iterator.follower, follower) do
      yield dsiAccess(i);
  }

  def computeFactoredOffs() {
    factoredOffs = 0:idxType;
    for i in 1..rank do {
      factoredOffs = factoredOffs + blk(i) * off(i);
    }
  }

  // change name to setup and call after constructor call sites
  // we want to get rid of all initialize functions everywhere
  def initialize() {
    if noinit == true then return;
    for param dim in 1..rank {
      off(dim) = dom.dsiDim(dim)._low;
      str(dim) = dom.dsiDim(dim)._stride;
    }
    blk(rank) = 1:idxType;
    for param dim in 1..rank-1 by -1 do
      blk(dim) = blk(dim+1) * dom.dsiDim(dim+1).length;
    computeFactoredOffs();
    size = blk(1) * dom.dsiDim(1).length;
    data = new _ddata(eltType);
    data.init(size);
  }

  pragma "inline"
  def getDataIndex(ind: idxType ...1) where rank == 1
    return getDataIndex(ind);

  pragma "inline"
  def getDataIndex(ind: rank* idxType) {
    var sum = origin;
    if stridable {
      for param i in 1..rank do
        sum += (ind(i) - off(i)) * blk(i) / abs(str(i)):idxType;
    } else {
      for param i in 1..rank do
        sum += ind(i) * blk(i);
      sum -= factoredOffs;
    }
    return sum;
  }

  // only need second version because wrapper record can pass a 1-tuple
  pragma "inline"
  def dsiAccess(ind: idxType ...1) var where rank == 1
    return dsiAccess(ind);

  pragma "inline"
  def dsiAccess(ind : rank*idxType) var {
    if boundsChecking then
      if !dom.dsiMember(ind) then
        halt("array index out of bounds: ", ind);
    return data(getDataIndex(ind));
  }

  def dsiReindex(d: DefaultArithmeticDom) {
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=d.rank,
                                         idxType=d.idxType,
                                         stridable=d.stridable,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.size = size: d.idxType;
    for param i in 1..rank {
      alias.off(i) = d.dsiDim(i)._low;
      alias.blk(i) = (blk(i) * dom.dsiDim(i)._stride / str(i)) : d.idxType;
      alias.str(i) = d.dsiDim(i)._stride;
    }
    alias.origin = origin:d.idxType;
    alias.computeFactoredOffs();
    return alias;
  }

  def dsiSlice(d: DefaultArithmeticDom) {
    var alias = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                         idxType=idxType,
                                         stridable=d.stridable,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.size = size;
    alias.blk = blk;
    alias.str = str;
    alias.origin = origin;
    for param i in 1..rank {
      alias.off(i) = d.dsiDim(i)._low;
      alias.origin += blk(i) * (d.dsiDim(i)._low - off(i)) / str(i);
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def dsiRankChange(d, param newRank: int, param newStridable: bool, args) {
    def isRange(r: range(?e,?b,?s)) param return 1;
    def isRange(r) param return 0;

    var alias = new DefaultArithmeticArr(eltType=eltType, rank=newRank,
                                         idxType=idxType,
                                         stridable=newStridable,
                                         dom=d, noinit=true);
    alias.data = data;
    alias.size = size;
    var i = 1;
    alias.origin = origin;
    for param j in 1..args.size {
      if isRange(args(j)) {
        alias.off(i) = d.dsiDim(i)._low;
        alias.origin += blk(j) * (d.dsiDim(i)._low - off(j)) / str(j);
        alias.blk(i) = blk(j);
        alias.str(i) = str(j);
        i += 1;
      } else {
        alias.origin += blk(j) * (args(j) - off(j)) / str(j);
      }
    }
    alias.computeFactoredOffs();
    return alias;
  }

  def dsiReallocate(d: domain) {
    if (d._value.type == dom.type) {
      var copy = new DefaultArithmeticArr(eltType=eltType, rank=rank,
                                          idxType=idxType,
                                          stridable=d._value.stridable,
                                          dom=d._value);
      for i in d((...dom.ranges)) do
        copy.dsiAccess(i) = dsiAccess(i);
      off = copy.off;
      blk = copy.blk;
      str = copy.str;
      origin = copy.origin;
      factoredOffs = copy.factoredOffs;
      size = copy.size;
      dsiDestroyData();
      data = copy.data;
      delete copy;
    } else {
      halt("illegal reallocation");
    }
  }
}

def DefaultArithmeticDom.dsiSerialWrite(f: Writer) {
  f.write("[", dsiDim(1));
  for i in 2..rank do
    f.write(", ", dsiDim(i));
  f.write("]");
}

def DefaultArithmeticArr.dsiSerialWrite(f: Writer) {
  def recursiveArrayWriter(in idx: rank*idxType, dim=1, in last=false) {
    var makeStridePositive = if dom.ranges(dim).stride > 0 then 1 else -1;
    if dim == rank {
      var first = true;
      for j in dom.ranges(dim) by makeStridePositive {
        if first then first = false; else f.write(" ");
        idx(dim) = j;
        f.write(dsiAccess(idx));
      }
    } else {
      for j in dom.ranges(dim) by makeStridePositive {
        var lastIdx = if dom.ranges(dim).stride > 0 then dom.ranges(dim).high
                                                    else dom.ranges(dim).low;
        idx(dim) = j;
        recursiveArrayWriter(idx, dim=dim+1,
                             last=(last || dim == 1) && (j == lastIdx));
      }
    }
    if !last && dim != 1 then
      f.writeln();
  }
  const zeroTup: rank*idxType;
  recursiveArrayWriter(zeroTup);
}


One last tiny modification...

One last tiny modification...

git-svn-id: 88467cb1fb04b8a755be7e1ee1026be4190196ef@17549 3a8e244f-b0f2-452b-bcba-4c88e055c3ca

//===> Description ===>
//
// Basic class and method definitions for a rectangular grid of
// generic dimension (though it must be a compile-time constant.)
//
// This version is based on an underlying mesh of width dx/2, which
// contains all cell centers, interface centers, and vertices.  As
// a result, the domin of cell centers is strided by 2, corresponding
// to jumps of dx.
//
//<=== Description <===

config param dimension = 2;
const dimensions = [1..dimension];

//======================================================================>
//==================== FUNDAMENTAL GRID DEFINITIONS  ===================>
//======================================================================>

//===> RectangularGrid class ===>
//==============================>
class RectangularGrid {

  var x_low, x_high:          dimension*real,
      i_low, i_high:          dimension*int,
      n_cells, n_ghost_cells: dimension*int;

  var dx: dimension*real;

  var ext_cells:      domain(dimension, stridable=true),
      cells: subdomain(ext_cells);

  var low_ghost_cells:  [dimensions] subdomain(ext_cells),
      high_ghost_cells: [dimensions] subdomain(ext_cells);

  //===> initialize method ===>
  def initialize() {

    sanityChecks();

    setDerivedFields();
  }
  //<=== initialize method <===

  //===> sanityChecks method ===>
  def sanityChecks() {
    var d_string: string;
    for d in dimensions do {
      d_string = format("%i", d);

      assert(x_low(d) < x_high(d),
	     "error: RectangularGrid: x_low(" + d_string + ") must be strictly less than x_high(" + d_string + ").");

      assert(n_cells(d) > 0,
             "error: RectangularGrid: n_cells(" + d_string + ") must be positive.");

      assert(n_ghost_cells(d) > 0,
	     "error: RectangularGrid: n_ghost_cells(" + d_string + ") must be positive.");
    }
  }
  //<=== sanityChecks method <===

  //===> setDerivedFields method ===>
  //--------------------------------------------------------------
  // After x_low, x_high, n_cells, and n_ghost_cells have
  // been provided, calculate:
  //     dx
  //     i_high
  //     cells
  //     ext_cells
  //     low_ghost_cells
  //     high_ghost_cells
  //--------------------------------------------------------------
  def setDerivedFields() {
    //==== dx ====
    dx = (x_high - x_low) / n_cells;

    //==== i_high ====
    for d in dimensions do
      i_high(d) = i_low(d) + 2*n_cells(d);

    //==== Physical cells ====
    var range_tuple: dimension*range(stridable = true);
    for d in dimensions do
      range_tuple(d) = i_low(d)+1 .. i_low(d)+2*n_cells(d)-1 by 2;
    cells = [(...range_tuple)];

    //==== Extended cells (includes ghost cells) ====
    var size: dimension*int;
    [d in dimensions] size(d) = 2*n_ghost_cells(d);
    ext_cells = cells.expand(size);

    //===> Ghost cells ===>
    //------------------------------------------------------
    // Note that all ghost cell domains contain the corners.
    //------------------------------------------------------
    for d in dimensions do {
      forall d_size in dimensions do size(d_size) = 0;

      size(d) = -(2*n_ghost_cells(d) - 1);
      low_ghost_cells(d) = ext_cells.interior(size);

      size(d) = 2*n_ghost_cells(d) - 1;
      high_ghost_cells(d) = ext_cells.interior(size);
    }
    //<=== Ghost cells <===

  }
  //<=== setDerivedFields method <===

  //===> GhostData subclass ===>
  //===========================>
  //--------------------------------------------------------------
  // Ideally, the fields low and high would be direct collections
  // of arrays, but I don't think that's possible at the moment.
  //--------------------------------------------------------------
  class GhostData {

    type data_type;
    var low, high: [dimensions] IsolatedArray(dimension, data_type);

    def initialize() {
      for d in dimensions {
	low(d) = new IsolatedArray(dim       = dimension,
				   data_type = data_type,
				   dom       = low_ghost_cells(d));

	high(d) = new IsolatedArray(dim       = dimension,
				    data_type = data_type,
				    dom       = high_ghost_cells(d));
      }

    }
  }
  //<===========================
  //<=== GhostData subclass <===

}
//<=== RectangularGrid class <===
//<==============================

//===> xValue method ===>
//======================>
//---------------------------------------
// Converts indices to coordinate values.
//----------------------------------------
def RectangularGrid.xValue (point_index: dimension*int) {

  var coord: dimension*real;

  if dimension == 1 then {
    coord(1) = x_low(1) + (point_index(1) - i_low(1)) * dx(1)/2.0;
  }
  else {
    forall d in dimensions do
      coord(d) = x_low(d) + (point_index(d) - i_low(d)) * dx(d)/2.0;
  }

  return coord;
}
//<======================
//<=== xValue method <===

//<=====================================================================
//<=================== FUNDAMENTAL GRID DEFINITIONS ====================
//<=====================================================================

//=======================================================>
//==================== GRID SOLUTIONS ===================>
//=======================================================>

//===> GridSolution class ===>
//===========================>
class GridSolution {
  var grid:  RectangularGrid;
  var value: [grid.ext_cells] real;
  var time:  real;
}
//<===========================
//<=== GridSolution class ====

//===> copySolution method ===>
//============================>
def RectangularGrid.copySolution(q: GridSolution) {

  //==== Make sure q lives on this grid ====
  assert(q.grid == this);

  //==== Copy q ====
  var q_copy = new GridSolution(grid  = this,
			        value = q.value,
			        time  = q.time);

}
//<=== copySolution method <===
//<============================

//===> initializeSolution method ===>
//==================================>
//---------------------------------------------------------------
// Provided an analytical function, evaluates it on the grid and
// returns a GridSolution.  As support for first-class functions
// develops, the input argument will become explicitly typed.
//---------------------------------------------------------------
def RectangularGrid.initializeSolution(q:                 GridSolution,
				       initial_condition: func(dimension*real, real)
				      ){

  //==== Check that q lives on this grid ====
  assert(q.grid == this);

  write("Writing solution on grid...");
  forall precell in cells {
    var cell = tuplify(precell);
    q.value(cell) = initial_condition(xValue(cell));
  }
  write("done.\n");

}

//-------------------------------------------------------------------
// A similar method that does this for a TrueSolution is convenient.
//-------------------------------------------------------------------
def RectangularGrid.initializeSolution(q:             GridSolution,
				       true_solution: TrueSolution
				      ){

  q.grid = this;

  forall precell in cells {
    var cell = tuplify(precell);
    q.value(cell) = true_solution.qTrue(xValue(cell), 0.0);
  }

}

//<==================================
//<=== initializeSolution method <===

//<=======================================================
//<=================== GRID SOLUTIONS ====================
//<=======================================================

//============================================================>
//==================== BOUNDARY CONDITIONS ===================>
//============================================================>

//===> GridBC class ===>
//=====================>
class GridBC {

  var grid: RectangularGrid;
  var low_boundary_faces:  [dimensions] domain(dimension, stridable=true);
  var high_boundary_faces: [dimensions] domain(dimension, stridable=true);

  //===> setBoundaryFaces method ===>
  //================================>
  //------------------------------------------------------
  // Sets up domains for the low and high boundary faces.
  //------------------------------------------------------
  def setBoundaryFaces() {

    var range_tuple: dimension*range(stridable=true);

    for d in dimensions {

      //==== All components except for d will equal grid.cells ====
      for d_range in dimensions do
        range_tuple(d_range) = grid.cells.dim(d_range);

      //==== low boundary faces ====
      range_tuple(d)        = grid.i_low(d) .. grid.i_low(d) by 2;
      low_boundary_faces(d) = [(...range_tuple)];

      //==== high_boundary_faces ====
      range_tuple(d)         = grid.i_high(d) .. grid.i_high(d) by 2;
      high_boundary_faces(d) = [(...range_tuple)];

    }
  }
  //<================================
  //<=== setBoundaryFaces method <===

  //==== Dummy routines; to be provided in derived classes ====
  def ghostFill(q: GridSolution){}
  def homogeneousGhostFill(q: GridSolution){}

}
//<=== GridBC Class <===
//<=====================

//===> PeriodicGridBC class ===>
//=============================>
class PeriodicGridBC: GridBC {

  var low_ghost_periodic:  [dimensions] subdomain(grid.ext_cells);
  var high_ghost_periodic: [dimensions] subdomain(grid.ext_cells);

  //===> initialize method ===>
  //============================>
  def initialize() {

    //===> Build periodic images of ghost cells ===>
    var shift: dimension*int;

    for d in dimensions do {
      [d_temp in dimensions] shift(d_temp) = 0;

      shift(d) = 2*grid.n_cells(d);
      low_ghost_periodic(d)  = grid.low_ghost_cells(d).translate(shift);

      shift(d) = -2*grid.n_cells(d);
      high_ghost_periodic(d) = grid.high_ghost_cells(d).translate(shift);
    }
    //<=== Build periodic images of ghost cells <===

  }
  //<============================
  //<=== initialize method <===

  //===> ghostFill method ===>
  //=========================>
  def ghostFill(q: GridSolution) {
    //==== Periodic BCs are homogeneous ====
    homogeneousGhostFill(q);
  }
  //<=========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  def homogeneousGhostFill(q: GridSolution) {

    //==== Make sure q lives on the same grid as the BC ====
    assert(q.grid == grid);

    //==== Copy values into ghost cells from their periodic images ====
    for d in dimensions do {
      q.value(grid.low_ghost_cells(d))  = q.value(low_ghost_periodic(d));
      q.value(grid.high_ghost_cells(d)) = q.value(high_ghost_periodic(d));
    }
  }
  //<====================================
  //<=== homogeneousGhostFill method <===

}
//<=============================
//<=== PeriodicGridBC class <===

//===> ZeroOrderExtrapGridBC class ===>
//=====================================>
class ZeroOrderExtrapGridBC: GridBC {

  //===> initialize method ===>
  //==========================>
  def intiialize() {
    setBoundaryFaces();
  }
  //<==========================
  //<=== initialize method <===

  //===> ghostFill method ===>
  //=========================>
  def ghostFill(q: GridSolution) {
    //==== Constant Dirichlet BCs are homogeneous ====
    homogeneousGhostFill(q);
  }
  //<========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  def homogeneousGhostFill(q: GridSolution) {

    for d in dimensions do {

      var shift: dimension*int;
      shift(d) = 1;

      //==== Low boundary ====
      forall preface in low_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face-shift) = q.value(face+shift);
      }

      //==== High ghost cells ====
      forall preface in high_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face+shift) = q.value(face-shift);
      }
    }

  }
  //<====================================
  //<=== homogeneousGhostFill method <===

}
//<====================================
//<=== ZeroOrderExtrapGridBC class <===

//===> TrueSolutionDirichletGridBC class ===>
//==========================================>
//----------------------------------------------------------------
// Provides Dirichlet boundary conditions corresponding to method
// qTrue.  Derive this class to define qTrue.
//----------------------------------------------------------------
class TrueSolutionDirichletGridBC: GridBC {

  var true_solution: TrueSolution;

  //===> initialize method ===>
  //==========================>
  def initialize() {
    setBoundaryFaces();
  }
  //<=== initialize method <===
  //<==========================

  //===> ghostFill method ===>
  //=========================>
  //---------------------------------------------------
  // Evaluates true_solution.qTrue on each ghost cell.
  //---------------------------------------------------
  def ghostFill(q: GridSolution) {

    for d in dimensions {

      forall precell in grid.low_ghost_cells(d) {
        var cell = tuplify(precell);
        q.value(cell) = true_solution.qTrue(grid.xValue(cell), q.time);
      }

      forall precell in grid.high_ghost_cells(d) {
        var cell = tuplify(precell);
        q.value(cell) = true_solution.qTrue(grid.xValue(cell), q.time);
      }

    }

  }
  //<=========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  //------------------------------------------------------------
  // For homogeneous Dirichlet BCs.  At each boundary face, the
  // nearest interior value is reflected about 0 to the nearest
  // ghost cell value.
  //------------------------------------------------------------
  def homogeneousGhostFill(q: GridSolution) {

    for d in dimensions {

      //==== For shifting from a face to a cell ====
      var shift: dimension*int;
      shift(d) = 1;

      //==== Low boundary ====
      forall preface in low_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face - shift) = -q.value(face + shift);
      }

      //==== High boundary ====
      forall preface in high_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face + shift) = -q.value(face - shift);
      }
    }

  }
  //<====================================
  //<=== homogeneousGhostFill method <===

}
//<==========================================
//<=== TrueSolutionDirichletGridBC class <===

//===> TrueSolutionNeumannGridBC class ===>
//========================================>
class TrueSolutionNeumannGridBC: GridBC {

  var true_solution: TrueSolution;

  //===> initialize method ===>
  //==========================>
  def initialize() {
    setBoundaryFaces();
  }
  //<=========================
  //<=== intialize method <===

  //===> ghostFill method ===>
  //=========================>
  def ghostFill(q: GridSolution) {

    writeln("Start of ghostFill");

    for d in dimensions {

      //==== dx in this dimension ====
      var dx = grid.dx(d);

      //==== For shifting faces to cells ====
      var shift: dimension*int;
      shift(d) = 1;

      //==== Low boundary ====
      forall preface in low_boundary_faces(d) {
        var face            = tuplify(preface);
        var normal_flux     = -true_solution.fluxComponent(grid.xValue(face), q.time, d);
        q.value(face-shift) = true_solution.normalFluxToGhost(normal_flux,
                                                              q.value(face+shift),
                                                              dx);
      }

      //==== High boundary ====
      forall preface in high_boundary_faces(d) {
        var face            = tuplify(preface);
        var normal_flux     = true_solution.fluxComponent(grid.xValue(face), q.time, d);
        q.value(face+shift) = true_solution.normalFluxToGhost(normal_flux,
                                                              q.value(face-shift),
                                                              dx);
      }

    }

  }
  //<=========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  def homogeneousGhostFill(q: GridSolution) {

    for d in dimensions do {
      //==== Low ghost cells ====
      forall cell in grid.low_ghost_cells(d) {
        var target_cell = cell;
        target_cell(d)  = grid.cells.dim(d).low;
        q.value(cell)   = q.value(target_cell);
      }

      //==== High ghost cells ====
      forall cell in grid.high_ghost_cells(d) {
        var target_cell = cell;
        target_cell(d)  = grid.cells.dim(d).high;
        q.value(cell)   = q.value(target_cell);
      }
    }

  }
  //<====================================
  //<=== homogeneousGhostFill method <===
}
//<========================================
//<=== TrueSolutionNeumannGridBC class <===

//<============================================================
//<=================== BOUNDARY CONDITIONS ====================
//<============================================================

//=======================================================>
//==================== OUTPUT METHODS ===================>
//=======================================================>

//===> writeTimeFile function =====>
//=================================>
//-------------------------------------------------------------
// Global routine; meant to be called by output method of any
// spatial object.  The time file is really simple, and this
// just formats a few parameters.
//-------------------------------------------------------------
def writeTimeFile(time:    real,
                  meqn:    int,
                  ngrids:  int,
                  naux:    int,
                  outfile: file
                 ) {

  //==== Formatting parameters ====
  var efmt:  string = "%26.16E",
      ifmt:  string = "%5i";

  //==== Write time file ====
  outfile.writeln( format(efmt, time),      "    time");
  outfile.writeln( format(ifmt, meqn),      "                 meqn");
  outfile.writeln( format(ifmt, ngrids),    "                 ngrids");
  outfile.writeln( format(ifmt, naux),      "                 naux");
  outfile.writeln( format(ifmt, dimension), "                 ndim");
  outfile.writeln("");
  outfile.writeln("");

}
//<=== writeTimeFile method <=====
//<===============================

//===> clawOutput method ===>
//==========================>
//-------------------------------------------------------------------
// Writes Clawpack-formatted output for a GridSolution, at the given
// frame_number.
//-------------------------------------------------------------------
def RectangularGrid.clawOutput(q:             GridSolution,
                               frame_number: int
                              ){

  //==== Make sure q lives on this grid ====
  assert(q.grid == this);

  //==== Names of output files ====
  var frame_string:      string = format("%04i", frame_number),
      time_filename:     string = "_output/fort.t" + frame_string,
      solution_filename: string = "_output/fort.q" + frame_string;

  //==== Time file ====
  var outfile = new file(time_filename, FileAccessMode.write);
  outfile.open();
  writeTimeFile(q.time, 1, 1, 0, outfile);
  outfile.close();
  delete outfile;

  //==== Solution file ====
  outfile = new file(solution_filename, FileAccessMode.write);
  outfile.open();
  writeSolution(q, 1, 1, outfile);
  outfile.close();
  delete outfile;

}
//<=== clawOutput method <===
//<==========================

//===> writeSolution method ====>
//==============================>
//---------------------------------------------------------
// Writes the data for a GridSolution living on this grid.
//---------------------------------------------------------
def RectangularGrid.writeSolution(q:           GridSolution,
                                  grid_number: int,
                                  AMR_level:   int,
                                  outfile:     file
                                 ){

  //==== Formatting parameters ====
  var efmt:  string = "%26.16E",
      ifmt:  string = "%5i",
      sfmt:  string = "%20s",
      linelabel: string;

  //==== Header ====
  outfile.writeln( format(ifmt, grid_number), "                 grid_number");
  outfile.writeln( format(ifmt, AMR_level),   "                 AMR_level");

  //==== Write n_cells ====
  for d in dimensions do {
    select d {
      when 1 do linelabel = "                 mx";
      when 2 do linelabel = "                 my";
      when 3 do linelabel = "                 mz";
      otherwise linelabel = "                 mx(" + format("%1i",d) + ")";
    }
    outfile.writeln( format(ifmt, n_cells(d)),  linelabel);
  }

  //==== Write x_low ====
  for d in dimensions do {
    select d {
      when 1 do linelabel = "    xlow";
      when 2 do linelabel = "    ylow";
      when 3 do linelabel = "    zlow";
      otherwise linelabel = "    xlow(" + format("%1i",d) + ")";
    }
    outfile.writeln( format(efmt, x_low(d)),  linelabel);
  }

  //==== Write dx ====
  for d in dimensions do {
    select d {
      when 1 do linelabel = "    dx";
      when 2 do linelabel = "    dy";
      when 3 do linelabel = "    dz";
      otherwise linelabel = "    dx(" + format("%1i",d) + ")";
    }
    outfile.writeln( format(efmt, dx(d)),  linelabel);
  }
  outfile.writeln("");

  //===> Write solution values ===>
  if dimension == 1 then {
    for cell in cells do
      outfile.writeln(format(efmt, q.value(cell)));
  }
  else {
    //------------------------------------------------------------
    //---- Transpose cells; iterating over the transpose
    //---- in row major order achieves column major order on the
    //---- original domain.
    //------------------------------------------------------------
    var range_tuple: dimension*range(stridable=true);
    [d in dimensions] range_tuple(d) = cells.dim(1 + dimension - d);

    var cells_transposed: domain(dimension, stridable=true);
    cells_transposed = [(...range_tuple)];

    var cell:       dimension*int;
    var n_newlines: int;
    for cell_transposed in cells_transposed do {

      //==== Write value ====
      [d in dimensions] cell(d) = cell_transposed(1 + dimension - d);
      outfile.writeln(format(efmt, q.value(cell)));

      //===> Newlines at the end of each dimension ===>
      //--------------------------------------------------------------
      // There are no newlines at the very end of output.  How that's
      // handled is up to the main output routine.
      //--------------------------------------------------------------
      n_newlines = 0;

      for d in dimensions do {
        if cell(d) == cells.dim(d).high then
          n_newlines += 1;
        else
          break;
      }

      if n_newlines == dimension then n_newlines = 0;

      for n in [1..n_newlines] do outfile.writeln("  ");
      //<=== Newlines at the end of each dimension <===
    }
  }
  //<=== Write solution values <===

}
//<=== writeSolution method <===
//<=============================

//<=======================================================
//<=================== OUTPUT METHODS ====================
//<=======================================================

//==============================================>
//==================== OTHER ===================>
//==============================================>

//===> Fix for the 1D/tuple problem ===>
def tuplify(idx) {
  if isTuple(idx) then
    return idx;
  else
    return tuple(idx);
}
//<=== Fix for the 1D/tuple problem <===

//===> TrueSolution class ===>
//===========================>
//----------------------------------------------------------------------
// Create a true solution to a PDE by deriving this class.  In general,
// it will contain a few parameters in addition to the qTrue method.
// (Hence, using a class is probably more suitable than a first-class
// functin.)
//----------------------------------------------------------------------
class TrueSolution {

  def qTrue(x: dimension*real, t: real) {
    return 0.0;
  }

  def fluxComponent(x: dimension*real, t: real, comp: int) {
    return 0.0;
  }

  def normalFluxToGhost(flux: real,
                        inner_value: real,
                        dx: real
                       ){
    return 0.0;
  }
}
//<===========================
//<=== TrueSolution class <===

//===> IsolatedArray record ===>
//=============================>
//-------------------------------------------------------------
// Contains an array and its domain.  "Isolated" in that the
// domain will be assigned as a copy.  Not sharp in that sense
// for my purposes, but needed to dodge the problem of making
// a tuple of generic length and generic type for now.
//-------------------------------------------------------------
record IsolatedArray {
  param dim: int;
  type data_type;
  var dom: domain(dim,stridable=true);
  var value: [dom] data_type;
}
//<=============================
//<=== IsolatedArray record <===

//===> intersectDomains routine ===>
//=================================>
//--------------------------------------------------------------
// Determines the intersection of two arithmetic domains of the
// same type.  Returns the domain of intersection, or an empty
// domain if the inputs don't intersect.
//--------------------------------------------------------------
def intersectDomains(D1, D2) {

  assert(D1.type == D2.type,
	 "error: intersectDomains: input domains must be of same type");

  //==== Assume the domains intersect ====
  var intersect = true;

  var overlap: D1.rank*range(stridable=true);

  for d in [1..D1.rank] {

    //==== Check that strides match ====
    assert(D1.dim(d).stride == D2.dim(d).stride,
           "error: intersectDomains: domains must have equal stride");

    //==== Compute the overlap in this dimension ====
    overlap(d) = max(D1.dim(d).low, D2.dim(d).low)
                 ..
                 min(D1.dim(d).high, D2.dim(d).high)
                 by
                 D1.dim(d).stride;

    //==== Check whether this rules out intersection ====
    if overlap(d).length == 0 then intersect = false;
  }

  //==== Return ====
  if intersect then
    return [(...overlap)];
  else {
    var empty_domain: D1.type;
    return empty_domain;
  }

}
//<=================================
//<=== intersectDomains routine <===

//<==============================================
//<=================== OTHER ====================
//<==============================================

//===> Description ===>
//
// Basic class and method definitions for a rectangular grid of
// generic dimension (though it must be a compile-time constant.)
//
// This version is based on an underlying mesh of width dx/2, which
// contains all cell centers, interface centers, and vertices.  As
// a result, the domin of cell centers is strided by 2, corresponding
// to jumps of dx.
//
//<=== Description <===

config param dimension = 2;
const dimensions = [1..dimension];

//======================================================================>
//==================== FUNDAMENTAL GRID DEFINITIONS  ===================>
//======================================================================>

//===> RectangularGrid class ===>
//==============================>
class RectangularGrid {

  var x_low, x_high:          dimension*real,
      i_low, i_high:          dimension*int,
      n_cells, n_ghost_cells: dimension*int;

  var dx: dimension*real;

  var ext_cells:      domain(dimension, stridable=true),
      cells: subdomain(ext_cells);

  var low_ghost_cells:  [dimensions] subdomain(ext_cells),
      high_ghost_cells: [dimensions] subdomain(ext_cells);

  //===> initialize method ===>
  def initialize() {

    sanityChecks();

    setDerivedFields();
  }
  //<=== initialize method <===

  //===> sanityChecks method ===>
  def sanityChecks() {
    var d_string: string;
    for d in dimensions do {
      d_string = format("%i", d);

      assert(x_low(d) < x_high(d),
	     "error: RectangularGrid: x_low(" + d_string + ") must be strictly less than x_high(" + d_string + ").");

      assert(n_cells(d) > 0,
             "error: RectangularGrid: n_cells(" + d_string + ") must be positive.");

      assert(n_ghost_cells(d) > 0,
	     "error: RectangularGrid: n_ghost_cells(" + d_string + ") must be positive.");
    }
  }
  //<=== sanityChecks method <===

  //===> setDerivedFields method ===>
  //--------------------------------------------------------------
  // After x_low, x_high, n_cells, and n_ghost_cells have
  // been provided, calculate:
  //     dx
  //     i_high
  //     cells
  //     ext_cells
  //     low_ghost_cells
  //     high_ghost_cells
  //--------------------------------------------------------------
  def setDerivedFields() {
    //==== dx ====
    dx = (x_high - x_low) / n_cells;

    //==== i_high ====
    for d in dimensions do
      i_high(d) = i_low(d) + 2*n_cells(d);

    //==== Physical cells ====
    var range_tuple: dimension*range(stridable = true);
    for d in dimensions do
      range_tuple(d) = i_low(d)+1 .. i_low(d)+2*n_cells(d)-1 by 2;
    cells = [(...range_tuple)];

    //==== Extended cells (includes ghost cells) ====
    var size: dimension*int;
    [d in dimensions] size(d) = 2*n_ghost_cells(d);
    ext_cells = cells.expand(size);

    //===> Ghost cells ===>
    //------------------------------------------------------
    // Note that all ghost cell domains contain the corners.
    //------------------------------------------------------
    for d in dimensions do {
      forall d_size in dimensions do size(d_size) = 0;

      size(d) = -(2*n_ghost_cells(d) - 1);
      low_ghost_cells(d) = ext_cells.interior(size);

      size(d) = 2*n_ghost_cells(d) - 1;
      high_ghost_cells(d) = ext_cells.interior(size);
    }
    //<=== Ghost cells <===

  }
  //<=== setDerivedFields method <===

  //===> GhostData subclass ===>
  //===========================>
  //--------------------------------------------------------------
  // Ideally, the fields low and high would be direct collections
  // of arrays, but I don't think that's possible at the moment.
  //--------------------------------------------------------------
  class GhostData {

    type data_type;
    var low, high: [dimensions] IsolatedArray(dimension, data_type);

    def initialize() {
      for d in dimensions {
	low(d) = new IsolatedArray(dim       = dimension,
				   data_type = data_type,
				   dom       = low_ghost_cells(d));
	high(d) = new IsolatedArray(dim       = dimension,
				    data_type = data_type,
				    dom       = high_ghost_cells(d));
      }

    }
  }
  //<===========================
  //<=== GhostData subclass <===

}
//<=== RectangularGrid class <===
//<==============================

//===> xValue method ===>
//======================>
//---------------------------------------
// Converts indices to coordinate values.
//----------------------------------------
def RectangularGrid.xValue (point_index: dimension*int) {

  var coord: dimension*real;

  if dimension == 1 then {
    coord(1) = x_low(1) + (point_index(1) - i_low(1)) * dx(1)/2.0;
  }
  else {
    forall d in dimensions do
      coord(d) = x_low(d) + (point_index(d) - i_low(d)) * dx(d)/2.0;
  }

  return coord;
}
//<======================
//<=== xValue method <===

//<=====================================================================
//<=================== FUNDAMENTAL GRID DEFINITIONS ====================
//<=====================================================================

//=======================================================>
//==================== GRID SOLUTIONS ===================>
//=======================================================>

//===> GridSolution class ===>
//===========================>
class GridSolution {
  var grid:  RectangularGrid;
  var value: [grid.ext_cells] real;
  var time:  real;
}
//<===========================
//<=== GridSolution class ====

//===> copySolution method ===>
//============================>
def RectangularGrid.copySolution(q: GridSolution) {

  //==== Make sure q lives on this grid ====
  assert(q.grid == this);

  //==== Copy q ====
  var q_copy = new GridSolution(grid  = this,
			        value = q.value,
			        time  = q.time);

}
//<=== copySolution method <===
//<============================

//===> initializeSolution method ===>
//==================================>
//---------------------------------------------------------------
// Provided an analytical function, evaluates it on the grid and
// returns a GridSolution.  As support for first-class functions
// develops, the input argument will become explicitly typed.
//---------------------------------------------------------------
def RectangularGrid.initializeSolution(q:                 GridSolution,
				       initial_condition: func(dimension*real, real)
				      ){

  //==== Check that q lives on this grid ====
  assert(q.grid == this);

  write("Writing solution on grid...");
  forall precell in cells {
    var cell = tuplify(precell);
    q.value(cell) = initial_condition(xValue(cell));
  }
  write("done.\n");

}

//-------------------------------------------------------------------
// A similar method that does this for a TrueSolution is convenient.
//-------------------------------------------------------------------
def RectangularGrid.initializeSolution(q:             GridSolution,
				       true_solution: TrueSolution
				      ){

  q.grid = this;

  forall precell in cells {
    var cell = tuplify(precell);
    q.value(cell) = true_solution.qTrue(xValue(cell), 0.0);
  }

}

//<==================================
//<=== initializeSolution method <===

//<=======================================================
//<=================== GRID SOLUTIONS ====================
//<=======================================================

//============================================================>
//==================== BOUNDARY CONDITIONS ===================>
//============================================================>

//===> GridBC class ===>
//=====================>
class GridBC {

  var grid: RectangularGrid;
  var low_boundary_faces:  [dimensions] domain(dimension, stridable=true);
  var high_boundary_faces: [dimensions] domain(dimension, stridable=true);

  //===> setBoundaryFaces method ===>
  //================================>
  //------------------------------------------------------
  // Sets up domains for the low and high boundary faces.
  //------------------------------------------------------
  def setBoundaryFaces() {

    var range_tuple: dimension*range(stridable=true);

    for d in dimensions {

      //==== All components except for d will equal grid.cells ====
      for d_range in dimensions do
        range_tuple(d_range) = grid.cells.dim(d_range);

      //==== low boundary faces ====
      range_tuple(d)        = grid.i_low(d) .. grid.i_low(d) by 2;
      low_boundary_faces(d) = [(...range_tuple)];

      //==== high_boundary_faces ====
      range_tuple(d)         = grid.i_high(d) .. grid.i_high(d) by 2;
      high_boundary_faces(d) = [(...range_tuple)];

    }
  }
  //<================================
  //<=== setBoundaryFaces method <===

  //==== Dummy routines; to be provided in derived classes ====
  def ghostFill(q: GridSolution){}
  def homogeneousGhostFill(q: GridSolution){}

}
//<=== GridBC Class <===
//<=====================

//===> PeriodicGridBC class ===>
//=============================>
class PeriodicGridBC: GridBC {

  var low_ghost_periodic:  [dimensions] subdomain(grid.ext_cells);
  var high_ghost_periodic: [dimensions] subdomain(grid.ext_cells);

  //===> initialize method ===>
  //============================>
  def initialize() {

    //===> Build periodic images of ghost cells ===>
    var shift: dimension*int;

    for d in dimensions do {
      [d_temp in dimensions] shift(d_temp) = 0;

      shift(d) = 2*grid.n_cells(d);
      low_ghost_periodic(d)  = grid.low_ghost_cells(d).translate(shift);

      shift(d) = -2*grid.n_cells(d);
      high_ghost_periodic(d) = grid.high_ghost_cells(d).translate(shift);
    }
    //<=== Build periodic images of ghost cells <===

  }
  //<============================
  //<=== initialize method <===

  //===> ghostFill method ===>
  //=========================>
  def ghostFill(q: GridSolution) {
    //==== Periodic BCs are homogeneous ====
    homogeneousGhostFill(q);
  }
  //<=========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  def homogeneousGhostFill(q: GridSolution) {

    //==== Make sure q lives on the same grid as the BC ====
    assert(q.grid == grid);

    //==== Copy values into ghost cells from their periodic images ====
    for d in dimensions do {
      q.value(grid.low_ghost_cells(d))  = q.value(low_ghost_periodic(d));
      q.value(grid.high_ghost_cells(d)) = q.value(high_ghost_periodic(d));
    }
  }
  //<====================================
  //<=== homogeneousGhostFill method <===

}
//<=============================
//<=== PeriodicGridBC class <===

//===> ZeroOrderExtrapGridBC class ===>
//=====================================>
class ZeroOrderExtrapGridBC: GridBC {

  //===> initialize method ===>
  //==========================>
  def intiialize() {
    setBoundaryFaces();
  }
  //<==========================
  //<=== initialize method <===

  //===> ghostFill method ===>
  //=========================>
  def ghostFill(q: GridSolution) {
    //==== Constant Dirichlet BCs are homogeneous ====
    homogeneousGhostFill(q);
  }
  //<========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  def homogeneousGhostFill(q: GridSolution) {

    for d in dimensions do {

      var shift: dimension*int;
      shift(d) = 1;

      //==== Low boundary ====
      forall preface in low_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face-shift) = q.value(face+shift);
      }

      //==== High ghost cells ====
      forall preface in high_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face+shift) = q.value(face-shift);
      }
    }

  }
  //<====================================
  //<=== homogeneousGhostFill method <===

}
//<====================================
//<=== ZeroOrderExtrapGridBC class <===

//===> TrueSolutionDirichletGridBC class ===>
//==========================================>
//----------------------------------------------------------------
// Provides Dirichlet boundary conditions corresponding to method
// qTrue.  Derive this class to define qTrue.
//----------------------------------------------------------------
class TrueSolutionDirichletGridBC: GridBC {

  var true_solution: TrueSolution;

  //===> initialize method ===>
  //==========================>
  def initialize() {
    setBoundaryFaces();
  }
  //<=== initialize method <===
  //<==========================

  //===> ghostFill method ===>
  //=========================>
  //---------------------------------------------------
  // Evaluates true_solution.qTrue on each ghost cell.
  //---------------------------------------------------
  def ghostFill(q: GridSolution) {

    for d in dimensions {

      forall precell in grid.low_ghost_cells(d) {
        var cell = tuplify(precell);
        q.value(cell) = true_solution.qTrue(grid.xValue(cell), q.time);
      }

      forall precell in grid.high_ghost_cells(d) {
        var cell = tuplify(precell);
        q.value(cell) = true_solution.qTrue(grid.xValue(cell), q.time);
      }

    }

  }
  //<=========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  //------------------------------------------------------------
  // For homogeneous Dirichlet BCs.  At each boundary face, the
  // nearest interior value is reflected about 0 to the nearest
  // ghost cell value.
  //------------------------------------------------------------
  def homogeneousGhostFill(q: GridSolution) {

    for d in dimensions {

      //==== For shifting from a face to a cell ====
      var shift: dimension*int;
      shift(d) = 1;

      //==== Low boundary ====
      forall preface in low_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face - shift) = -q.value(face + shift);
      }

      //==== High boundary ====
      forall preface in high_boundary_faces(d) {
        var face = tuplify(preface);
        q.value(face + shift) = -q.value(face - shift);
      }
    }

  }
  //<====================================
  //<=== homogeneousGhostFill method <===

}
//<==========================================
//<=== TrueSolutionDirichletGridBC class <===

//===> TrueSolutionNeumannGridBC class ===>
//========================================>
class TrueSolutionNeumannGridBC: GridBC {

  var true_solution: TrueSolution;

  //===> initialize method ===>
  //==========================>
  def initialize() {
    setBoundaryFaces();
  }
  //<=========================
  //<=== intialize method <===

  //===> ghostFill method ===>
  //=========================>
  def ghostFill(q: GridSolution) {

    writeln("Start of ghostFill");

    for d in dimensions {

      //==== dx in this dimension ====
      var dx = grid.dx(d);

      //==== For shifting faces to cells ====
      var shift: dimension*int;
      shift(d) = 1;

      //==== Low boundary ====
      forall preface in low_boundary_faces(d) {
        var face            = tuplify(preface);
        var normal_flux     = -true_solution.fluxComponent(grid.xValue(face), q.time, d);
        q.value(face-shift) = true_solution.normalFluxToGhost(normal_flux,
                                                              q.value(face+shift),
                                                              dx);
      }

      //==== High boundary ====
      forall preface in high_boundary_faces(d) {
        var face            = tuplify(preface);
        var normal_flux     = true_solution.fluxComponent(grid.xValue(face), q.time, d);
        q.value(face+shift) = true_solution.normalFluxToGhost(normal_flux,
                                                              q.value(face-shift),
                                                              dx);
      }

    }

  }
  //<=========================
  //<=== ghostFill method <===

  //===> homogeneousGhostFill method ===>
  //====================================>
  def homogeneousGhostFill(q: GridSolution) {

    for d in dimensions do {
      //==== Low ghost cells ====
      forall cell in grid.low_ghost_cells(d) {
        var target_cell = cell;
        target_cell(d)  = grid.cells.dim(d).low;
        q.value(cell)   = q.value(target_cell);
      }

      //==== High ghost cells ====
      forall cell in grid.high_ghost_cells(d) {
        var target_cell = cell;
        target_cell(d)  = grid.cells.dim(d).high;
        q.value(cell)   = q.value(target_cell);
      }
    }

  }
  //<====================================
  //<=== homogeneousGhostFill method <===
}
//<========================================
//<=== TrueSolutionNeumannGridBC class <===

//<============================================================
//<=================== BOUNDARY CONDITIONS ====================
//<============================================================

//=======================================================>
//==================== OUTPUT METHODS ===================>
//=======================================================>

//===> writeTimeFile function =====>
//=================================>
//-------------------------------------------------------------
// Global routine; meant to be called by output method of any
// spatial object.  The time file is really simple, and this
// just formats a few parameters.
//-------------------------------------------------------------
def writeTimeFile(time:    real,
                  meqn:    int,
                  ngrids:  int,
                  naux:    int,
                  outfile: file
                 ) {

  //==== Formatting parameters ====
  var efmt:  string = "%26.16E",
      ifmt:  string = "%5i";

  //==== Write time file ====
  outfile.writeln( format(efmt, time),      "    time");
  outfile.writeln( format(ifmt, meqn),      "                 meqn");
  outfile.writeln( format(ifmt, ngrids),    "                 ngrids");
  outfile.writeln( format(ifmt, naux),      "                 naux");
  outfile.writeln( format(ifmt, dimension), "                 ndim");
  outfile.writeln("");
  outfile.writeln("");

}
//<=== writeTimeFile method <=====
//<===============================

//===> clawOutput method ===>
//==========================>
//-------------------------------------------------------------------
// Writes Clawpack-formatted output for a GridSolution, at the given
// frame_number.
//-------------------------------------------------------------------
def RectangularGrid.clawOutput(q:             GridSolution,
                               frame_number: int
                              ){

  //==== Make sure q lives on this grid ====
  assert(q.grid == this);

  //==== Names of output files ====
  var frame_string:      string = format("%04i", frame_number),
      time_filename:     string = "_output/fort.t" + frame_string,
      solution_filename: string = "_output/fort.q" + frame_string;

  //==== Time file ====
  var outfile = new file(time_filename, FileAccessMode.write);
  outfile.open();
  writeTimeFile(q.time, 1, 1, 0, outfile);
  outfile.close();
  delete outfile;

  //==== Solution file ====
  outfile = new file(solution_filename, FileAccessMode.write);
  outfile.open();
  writeSolution(q, 1, 1, outfile);
  outfile.close();
  delete outfile;

}
//<=== clawOutput method <===
//<==========================

//===> writeSolution method ====>
//==============================>
//---------------------------------------------------------
// Writes the data for a GridSolution living on this grid.
//---------------------------------------------------------
def RectangularGrid.writeSolution(q:           GridSolution,
                                  grid_number: int,
                                  AMR_level:   int,
                                  outfile:     file
                                 ){

  //==== Formatting parameters ====
  var efmt:  string = "%26.16E",
      ifmt:  string = "%5i",
      sfmt:  string = "%20s",
      linelabel: string;

  //==== Header ====
  outfile.writeln( format(ifmt, grid_number), "                 grid_number");
  outfile.writeln( format(ifmt, AMR_level),   "                 AMR_level");

  //==== Write n_cells ====
  for d in dimensions do {
    select d {
      when 1 do linelabel = "                 mx";
      when 2 do linelabel = "                 my";
      when 3 do linelabel = "                 mz";
      otherwise linelabel = "                 mx(" + format("%1i",d) + ")";
    }
    outfile.writeln( format(ifmt, n_cells(d)),  linelabel);
  }

  //==== Write x_low ====
  for d in dimensions do {
    select d {
      when 1 do linelabel = "    xlow";
      when 2 do linelabel = "    ylow";
      when 3 do linelabel = "    zlow";
      otherwise linelabel = "    xlow(" + format("%1i",d) + ")";
    }
    outfile.writeln( format(efmt, x_low(d)),  linelabel);
  }

  //==== Write dx ====
  for d in dimensions do {
    select d {
      when 1 do linelabel = "    dx";
      when 2 do linelabel = "    dy";
      when 3 do linelabel = "    dz";
      otherwise linelabel = "    dx(" + format("%1i",d) + ")";
    }
    outfile.writeln( format(efmt, dx(d)),  linelabel);
  }
  outfile.writeln("");

  //===> Write solution values ===>
  if dimension == 1 then {
    for cell in cells do
      outfile.writeln(format(efmt, q.value(cell)));
  }
  else {
    //------------------------------------------------------------
    //---- Transpose cells; iterating over the transpose
    //---- in row major order achieves column major order on the
    //---- original domain.
    //------------------------------------------------------------
    var range_tuple: dimension*range(stridable=true);
    [d in dimensions] range_tuple(d) = cells.dim(1 + dimension - d);

    var cells_transposed: domain(dimension, stridable=true);
    cells_transposed = [(...range_tuple)];

    var cell:       dimension*int;
    var n_newlines: int;
    for cell_transposed in cells_transposed do {

      //==== Write value ====
      [d in dimensions] cell(d) = cell_transposed(1 + dimension - d);
      outfile.writeln(format(efmt, q.value(cell)));

      //===> Newlines at the end of each dimension ===>
      //--------------------------------------------------------------
      // There are no newlines at the very end of output.  How that's
      // handled is up to the main output routine.
      //--------------------------------------------------------------
      n_newlines = 0;

      for d in dimensions do {
        if cell(d) == cells.dim(d).high then
          n_newlines += 1;
        else
          break;
      }

      if n_newlines == dimension then n_newlines = 0;

      for n in [1..n_newlines] do outfile.writeln("  ");
      //<=== Newlines at the end of each dimension <===
    }
  }
  //<=== Write solution values <===

}
//<=== writeSolution method <===
//<=============================

//<=======================================================
//<=================== OUTPUT METHODS ====================
//<=======================================================

//==============================================>
//==================== OTHER ===================>
//==============================================>

//===> Fix for the 1D/tuple problem ===>
def tuplify(idx) {
  if isTuple(idx) then
    return idx;
  else
    return tuple(idx);
}
//<=== Fix for the 1D/tuple problem <===

//===> TrueSolution class ===>
//===========================>
//----------------------------------------------------------------------
// Create a true solution to a PDE by deriving this class.  In general,
// it will contain a few parameters in addition to the qTrue method.
// (Hence, using a class is probably more suitable than a first-class
// functin.)
//----------------------------------------------------------------------
class TrueSolution {

  def qTrue(x: dimension*real, t: real) {
    return 0.0;
  }

  def fluxComponent(x: dimension*real, t: real, comp: int) {
    return 0.0;
  }

  def normalFluxToGhost(flux: real,
                        inner_value: real,
                        dx: real
                       ){
    return 0.0;
  }
}
//<===========================
//<=== TrueSolution class <===

//===> IsolatedArray record ===>
//=============================>
//-------------------------------------------------------------
// Contains an array and its domain.  "Isolated" in that the
// domain will be assigned as a copy.  Not sharp in that sense
// for my purposes, but needed to dodge the problem of making
// a tuple of generic length and generic type for now.
//-------------------------------------------------------------
record IsolatedArray {
  param dim: int;
  type data_type;
  var dom: domain(dim, stridable=true);
  var value: [dom] data_type;
}
//<=============================
//<=== IsolatedArray record <===

//===> intersectDomains routine ===>
//=================================>
//--------------------------------------------------------------
// Determines the intersection of two arithmetic domains of the
// same type.  Returns the domain of intersection, or an empty
// domain if the inputs don't intersect.
//--------------------------------------------------------------
def intersectDomains(D1, D2) {

  assert(D1.type == D2.type,
	 "error: intersectDomains: input domains must be of same type");

  //==== Assume the domains intersect ====
  var intersect = true;

  var overlap: D1.rank*range(stridable=true);

  for d in [1..D1.rank] {

    //==== Check that strides match ====
    assert(D1.dim(d).stride == D2.dim(d).stride,
           "error: intersectDomains: domains must have equal stride");

    //==== Compute the overlap in this dimension ====
    overlap(d) = max(D1.dim(d).low, D2.dim(d).low)
                 ..
                 min(D1.dim(d).high, D2.dim(d).high)
                 by
                 D1.dim(d).stride;

    //==== Check whether this rules out intersection ====
    if overlap(d).length == 0 then intersect = false;
  }

  //==== Return ====
  if intersect then
    return [(...overlap)];
  else {
    var empty_domain: D1.type;
    return empty_domain;
  }

}
//<=================================
//<=== intersectDomains routine <===

//<==============================================
//<=================== OTHER ====================
//<==============================================