message: partition very large atomic chains per residue
diff: @@ -397,6 +397,8 @@ namespace Structure {
  if (isWaterChain(model, c as ChainIndex)) {
  partitionAtomicUnit(model, elements, builder);
  + } else if (elements.length > 200000) {
  + partitionAtomicUnitPerResidue(model, elements, builder);
  } else {
  builder.addUnit(Unit.Kind.Atomic, model, SymmetryOperator.Default, elements);
  }
  @@ -435,6 +437,36 @@ namespace Structure {
  }
  }
  + function partitionAtomicUnitPerResidue(model: Model, indices: SortedArray, builder: StructureBuilder) {
  + model.atomicHierarchy.residueAtomSegments.offsets
  +
  + const startIndices: number[] = []
  + const endIndices: number[] = []
  +
  + const residueIt = Segmentation.transientSegments(model.atomicHierarchy.residueAtomSegments, indices)
  + while (residueIt.hasNext) {
  + const residueSegment = residueIt.move();
  + startIndices[startIndices.length] = residueSegment.start
  + endIndices[endIndices.length] = residueSegment.end
  + }
  +
  + const { x, y, z } = model.atomicConformation;
  + const lookup = GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(startIndices) }, Vec3.create(256, 256, 256));
  + const { offset, count, array } = lookup.buckets;
  +
  + for (let i = 0, _i = offset.length; i < _i; i++) {
  + const start = offset[i];
  + const set: number[] = [];
  + for (let j = 0, _j = count[i]; j < _j; j++) {
  + const k = array[start + j]
  + for (let l = startIndices[k], _l = endIndices[k]; l < _l; l++) {
  + set[set.length] = l;
  + }
  + }
  + builder.addUnit(Unit.Kind.Atomic, model, SymmetryOperator.Default, SortedArray.ofSortedArray(set));
  + }
  + }
  +
  function addCoarseUnits(builder: StructureBuilder, model: Model, elements: CoarseElements, kind: Unit.Kind) {
  const { chainElementSegments } = elements;
  for (let cI = 0; cI < chainElementSegments.count; cI++) {
  