-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.ts
129 lines (109 loc) · 4.77 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
* Copyright (c) 2016 - now, David Sehnal, licensed under Apache 2.0, See LICENSE file for more info.
*/
import * as CCP4 from './ccp4'
import * as File from '../common/file'
import * as Data from './data-model'
import * as Sampling from './sampling'
import * as DataFormat from '../common/data-format'
import * as fs from 'fs'
export default async function pack(input: { name: string, filename: string }[], blockSize: number, isPeriodic: boolean, outputFilename: string) {
try {
await create(outputFilename, input, blockSize, isPeriodic);
} catch (e) {
console.error('[Error] ' + e);
}
}
function getTime() {
let t = process.hrtime();
return t[0] * 1000 + t[1] / 1000000;
}
function updateAllocationProgress(progress: Data.Progress, progressDone: number) {
let old = (100 * progress.current / progress.max).toFixed(0);
progress.current += progressDone;
let $new = (100 * progress.current / progress.max).toFixed(0);
if (old !== $new) {
process.stdout.write(`\rAllocating... ${$new}%`);
}
}
/**
* Pre allocate the disk space to be able to do "random" writes into the entire file.
*/
async function allocateFile(ctx: Data.Context) {
const { totalByteSize, file } = ctx;
const buffer = new Buffer(Math.min(totalByteSize, 8 * 1024 * 1024));
const progress: Data.Progress = { current: 0, max: Math.ceil(totalByteSize / buffer.byteLength) };
let written = 0;
while (written < totalByteSize) {
written += fs.writeSync(file, buffer, 0, Math.min(totalByteSize - written, buffer.byteLength));
updateAllocationProgress(progress, 1);
}
}
function determineBlockSize(data: CCP4.Data, blockSize: number) {
const { extent } = data.header;
const maxLayerSize = 1024 * 1024 * 1024;
const valueCount = extent[0] * extent[1];
if (valueCount * blockSize <= maxLayerSize) return blockSize;
while (blockSize > 0) {
blockSize -= 4;
if (valueCount * blockSize <= maxLayerSize) return blockSize;
}
throw new Error('Could not determine a valid block size.');
}
async function writeHeader(ctx: Data.Context) {
const header = DataFormat.encodeHeader(Data.createHeader(ctx));
await File.writeInt(ctx.file, header.byteLength, 0);
await File.writeBuffer(ctx.file, 4, header);
}
async function create(filename: string, sourceDensities: { name: string, filename: string }[], sourceBlockSize: number, isPeriodic: boolean) {
const startedTime = getTime();
if (sourceBlockSize % 4 !== 0 || sourceBlockSize < 4) {
throw Error('Block size must be a positive number divisible by 4.');
}
if (!sourceDensities.length) {
throw Error('Specify at least one source density.');
}
process.stdout.write('Initializing... ');
const files: number[] = [];
try {
// Step 1a: Read the CCP4 headers
const channels: CCP4.Data[] = [];
for (const s of sourceDensities) channels.push(await CCP4.open(s.name, s.filename));
// Step 1b: Check if the CCP4 headers are compatible.
const isOk = channels.reduce((ok, s) => ok && CCP4.compareHeaders(channels[0].header, s.header), true);
if (!isOk) {
throw new Error('Input file headers are not compatible (different grid, etc.).');
}
const blockSize = determineBlockSize(channels[0], sourceBlockSize);
for (const ch of channels) CCP4.assignSliceBuffer(ch, blockSize);
// Step 1c: Create data context.
const context = await Sampling.createContext(filename, channels, blockSize, isPeriodic);
for (const s of channels) files.push(s.file);
files.push(context.file);
process.stdout.write(' done.\n');
console.log(`Block size: ${blockSize}`);
// Step 2: Allocate disk space.
process.stdout.write('Allocating... 0%');
await allocateFile(context);
process.stdout.write('\rAllocating... done.\n');
// Step 3: Process and write the data
process.stdout.write('Writing data... 0%');
await Sampling.processData(context);
process.stdout.write('\rWriting data... done.\n');
// Step 4: Write the header at the start of the file.
// The header is written last because the sigma/min/max values are computed
// during step 3.
process.stdout.write('Writing header... ');
await writeHeader(context);
process.stdout.write('done.\n');
// Step 5: Report the time, d'ph.
const time = getTime() - startedTime;
console.log(`[Done] ${time.toFixed(0)}ms.`);
} finally {
for (let f of files) File.close(f);
// const ff = await File.openRead(filename);
// const hh = await DataFormat.readHeader(ff);
// File.close(ff);
// console.log(hh.header);
}
}