Skip to content

Commit 7097f4f

Browse files
authored
Merge pull request #626 from EarthyScience/jp/getarray
Consolidate DataFetchers
2 parents 59734bf + d1f9644 commit 7097f4f

9 files changed

Lines changed: 362 additions & 276 deletions

File tree

next.config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@ const nextConfig = {
4242
(config.resolve.alias as { [key: string]: string })['@/components'] = path.resolve(__dirname, 'src/components');
4343
return config;
4444
},
45+
async headers() { // Needed for SharedArrayBuffer. Security reasons won't let sharedArrayBuffer
46+
return [
47+
{
48+
source: '/(.*)',
49+
headers: [
50+
{
51+
key: 'Cross-Origin-Opener-Policy',
52+
value: 'same-origin',
53+
},
54+
{
55+
key: 'Cross-Origin-Embedder-Policy',
56+
value: 'require-corp',
57+
},
58+
],
59+
},
60+
]
61+
},
4562
};
4663

4764
console.log('Current NODE_ENV:', process.env.NODE_ENV);

src/components/plots/AnalysisWG.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useAnalysisStore } from '@/GlobalStates/AnalysisStore';
77
import { useGlobalStore } from '@/GlobalStates/GlobalStore';
88
import { usePlotStore } from '@/GlobalStates/PlotStore';
99
import { useShallow } from 'zustand/shallow';
10-
import { GetArray } from '../zarr/ZarrLoaderLRU';
10+
import { GetArray } from '../zarr/GetArray';
1111
import { CreateTexture } from '../textures';
1212

1313
// The new centralized map for all operations
@@ -98,7 +98,7 @@ const AnalysisWG = ({ setTexture, }: { setTexture: React.Dispatch<React.SetState
9898
}
9999

100100
// --- 2. Dispatch GPU computation based on the operation ---
101-
const inputArray = analysisMode ? analysisArray : GetCurrentArray(analysisStore)
101+
const inputArray = analysisMode ? analysisArray : await GetCurrentArray(analysisStore)
102102
const shapeInfo = { shape: dataShape, strides};
103103
const kernelParams = { kernelDepth, kernelSize };
104104
// [1538316, 1481, 1]

src/components/plots/Plot.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { OrbitControls, useTexture } from '@react-three/drei';
22
import React, { useMemo, useRef, useState, useEffect } from 'react';
33
import * as THREE from 'three';
44
import { PointCloud, DataCube, FlatMap, Sphere, CountryBorders, AxisLines, SphereBlocks, FlatBlocks, KeyFramePreviewer } from '@/components/plots';
5-
import { Canvas, invalidate, useThree, useLoader } from '@react-three/fiber';
5+
import { Canvas, invalidate, useThree } from '@react-three/fiber';
66
import { CreateTexture } from '@/components/textures';
77
import { useAnalysisStore } from '@/GlobalStates/AnalysisStore';
88
import { useGlobalStore } from '@/GlobalStates/GlobalStore';

src/components/zarr/GetArray.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { useGlobalStore } from "@/GlobalStates/GlobalStore";
2+
import { useZarrStore } from "@/GlobalStates/ZarrStore";
3+
import { useCacheStore } from "@/GlobalStates/CacheStore";
4+
import { useErrorStore } from "@/GlobalStates/ErrorStore";
5+
import { calculateStrides } from "@/utils/HelperFuncs";
6+
import { ToFloat16, CompressArray, DecompressArray, copyChunkToArray, RescaleArray, copyChunkToArray2D } from "./utils";
7+
import { NCFetcher, zarrFetcher } from "./dataFetchers";
8+
import { Convolve } from "../computation/webGPU";
9+
import { coarsen3DArray } from "@/utils/HelperFuncs";
10+
11+
export async function GetArray(varOveride?: string) {
12+
const { idx4D, initStore, variable, setProgress, setStrides, setStatus } = useGlobalStore.getState();
13+
const { compress, xSlice, ySlice, zSlice, coarsen, kernelSize, kernelDepth, fetchNC, setCurrentChunks, setArraySize } = useZarrStore.getState();
14+
const { cache } = useCacheStore.getState();
15+
const fetcher = fetchNC ? NCFetcher() : zarrFetcher()
16+
const targetVariable = varOveride ?? variable;
17+
const meta = await fetcher.getMetadata(targetVariable);
18+
const { shape, chunkShape, fillValue, dtype } = meta;
19+
const rank = shape.length;
20+
const hasZ = rank >= 3;
21+
const xDimIndex = rank - 1, yDimIndex = rank - 2, zDimIndex = rank - 3;
22+
23+
const calcDim = (slice: [number, number | null], dimIdx: number) => { // This function provides information for extraction from each dimension of datarray
24+
if (dimIdx < 0) return { start: 0, end: 1, size: 0, chunkDim: 1 };
25+
const dimSize = shape[dimIdx];
26+
const chunkDim = chunkShape[dimIdx];
27+
const start = Math.floor(slice[0] / chunkDim);
28+
const sliceEnd = slice[1] ?? dimSize;
29+
return { start, end: Math.ceil(sliceEnd / chunkDim), size: sliceEnd - slice[0], chunkDim };
30+
};
31+
32+
const xDim = calcDim(xSlice, xDimIndex);
33+
const yDim = calcDim(ySlice, yDimIndex);
34+
const zDim = calcDim(zSlice, zDimIndex);
35+
36+
let outputShape = hasZ ? [zDim.size, yDim.size, xDim.size] : [yDim.size, xDim.size];
37+
if (coarsen) {
38+
outputShape = outputShape.map((dim, idx) => Math.floor(dim / (hasZ && idx === 0 ? kernelDepth : kernelSize)));
39+
}
40+
41+
const totalElements = outputShape.reduce((a, b) => a * b, 1);
42+
if (totalElements > 1e9) { useErrorStore.getState().setError("largeArray"); throw new Error("Cannot allocate array."); }
43+
44+
const destStride = calculateStrides(outputShape);
45+
setStrides(destStride);
46+
setArraySize(totalElements);
47+
setCurrentChunks({ x: [xDim.start, xDim.end], y: [yDim.start, yDim.end], z: [zDim.start, zDim.end] }); // These are used in GetCurrentArray() function
48+
49+
const typedArray = new Float16Array(totalElements);
50+
51+
let scalingFactor: number | null = null;
52+
const totalChunks = (zDim.end - zDim.start) * (yDim.end - yDim.start) * (xDim.end - xDim.start);
53+
let iter = 1;
54+
const rescaleIDs: string[] = [];
55+
56+
setStatus("Downloading...");
57+
setProgress(0);
58+
59+
for (let z = zDim.start; z < zDim.end; z++) {
60+
for (let y = yDim.start; y < yDim.end; y++) {
61+
for (let x = xDim.start; x < xDim.end; x++) {
62+
const chunkID = `z${z}_y${y}_x${x}`;
63+
const cacheBase = rank > 3 ? `${initStore}_${targetVariable}_${idx4D}` : `${initStore}_${targetVariable}`;
64+
const cacheName = `${cacheBase}_chunk_${chunkID}`;
65+
const cachedChunk = cache.get(cacheName);
66+
const isCacheValid = cachedChunk &&
67+
cachedChunk.kernel.kernelSize === (coarsen ? kernelSize : undefined) &&
68+
cachedChunk.kernel.kernelDepth === (coarsen ? kernelDepth : undefined);
69+
70+
if (isCacheValid) {
71+
const chunkData = cachedChunk.compressed ? DecompressArray(cachedChunk.data) : cachedChunk.data.slice();
72+
copyChunkToArray(
73+
chunkData,
74+
cachedChunk.shape,
75+
cachedChunk.stride,
76+
typedArray,
77+
outputShape,
78+
destStride as any, [z, y, x],
79+
[zDim.start, yDim.start, xDim.start]
80+
);
81+
} else {
82+
const raw = await fetcher.fetchChunk({ variable:targetVariable, rank, shape, chunkShape, x, y, z, xDimIndex, yDimIndex, zDimIndex, idx4D });
83+
84+
const rawData = Number.isFinite(fillValue) ? raw.data.map((v: number) => v === fillValue ? NaN : v) : raw.data; // Don't map if no fillvalue
85+
86+
let [chunkF16, newScalingFactor] = ToFloat16(rawData, scalingFactor);
87+
let thisShape = raw.shape;
88+
let chunkStride = raw.stride;
89+
90+
if (coarsen) {
91+
chunkF16 = await Convolve(chunkF16, { shape: chunkShape, strides: chunkStride }, "Mean3D", { kernelSize, kernelDepth }) as Float16Array;
92+
thisShape = thisShape.map((dim, idx) => Math.floor(dim / (idx === 0 ? kernelDepth : kernelSize)));
93+
chunkF16 = coarsen3DArray(chunkF16, chunkShape, chunkStride as any, kernelSize, kernelDepth, thisShape.reduce((a, b) => a * b, 1));
94+
chunkStride = calculateStrides(thisShape);
95+
}
96+
97+
if (newScalingFactor != null && newScalingFactor !== scalingFactor) {
98+
const delta = scalingFactor ? newScalingFactor - scalingFactor : newScalingFactor;
99+
RescaleArray(typedArray, delta);
100+
scalingFactor = newScalingFactor;
101+
for (const id of rescaleIDs) {
102+
const tempChunk = cache.get(`${cacheBase}_chunk_${id}`);
103+
tempChunk.scaling = scalingFactor;
104+
RescaleArray(tempChunk.data, delta);
105+
cache.set(`${cacheBase}_chunk_${id}`, tempChunk);
106+
}
107+
}
108+
109+
if (hasZ) {
110+
copyChunkToArray(chunkF16, thisShape.slice(-3), chunkStride.slice(-3) as any, typedArray, outputShape, destStride as any, [z, y, x], [zDim.start, yDim.start, xDim.start]);
111+
} else {
112+
copyChunkToArray2D(chunkF16, thisShape, chunkStride as any, typedArray, outputShape, destStride as any, [y, x], [yDim.start, xDim.start]);
113+
}
114+
115+
cache.set(cacheName, {
116+
data: compress ? CompressArray(chunkF16, 7) : chunkF16,
117+
shape: chunkShape, stride: chunkStride,
118+
scaling: scalingFactor, compressed: compress, coarsened: coarsen,
119+
kernel: { kernelDepth: coarsen ? kernelDepth : undefined, kernelSize: coarsen ? kernelSize : undefined }
120+
});
121+
rescaleIDs.push(chunkID);
122+
}
123+
setProgress(Math.round(iter++ / totalChunks * 100));
124+
}
125+
}
126+
}
127+
128+
setProgress(0);
129+
return { data: typedArray, shape: outputShape, dtype, scalingFactor };
130+
}

0 commit comments

Comments
 (0)