density.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { sortArray } from '../../mol-data/util';
  7. import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
  8. import { WebGLContext } from '../../mol-gl/webgl/context';
  9. import { Task } from '../../mol-task';
  10. import { AlphaOrbital, createGrid, CubeGrid, CubeGridComputationParams, initCubeGrid } from './data-model';
  11. import { gpuComputeAlphaOrbitalsDensityGridValues } from './gpu/compute';
  12. export function createSphericalCollocationDensityGrid(
  13. params: CubeGridComputationParams, orbitals: AlphaOrbital[], webgl?: WebGLContext
  14. ): Task<CubeGrid> {
  15. return Task.create('Spherical Collocation Grid', async (ctx) => {
  16. const cubeGrid = initCubeGrid(params);
  17. let matrix: Float32Array;
  18. if (canComputeGrid3dOnGPU(webgl)) {
  19. // console.time('gpu');
  20. matrix = await gpuComputeAlphaOrbitalsDensityGridValues(ctx, webgl!, cubeGrid, orbitals);
  21. // console.timeEnd('gpu');
  22. } else {
  23. throw new Error('Missing OES_texture_float WebGL extension.');
  24. }
  25. const grid = createGrid(cubeGrid, matrix, [0, 1, 2]);
  26. let isovalues: { negative?: number, positive?: number } | undefined;
  27. if (!params.doNotComputeIsovalues) {
  28. isovalues = computeDensityIsocontourValues(matrix, 0.85);
  29. }
  30. return { grid, isovalues };
  31. });
  32. }
  33. export function computeDensityIsocontourValues(input: Float32Array, cumulativeThreshold: number) {
  34. let weightSum = 0;
  35. for (let i = 0, _i = input.length; i < _i; i++) {
  36. const v = input[i];
  37. const w = Math.abs(v);
  38. weightSum += w;
  39. }
  40. const avgWeight = weightSum / input.length;
  41. let minWeight = 3 * avgWeight;
  42. // do not try to identify isovalues for degenerate data
  43. // e.g. all values are almost same
  44. if (Math.abs(avgWeight - input[0] * input[0]) < 1e-5) {
  45. return { negative: void 0, positive: void 0 };
  46. }
  47. let size = 0;
  48. while (true) {
  49. let csum = 0;
  50. size = 0;
  51. for (let i = 0, _i = input.length; i < _i; i++) {
  52. const v = input[i];
  53. const w = Math.abs(v);
  54. if (w >= minWeight) {
  55. csum += w;
  56. size++;
  57. }
  58. }
  59. if (csum / weightSum > cumulativeThreshold) {
  60. break;
  61. }
  62. minWeight -= avgWeight;
  63. }
  64. const values = new Float32Array(size);
  65. const weights = new Float32Array(size);
  66. const indices = new Int32Array(size);
  67. let o = 0;
  68. for (let i = 0, _i = input.length; i < _i; i++) {
  69. const v = input[i];
  70. const w = Math.abs(v);
  71. if (w >= minWeight) {
  72. values[o] = v;
  73. weights[o] = w;
  74. indices[o] = o;
  75. o++;
  76. }
  77. }
  78. sortArray(
  79. indices,
  80. (indices, i, j) => weights[indices[j]] - weights[indices[i]]
  81. );
  82. let cweight = 0,
  83. cutoffIndex = 0;
  84. for (let i = 0; i < size; i++) {
  85. cweight += weights[indices[i]];
  86. if (cweight / weightSum >= cumulativeThreshold) {
  87. cutoffIndex = i;
  88. break;
  89. }
  90. }
  91. let positive = Number.POSITIVE_INFINITY,
  92. negative = Number.NEGATIVE_INFINITY;
  93. for (let i = 0; i < cutoffIndex; i++) {
  94. const v = values[indices[i]];
  95. if (v > 0) {
  96. if (v < positive) positive = v;
  97. } else if (v < 0) {
  98. if (v > negative) negative = v;
  99. }
  100. }
  101. return {
  102. negative: negative !== Number.NEGATIVE_INFINITY ? negative : void 0,
  103. positive: positive !== Number.POSITIVE_INFINITY ? positive : void 0,
  104. };
  105. }