ccs-sim
Version:
Modelling CCS systems
303 lines (302 loc) • 13.4 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const yaml_1 = __importDefault(require("yaml"));
const snapshotBuilder_1 = __importDefault(require("./snapshotBuilder"));
const OLGA = {
parse: (fileString) => {
const lines = fileString.split('\n');
const linesReversed = lines.slice().reverse();
const lineThatStartsWith = (word, backwards = false) => {
const searchArr = backwards ? linesReversed : lines;
const idx = searchArr.findIndex((line) => line.startsWith(word.toUpperCase()));
if (idx < 0) {
throw new Error(`Line not found: ${word}`);
}
if (backwards) {
return [lines.length - idx - 1, linesReversed[idx]];
}
return [idx, lines[idx]];
};
const lastLineThatStartsWith = (word) => {
return lineThatStartsWith(word, true);
};
const keyLines = {
initialConditions: lineThatStartsWith('initialconditions'),
geometry: lineThatStartsWith('geometry'),
firstPipe: lineThatStartsWith('pipe'),
lastPipe: lastLineThatStartsWith('pipe'),
};
const readLineProperties = (line) => {
if (typeof line !== 'string') {
line = line[1];
}
line = line.replace(/NSEGMENT=\d+,\s/g, '');
line = line.replace(/LSEGMENT=.+\).+?\s/g, '');
const [type, parameterStrings] = [
line.substring(0, line.indexOf(' ')),
line.substring(line.indexOf(' ')).trim().split(', '),
];
const unitConversion = (valueString) => {
const matchName = valueString.match(/".+?"/);
if (matchName) {
return [matchName[0].substring(1, matchName[0].length - 1), '-'];
}
const matchNum = valueString.match(/-?[0-9]+\.?[0-9]*/);
if (matchNum) {
const numVal = matchNum[0];
let num = Number(numVal);
let unitString = valueString.substring(numVal.length).trim();
switch (unitString) {
case 'km':
num = num * 1000;
unitString = 'm';
break;
case 'mm':
num = num / 1000;
unitString = 'm';
break;
}
return [Number(num.toFixed(4)), unitString];
}
return [null, null];
};
const parameters = parameterStrings.reduce((acc, param) => {
const [property, valueString] = param
.split('=')
.map((s) => s.trim());
const converted = unitConversion(valueString);
if (converted[0] || converted[1]) {
acc[property] = converted;
}
return acc;
}, type === 'GEOMETRY' ? { YSTART: [0, 'm'] } : {});
return { type, parameters };
};
const INLET = readLineProperties(keyLines.geometry);
let prevX = 0;
const getXLength = (lineParams) => {
if (!lineParams.XEND)
return 0;
const length = lineParams.XEND[0] - prevX;
prevX = lineParams.XEND[0];
return length;
};
const getYGain = (lineParams) => {
const elevation = endElevation;
if (lineParams.YEND) {
endElevation = lineParams.YEND;
}
return elevation[0];
};
let endElevation = INLET.parameters.YSTART;
const transformProperties = (lineProps) => {
const params = lineProps.parameters;
const instructionMap = {
GEOMETRY: 'inlet',
PIPE: 'pipeseg',
};
const instructionType = instructionMap[lineProps.type];
const x = getXLength(params);
const y = getYGain(params);
const length = instructionType === instructionMap.GEOMETRY
? 0
: Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
const transformed = {
[instructionType]: {
name: params.LABEL[0],
length,
elevation: y,
diameters: params.DIAMETER ? [params.DIAMETER[0]] : undefined,
},
};
for (const key of Object.keys(transformed[instructionType])) {
if (!transformed[instructionType][key] &&
key !== 'elevation') {
delete transformed[instructionType][key];
}
}
const maxSegLength = 200;
const reduceToMaxLengthArr = (len) => {
if (len < maxSegLength)
return [len];
const lengths = [];
const sum = () => lengths.reduce((acc, a) => acc + a, 0);
const remainder = () => len - sum();
while (remainder() >= maxSegLength) {
lengths.push(maxSegLength);
}
if (remainder()) {
lengths.push(remainder());
}
else
return [maxSegLength];
return lengths;
};
if (instructionType === 'pipeseg' &&
transformed.pipeseg.length &&
transformed.pipeseg.length > maxSegLength) {
const fullLength = transformed.pipeseg.length;
const seriesLengths = reduceToMaxLengthArr(fullLength);
// Elevation
let lengthSoFar = 0;
const startElevation = transformed.pipeseg
.elevation;
const elevationIncrease = endElevation[0] - startElevation;
const elevations = seriesLengths.map((sLength) => {
const cos = x / length;
lengthSoFar += cos * sLength;
const yGain = (elevationIncrease * lengthSoFar) / fullLength;
return Number((startElevation + yGain).toFixed(4));
});
elevations.unshift(startElevation);
elevations.pop();
transformed.pipeseries = {
n: seriesLengths.length,
pipeDef: {
name: transformed.pipeseg.name,
length: fullLength,
elevation: startElevation,
diameters: [
...transformed.pipeseg.diameters,
],
},
elevations,
lengths: seriesLengths,
};
delete transformed.pipeseg;
}
return transformed;
};
const pipes = lines
.slice(keyLines.firstPipe[0], keyLines.lastPipe[0] + 1)
.map(readLineProperties);
const data = {
instructions: [
transformProperties(INLET),
...pipes.map(transformProperties),
],
};
return data;
},
};
class Parser {
constructor() {
this.keyPoints = [];
// do nothing
}
readFile(fileName, save = false) {
const file = fs_1.default.readFileSync(fileName, 'utf-8');
if (!file) {
throw new Error(`No file: ${fileName}`);
}
const fileExtension = fileName.split('.').pop();
switch (fileExtension) {
case 'yml':
case 'yaml':
this.data = yaml_1.default.parse(file);
break;
case 'genkey':
this.data = OLGA.parse(file);
break;
default:
throw new Error(`File type not supported: ${fileExtension}`);
}
if (save) {
fs_1.default.writeFileSync(`${fileName.substring(0, fileName.indexOf('.'))}.yml`, yaml_1.default.stringify(this.data));
}
return this.data;
}
build() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.data) {
throw new Error(`No data - call this.readFile(fileName) before this.build()`);
}
let builder = new snapshotBuilder_1.default();
for (const instruction of this.data.instructions) {
for (let [type, parameters] of Object.entries(instruction)) {
type = type.toLowerCase();
if (['selectsplitter', 'branch', 'setfluid'].includes(type)) {
switch (type) {
case 'selectsplitter':
const { id } = parameters;
builder.selectSplitter(id);
break;
case 'branch':
const pipeDef = parameters;
builder = builder.branch(pipeDef);
break;
case 'setfluid':
const { pressure, temperature, flowrate } = parameters;
yield builder.setFluid(pressure, temperature, flowrate);
break;
}
continue;
}
switch (type) {
case 'inlet':
{
const { name, physical } = parameters;
builder = builder.addInlet(name, physical);
}
break;
case 'pipeseg':
{
const pipeDef = parameters;
builder = builder.chainAddPipeSeg(pipeDef);
}
break;
case 'splitter':
{
const { name, physical } = parameters;
builder = builder.addSplitter(name, physical);
}
break;
case 'well':
{
const { name, physical, realReservoirName } = parameters;
builder = builder.addWell(name, physical, realReservoirName);
}
break;
case 'reservoir':
{
const { name, physical, pressure } = parameters;
builder = builder.addReservoir(name, physical, pressure);
}
break;
case 'pipeseries':
{
const { n, pipeDef, elevations, lengths } = parameters;
builder = builder.addPipeSeries(n, pipeDef, elevations, lengths);
}
break;
case 'valve':
{
const { name, physical, inputPressure } = parameters;
builder = builder.addValve(name, physical, inputPressure);
}
break;
default:
throw new Error(`${type} not supported`);
}
}
}
this.keyPoints = builder.keyPoints;
this.fluid = builder.fluid;
return builder.elements[0];
});
}
}
exports.default = Parser;
;