Browse Source

Add source code of the pictures page

compton 2 years ago
parent
commit
1d55b56366
5 changed files with 776 additions and 0 deletions
  1. 7 0
      page/README.md
  2. 362 0
      page/init.php
  3. 102 0
      page/main.css
  4. 0 0
      page/plots.php
  5. 305 0
      page/summary.php

+ 7 - 0
page/README.md

@@ -0,0 +1,7 @@
+Здесь лежит исходный код для страниц с изображениями 
+[cmd.inp.nsk.su/~compton/pictures](https://cmd.inp.nsk.su/~compton/pictures/)
+
+* `init.php` - страница с детальной (интерактивной) информацией о конкретной энергетической точке
+* `summary.php` - страница с информацией в целом о сезоне
+* `plots.php` - неинтерактивная информация об усреднениях по точкам в сезоне
+* `main.css` - стили css для сайта

+ 362 - 0
page/init.php

@@ -0,0 +1,362 @@
+<!-- https://www.createwithdata.com/chartjs-and-csv/ -->
+<!-- https://towardsdatascience.com/4-ways-to-improve-your-plotly-graphs-517c75947f7e -->
+
+<?php
+$selected_csv = 
+
+$branch = 'dev';
+$season = 'NNBAR2021';
+$url = "https://cmd.inp.nsk.su/~compton/gitlist/compton_tables/raw/".$branch."/tables/".$season."/";
+$url_total_info = "https://cmd.inp.nsk.su/~compton/gitlist/compton_tables/raw/".$branch."/tables/".$season.".csv";
+$text = file_get_contents($url);
+$arrays = explode("\n", $text);
+
+$cleanArrs = array_filter($arrays, function($value) {
+   return end(explode('.', $value)) == "csv";
+});
+
+function isSelected($a, $b){
+    if ($a==$b){
+        return "selected";
+    }
+    return "";
+}
+
+$selected_csv = isset($_GET["csv_file"]) ? $_GET["csv_file"] : reset($cleanArrs);
+
+?>
+
+<html>
+<head>
+    <!--<script src="plotly-2.11.1.min.js"></script> -->
+    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet" href="main.css">
+    <title>Compton interactive plots</title>
+    <!-- <script src="chart.js"></script> -->
+</head>
+<body>
+    <div id="gd" style="height: max(80vh, 600px); padding-bottom: 20px;"></div>
+            
+    <form name="form" action="" method="get">
+        <p style="text-align: center;">Select energy point:</p>
+        <select name="csv_file" class="select-css" onchange="this.form.submit()">
+            <?
+            foreach($cleanArrs as $file){
+                ?><option value="<?echo $file?>" <?echo isSelected($file, $selected_csv)?>><?echo $file?></option>
+                <?
+            }
+            ?>
+        </select>
+    </form>
+
+
+    <script>
+        function makeplot(){
+            Plotly.d3.csv('<?echo $url_total_info;?>', (allRows)=>{
+                const {mean_energy, mean_spread} = parseResultsTable(allRows, <?echo reset(explode('_', $selected_csv));?>);        
+                Plotly.d3.csv('<?echo $url.$selected_csv;?>', function(data){processData(data, mean_energy, mean_spread)});
+            }
+            );
+        }
+        
+        function parseResultsTable(allRows, energy_point){
+            // Extracts a row following the energy point in the total csv file (or null if energy point is not exists)
+            for (var i=0; i<allRows.length; i++){
+                if (allRows[i].energy_point == energy_point ){
+                    return allRows[i];
+                }
+            }
+            return null;
+        }
+        
+        function parseRow(row){
+            // Parses a row from the detailed csv file
+            row['start_time'] = Date.parse(row['compton_start']);
+            row['stop_time'] = Date.parse(row['compton_stop']);
+            row['center_time'] = new Date((row['start_time'] + row['stop_time'])/2);
+            row['timedelta'] = (row['stop_time'] - row['start_time'])/2/1000; // in seconds
+            text_str = "<b>Compton</b><br>" + "<i>Start: </i>" + 
+                    row['compton_start'] + "<br><i>Stop: </i>" + row['compton_stop'] + "<br><br>";
+            row['text_str'] = text_str + "<b>Runs: </b>" + row['run_first'] + " - " + row['run_last'] + "<br><br>";
+            return row;
+        }
+        
+        function processSpread(data, elementId, mean_value){
+            let x = [], y = [], std_y = []
+            for (var i=0; i<data.length; i++){
+                const {center_time, spread_mean, spread_std} = parseRow(data[i]);
+                x.push(center_time);
+                y.push(spread_mean);
+                std_y.push(spread_std);
+            }
+            makeSpreadPlot(elementId, x, y, std_y, mean_value);
+        }
+        
+        function kde(x, y, w) {
+            const ts = (t) => t.getTime()/1000;
+            const toDateTime = (secs) => {
+                let t = new Date(0);
+                t.setSeconds(secs);
+                return t;
+            };
+            const steps = 1000;
+            const dt =  (ts(x[x.length - 1]) - ts(x[0]))/steps;
+            const kernel = (x, x0, w0, y0) => {
+                if (Math.abs(x-x0)>w0){
+                    return 0;
+                }
+                return y0/w0;
+                //return y0*3*(1-((x-x0)/w0/2)**2)/4;
+            }; 
+            const get_est = (timestamp) => {
+                let val = 0
+                for (var i=0; i<x.length; i++){
+                    val += kernel(timestamp, ts(x[i]), w[i], y[i]);
+                }
+                return val;
+            };
+            //console.log(x, y);
+            const timestamp_arr = Plotly.d3.range(steps).map(function(i){return ts(x[0])+i*dt;});
+            
+            let kdex = [];
+            let kdey = [];
+            for (var j=0; j<timestamp_arr.length; j++){
+                kdex.push(toDateTime(timestamp_arr[j]));
+                kdey.push(get_est(timestamp_arr[j]));
+            }
+            //console.log(kdex, kdey);
+            return [kdex, kdey]
+        }
+        
+        function oldAverage(E, L){
+            //Averager by the old method with E and L only
+            if (E.length !== L.length){
+                return null;
+            }
+            let EL = 0;
+            let sL = 0;
+            for (let i = 0; i<E.length; i++){
+                EL += parseFloat(E[i])*parseFloat(L[i]);
+                sL += parseFloat(L[i]);
+            }
+            return EL/sL;
+        }
+        
+        function processData(allRows, mean_energy, mean_spread) {
+            // Processes all data rows
+            var dict = {};
+            dict['x'] = [];
+            dict['e_mean'] = [];
+            dict['e_std'] = [];
+            dict['spread_mean'] = [];
+            dict['spread_std'] = [];
+            dict['compton'] = [];
+            dict['lum'] = [];
+            dict['twidth'] = [];
+            
+            for (var i=0; i<allRows.length; i++){
+                const row = parseRow(allRows[i]);
+                                
+                dict['x'].push( row['center_time'] );
+                dict['e_mean'].push( row['e_mean'] );
+                dict['e_std'].push( row['e_std'] );
+                dict['spread_mean'].push( row['spread_mean'] );
+                dict['spread_std'].push( row['spread_std'] );
+                dict['compton'].push( row['text_str'] );
+                dict['lum'].push( row['luminosity'] );
+                dict['twidth'].push( row['timedelta'] );
+            }
+            
+            const [a, b] = kde(dict['x'], dict['lum'], dict['twidth']);
+            dict['kdex'] = a;
+            dict['kdey'] = b;
+            //console.log(dict['kdex'], dict['kdey']);
+            //oldAverage(y, dict['lum']);
+            
+            dict['mean_energy_total'] = mean_energy;
+            dict['old_mean_energy_total'] = oldAverage(dict['e_mean'], dict['lum']);
+            dict['mean_spread_total'] = mean_spread;
+            
+            makePlotly(dict, "gd");
+        }
+        
+        function makePlotly(dict, elementId){
+            const getYRange = (y, std_y) => {
+                const ys = [...y].sort();
+                const std_ys = [...std_y].sort();
+                let idx = Math.floor(ys.length/2);
+                const y0 = parseFloat(ys[idx]);
+                const std0 = parseFloat(std_ys[idx]);
+                return [y0-6*std0, y0+6*std0];
+            };
+            
+            var trace1 = {
+                x: dict['x'],
+                y: dict['e_mean'],
+                yaxis: 'y3',
+                mode: 'markers',
+                text: dict['compton'],
+                hovertemplate: "%{text}<br><br>" + "<extra></extra>",
+                hovermode: "x",
+                error_y: {
+                    type: 'data',
+                    array: dict['e_std'],
+                    color: '#260101',
+                },
+                showlegend: false,
+                marker: {
+                    color: '#260101',
+                },
+                type: "scatter",
+            };
+            var trace2 = {
+                x: dict['x'],
+                y: dict['spread_mean'],
+                yaxis: 'y2',
+                mode: 'markers',
+                text: dict['compton'],
+                hovertemplate: "%{text}<br><br>" + "<extra></extra>",
+                hovermode: "x",
+                error_y: {
+                    type: 'data',
+                    array: dict['spread_std'],
+                    color: '#260101',
+                },
+                showlegend: false,
+                marker: {
+                    color: '#260101',
+                },
+                type: "scatter",
+            };
+            var trace3 = {
+                x: dict['kdex'],
+                y: dict['kdey'],
+                hovertemplate: "%{y}<br><br>" + "<extra></extra>",
+                hovermode: "x",
+                showlegend: false,
+                marker: {
+                    color: '#F23030',
+                },
+                line: {
+                    shape: 'hvh',
+                },
+                type: "scatter",
+            };
+            var traces = [trace1, trace2, trace3];
+            
+            var updatemenus = [];
+            if (dict['mean_energy_total']){
+                updatemenus = [{
+                buttons: [
+                    {
+                        args:[{'shapes[0].visible': true, 'shapes[1].visible': false, 'title': 'Mean energy: ' + parseFloat(dict['mean_energy_total']).toFixed(3) + ' MeV',}],
+                        label: 'Current average method',
+                        method: 'relayout'
+                    }, {
+                        args:[{'shapes[0].visible': false, 'shapes[1].visible': true, 'title': 'Mean energy: ' + dict['old_mean_energy_total'].toFixed(3)  + ' MeV',}],
+                        label: 'Former average method',
+                        method: 'relayout'
+                    },
+                ],
+                direction: 'center',
+                showactive: 'true',
+                type: 'dropdown',
+                y: 1.1,
+                xanchor: 'left',
+                yanchor: 'top',
+                active: 0,
+            }];
+            }
+            
+            var layout = {
+                title: 'Mean energy: ' + dict['mean_energy_total'] + ' MeV',
+                updatemenus: updatemenus,
+                font: {
+                    size: 18,
+                },
+                xaxis: {
+                    title: "Time, NSK",
+                    automargin: true,
+                },
+                yaxis3: {
+                    domain: [0.6, 1],
+                    title: "Mean energy, MeV",
+                    automargin: true,
+                    //showspikes: true,
+                    //spikemode: "across",
+                    //spikesnap: "data",
+                },
+                yaxis2: {
+                    domain: [0.3, 0.5],
+                    title: "Spread, MeV",
+                    autorange: false,
+                    range: getYRange(dict['spread_mean'], dict['spread_std']),
+                    //showspikes: true,
+                    //spikemode: "across",
+                    //spikesnap: "data",
+                },
+                yaxis: {
+                    domain: [0, 0.2],
+                    automargin: true,
+                    zeroline: true,
+                    rangemode: 'positive',
+                    title: "L, nb<sup>-1</sup>/s",
+                    hoverformat: '.2f',
+                },
+                paper_bgcolor: 'rgba(0,0,0,0)',
+                plot_bgcolor: 'rgba(0,0,0,0)',
+                autosize: true,
+            };
+            
+            if (dict['mean_energy_total']){
+                layout['shapes'] = [{
+                    type: 'line',
+                    yref: 'y3',
+                    xref: 'paper',
+                    x0: 0,
+                    x1: 1,
+                    y0: dict['mean_energy_total'],
+                    y1: dict['mean_energy_total'],
+                    line: {
+                        color: '#590A0A',
+                    },
+                },
+                {
+                    type: 'line',
+                    yref: 'y3',
+                    xref: 'paper',
+                    x0: 0,
+                    x1: 1,
+                    y0: dict['old_mean_energy_total'],
+                    y1: dict['old_mean_energy_total'],
+                    line: {
+                        color: '#590A0A',
+                    },
+                    visible: false,
+                },
+                {
+                    type: 'line',
+                    yref: 'y2',
+                    xref: 'paper',
+                    x0: 0,
+                    x1: 1,
+                    y0: dict['mean_spread_total'],
+                    y1: dict['mean_spread_total'],
+                    line: {
+                        color: '#590A0A',
+                    },
+                    visible: true,
+                }];
+            }
+            
+            Plotly.newPlot('gd', traces, layout, {modeBarButtonsToRemove: ['toImage'], responsive: true,});
+            
+        }
+        
+        makeplot();
+        
+    </script>
+
+</body>
+</html>

+ 102 - 0
page/main.css

@@ -0,0 +1,102 @@
+@import url('https://fonts.googleapis.com/css2?family=Raleway&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Open+Sans&display=swap');
+
+a{
+    font-size: 36px;
+    text-decoration: none;
+    color: #26110C;
+    transition-duration: 1s;
+    font-family: 'Raleway', sans-serif;
+}
+
+a:hover{
+    color: #732727;
+}
+
+a:visited{
+    color: #26110C;
+}
+
+body{
+    background-color: #F2A76687; /*#D9B9A7;*/
+}
+
+br{
+    margin-bottom: 18px;
+}
+
+div.main_block{
+    width: max(50%, 500px);
+    max-width: min(100%, 800px);
+    margin: 0 auto;
+    padding-top: 50px;
+}
+
+h1{
+    font-family: 'Raleway', sans-serif;
+    font-size: 48px;
+}
+
+h2{
+    font-family: 'Raleway', sans-serif;
+    font-size: 40px;
+}
+
+h3{
+    font-family: 'Open Sans', sans-serif;
+    font-size: 32px;  
+}
+
+h4{
+    font-family: 'Open Sans', sans-serif;
+    font-size: 24px;
+}
+
+hr{
+    border: 0;
+    border-bottom: dashed;
+}
+
+img{
+    padding: 15px 0;
+    width: 100%;
+}
+
+p{
+    font-family: 'Raleway', sans-serif;
+    font-size: 28px;
+}
+
+select {
+
+  /* styling */
+  background-color: #F2A76652;
+  border: thin solid #F23030;
+  border-radius: 4px;
+  display: block;
+  font: inherit;
+  font-size: 20px;
+  line-height: 1.5em;
+  padding: 0.5em 3.5em 0.5em 1em;
+
+  /* reset */
+
+  margin: 0 auto;      
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-appearance: none;
+  -moz-appearance: none;
+}
+
+select.select-css {
+    background-image:
+	linear-gradient(45deg, transparent 50%, #260101 0%),
+	linear-gradient(135deg, #260101 50%, transparent 0%);
+  background-position:
+      calc(100% - 21px) calc(1em + 2px),
+      calc(100% - 13px) calc(1em + 2px);
+  background-size:
+      8px 8px, 8px 8px, 2.5em 2.5em;
+  background-repeat: no-repeat;
+}

+ 0 - 0
plots.php → page/plots.php


+ 305 - 0
page/summary.php

@@ -0,0 +1,305 @@
+<?php
+$selected_csv = 
+
+$branch = 'dev';
+$season = 'NNBAR2021';
+$url = "https://cmd.inp.nsk.su/~compton/gitlist/compton_tables/raw/".$branch."/tables/";
+$url_total_info = "https://cmd.inp.nsk.su/~compton/gitlist/compton_tables/raw/".$branch."/tables/".$season.".csv";
+$text = file_get_contents($url);
+$arrays = explode("\n", $text);
+
+$cleanArrs = array_filter($arrays, function($value) {
+   return end(explode('.', $value)) == "csv";
+});
+
+function isSelected($a, $b){
+    if ($a==$b){
+        return "selected";
+    }
+    return "";
+}
+
+$selected_csv = isset($_GET["csv_file"]) ? $_GET["csv_file"] : reset($cleanArrs);
+$selected_season = substr($selected_csv, 0, -4);
+
+?>
+
+<html>
+<head>
+    <script src="plotly-latest.min.js"></script>
+    <script src="fmin.min.js"></script>
+    <!--<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> -->
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet" href="main.css">
+    <title>Compton interactive plots</title>
+    <!-- <script src="chart.js"></script> -->
+</head>
+<body>
+
+<div style="display: flex; align-items: center; flex-wrap: wrap; justify-content: center; margin-top: min(20px,2%); margin-bottom: 40px;">
+    <h2 style="margin: 0;"><a href="index.php" style="font-size: inherit;">Pictures</a> / Season</h2>
+    <form name="form" action="" style="padding: 0 15px; margin: auto 0;" method="get">
+        <select name="csv_file" class="select-css" style="padding: 0.3em 1.3em 0.3em 0.4em;" onchange="this.form.submit()">
+            <?
+            foreach($cleanArrs as $file){
+                ?><option value="<?echo $file?>" <?echo isSelected($file, $selected_csv)?>><?echo substr($file, 0, -4)?></option>
+                <?
+            }
+            ?>
+        </select>
+    </form>
+    <h2 style="margin: 0;">summary</h2>
+</div>
+
+<div style="display: flex; align-items: center; flex-wrap: wrap; justify-content: center;">
+    <div style="max-width: 900px; width: 45%; min-width: min(100%, 600px); margin: 0 max(1%, 10px);">
+        <h3>Mean energy measurements</h3>
+        <div id="mean_energy_plot"></div>
+    </div>
+    <div style="max-width: 900px; width: 45%; min-width: min(100%, 600px); margin: 0 max(1%, 10px);">
+        <h3>Spread measurements</h3>
+        <div id="spread_plot"></div>
+    </div>
+    <div style="max-width: 900px; width: 45%; min-width: min(100%, 600px); margin: 0 max(1%, 10px);">
+        <h3>Mean energy deviations</h3>
+        <div id="energy_dev_plot"></div>
+    </div>
+    <div style="max-width: 900px; width: 45%; min-width: min(100%, 600px); margin: 0 max(1%, 10px);">
+        <h3>Impact of mean energy deviations</h3>
+        <div id="impact_energy_dev_plot"></div>
+    </div>
+</div>
+<div style="margin-top: 40px; width: 100%;">
+    <div style="width: min(100%, 600px); margin: 0 auto;">
+        <h3 style="padding-right: 40px;">Methodology</h3>
+        <p style="font-size: inherit;"><b>Mean energy measurements:</b> a difference between measured mean beam energy and target beam energy vs target beam energy.</p>
+        <p style="font-size: inherit;"><b>Spread measurements:</b> a measured mean beam spread vs measured mean beam energy for the given energy points. A line shows the best fit of these points to a linear function (as a result of chi-square minimization)</p>
+        <p style="font-size: inherit;"><b>Mean energy deviations:</b> a measured mean energy deviations (so-called mean_energy_sys_err) vs measured mean beam energy for the given energy points.</p>
+        <p style="font-size: inherit;"><b>Impact of mean energy deviations:</b> a part of the mean energy deviation in total uncertainty (as d/√(d<sup>2</sup> + s<sup>2</sup>), where d is the mean energy deviation and s is the mean spread) for the given energy points.</p>
+    </div>
+</div>
+
+<script>
+        function makeplot(){
+            Plotly.d3.csv('<?echo $url.$selected_csv;?>', (allRows)=>{
+                const {point_name, energy_point, mean_energy, mean_energy_stat_err, mean_energy_sys_err, mean_spread, mean_spread_stat_err} = parseResultsTable(allRows);
+                makeMeanEnergyPlot(energy_point, mean_energy, mean_energy_stat_err, point_name, "mean_energy_plot");
+                makeSpreadPlot(mean_energy, mean_spread, mean_spread_stat_err, point_name, "spread_plot");
+                makeMeanDevPlot(mean_energy, mean_energy_sys_err, point_name, "energy_dev_plot");
+                makeImpactDevPlot(point_name, mean_spread, mean_energy_sys_err, "impact_energy_dev_plot");
+            }
+            );
+        }
+        
+        function parseResultsTable(allRows){
+            // Extracts a row following the energy point in the total csv file (or null if energy point is not exists)
+            let data = {};
+            for(let key of Object.keys(allRows[0])){
+                data[key] = [];
+            }
+            data['point_name'] = [];
+            
+            let float_keys = ['energy_point', 'mean_energy', 'mean_energy_stat_err', 'mean_spread', 'mean_spread_stat_err', 'mean_energy_sys_err'];
+            
+            for (var i=0; i<allRows.length; i++){
+                for(let key of Object.keys(allRows[i])){
+                    let value = allRows[i][key];
+                    if (float_keys.includes(key)){
+                        value = parseFloat(value);
+                    }
+                    data[key].push(value);
+                }
+                data['point_name'].push(allRows[i]['energy_point']+'_'+allRows[i]['first_run']);
+            }
+            return data;
+        }
+        
+        function linear_coeffs(x, y, yerr, remove_outliers=false){
+            // Returns a chi square minimizer for linear function
+            function chi2min(X){
+                let k = X[0], b = X[1];
+                let chi2 = 0;
+                for(let i=0; i<x.length; i++){
+                    if (remove_outliers&&(y[i]<0.06))
+                        continue;
+                    y0 = k*x[i] + b;
+                    chi2 += Math.pow((y[i] - y0)/yerr[i], 2);
+                }
+                return chi2;
+            }
+            return chi2min;
+        }
+        
+        function getPlotlyDefaults(){
+            var layout = {
+                margin: {
+                    l: 50,
+                    r: 50,
+                    b: 50,
+                    t: 50,
+                    pad: 4
+                },
+                font: {
+                    size: 18,
+                },
+                xaxis: {
+                    title: "Mean beam energy, MeV",
+                    automargin: true,
+                },
+                yaxis: {
+                    //domain: [0, 0.2],
+                    automargin: true,
+                    rangemode: 'positive',
+                    title: "Spread, MeV",
+                    hoverformat: '.2f',
+                },
+                showlegend: true,
+                legend: {
+                    x: 0,
+                    xanchor: 'left',
+                    y: 1,
+                },
+                paper_bgcolor: 'rgba(0,0,0,0)',
+                plot_bgcolor: 'rgba(0,0,0,0)',
+                autosize: true,
+            };
+            var config = {
+                toImageButtonOptions: {
+                    format: 'png', // one of png, svg, jpeg, webp
+                    filename: 'spread_plot_<?echo $selected_season;?>',
+                    scale: 4,
+                  },
+                responsive: true,
+                modeBarButtonsToRemove: ['select2d', 'lasso2d', 'zoomIn2d', 'zoomOut2d', 'resetScale2d', 'hoverClosestGl2d', 'hoverClosestPie', 'toggleHover', 'toggleSpikelines'],
+                displayModeBar: true,
+            };
+            return {
+                "layout" : layout, "config" : config,
+            };
+        }
+        
+        function makeMeanEnergyPlot(energy_point, mean_energy, mean_energy_stat_err, texts, elementId){
+            // Plots mean energy - target energy vs target energy
+            var energy_diff = [];
+            Plotly.d3.zip(mean_energy, energy_point).forEach(e => energy_diff.push(e[0] - e[1]));
+            var trace1 = {
+                x: energy_point,
+                y: energy_diff,
+                yaxis: 'y',
+                mode: 'markers',
+                text: Plotly.d3.zip(texts, mean_energy),
+                text2: mean_energy,
+                hovertemplate: "Point: %{text[0]}<br>Mean energy = %{text[1]:.3f} ± %{error_y.array:.3f} MeV" + "<extra></extra>",
+                //hovermode: "x",
+                error_y: {
+                    type: 'data',
+                    array: mean_energy_stat_err,
+                    color: '#260101',
+                },
+                showlegend: false,
+                marker: {
+                    color: '#260101',
+                },
+                type: "scatter",
+            };
+            var traces = [trace1];
+            
+            var {layout, config} = getPlotlyDefaults();
+            layout.xaxis.title = "Target energy, MeV";
+            layout.yaxis.title = "Mean energy - Target energy, MeV";
+            
+            Plotly.newPlot(elementId, traces, layout, config);
+            
+        }
+        
+        function makeMeanDevPlot(mean_energy, mean_energy_sys_err, texts, elementId){
+            // Plots mean energy deviations vs mean measured energy
+            var trace1 = {
+                x: mean_energy,
+                y: mean_energy_sys_err,
+                text: texts,
+                mode: 'markers',
+                marker: {
+                    size: 12,
+                    symbol: 'diamond',
+                },
+                hovertemplate: "Point: %{text}<br>x = %{x:.2f} MeV<br>y = %{y:.3f} MeV" + "<extra></extra>",
+                showlegend: false,
+            };
+            var traces = [trace1];
+            
+            var {layout, config} = getPlotlyDefaults()
+            
+            layout.yaxis.title = "Mean energy deviation, MeV";
+            Plotly.newPlot(elementId, traces, layout, config);
+            
+        }
+        
+        function makeSpreadPlot(mean_energy, mean_spread, mean_spread_stat_err, texts, elementId){
+            // Plots spreads vs mean measured energy
+            var chi2min = linear_coeffs(mean_energy, mean_spread, mean_spread_stat_err, true);
+            var solution = fmin.nelderMead(chi2min, [0.001, 0]);       
+            
+            var trace1 = {
+                x: mean_energy,
+                y: mean_spread,
+                yaxis: 'y',
+                mode: 'markers',
+                text: texts,
+                hovertemplate: "Point: %{text}<br>x = %{x:.2f} MeV<br>y = %{y:.3f} ± %{error_y.array:.3f} MeV" + "<extra></extra>",
+                hovermode: "x",
+                error_y: {
+                    type: 'data',
+                    array: mean_spread_stat_err,
+                    color: '#260101',
+                },
+                showlegend: false,
+                marker: {
+                    color: '#260101',
+                },
+                type: "scatter",
+            };
+            var trace2 = {
+                x: [Plotly.d3.min(mean_energy), Plotly.d3.max(mean_energy)],
+                y: [solution.x[0]*Plotly.d3.min(mean_energy) + solution.x[1], solution.x[0]*Plotly.d3.max(mean_energy) + solution.x[1]],
+                mode: 'lines',
+                name: 'Fit: y = ' + Math.round(parseFloat(solution.x[0])*1e5)/1e5 + 'x ' + (Math.sign(solution.x[1])>0 ? "+" : "") + Math.round(solution.x[1]*1e3)/1e3,
+                hovermode: false,
+                hoverinfo: 'none',
+            };
+            var traces = [trace1, trace2];
+            
+            var {layout, config} = getPlotlyDefaults()
+            
+            Plotly.newPlot(elementId, traces, layout, config);
+            
+        }
+        
+        function makeImpactDevPlot(point_name, mean_spread, mean_energy_sys_err, elementId){
+            let arr = Plotly.d3.zip(mean_spread, mean_energy_sys_err);
+            let impact_factor = [];
+            arr.forEach(e => impact_factor.push(e[1]/Math.pow(e[0]**2+e[1]**2, 0.5)));
+            var data = [{
+                type: 'bar',
+                x: impact_factor,
+                y: point_name,
+                orientation: 'h',
+                showlegend: false,
+                text: impact_factor.map(e => String(Math.round(e*1e2)) + '%'),
+                textposition: 'auto',
+                hovertemplate: "Point: %{y}<br>Impact of %{text} <extra></extra>",
+                //hoverinfo: 'none',
+            }];
+        
+            var {layout, config} = getPlotlyDefaults();
+            layout.xaxis.range = [0, 1];
+            layout.yaxis.title = "";
+            layout.xaxis.title = "Impact of mean energy deviations";
+            Plotly.newPlot(elementId, data, layout, config);
+        }
+        
+        makeplot();
+</script>
+
+</body>
+</html>