PageSourceSearch

https://sgdml.org/js/benchmark_charts.js

js sgdml.org collected 2026-10-03 21:31:33 UTC 17,578 bytes, 533 lines download raw bytes

1var datasetName = '';
2var datasetProp = 'Forces';
3
4var type = 'linear';
5var allLegendItemsHidden = false;
6var nLegendItemsHidden = 0
7
8var labelColor = 'rgb(109, 117, 125)';
9var gridLineColor = 'rgb(222, 226, 230)';
10var backgroundColor = 'rgb(248, 248, 250)';
11var defaultLineColor = 'rgb(197, 204, 211)';
12var highlightLineColor = 'rgb(139, 153, 167)';
13var specialLineColor = 'rgb(40, 167, 69)';
14
15var highlightIndex = -1; //index of currently hightlighted line
16
17// Custom plug-in that enables setting the background color of this chart
18Chart.pluginService.register({
19    beforeDraw: function(chart, easing) {
20        if (chart.config.options.chartArea && chart.config.options.chartArea.backgroundColor) {
21            var helpers = Chart.helpers;
22            var ctx = chart.chart.ctx;
23            var chartArea = chart.chartArea;
24
25            ctx.save();
26            ctx.fillStyle = chart.config.options.chartArea.backgroundColor;
27            ctx.fillRect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
28            ctx.restore();
29        }
30    }
31});
32
33function loadChart() {
34
35    var datasetFile = datasetName + ((datasetProp == 'Forces') ? '_F' : '_E') + '.json'
36    $.getJSON('benchmark/datasets/' + datasetFile + '?' + Math.random().toString(36), function(res) {
37
38        function findWithAttr(array, attr, value) {
39            for (var i = 0; i < array.length; i += 1)
40                if (array[i][attr] === value)
41                    return i;
42            return -1;
43        }
44
45        // Helper to get the dataset key
46        function getDatasetKey(dataset) {
47            return dataset.model_variant || dataset.ref_key;
48        }
49
50        var chart = window.chart;
51        var chartData = chart.config.data;
52
53        // Remove no longer needed datasets
54        var datasetKeys = chartData.datasets.map(dataset => dataset.label);
55        datasetKeys.forEach(key => {
56            var idx = findWithAttr(res, 'label', key);
57
58            if (idx < 0) {
59                var chartDatasetIdx = chartData.datasets
60                    .map(dataset => getDatasetKey(dataset))
61                    .indexOf(key);
62                chartData.datasets.splice(chartDatasetIdx, 1);
63            }
64        });
65
66        // Add new datasets
67        res.forEach(dataset => {
68            var datasetKey = getDatasetKey(dataset);
69            var existingIdx = chartData.datasets
70                .map(d => getDatasetKey(d))
71                .indexOf(datasetKey);
72
73            if (existingIdx >= 0) {
74                chartData.datasets[existingIdx].data = dataset.data;
75            } else {
76                chartData.datasets.push({
77                    data: dataset.data,
78                    label: datasetKey,
79                    fill: false,
80                    lineTension: 0,
81                    hitRadius: 10,
82                    radius: 4,
83                    showLine: true,
84                    borderCapStyle: 'round',
85                    borderDash: (datasetKey === 'GDML' ? [0, 6] : 0),
86                    pointStyle: 'circle',
87                    borderWidth: 3,
88                    borderColor: (datasetKey.includes("GDML") ? specialLineColor : defaultLineColor),
89                    pointBackgroundColor: (datasetKey.includes("GDML") ? specialLineColor : defaultLineColor),
90                    pointBorderColor: backgroundColor,
91                    pointHoverBorderColor: backgroundColor,
92                    pointBorderWidth: 1,
93                    pointHoverWidth: 1,
94                    pointHoverRadius: 6,
95                });
96            }
97        });
98
99        // Sort datasets: GDML first, sGDML second, rest alphabetically
100        chartData.datasets.sort((a, b) => {
101            if (a.label === "GDML") return -1;
102            if (b.label === "GDML") return 1;
103            if (a.label === "sGDML" && b.label !== "GDML") return -1;
104            if (b.label === "sGDML" && a.label !== "GDML") return 1;
105            return a.label.localeCompare(b.label);
106        });
107
108        chart.config.options.scales.yAxes[0].scaleLabel.labelString = 'Mean absolute error ' + ((datasetProp == 'Forces') ? '[kcal/mol/Å]' : '[kcal/mol]')
109        chart.update();
110    }).fail(function(d, textStatus, error) {
111        console.error('getJSON failed, status: ' + textStatus + ', error: ' + error);
112        alert('Oh nooo! I was not able to fetch the data.');
113    });
114}
115
116function highlightDataset(dataset, highlight) {
117
118    if (!dataset.label.includes("GDML")) {
119        color = highlight ? highlightLineColor : defaultLineColor;
120        dataset.borderColor = color;
121        dataset.pointBackgroundColor = color;
122    }
123}
124
125function setCurveHidingButtonStatus(on) {
126    if (on) {
127        $('#all-off-toggle').addClass('active');
128    } else {
129        $('#all-off-toggle').removeClass('active');
130    }
131}
132
133function populateRefs() {
134
135    var datasetFile = datasetName + ((datasetProp == 'Forces') ? '_F' : '_E') + '.json'
136
137    var refKeys = [];
138    $.getJSON('benchmark/datasets/' + datasetFile + '?' + Math.random().toString(36), function(res) {
139
140        const uniqueRefs = {};
141        res.forEach(dataset => {
142          const refKey   = dataset.ref_key;
143          const refLabel = ('ref_label' in dataset) ? dataset.ref_label : refKey;
144
145          const comboKey = JSON.stringify([refKey, refLabel]); // avoids delimiter collisions
146          if (!(comboKey in uniqueRefs)) {
147            uniqueRefs[comboKey] = {refKey:refKey, refLabel:refLabel};
148          }
149        });
150
151        $.getJSON('benchmark/refs.json?' + Math.random().toString(36), function(refs) {
152
153            var list = document.getElementById('refs')
154            list.innerHTML = '';
155
156            // Sort: GDML first, sGDML second, others A→Z by refLabel
157            const refArray = Object.values(uniqueRefs);
158            refArray.sort((a, b) => {
159                const A = a.refLabel;
160                const B = b.refLabel;
161
162                if (A === "GDML") return -1;
163                if (B === "GDML") return 1;
164                if (A === "sGDML" && B !== "GDML") return -1;
165                if (B === "sGDML" && A !== "GDML") return 1;
166
167                return A.localeCompare(B, undefined, { sensitivity: 'base' });
168            });
169
170            refArray.forEach(({refKey, refLabel}) => {
171
172                var item = document.createElement('p');
173                item.setAttribute('style', 'margin-left: 20px; text-indent: -20px; line-height: 1.2; margin-bottom: 5px;');
174
175                var refStr = '<strong>[' + refLabel + ']</strong> '
176                if (refKey in refs) {
177
178                    var ref = refs[refKey];
179                    refStr += ref.authors + ' (' + ref.year + '). <em>' + ref.title + '</em>.';
180
181                    if (ref.journal != '')
182                        refStr += ' ' + ref.journal
183
184                    if (ref.volume != '')
185                        refStr += ', ' + ref.volume
186
187                    if (ref.page != '')
188                        refStr += ', ' + ref.page
189                } else
190                    refStr += '<span style="color:red;">MISSING!</span>'
191
192                refStr = '<small>' + refStr + '</small>'
193                item.innerHTML = refStr;
194
195                list.appendChild(item);
196            });
197        });
198    });
199}
200
201$(function() {
202
203    var yAxes_lin = {
204        type: 'linear',
205        ticks: {
206            fontColor: labelColor,
207            fontSize: 14,
208            padding: 12,
209            autoskip: true,
210            autoSkipPadding: 30,
211            callback: function(label, index, labels) {
212                return label.toFixed(2);
213            }
214        },
215        scaleLabel: {
216            labelString: 'Mean absolute error [kcal/mol/A]',
217            display: true,
218            fontColor: labelColor,
219            fontSize: 14,
220            fontStyle: 'bold',
221        },
222        gridLines: {
223            //borderDash: [2, 2],
224            color: gridLineColor,
225            zeroLineWidth: 0,
226            drawTicks: false,
227        }
228    }
229
230    var yAxes_log = Object.assign(yAxes_lin)
231    yAxes_log['type'] = 'logarithmic'
232
233    var opts = {
234        chartArea: {
235            backgroundColor: backgroundColor,
236        },
237        responsive: true,
238        legend: {
239            labels: {
240                //padding: 20,
241                fontColor: labelColor,
242                usePointStyle: true,
243                boxWidth: 5.5,
244            },
245            position: 'right',
246            align: 'start',
247            // onHover: function(event, legendItem) {
248
249            // 	var chart = window.chart
250
251            // 	if (chart.hoveringLegendIndex != legendItem.datasetIndex) {
252            // 		chart.hoveringLegendIndex = legendItem.datasetIndex;
253
254            // 		var dataset = chart.data.datasets[legendItem.datasetIndex]
255            // 		highlightDataset(dataset, true)
256
257            // 		if (!dataset.label.includes('GDML'))
258            // 			legendItem.fillStyle = highlightLineColor;
259
260            // 		console.log('enter: ' + legendItem.datasetIndex)
261
262            // 		chart.draw();
263            // 	}
264            // },
265            // onLeave: function(event, legendItem) {
266
267            // 	var chart = window.chart;
268
269            // 	if (chart.hoveringLegendIndex >= 0) {
270            // 		chart.hoveringLegendIndex = -1;
271
272            // 		console.log('leave: ' + legendItem.datasetIndex);
273
274            // 		var dataset = chart.data.datasets[legendItem.datasetIndex];
275            // 		highlightDataset(dataset, false)
276
277            // 		if (!dataset.label.includes('GDML'))
278            // 			legendItem.fillStyle = defaultLineColor;
279
280            // 		chart.update();
281            // 	}
282            // },
283            onClick: function(event, legendItem) {
284
285                //if (legendItem.text.includes('GDML')){
286                // 	event.stopPropagation();
287                //    return;
288                //}
289
290                var chart = window.chart
291
292                var datasets = chart.data.datasets
293                var filteredDatasets = datasets.filter(function(dataset) {
294                    return !dataset.label.includes('GDML');
295                });
296
297                var index = legendItem.datasetIndex;
298                var dataset = datasets[index];
299                var meta = chart.getDatasetMeta(index);
300
301                dataset.hidden = !dataset.hidden
302                meta.hidden = meta.hidden === null ? dataset.hidden : null;
303
304                if (!dataset.label.includes('GDML'))
305                    nLegendItemsHidden += meta.hidden ? 1 : -1;
306
307                if (nLegendItemsHidden == 0) {
308                    setCurveHidingButtonStatus(false);
309                    allLegendItemsHidden = false;
310                } else if (nLegendItemsHidden == filteredDatasets.length) {
311                    setCurveHidingButtonStatus(true);
312                    allLegendItemsHidden = true;
313                }
314                chart.update();
315
316                console.log(nLegendItemsHidden);
317            },
318        },
319        tooltips: {
320            mode: 'point',
321            intersect: true,
322            displayColors: false,
323            titleFontSize: 16,
324            bodyFontSize: 14,
325            itemSort: (a, b, data) => b.yLabel - a.yLabel,
326            callbacks: {
327                label: function(tooltipItem, data) {
328                    var label = data.datasets[tooltipItem.datasetIndex].label || '';
329
330                    if (label)
331                        label += ': ';
332                    label += (Math.round(tooltipItem.yLabel * 1000) / 1000).toFixed(3);
333
334                    return label;
335                },
336                title: function(tooltipItem, data) {
337                    var tick = tooltipItem[0].xLabel
338
339                    tick_str = tick
340                    if (tick === 1000 || tick === 15000 || tick === 50000 || tick === 100000)
341                        tick_str = (tick / 1000).toString() + 'k';
342
343                    return tick_str + ' points';
344                },
345            },
346        },
347        hover: {
348            mode: 'nearest',
349            intersect: true,
350        },
351        onHover: function(event, activeElements) {
352
353            if (highlightIndex >= 0) {
354                this.data.datasets.forEach(function(dataset) {
355                    highlightDataset(dataset, false)
356                });
357            }
358
359            if (!activeElements || !activeElements.length) {
360                if (highlightIndex == -1)
361                    return
362                else
363                    highlightIndex = -1;
364            } else {
365                highlightIndex = activeElements[0]._datasetIndex;
366                var dataset = this.data.datasets[highlightIndex];
367                highlightDataset(dataset, true)
368            }
369
370            // Twice, otherwise legend does not update
371            this.update();
372            this.update();
373        },
374        title: {
375            display: false,
376        },
377        scales: {
378            xAxes: [{
379                type: 'logarithmic',
380                position: 'bottom',
381                ticks: {
382                    userCallback: function(tick) {
383
384                        if (tick === 200 || tick === 400 || tick === 600 || tick === 800)
385                            return tick.toString();
386
387                        if (tick === 1000 || tick === 15000 || tick === 50000 || tick === 100000)
388                            return (tick / 1000).toString() + 'k';
389
390                    },
391                    min: 200,
392                    max: 50000,
393                    autoSkip: true,
394                    stepSize: 100,
395                    fontColor: labelColor,
396                    fontSize: 14,
397                    padding: 12,
398                },
399                scaleLabel: {
400                    labelString: 'Training dataset size [# points]',
401                    display: true,
402                    fontColor: labelColor,
403                    fontSize: 14,
404                    fontStyle: 'bold',
405                },
406                gridLines: {
407                    //borderDash: [2, 2],
408                    color: gridLineColor,
409                    zeroLineWidth: 0,
410                    drawTicks: false,
411                }
412            }],
413            yAxes: [yAxes_lin]
414        },
415    };
416
417    var ctx = document.getElementById('chart').getContext('2d');
418    var chart = window.chart = Chart.Line(ctx, {
419        data: {},
420        type: 'line',
421        options: opts,
422    });
423
424    $('#scale-toggle').change(function() {
425
426        type = type === 'linear' ? 'logarithmic' : 'linear';
427
428        chart.options.scales.yAxes[0] = (type === 'linear') ? yAxes_lin : yAxes_log;
429        chart.options.scales.yAxes[0].display = true;
430        chart.options.scales.yAxes[0].type = type;
431
432        chart.update();
433    })
434
435    $('#all-off-toggle').on('click', function(event) {
436
437        var datasets = chart.data.datasets
438        var filteredDatasets = datasets.filter(function(dataset) {
439            return !dataset.label.includes('GDML');
440        });
441
442        allLegendItemsHidden = !allLegendItemsHidden;
443        nLegendItemsHidden = allLegendItemsHidden ? filteredDatasets.length : 0;
444        setCurveHidingButtonStatus(allLegendItemsHidden)
445
446        filteredDatasets.forEach(function(dataset) {
447            Object.keys(dataset._meta).forEach(key => {
448                dataset._meta[key].hidden = allLegendItemsHidden;
449            })
450        });
451
452        chart.update();
453    })
454
455    $('#benchmarkModal').on('show.bs.modal', function(e) {
456        datasetName = $(e.relatedTarget).data('dataset-name');
457        document.getElementById('benchmarkModalMoleculeName').innerHTML = datasetName;
458
459        // Reset tabs
460        datasetProp = 'Forces';
461        $(this).find('.nav-tabs a:first').tab('show');
462
463        // Reset scale type
464        type = 'linear';
465        chart.options.scales.yAxes[0].type = type;
466        $('#scale-toggle').prop('checked', false).change()
467
468        // Reset chart
469        chart["config"]["data"] = {
470            'datasets': []
471        }
472        chart.update();
473
474        // Reset toggle button
475        allLegendItemsHidden = false;
476        nLegendItemsHidden = 0
477        setCurveHidingButtonStatus(false)
478
479        populateRefs();
480
481    });
482
483    $('#benchmarkModal').on('shown.bs.modal', function(e) {
484        loadChart();
485    });
486
487    $('a[data-toggle="tab"]').on('show.bs.tab', function(e) {
488        datasetProp = $(e.target).text();
489
490        if (datasetProp == 'Forces' || datasetProp == 'Energy') {
491            populateRefs();
492        }
493
494    });
495
496    $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
497        tabName = $(e.target).text();
498        if (tabName == 'Forces' || tabName == 'Energy') {
499            loadChart();
500        }
501    });
502
503
504    //disable hoever effects if mouse moves off chart canvas
505    //chart.hoveringLegendIndex = -1
506    // chart.canvas.addEventListener('mousemove', function(e) {
507
508    // 	//if (chart.hoveringLegendIndex >= 0) {
509
510    // 		if (e.layerX < chart.legend.left || chart.legend.right < e.layerX
511    // 			|| e.layerY < chart.legend.top || chart.legend.bottom < e.layerY
512    // 			) {
513
514    // 			//console.log('out')
515
516    // 			//if (chart.hoveringLegendIndex >= 0) {
517
518    // 				//var dataset = chart.data.datasets[legendItem.datasetIndex]
519    // 				//if (!dataset.label.includes("GDML")) {
520    // 				//	dataset.borderColor = 'rgb(197, 204, 211)';
521    // 				//	dataset.pointBackgroundColor = 'rgb(197, 204, 211)';
522    // 				//}
523    // 				//dataset.radius = 4;
524
525    // 				//chart.hoveringLegendIndex = -1
526    // 				chart.update()
527    // 			//}
528    // 			//chart.update()
529    // 		}
530    // 	//}
531    // });
532
533});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.