diff --git a/frontend/dist/themes/default/assets/fonts/outline-icons.woff b/frontend/dist/themes/default/assets/fonts/outline-icons.woff new file mode 100644 index 000000000..ad077c6be Binary files /dev/null and b/frontend/dist/themes/default/assets/fonts/outline-icons.woff differ diff --git a/frontend/dist/themes/default/assets/fonts/outline-icons.woff2 b/frontend/dist/themes/default/assets/fonts/outline-icons.woff2 new file mode 100644 index 000000000..56328948b Binary files /dev/null and b/frontend/dist/themes/default/assets/fonts/outline-icons.woff2 differ diff --git a/frontend/js/helpers/config.js.example b/frontend/js/helpers/config.js.example index f2bb72150..6f9361ac2 100644 --- a/frontend/js/helpers/config.js.example +++ b/frontend/js/helpers/config.js.example @@ -20,66 +20,80 @@ const TOTAL_CHART_BOTTOM_TITLE = 'Total energy consumption'; const TOTAL_CHART_BOTTOM_LABEL = 'Machine Energy'; // function must return boolean const total_chart_bottom_condition = (metric) => { - if(metric.match(/^.*_energy.*_machine$/) !== null) return true; - return false; + return metric.match(/^.*_energy.*_machine$/) !== null; } // title and filter function for the top left most chart in the Detailed Metrics / Compare view const TOP_BAR_CHART_TITLE = 'Energy metrics' const top_bar_chart_condition = (metric) => { - if(metric.indexOf('_energy_') !== -1) return true; - return false; + return metric.indexOf('_energy_') !== -1; } // title and filter function for the top right radar chart in the Detailed Metrics / Compare view const RADAR_CHART_TITLE = 'General component distribution' const radar_chart_condition = (metric) => { - if(metric.indexOf('cpu_frequency_sysfs_core') == -1) return true; - return false; + return metric.indexOf('cpu_frequency_sysfs_core') == -1; } // filter function for the CO₂ calculations in the Detailed Metrics // please note that this metric must be unique per phase const phase_time_metric_condition = (metric) => { - if(metric == 'phase_time_syscall_system') return true; - return false; + return metric == 'phase_time_syscall_system'; } const psu_machine_carbon_metric_condition = (metric) => { - if(metric.match(/^psu_carbon_.*_machine$/) !== null) return true; - return false; + return metric.match(/^psu_carbon_.*_machine$/) !== null; } const network_carbon_metric_condition = (metric) => { - if(metric == 'network_carbon_formula_global') return true; - return false; + return metric == 'network_carbon_formula_global'; } const network_energy_metric_condition = (metric) => { - if(metric == 'network_energy_formula_global') return true; - return false; + return metric == 'network_energy_formula_global'; +} + +const network_io_metric_condition = (metric) => { + return metric == 'network_io_cgroup_container'; } const machine_power_metric_condition = (metric) => { - if(metric.match(/^.*_power_.*_machine$/) !== null) return true; - return false; + return metric.match(/^.*_power_.*_machine$/) !== null; } const machine_energy_metric_condition = (metric) => { - if(metric.match(/^.*_energy_.*_machine$/) !== null) return true; - return false; + return metric.match(/^.*_energy_.*_machine$/) !== null; } const sci_metric_condition = (metric) => { - if(metric == 'software_carbon_intensity_global') return true; - return false; + return metric == 'software_carbon_intensity_global'; } const embodied_carbon_share_metric_condition = (metric) => { - if(metric == 'embodied_carbon_share_machine') return true; - return false; + return metric == 'embodied_carbon_share_machine' } +/* + Here you can define the ordering of the information cards shown in the detail view + You can also remove some if you want or change the icon. The name will be changed when the values are written + detail name defined in METRIC_MAPPINGS +*/ +const HARDWARECARDS = [ + { key: 'cpu', name: 'CPU', icon: 'microchip', variable:true }, + { key: 'gpu', name: 'GPU', icon: 'camera retro', variable:true }, + { key: 'dram', name: 'DRAM', icon: 'memory', variable:true }, + { key: 'disk', name: 'Disk', icon: 'hdd', variable:true }, + { key: 'machine', name: 'Machine', icon: 'server', variable:true }, +]; + +const EXTRACARDS = [ + { key: 'runtime', name: 'Phase Duration', icon: 'hourglass half', variable:false }, + { key: 'network-data', name: 'Data Transferred', icon: 'exchange', variable:false }, + { key: 'network', name: 'Network', icon: 'sitemap', variable:true }, + { key: 'embodied-carbon', name: 'Embodied Carbon', icon: 'industry', variable:false }, + { key: 'sci', name: 'SCI', icon: 'leaf', variable:false }, +]; + /* Here you can statically define the badges that shall be shown in the timeline view although this could also be done dynamically it would be a slightly more heavy lifting for the database and only reflect the latest run. @@ -246,6 +260,11 @@ METRIC_MAPPINGS = { "source": "Formula", "explanation": "Estimated external energy cost for network infrastructure." }, + "network_power_formula_global": { + "clean_name": "Network Transmission", + "source": "Formula", + "explanation": "Estimated external energy cost for network infrastructure." + }, "network_carbon_formula_global": { "clean_name": "Network Transmission CO₂", "source": "Formula", diff --git a/frontend/js/helpers/metric-boxes.js b/frontend/js/helpers/metric-boxes.js index 8cc9dedaf..54719d9a5 100644 --- a/frontend/js/helpers/metric-boxes.js +++ b/frontend/js/helpers/metric-boxes.js @@ -1,180 +1,72 @@ -/* - WebComponent function without ShadowDOM - to expand the template in the HTML pages -*/ class PhaseMetrics extends HTMLElement { connectedCallback() { - this.innerHTML = ` - -
-
-
-
Phase Duration
-
-
-
- N/A -
-
-
- -
-
- -
-
-
-
-
-
-
Machine Power
-
-
-
- N/A -
-
-
- - -
-
- -
-
-
-
-
-
-
Machine Energy
-
-
-
- N/A -
-
-
- - -
-
- -
-
-
-
-
-
-
Network Transmission Energy
-
-
-
- N/A -
-
-
- via Formula - -
-
- -
-
-
-
-
-
-
Machine CO2 (usage)
-
-
-
- N/A -
-
-
- via Formula - -
-
- -
+ + const createCard = ({ key, name, icon, variable }, suffix = '', colour) => { + const cardClass = variable ? `${key}-${suffix}` : key; + const colourClass = variable ? colour : 'green'; + return ` +
+
+ ${name} +
-
-
-
-
-
Network Transmission CO2
-
-
-
- N/A +
+
+ N/A +
+
-
- via Formula - -
-
- -
+
`; + }; + + const buildTab = (tab, active = false, colour='green') => ` +
+
+ ${HARDWARECARDS.map(card => createCard(card, tab, colour)).join('')}
-
-
-
-
Machine CO2 (manufacturing)
-
-
-
- N/A -
-
-
- via Formula - -
-
- -
-
+

Impact

+
+ ${EXTRACARDS.map(card => createCard(card, tab, colour)).join('')}
-
-
-
-
SCI
-
-
-
- N/A -
-
-
- see Details - -
-
- -
+ +
`; + + + this.innerHTML = ` +
+
+ +

Hardware

+ ${buildTab('power', true, 'orange')} + ${buildTab('energy', false, 'blue')} + ${buildTab('co2', false, 'black')}
-
-
-
- -
-

Detailed metrics

- - - -
- -

Detailed Charts

-
+
+
+ +
+

Detailed metrics

+ + + +
+ +

Detailed Charts

+
+
-
`; + `; + + $(this).find('.menu .item').tab(); } } @@ -361,40 +253,74 @@ const calculateCO2 = (phase, total_CO2_in_ug) => { } } -const updateKeyMetric = (phase, metric_name, clean_name, detail_name, value, std_dev_text, unit, raw_value, raw_unit, explanation, source) => { +const updateKeyMetric = ( + phase, metric_name, clean_name, detail_name, + value, std_dev_text, unit, raw_value, raw_unit, + explanation, source +) => { let selector = null; - // key metrics are already there, cause we want a fixed order, so we just replace - if(machine_energy_metric_condition(metric_name)) { - selector = '.machine-energy'; - } else if(network_energy_metric_condition(metric_name)) { - selector = '.network-energy'; - } else if(phase_time_metric_condition(metric_name)) { - selector = '.phase-duration'; - } else if(network_carbon_metric_condition(metric_name)) { - selector = '.network-co2'; - } else if(embodied_carbon_share_metric_condition(metric_name)) { + + + + if (phase_time_metric_condition(metric_name)) { + selector = '.runtime'; + } else if (network_io_metric_condition(metric_name)) { + selector = '.network-data'; + } else if (embodied_carbon_share_metric_condition(metric_name)) { selector = '.embodied-carbon'; - } else if(sci_metric_condition(metric_name)) { - selector = '.software-carbon-intensity'; - } else if(machine_power_metric_condition(metric_name)) { - selector = '.machine-power'; - } else if(psu_machine_carbon_metric_condition(metric_name)) { + }else if (psu_machine_carbon_metric_condition(metric_name)) { selector = '.machine-co2'; + } else if (sci_metric_condition(metric_name)) { + selector = '.sci'; } else { - return; // could not match key metric + const isPower = metric_name.includes('_power_'); + const isEnergy = metric_name.includes('_energy_'); + const isCO2 = metric_name.includes('_carbon_'); + + let component = null; + if (metric_name.includes('cpu')) component = 'cpu'; + else if (metric_name.includes('memory') || metric_name.includes('dram')) component = 'dram'; + else if (metric_name.includes('gpu')) component = 'gpu'; + else if (metric_name.includes('disk')) component = 'disk'; + else if (metric_name.includes('psu') && metric_name.includes('machine')) component = 'machine'; + else if (metric_name.includes('network')) component = 'network'; + + if (component !== null) { + if (isPower) selector = `.${component}-power`; + else if (isEnergy) selector = `.${component}-energy`; + else if (isCO2) selector = `.${component}-co2`; + } } + if (selector === null) { + return; + } - document.querySelector(`div.tab[data-tab='${phase}'] ${selector} .value span`).innerText = `${value} ${std_dev_text}` + const cards = document.querySelectorAll(`div.tab[data-tab='${phase}'] ${selector}`); - document.querySelector(`div.tab[data-tab='${phase}'] ${selector} .value`).setAttribute('title', `${raw_value} [${raw_unit}]`) + if (cards.length === 0) { + console.warn(`No card found for selector "${selector}" in phase "${phase}"`); + return; + } - document.querySelector(`div.tab[data-tab='${phase}'] ${selector} .si-unit`).innerText = `[${unit}]` - if(std_dev_text != '') document.querySelector(`div.tab[data-tab='${phase}'] ${selector} .metric-type`).innerText = `(AVG + STD.DEV)`; - else if(String(value).indexOf('%') !== -1) document.querySelector(`div.tab[data-tab='${phase}'] ${selector} .metric-type`).innerText = `(Diff. in %)`; + cards.forEach(card => { + const valueNode = card.querySelector('.value'); + valueNode.innerText = `${value} ${std_dev_text}`; + valueNode.setAttribute('title', `${raw_value} [${raw_unit}]`); - node = document.querySelector(`div.tab[data-tab='${phase}'] ${selector} .source`) - if (node !== null) node.innerText = source // not every key metric shall have a custom detail_name + const unitNode = card.querySelector('.si-unit'); + if (unitNode) unitNode.innerText = unit; -} + const helpNode = card.querySelector('.help'); + if (helpNode) helpNode.setAttribute('data-tooltip', explanation || 'No data available'); + + const metricNameNode = card.querySelector('.metric-name'); + if (metricNameNode) metricNameNode.innerText = clean_name || ''; + + const sourceNode = card.querySelector('.source'); + if (sourceNode) sourceNode.innerText = source || ''; + }); + + +}; diff --git a/lib/phase_stats.py b/lib/phase_stats.py index 88b7e97a6..5acea5c00 100644 --- a/lib/phase_stats.py +++ b/lib/phase_stats.py @@ -266,6 +266,11 @@ def build_and_store_phase_stats(run_id, sci=None): network_io_in_kWh = Decimal(sum(network_bytes_total)) / 1_000_000_000 * Decimal(sci['N']) network_io_in_uJ = network_io_in_kWh * 3_600_000_000_000 csv_buffer.write(generate_csv_line(run_id, 'network_energy_formula_global', '[FORMULA]', f"{idx:03}_{phase['name']}", network_io_in_uJ, 'TOTAL', None, None, None, None, None, 'uJ')) + + #power calculations + network_io_power_in_mW = (network_io_in_kWh * Decimal('3600000') / Decimal(duration_in_s) * Decimal('1000')) + csv_buffer.write(generate_csv_line(run_id, 'network_power_formula_global', '[FORMULA]', f"{idx:03}_{phase['name']}", network_io_power_in_mW, 'TOTAL', None, None, None, None, None, 'mW')) + # co2 calculations network_io_carbon_in_ug = network_io_in_kWh * Decimal(sci['I']) * 1_000_000 if '[' not in phase['name']: # only for runtime sub phases diff --git a/tools/import_data.py b/tools/import_data.py index eb8bcadfc..3b8716187 100644 --- a/tools/import_data.py +++ b/tools/import_data.py @@ -18,3 +18,4 @@ data = fp.read() DB().query(data) + DB().shutdown()