density.ts 3.8 KB

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