cpu.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Box3D } from '../../geometry';
  7. import { Vec3, Mat4, Tensor } from '../../linear-algebra';
  8. import { RuntimeContext } from 'mol-task';
  9. import { PositionData, DensityData } from '../common';
  10. import { OrderedSet } from 'mol-data/int';
  11. import { GaussianDensityProps, getDelta } from '../gaussian-density';
  12. export async function GaussianDensityCPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> {
  13. const { resolution, radiusOffset, smoothness } = props
  14. const { indices, x, y, z } = position
  15. const n = OrderedSet.size(indices)
  16. const radii = new Float32Array(n)
  17. let maxRadius = 0
  18. for (let i = 0; i < n; ++i) {
  19. const r = radius(OrderedSet.getAt(indices, i)) + radiusOffset
  20. if (maxRadius < r) maxRadius = r
  21. radii[i] = r
  22. if (i % 100000 === 0 && ctx.shouldUpdate) {
  23. await ctx.update({ message: 'calculating max radius', current: i, max: n })
  24. }
  25. }
  26. const pad = maxRadius * 2 + resolution
  27. const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad))
  28. const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min)
  29. const min = expandedBox.min
  30. const delta = getDelta(Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad)), resolution)
  31. const dim = Vec3.zero()
  32. Vec3.ceil(dim, Vec3.mul(dim, extent, delta))
  33. // console.log('grid dim cpu', dim)
  34. const space = Tensor.Space(dim, [0, 1, 2], Float32Array)
  35. const data = space.create()
  36. const field = Tensor.create(space, data)
  37. const idData = space.create()
  38. const idField = Tensor.create(space, idData)
  39. const iu = dim[2], iv = dim[1], iuv = iu * iv
  40. const densData = space.create()
  41. const v = Vec3()
  42. const c = Vec3()
  43. const alpha = smoothness
  44. const _r2 = maxRadius * 2
  45. const _radius2 = Vec3.create(_r2, _r2, _r2)
  46. Vec3.mul(_radius2, _radius2, delta)
  47. const updateChunk = Math.ceil(1000000 / (_radius2[0] * _radius2[1] * _radius2[2]))
  48. const beg = Vec3()
  49. const end = Vec3()
  50. const rad2 = Vec3()
  51. const gridPad = 1 / Math.max(...delta)
  52. const invDelta = Vec3.inverse(Vec3(), delta)
  53. const [ invDeltaX, invDeltaY, invDeltaZ ] = invDelta
  54. console.time('gaussian density cpu')
  55. for (let i = 0; i < n; ++i) {
  56. const j = OrderedSet.getAt(indices, i)
  57. Vec3.set(v, x[j], y[j], z[j])
  58. Vec3.sub(v, v, min)
  59. const [ vx, vy, vz ] = v
  60. Vec3.mul(c, v, delta)
  61. const rad = radii[i]
  62. const rSq = rad * rad
  63. const rSqInv = 1 / rSq
  64. const r2 = radiusOffset + rad * 2 + gridPad
  65. const r2sq = r2 * r2
  66. Vec3.set(rad2, r2, r2, r2)
  67. Vec3.mul(rad2, rad2, delta)
  68. const [ begX, begY, begZ ] = Vec3.floor(beg, Vec3.sub(beg, c, rad2))
  69. const [ endX, endY, endZ ] = Vec3.ceil(end, Vec3.add(end, c, rad2))
  70. for (let xi = begX; xi < endX; ++xi) {
  71. const dx = xi * invDeltaX - vx
  72. const xIdx = xi * iuv
  73. for (let yi = begY; yi < endY; ++yi) {
  74. const dy = yi * invDeltaY - vy
  75. const dxySq = dx * dx + dy * dy
  76. const xyIdx = yi * iu + xIdx
  77. for (let zi = begZ; zi < endZ; ++zi) {
  78. const dz = zi * invDeltaZ - vz
  79. const dSq = dxySq + dz * dz
  80. if (dSq <= r2sq) {
  81. const dens = Math.exp(-alpha * (dSq * rSqInv))
  82. const idx = zi + xyIdx
  83. data[idx] += dens
  84. if (dens > densData[idx]) {
  85. densData[idx] = dens
  86. idData[idx] = i
  87. }
  88. }
  89. }
  90. }
  91. }
  92. if (i % updateChunk === 0 && ctx.shouldUpdate) {
  93. await ctx.update({ message: 'filling density grid', current: i, max: n })
  94. }
  95. }
  96. console.timeEnd('gaussian density cpu')
  97. const transform = Mat4.identity()
  98. Mat4.fromScaling(transform, Vec3.inverse(Vec3.zero(), delta))
  99. Mat4.setTranslation(transform, expandedBox.min)
  100. return { field, idField, transform }
  101. }