solar-scores
Version:
Compute scores for solar decathlon competition - Cali 2015
314 lines (270 loc) • 9.16 kB
JavaScript
;
const clone = require('lodash.clone');
const moment = require('moment');
var periods = require('./periods').all;
var periodsByDay = require('./periods').byDay;
const oneByDay = require('./periods').oneByDay;
exports.filterValues = function (arr, param) {
// We expect defined values
return arr.filter(function (val) {
return val[param] !== null && val[param] !== undefined;
});
};
exports.getSingleScoringFunction = function (fullPointThreshold, noPointThreshold) {
const fraction = 1 / (fullPointThreshold - noPointThreshold);
const constant = -fraction * noPointThreshold;
if (fullPointThreshold < noPointThreshold) {
return function (value) {
if (value <= fullPointThreshold) return 1;
if (value >= noPointThreshold) return 0;
return value * fraction + constant;
};
} else {
return function (value) {
if (value >= fullPointThreshold) return 1;
if (value <= noPointThreshold) return 0;
return value * fraction + constant;
};
}
};
exports.getDoubleScoringFunction = function (noPoint1, fullPointStart, fullPointEnd, noPoint2) {
const fraction1 = 1 / (fullPointStart - noPoint1);
const constant1 = -fraction1 * noPoint1;
const fraction2 = 1 / (fullPointEnd - noPoint2);
const constant2 = -fraction2 * noPoint2;
return function (value) {
if (value <= noPoint1 || value >= noPoint2) return 0;
if (value >= fullPointStart && value <= fullPointEnd) return 1;
if (value < fullPointStart) return value * fraction1 + constant1;
if (value > fullPointEnd) return value * fraction2 + constant2;
};
};
//exports.getResampledMax = function(series, resolution) {
// if(series.length === 1) return series[0];
//
//};
exports.getElapsedFraction = function (scoreType, date) {
var now = date || new Date();
if (typeof scoreType === 'string') {
var p = periods[scoreType];
} else {
p = scoreType;
}
var elapsedDuration = 0;
var totalDuration = 0;
for (var i = 0; i < p.length; i++) {
totalDuration += p[i].duration;
if (now < p[i].begin) {
continue;
} else if (now < p[i].end) {
elapsedDuration += moment.duration(moment(now).diff(p[i].begin)).asSeconds();
} else {
elapsedDuration += p[i].duration;
}
}
return elapsedDuration / totalDuration;
};
exports.filterByEpoch = function (arr, scoreType) {
var period;
if (typeof scoreType === 'string') {
period = periods[scoreType];
} else {
period = scoreType;
}
if (period.length > 0) {
arr = arr.filter(function (val) {
return new Date(val.epoch) >= period[0].begin;
});
}
for (let i = 1; i < period.length; i++) {
arr = arr.filter(function (val) {
let e = new Date(val.epoch);
return e <= period[i - 1].end || e >= period[i].begin;
});
}
if (period.length > 0) {
arr = arr.filter(function (val) {
return new Date(val.epoch) <= period[period.length - 1].end;
});
}
return arr;
};
exports.filterByEpochPerDay = function (arr, scoreType) {
var byDay;
if (typeof scoreType === 'string') {
byDay = periodsByDay[scoreType];
} else {
byDay = scoreType;
}
//console.log(byDay[0].periods[0]);
var byDay = clone(byDay, true);
for (let i = 0; i < byDay.length; i++) {
byDay[i].data = new Array(byDay[i].periods.length);
for (let j = 0; j < byDay[i].periods.length; j++) {
byDay[i].data[j] = exports.filterByEpoch(arr, [byDay[i].periods[j]])
}
//byDay[i].data = exports.filterByEpoch(arr, byDay[i].periods);
}
return byDay;
};
// Series must be asc
// periods must be asc
exports.resampleWithMax = function (series, params, scoreType, resolution) {
// Resolution is 5 minutes
var period;
if (typeof scoreType === 'string') {
period = periods[scoreType]
} else {
period = scoreType;
}
resolution = resolution || 60 * 5 * 1000;
var semiRes = resolution / 2;
var res1000 = resolution / 1000;
let l = 0;
let pl = new Array(period.length);
for (let i = 0; i < period.length; i++) {
pl[i] = Math.floor(period[i].duration / res1000) + 1;
l += pl[i];
}
var epochs = new Array(l);
let count = 0;
for (let i = 0; i < period.length; i++) {
for (let j = 0; j < pl[i]; j++) {
epochs[count] = period[i].begin.getTime() + j * resolution;
count++;
}
}
var result = epochs.map(function (epoch) {
return {
value: null,
epoch: epoch
};
});
// Series are assumed to be ordered asc
for (let i = 0; i < series.length; i++) {
let currentIdx = 0;
for (let j = 0; j < series[i].length; j++) {
let ediff;
do {
var diff = epochs[currentIdx] - series[i][j].epoch;
ediff = Math.abs(diff);
currentIdx++;
} while ((ediff > semiRes) && (currentIdx < epochs.length) && diff < semiRes );
currentIdx = currentIdx - 1;
if (ediff <= semiRes) {
let newValue = series[i][j][params[i]];
if (result[currentIdx].value === null || result[currentIdx].value < newValue) {
result[currentIdx].value = newValue;
}
}
}
}
return result;
};
exports.finalizeScores = function (byDay) {
var result = {
points: 0,
max: 0,
maxUntilNow: 0
};
var perDay = new Array(byDay.length);
for (let i = 0; i < byDay.length; i++) {
perDay[i] = {};
perDay[i].day = byDay[i].day;
let l = byDay[i].periods.length;
perDay[i].points = 0;
perDay[i].maxUntilNow = 0;
perDay[i].max = 0;
for (let j = 0; j < l; j++) {
perDay[i].max += byDay[i].periods[j].points;
perDay[i].points += byDay[i].points[j];
perDay[i].maxUntilNow += byDay[i].maxUntilNow[j];
perDay[i].fraction = byDay[i].fraction[j];
}
perDay[i].projected = perDay[i].points * (perDay[i].max / perDay[i].maxUntilNow);
result.points += perDay[i].points;
result.max += perDay[i].max;
result.maxUntilNow += perDay[i].maxUntilNow;
}
result.projected = result.points * (result.max / result.maxUntilNow);
result.perDay = perDay;
return result;
};
exports.interpolate = function (data) {
var idx = 0;
var last;
while (idx < data.length) {
while (data[idx] === null && idx < data.length) {
idx++;
}
var lastIdx = (last || 0) + 1;
if (last === undefined && data[0] === null) {
for (let i = 0; i < idx; i++) {
data[i] = data[idx] || null;
}
} else if ((data[idx] !== null && idx !== data.length) || (data[idx - 1] !== null && idx === data.length)) {
var slope = (data[idx] - data[last]) / (idx - last);
for (let i = lastIdx; i < idx; i++) {
var a = i - lastIdx + 1;
data[i] = data[last] + a * slope;
}
} else {
for (let i = lastIdx; i < idx; i++) {
data[i] = data[last];
}
}
last = idx;
idx++;
}
return data;
};
exports.average = function (data) {
data = data.filter(function (val) {
return val !== null;
});
if (!data.length) return null;
var avg = 0;
for (let i = 0; i < data.length; i++) {
avg += data[i];
}
return avg / data.length;
};
exports.diff = function (data) {
if (data.length <= 1) return [];
let diff = new Array(data.length - 1);
for(let i=1; i<data.length; i++) {
diff[i-1] = data[i] - data[i-1];
}
return diff;
};
exports.getRefTemperature = function (values, param, date) {
// Each day we need to recalculate the periods
var periods = [];
for(let j=6; j>=0; j--) {
periods.push({
begin: moment(date).add(-1*j-1, 'days').hours(6).minutes(0).seconds(0).toDate(),
end: moment(date).add(-1*j-1, 'days').hours(18).minutes(0).seconds(0).toDate(),
duration: 3600 * 12
});
}
periods = oneByDay(periods);
var extPerDay = exports.filterByEpochPerDay(values, periods);
var extAvg = extPerDay.map(function(day) {
var temp = day.data[0].map(function(val) {
return val[param];
});
return exports.average(temp);
});
var coefficients = [1, 0.8, 0.6, 0.5, 0.4, 0.3, 0.2];
var tMax = 0;
var factor = 0;
for(let i=0; i<coefficients.length; i++) {
if(extAvg[i] !== null) {
tMax += coefficients[i] * extAvg[i];
factor += coefficients[i];
}
}
tMax /= factor;
tMax = 19.9 + 0.255 * tMax;
return tMax;
};