From b52330ebf038ca7b1a910160b05b8d77e9c5af34 Mon Sep 17 00:00:00 2001
From: Christoph Kluge <christoph.kluge@fau.de>
Date: Thu, 31 Aug 2023 14:48:19 +0200
Subject: [PATCH 1/4] simplify bins return

---
 web/frontend/src/utils.js | 27 ++++++---------------------
 1 file changed, 6 insertions(+), 21 deletions(-)

diff --git a/web/frontend/src/utils.js b/web/frontend/src/utils.js
index 5e9cdaef..42942fd0 100644
--- a/web/frontend/src/utils.js
+++ b/web/frontend/src/utils.js
@@ -350,31 +350,16 @@ export function binsFromFootprint(weights, scope, values, numBins) {
             scopeWeights = weights.nodeHours
     }
 
-    const bins = new Array(numBins).fill(0)
+    const rawBins = new Array(numBins).fill(0)
     for (let i = 0; i < values.length; i++)
         bins[Math.floor(((values[i] - min) / (max - min)) * numBins)] += scopeWeights ? scopeWeights[i] : 1
 
-    // Manual Canvas Original
-    // return {
-    //     label: idx => {
-    //         let start = min + (idx / numBins) * (max - min)
-    //         let stop = min + ((idx + 1) / numBins) * (max - min)
-    //         return `${formatNumber(start)} - ${formatNumber(stop)}`
-    //     },
-    //     bins: bins.map((count, idx) => ({ value: idx, count: count })),
-    //     min: min,
-    //     max: max
-    // }
+    const bins = rawBins.map((count, idx) => ({ 
+        value: Math.floor(min + ((idx + 1) / numBins) * (max - min)),
+        count: count 
+    }))
 
     return {
-        bins: bins.map((count, idx) => ({ 
-            value: idx => { // Use bins' max value instead of mean
-                // let start = min + (idx / numBins) * (max - min)
-                let stop = min + ((idx + 1) / numBins) * (max - min)
-                // return `${formatNumber(Math.floor((start+stop)/2))}`
-                return Math.floor(stop)
-            }, 
-            count: count 
-        }))
+        bins: bins
     }
 }

From 64796519c6eeae8c5aa982857ad2b954ba3b2fa8 Mon Sep 17 00:00:00 2001
From: Christoph Kluge <christoph.kluge@fau.de>
Date: Thu, 31 Aug 2023 15:10:57 +0200
Subject: [PATCH 2/4] change: use continue for rooflineHeatmap errors

- hard errors blocked rendering in frontend
---
 internal/graph/util.go | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/internal/graph/util.go b/internal/graph/util.go
index b61bcc73..3a2c3b16 100644
--- a/internal/graph/util.go
+++ b/internal/graph/util.go
@@ -6,7 +6,6 @@ package graph
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"math"
 
@@ -33,7 +32,7 @@ func (r *queryResolver) rooflineHeatmap(
 		return nil, err
 	}
 	if len(jobs) > MAX_JOBS_FOR_ANALYSIS {
-		return nil, fmt.Errorf("GRAPH/STATS > too many jobs matched (max: %d)", MAX_JOBS_FOR_ANALYSIS)
+		return nil, fmt.Errorf("GRAPH/UTIL > too many jobs matched (max: %d)", MAX_JOBS_FOR_ANALYSIS)
 	}
 
 	fcols, frows := float64(cols), float64(rows)
@@ -50,20 +49,24 @@ func (r *queryResolver) rooflineHeatmap(
 
 		jobdata, err := metricdata.LoadData(job, []string{"flops_any", "mem_bw"}, []schema.MetricScope{schema.MetricScopeNode}, ctx)
 		if err != nil {
-			log.Error("Error while loading metrics for roofline")
+			log.Errorf("Error while loading roofline metrics for job %d", job.ID)
 			return nil, err
 		}
 
 		flops_, membw_ := jobdata["flops_any"], jobdata["mem_bw"]
 		if flops_ == nil && membw_ == nil {
-			return nil, fmt.Errorf("GRAPH/STATS > 'flops_any' or 'mem_bw' missing for job %d", job.ID)
+			log.Infof("rooflineHeatmap(): 'flops_any' or 'mem_bw' missing for job %d", job.ID)
+			continue
+			// return nil, fmt.Errorf("GRAPH/UTIL > 'flops_any' or 'mem_bw' missing for job %d", job.ID)
 		}
 
 		flops, ok1 := flops_["node"]
 		membw, ok2 := membw_["node"]
 		if !ok1 || !ok2 {
+			log.Info("rooflineHeatmap() query not implemented for where flops_any or mem_bw not available at 'node' level")
+			continue
 			// TODO/FIXME:
-			return nil, errors.New("GRAPH/STATS > todo: rooflineHeatmap() query not implemented for where flops_any or mem_bw not available at 'node' level")
+			// return nil, errors.New("GRAPH/UTIL > todo: rooflineHeatmap() query not implemented for where flops_any or mem_bw not available at 'node' level")
 		}
 
 		for n := 0; n < len(flops.Series); n++ {
@@ -99,7 +102,7 @@ func (r *queryResolver) jobsFootprints(ctx context.Context, filter []*model.JobF
 		return nil, err
 	}
 	if len(jobs) > MAX_JOBS_FOR_ANALYSIS {
-		return nil, fmt.Errorf("GRAPH/STATS > too many jobs matched (max: %d)", MAX_JOBS_FOR_ANALYSIS)
+		return nil, fmt.Errorf("GRAPH/UTIL > too many jobs matched (max: %d)", MAX_JOBS_FOR_ANALYSIS)
 	}
 
 	avgs := make([][]schema.Float, len(metrics))

From cffdd055c9c2b2c28871ccc6078c38c6af82d977 Mon Sep 17 00:00:00 2001
From: Christoph Kluge <christoph.kluge@fau.de>
Date: Thu, 31 Aug 2023 15:17:40 +0200
Subject: [PATCH 3/4] change: use continue for ccms.loadStats errors

---
 internal/metricdata/cc-metric-store.go | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/internal/metricdata/cc-metric-store.go b/internal/metricdata/cc-metric-store.go
index cfaa6fd1..4874975c 100644
--- a/internal/metricdata/cc-metric-store.go
+++ b/internal/metricdata/cc-metric-store.go
@@ -533,7 +533,9 @@ func (ccms *CCMetricStore) LoadStats(
 		metric := ccms.toLocalName(query.Metric)
 		data := res[0]
 		if data.Error != nil {
-			return nil, fmt.Errorf("METRICDATA/CCMS > fetching %s for node %s failed: %s", metric, query.Hostname, *data.Error)
+			log.Infof("fetching %s for node %s failed: %s", metric, query.Hostname, *data.Error)
+			continue
+			// return nil, fmt.Errorf("METRICDATA/CCMS > fetching %s for node %s failed: %s", metric, query.Hostname, *data.Error)
 		}
 
 		metricdata, ok := stats[metric]
@@ -543,7 +545,9 @@ func (ccms *CCMetricStore) LoadStats(
 		}
 
 		if data.Avg.IsNaN() || data.Min.IsNaN() || data.Max.IsNaN() {
-			return nil, fmt.Errorf("METRICDATA/CCMS > fetching %s for node %s failed: %s", metric, query.Hostname, "avg/min/max is NaN")
+			log.Infof("fetching %s for node %s failed: one of avg/min/max is NaN", metric, query.Hostname)
+			continue
+			// return nil, fmt.Errorf("METRICDATA/CCMS > fetching %s for node %s failed: %s", metric, query.Hostname, "avg/min/max is NaN")
 		}
 
 		metricdata[query.Hostname] = schema.MetricStatistics{

From a2a4b2e6c2a0631d95adb0270e63dbde1a1002f7 Mon Sep 17 00:00:00 2001
From: Christoph Kluge <christoph.kluge@fau.de>
Date: Thu, 31 Aug 2023 15:19:54 +0200
Subject: [PATCH 4/4] fix variable

---
 web/frontend/src/utils.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web/frontend/src/utils.js b/web/frontend/src/utils.js
index 42942fd0..06509167 100644
--- a/web/frontend/src/utils.js
+++ b/web/frontend/src/utils.js
@@ -352,7 +352,7 @@ export function binsFromFootprint(weights, scope, values, numBins) {
 
     const rawBins = new Array(numBins).fill(0)
     for (let i = 0; i < values.length; i++)
-        bins[Math.floor(((values[i] - min) / (max - min)) * numBins)] += scopeWeights ? scopeWeights[i] : 1
+        rawBins[Math.floor(((values[i] - min) / (max - min)) * numBins)] += scopeWeights ? scopeWeights[i] : 1
 
     const bins = rawBins.map((count, idx) => ({ 
         value: Math.floor(min + ((idx + 1) / numBins) * (max - min)),