From 5cf6dd171542d4391caaedf9583b1823162be5ab Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 10 Dec 2024 23:53:05 +0100 Subject: [PATCH 1/2] fixing double counting usage for multiple units --- boaviztapi/service/impacts_computation.py | 102 +++++++++---------- tests/api/test_component.py | 114 +++++++++++----------- tests/unit/test_bottom_up.py | 26 ++--- tests/unit/test_verbose.py | 56 +++++------ 4 files changed, 150 insertions(+), 148 deletions(-) diff --git a/boaviztapi/service/impacts_computation.py b/boaviztapi/service/impacts_computation.py index fd04031e..02f515ba 100644 --- a/boaviztapi/service/impacts_computation.py +++ b/boaviztapi/service/impacts_computation.py @@ -6,7 +6,7 @@ from boaviztapi.model.device.server import DeviceServer from boaviztapi.model.services.cloud_instance import Service, ServiceCloudInstance from boaviztapi.model.component import ComponentCPU, ComponentCase, ComponentPowerSupply, ComponentRAM, Component, \ - ComponentAssembly, ComponentHDD, ComponentSSD + ComponentAssembly, ComponentHDD, ComponentSSD, ComponentMotherboard from boaviztapi.model.component.functional_block import ComponentFunctionalBlock from boaviztapi.model.device import Device from boaviztapi.model.device.iot import DeviceIoT @@ -25,9 +25,9 @@ def compute_single_impact(model: Union[Component, Device, Service], impact, min_impact, max_impact, warnings = impact_function(criteria, duration, model) result = Impact( - value=impact * model.units.value * allocation, - min=min_impact * model.units.min * allocation, - max=max_impact * model.units.max * allocation, + value=impact * allocation, + min=min_impact * allocation, + max=max_impact * allocation, warnings=list(set(warnings)) ) @@ -66,23 +66,23 @@ def simple_impact_use(impact_type: str, duration: int, model: Union[Component, D impact_factor = model.usage.elec_factors[impact_type] - impacts = impact_factor.value * (model.usage.avg_power.value / 1000) * model.usage.use_time_ratio.value * duration - max_impact = impact_factor.max * (model.usage.avg_power.max / 1000) * model.usage.use_time_ratio.min * duration - min_impact = impact_factor.min * (model.usage.avg_power.min / 1000) * model.usage.use_time_ratio.max * duration + impacts = impact_factor.value * (model.usage.avg_power.value / 1000) * model.usage.use_time_ratio.value * duration * model.units.value + max_impact = impact_factor.max * (model.usage.avg_power.max / 1000) * model.usage.use_time_ratio.min * duration * model.units.max + min_impact = impact_factor.min * (model.usage.avg_power.min / 1000) * model.usage.use_time_ratio.max * duration * model.units.min return impacts, min_impact, max_impact, [] def simple_embedded(impact_type: str, duration: int, model: [Device, Component, Service]) -> ComputedImpacts: if hasattr(model, 'type') and model.type is not None: - impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)[model.type.value]["impact"]) - min_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)[model.type.value]["impact"]) - max_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)[model.type.value]["impact"]) + impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)[model.type.value]["impact"]) * model.units.value + min_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)[model.type.value]["impact"]) * model.units.min + max_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)[model.type.value]["impact"]) * model.units.max else: - impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]) - min_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]) - max_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]) + impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]) * model.units.value + min_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]) * model.units.min + max_impact = float(get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]) * model.units.max warnings = ["Generic data used for impact calculation."] @@ -125,9 +125,9 @@ def cpu_impact_embedded(impact_type: str, duration: int, cpu: ComponentCPU) -> C ) impact = Impact( - value=cpu.die_size.value * cpu_die_impact.value + cpu_impact.value, - min=cpu.die_size.min * cpu_die_impact.min + cpu_impact.min, - max=cpu.die_size.max * cpu_die_impact.max + cpu_impact.max) + value=(cpu.die_size.value * cpu_die_impact.value + cpu_impact.value) * cpu.units.value, + min=(cpu.die_size.min * cpu_die_impact.min + cpu_impact.min) * cpu.units.min, + max=(cpu.die_size.max * cpu_die_impact.max + cpu_impact.max ) * cpu.units.max) impact.allocate(duration, cpu.usage.hours_life_time) @@ -136,9 +136,9 @@ def cpu_impact_embedded(impact_type: str, duration: int, cpu: ComponentCPU) -> C def assembly_impact_embedded(impact_type: str, duration: int, model: ComponentAssembly) -> ComputedImpacts: impact = Impact( - value=get_impact_factor(item='assembly', impact_type=impact_type)['impact'], - min=get_impact_factor(item='assembly', impact_type=impact_type)['impact'], - max=get_impact_factor(item='assembly', impact_type=impact_type)['impact'] + value=get_impact_factor(item='assembly', impact_type=impact_type)['impact'] * model.units.value, + min=get_impact_factor(item='assembly', impact_type=impact_type)['impact'] * model.units.min, + max=get_impact_factor(item='assembly', impact_type=impact_type)['impact'] * model.units.max ) impact.allocate(duration, model.usage.hours_life_time) @@ -155,9 +155,9 @@ def casing_impact_embedded(impact_type: str, duration: int, case: ComponentCase) computed_impact = impact_manufacture_rack(impact_type, case) impact = Impact( - value=computed_impact[0], - min=computed_impact[1], - max=computed_impact[2] + value=computed_impact[0] * case.units.value, + min=computed_impact[1] * case.units.min, + max=computed_impact[2] * case.units.max ) impact.allocate(duration, case.usage.hours_life_time) @@ -180,7 +180,7 @@ def impact_manufacture_rack(impact_type: str, case: ComponentCase) -> ComputedIm else: return impact_factor.value, blade_impact[1], impact_factor.max, [ "End of life is not included in the calculation"] - return impact_factor.value, impact_factor.min, impact_factor.max, ["End of life is not included in the calculation"] + return impact_factor.value * case.units.value, impact_factor.min * case.units.min, impact_factor.max * case.units.max, ["End of life is not included in the calculation"] def impact_manufacture_blade(impact_type: str, case: ComponentCase) -> ComputedImpacts: @@ -195,7 +195,7 @@ def impact_manufacture_blade(impact_type: str, case: ComponentCase) -> ComputedI else: return impact.value, rack_impact[1], impact.max, ["End of life is not included in the calculation"] - return impact.value, impact.min, impact.max, ["End of life is not included in the calculation"] + return impact.value * case.units.value, impact.min * case.units.min, impact.max * case.units.max, ["End of life is not included in the calculation"] def get_impact_constants_blade(impact_type: str) -> Tuple[Impact, Impact]: @@ -225,9 +225,9 @@ def compute_impact_manufacture_blade(impact_blade_server: Impact, def iot_functional_blocks_impact_embedded(impact_type: str, duration: int, function_blocks: ComponentFunctionalBlock) -> ComputedImpacts: impact = Impact( - value=get_iot_impact_factor(function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type), - min=get_iot_impact_factor(function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type), - max=get_iot_impact_factor(function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type) + value=get_iot_impact_factor(function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type) * function_blocks.units.value, + min=get_iot_impact_factor(function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type) * function_blocks.units.min, + max=get_iot_impact_factor(function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type) * function_blocks.units.max ) impact.allocate(duration, function_blocks.usage.hours_life_time) @@ -237,9 +237,9 @@ def iot_functional_blocks_impact_embedded(impact_type: str, duration: int, def hdd_impact_embedded(impact_type: str, duration: int, hdd: ComponentHDD) -> ComputedImpacts: impact = Impact( - value=get_impact_factor(item='hdd', impact_type=impact_type)['impact'], - min=get_impact_factor(item='hdd', impact_type=impact_type)['impact'], - max=get_impact_factor(item='hdd', impact_type=impact_type)['impact'] + value=get_impact_factor(item='hdd', impact_type=impact_type)['impact'] * hdd.units.value, + min=get_impact_factor(item='hdd', impact_type=impact_type)['impact'] * hdd.units.min, + max=get_impact_factor(item='hdd', impact_type=impact_type)['impact'] * hdd.units.max ) impact.allocate(duration, hdd.usage.hours_life_time) @@ -247,11 +247,11 @@ def hdd_impact_embedded(impact_type: str, duration: int, hdd: ComponentHDD) -> C return impact.value, impact.min, impact.max, ["End of life is not included in the calculation"] -def motherboard_impact_embedded(impact_type: str, duration: int, motherboard: Motherboard) -> ComputedImpacts: +def motherboard_impact_embedded(impact_type: str, duration: int, motherboard: ComponentMotherboard) -> ComputedImpacts: impact = Impact( - value=get_impact_factor(item='motherboard', impact_type=impact_type)['impact'], - min=get_impact_factor(item='motherboard', impact_type=impact_type)['impact'], - max=get_impact_factor(item='motherboard', impact_type=impact_type)['impact'] + value=get_impact_factor(item='motherboard', impact_type=impact_type)['impact'] * motherboard.units.value, + min=get_impact_factor(item='motherboard', impact_type=impact_type)['impact'] * motherboard.units.min, + max=get_impact_factor(item='motherboard', impact_type=impact_type)['impact'] * motherboard.units.max ) impact.allocate(duration, motherboard.usage.hours_life_time) @@ -278,9 +278,9 @@ def server_power_supply_impact_embedded(impact_type: str, duration: int, ) impact = Impact( - value=power_supply.unit_weight.value * impact_factor.value, - min=power_supply.unit_weight.min * impact_factor.min, - max=power_supply.unit_weight.max * impact_factor.max + value=power_supply.unit_weight.value * impact_factor.value * power_supply.units.value, + min=power_supply.unit_weight.min * impact_factor.min * power_supply.units.min, + max=power_supply.unit_weight.max * impact_factor.max * power_supply.units.max ) impact.allocate(duration, power_supply.usage.hours_life_time) @@ -302,9 +302,9 @@ def ram_impact_embedded(impact_type: str, duration: int, ram: ComponentRAM) -> C ) impact = Impact( - value=(ram.capacity.value / ram.density.value) * ram_die_impact.value + ram_impact.value, - min=(ram.capacity.min / ram.density.max) * ram_die_impact.min + ram_impact.min, - max=(ram.capacity.max / ram.density.min) * ram_die_impact.max + ram_impact.max + value=((ram.capacity.value / ram.density.value) * ram_die_impact.value + ram_impact.value) * ram.units.value, + min=((ram.capacity.min / ram.density.max) * ram_die_impact.min + ram_impact.min) * ram.units.min, + max=((ram.capacity.max / ram.density.min) * ram_die_impact.max + ram_impact.max) * ram.units.max ) impact.allocate(duration, ram.usage.hours_life_time) @@ -348,9 +348,9 @@ def ssd_impact_embedded(impact_type: str, duration: int, ssd: ComponentSSD) -> C ) impact = Impact( - value=(ssd.capacity.value / ssd.density.value) * ssd_die_impact.value + ssd_impact.value, - min=(ssd.capacity.min / ssd.density.max) * ssd_die_impact.min + ssd_impact.min, - max=(ssd.capacity.max / ssd.density.min) * ssd_die_impact.max + ssd_impact.max + value=((ssd.capacity.value / ssd.density.value) * ssd_die_impact.value + ssd_impact.value) * ssd.units.value, + min=((ssd.capacity.min / ssd.density.max) * ssd_die_impact.min + ssd_impact.min) * ssd.units.min, + max=((ssd.capacity.max / ssd.density.min) * ssd_die_impact.max + ssd_impact.max) * ssd.units.max ) impact.allocate(duration, ssd.usage.hours_life_time) @@ -371,7 +371,7 @@ def iot_impact_embedded(impact_type: str, duration: int, iot_device: DeviceIoT) max_impacts.append(single_impact.max) warnings = warnings + single_impact.warnings - return sum(impacts), sum(min_impacts), sum(max_impacts), warnings + return sum(impacts) * iot_device.units.value, sum(min_impacts) * iot_device.units.min, sum(max_impacts) * iot_device.units.max, warnings def iot_impact_use(impact_type: str, duration: int, iot_device: DeviceIoT) -> ComputedImpacts: @@ -380,11 +380,11 @@ def iot_impact_use(impact_type: str, duration: int, iot_device: DeviceIoT) -> Co impact_factor = iot_device.usage.elec_factors[impact_type] impact = impact_factor.value * ( - iot_device.usage.avg_power.value / 1000) * iot_device.usage.use_time_ratio.value * duration + iot_device.usage.avg_power.value / 1000) * iot_device.usage.use_time_ratio.value * duration * iot_device.units.value min_impact = impact_factor.min * ( - iot_device.usage.avg_power.min / 1000) * iot_device.usage.use_time_ratio.min * duration + iot_device.usage.avg_power.min / 1000) * iot_device.usage.use_time_ratio.min * duration * iot_device.units.min max_impact = impact_factor.max * ( - iot_device.usage.avg_power.max / 1000) * iot_device.usage.use_time_ratio.max * duration + iot_device.usage.avg_power.max / 1000) * iot_device.usage.use_time_ratio.max * duration * iot_device.units.max return impact, min_impact, max_impact, [] @@ -419,7 +419,7 @@ def server_impact_embedded(impact_type: str, duration: int, server: DeviceServer impact.allocate(duration, server.usage.hours_life_time) - return impact.value, impact.min, impact.max, warnings + return impact.value * server.units.value, impact.min * server.units.min, impact.max * server.units.max, warnings def server_impact_use(impact_type: str, duration: int, server: DeviceServer) -> ComputedImpacts: @@ -438,9 +438,9 @@ def server_impact_use(impact_type: str, duration: int, server: DeviceServer) -> for ram in server.ram: compute_single_impact(ram, USE, impact_type, duration) - impact = impact_factor.value * (server.usage.avg_power.value / 1000) * server.usage.use_time_ratio.value * duration - min_impact = impact_factor.min * (server.usage.avg_power.min / 1000) * server.usage.use_time_ratio.min * duration - max_impact = impact_factor.max * (server.usage.avg_power.max / 1000) * server.usage.use_time_ratio.max * duration + impact = impact_factor.value * (server.usage.avg_power.value / 1000) * server.usage.use_time_ratio.value * duration * server.units.value + min_impact = impact_factor.min * (server.usage.avg_power.min / 1000) * server.usage.use_time_ratio.min * duration * server.units.min + max_impact = impact_factor.max * (server.usage.avg_power.max / 1000) * server.usage.use_time_ratio.max * duration * server.units.max return impact, min_impact, max_impact, [] diff --git a/tests/api/test_component.py b/tests/api/test_component.py index 3f3fbd4f..d0b94b60 100644 --- a/tests/api/test_component.py +++ b/tests/api/test_component.py @@ -244,31 +244,33 @@ async def test_multiple_cpu(): "units": 3, "core_units": 12, "die_size_per_core": 24.5}) assert res.json() == {'impacts': {'adp': {'description': 'Use of minerals and fossil ressources', - 'embedded': {'max': 0.06121, - 'min': 0.06121, - 'value': 0.06121, - 'warnings': ['End of life is not included in ' - 'the calculation']}, - 'unit': 'kgSbeq', - 'use': {'max': 0.01145, 'min': 0.0005689, 'value': 0.003}}, - 'gwp': {'description': 'Total climate change', - 'embedded': {'max': 44.8, - 'min': 44.8, - 'value': 44.8, - 'warnings': ['End of life is not included in ' - 'the calculation']}, - 'unit': 'kgCO2eq', - 'use': {'max': 38790.0, 'min': 991.3, 'value': 16000.0}}, - 'pe': {'description': 'Consumption of primary energy', - 'embedded': {'max': 701.7, - 'min': 701.7, - 'value': 701.7, - 'warnings': ['End of life is not included in ' - 'the calculation']}, - 'unit': 'MJ', - 'use': {'max': 20180000.0, - 'min': 560.3, - 'value': 1000000.0}}}} + 'embedded': {'max': 0.06121, + 'min': 0.06121, + 'value': 0.06121, + 'warnings': ['End of life is not included in ' + 'the calculation']}, + 'unit': 'kgSbeq', + 'use': {'max': 0.003816, + 'min': 0.0001896, + 'value': 0.0009}}, + 'gwp': {'description': 'Total climate change', + 'embedded': {'max': 44.8, + 'min': 44.8, + 'value': 44.8, + 'warnings': ['End of life is not included in ' + 'the calculation']}, + 'unit': 'kgCO2eq', + 'use': {'max': 12930.0, 'min': 330.4, 'value': 5000.0}}, + 'pe': {'description': 'Consumption of primary energy', + 'embedded': {'max': 701.7, + 'min': 701.7, + 'value': 701.7, + 'warnings': ['End of life is not included in ' + 'the calculation']}, + 'unit': 'MJ', + 'use': {'max': 6726000.0, + 'min': 186.8, + 'value': 200000.0}}}} @pytest.mark.asyncio @@ -545,37 +547,37 @@ async def test_complete_ram(): res = await ac.post('/v1/component/ram?verbose=false', json={"units": 12, "capacity": 32, "density": 1.79}) assert res.json() == {'impacts': {'adp': {'description': 'Use of minerals and fossil ressources', - 'embedded': {'max': 0.0338, - 'min': 0.0338, - 'value': 0.0338, - 'warnings': ['End of life is not included in ' - 'the calculation']}, - 'unit': 'kgSbeq', - 'use': {'max': 0.009134, - 'min': 0.000454, - 'value': 0.0022}}, - 'gwp': {'description': 'Total climate change', - 'embedded': {'max': 534.6, - 'min': 534.6, - 'value': 534.6, - 'warnings': ['End of life is not included in ' - 'the calculation']}, - 'unit': 'kgCO2eq', - 'use': {'max': 30950.0, 'min': 791.0, 'value': 13000.0}}, - 'pe': {'description': 'Consumption of primary energy', - 'embedded': {'max': 6745.0, - 'min': 6745.0, - 'value': 6745.0, - 'warnings': ['End of life is not included in ' - 'the calculation']}, - 'unit': 'MJ', - 'use': {'max': 16100000.0, - 'min': 447.1, - 'value': 400000.0, - 'warnings': ['Uncertainty from technical ' - 'characteristics is very important. ' - 'Results should be interpreted with ' - 'caution (see min and max values)']}}}} + 'embedded': {'max': 0.0338, + 'min': 0.0338, + 'value': 0.0338, + 'warnings': ['End of life is not included in ' + 'the calculation']}, + 'unit': 'kgSbeq', + 'use': {'max': 0.0007612, + 'min': 3.783e-05, + 'value': 0.00018}}, + 'gwp': {'description': 'Total climate change', + 'embedded': {'max': 534.6, + 'min': 534.6, + 'value': 534.6, + 'warnings': ['End of life is not included in ' + 'the calculation']}, + 'unit': 'kgCO2eq', + 'use': {'max': 2579.0, 'min': 65.92, 'value': 1100.0}}, + 'pe': {'description': 'Consumption of primary energy', + 'embedded': {'max': 6745.0, + 'min': 6745.0, + 'value': 6745.0, + 'warnings': ['End of life is not included in ' + 'the calculation']}, + 'unit': 'MJ', + 'use': {'max': 1342000.0, + 'min': 37.26, + 'value': 40000.0, + 'warnings': ['Uncertainty from technical ' + 'characteristics is very important. ' + 'Results should be interpreted with ' + 'caution (see min and max values)']}}}} @pytest.mark.asyncio diff --git a/tests/unit/test_bottom_up.py b/tests/unit/test_bottom_up.py index 1eea2500..0ddd6842 100644 --- a/tests/unit/test_bottom_up.py +++ b/tests/unit/test_bottom_up.py @@ -38,7 +38,7 @@ def test_bottom_up_component_cpu_complete(complete_cpu_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgSbeq', - 'use': {'max': 0.005088, 'min': 0.0002528, 'value': 0.0012}}, + 'use': {'max': 0.002544, 'min': 0.0001264, 'value': 0.0006}}, 'gwp': {'description': 'Total climate change', 'embedded': {'max': 41.45, 'min': 41.45, @@ -46,7 +46,7 @@ def test_bottom_up_component_cpu_complete(complete_cpu_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgCO2eq', - 'use': {'max': 17240.0, 'min': 440.6, 'value': 7000.0}}, + 'use': {'max': 8620.0, 'min': 220.3, 'value': 3600.0}}, 'pe': {'description': 'Consumption of primary energy', 'embedded': {'max': 623.6, 'min': 623.6, @@ -54,7 +54,7 @@ def test_bottom_up_component_cpu_complete(complete_cpu_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'MJ', - 'use': {'max': 8967000.0, 'min': 249.0, 'value': 200000.0}}} + 'use': {'max': 4484000.0, 'min': 124.5, 'value': 100000.0}}} def test_bottom_up_component_cpu_incomplete(incomplete_cpu_model): @@ -220,7 +220,7 @@ def test_bottom_up_component_ram_complete(complete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgSbeq', - 'use': {'max': 0.009134, 'min': 0.000454, 'value': 0.0022}}, + 'use': {'max': 0.0007612, 'min': 3.783e-05, 'value': 0.00018}}, 'gwp': {'description': 'Total climate change', 'embedded': {'max': 534.6, 'min': 534.6, @@ -228,7 +228,7 @@ def test_bottom_up_component_ram_complete(complete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgCO2eq', - 'use': {'max': 30950.0, 'min': 791.0, 'value': 13000.0}}, + 'use': {'max': 2579.0, 'min': 65.92, 'value': 1100.0}}, 'pe': {'description': 'Consumption of primary energy', 'embedded': {'max': 6745.0, 'min': 6745.0, @@ -236,9 +236,9 @@ def test_bottom_up_component_ram_complete(complete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'MJ', - 'use': {'max': 16100000.0, - 'min': 447.1, - 'value': 400000.0, + 'use': {'max': 1342000.0, + 'min': 37.26, + 'value': 40000.0, 'warnings': ['Uncertainty from technical characteristics is ' 'very important. Results should be interpreted ' 'with caution (see min and max values)']}}} @@ -253,7 +253,7 @@ def test_bottom_up_component_ram_incomplete(incomplete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgSbeq', - 'use': {'max': 0.009134, 'min': 0.000454, 'value': 0.0022}}, + 'use': {'max': 0.0007612, 'min': 3.783e-05, 'value': 0.00018}}, 'gwp': {'description': 'Total climate change', 'embedded': {'max': 4287.0, 'min': 104.9, @@ -261,7 +261,7 @@ def test_bottom_up_component_ram_incomplete(incomplete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgCO2eq', - 'use': {'max': 30950.0, 'min': 791.0, 'value': 13000.0}}, + 'use': {'max': 2579.0, 'min': 65.92, 'value': 1100.0}}, 'pe': {'description': 'Consumption of primary energy', 'embedded': {'max': 53300.0, 'min': 1412.0, @@ -269,9 +269,9 @@ def test_bottom_up_component_ram_incomplete(incomplete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'MJ', - 'use': {'max': 16100000.0, - 'min': 447.1, - 'value': 400000.0, + 'use': {'max': 1342000.0, + 'min': 37.26, + 'value': 40000.0, 'warnings': ['Uncertainty from technical characteristics is ' 'very important. Results should be interpreted ' 'with caution (see min and max values)']}}} diff --git a/tests/unit/test_verbose.py b/tests/unit/test_verbose.py index 886e5dfe..3d973269 100644 --- a/tests/unit/test_verbose.py +++ b/tests/unit/test_verbose.py @@ -9,29 +9,29 @@ def test_verbose_component_cpu_1(complete_cpu_model): assert verbose["die_size_per_core"] == {'status': 'INPUT', 'unit': 'mm2', 'value': 24.5} assert verbose["impacts"] == {'adp': {'description': 'Use of minerals and fossil ressources', - 'embedded': {'max': 0.04081, - 'min': 0.04081, - 'value': 0.04081, - 'warnings': ['End of life is not included in the ' - 'calculation']}, - 'unit': 'kgSbeq', - 'use': {'max': 0.005088, 'min': 0.0002528, 'value': 0.0012}}, - 'gwp': {'description': 'Total climate change', - 'embedded': {'max': 41.45, - 'min': 41.45, - 'value': 41.45, - 'warnings': ['End of life is not included in the ' - 'calculation']}, - 'unit': 'kgCO2eq', - 'use': {'max': 17240.0, 'min': 440.6, 'value': 7000.0}}, - 'pe': {'description': 'Consumption of primary energy', - 'embedded': {'max': 623.6, - 'min': 623.6, - 'value': 623.6, - 'warnings': ['End of life is not included in the ' - 'calculation']}, - 'unit': 'MJ', - 'use': {'max': 8967000.0, 'min': 249.0, 'value': 200000.0}}} + 'embedded': {'max': 0.04081, + 'min': 0.04081, + 'value': 0.04081, + 'warnings': ['End of life is not included in the ' + 'calculation']}, + 'unit': 'kgSbeq', + 'use': {'max': 0.002544, 'min': 0.0001264, 'value': 0.0006}}, + 'gwp': {'description': 'Total climate change', + 'embedded': {'max': 41.45, + 'min': 41.45, + 'value': 41.45, + 'warnings': ['End of life is not included in the ' + 'calculation']}, + 'unit': 'kgCO2eq', + 'use': {'max': 8620.0, 'min': 220.3, 'value': 3600.0}}, + 'pe': {'description': 'Consumption of primary energy', + 'embedded': {'max': 623.6, + 'min': 623.6, + 'value': 623.6, + 'warnings': ['End of life is not included in the ' + 'calculation']}, + 'unit': 'MJ', + 'use': {'max': 4484000.0, 'min': 124.5, 'value': 100000.0}}} def test_verbose_component_cpu_2(incomplete_cpu_model): @@ -75,7 +75,7 @@ def test_verbose_component_ram(complete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgSbeq', - 'use': {'max': 0.009134, 'min': 0.000454, 'value': 0.0022}}, + 'use': {'max': 0.0007612, 'min': 3.783e-05, 'value': 0.00018}}, 'gwp': {'description': 'Total climate change', 'embedded': {'max': 534.6, 'min': 534.6, @@ -83,7 +83,7 @@ def test_verbose_component_ram(complete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'kgCO2eq', - 'use': {'max': 30950.0, 'min': 791.0, 'value': 13000.0}}, + 'use': {'max': 2579.0, 'min': 65.92, 'value': 1100.0}}, 'pe': {'description': 'Consumption of primary energy', 'embedded': {'max': 6745.0, 'min': 6745.0, @@ -91,9 +91,9 @@ def test_verbose_component_ram(complete_ram_model): 'warnings': ['End of life is not included in the ' 'calculation']}, 'unit': 'MJ', - 'use': {'max': 16100000.0, - 'min': 447.1, - 'value': 400000.0, + 'use': {'max': 1342000.0, + 'min': 37.26, + 'value': 40000.0, 'warnings': ['Uncertainty from technical characteristics is ' 'very important. Results should be interpreted ' 'with caution (see min and max values)']}}} From 59caf3e5e3712b52838f7104f47c87d876c9e621 Mon Sep 17 00:00:00 2001 From: "da.ekchajzer$" Date: Wed, 11 Dec 2024 11:06:32 +0100 Subject: [PATCH 2/2] fixing issue #348 + add tests for existing platforms --- boaviztapi/data/archetypes/server.csv | 1 + tests/api/test_cloud.py | 1 + tests/unit/test_data.py | 35 +++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/unit/test_data.py diff --git a/boaviztapi/data/archetypes/server.csv b/boaviztapi/data/archetypes/server.csv index d6234182..9e26f81b 100644 --- a/boaviztapi/data/archetypes/server.csv +++ b/boaviztapi/data/archetypes/server.csv @@ -201,3 +201,4 @@ scw_pop2hm.base,Scaleway,rack,2,32,,AMD EPYC 7543,128,16,32,2,234,0,0,0,,0,2,0,5 scw_pro2.base,Scaleway,rack,2,32,,AMD EPYC 7543,128,24,32,2,468,0,0,0,,0,2,0,50;0;100,1,52560,0.33;0.2;0.6, scw_renders.base,Scaleway,rack,2,20,,Intel Xeon Gold 6148,40,12,32,2,3750,0,0,8,Nvidia Tesla P100,24,4,0,50;0;100,1,52560,0.33;0.2;0.6, scw_stardust1.base,Scaleway,rack,1,16,,AMD EPYC 7281,64,8,32,5,976,0,0,0,,0,2,0,50;0;100,1,52560,0.33;0.2;0.6, +platform_aws_t4g,AWS,rack,1,,,Annapurna Labs Graviton2,,8,32,0,0,0,0,0,,0,2;2;2,2.99;1;5,50;0;100,1,35040,0.33;0.2;0.6,RAM configuration was not verified \ No newline at end of file diff --git a/tests/api/test_cloud.py b/tests/api/test_cloud.py index ab2ba03c..242d1247 100644 --- a/tests/api/test_cloud.py +++ b/tests/api/test_cloud.py @@ -991,3 +991,4 @@ async def test_empty_usage_scw_dev1_l(): ) await test.check_result() + diff --git a/tests/unit/test_data.py b/tests/unit/test_data.py new file mode 100644 index 00000000..a4c1b148 --- /dev/null +++ b/tests/unit/test_data.py @@ -0,0 +1,35 @@ +import csv +import os.path + +import pytest + +cloud_path = os.path.join(os.path.dirname(__file__), "../../boaviztapi/data/archetypes/cloud/") +providers_path = os.path.join(os.path.dirname(__file__),"../../boaviztapi/data/archetypes/cloud/providers.csv") +servers_patch = os.path.join(os.path.dirname(__file__),"../../boaviztapi/data/archetypes/server.csv") + +@pytest.fixture +def providers(): + with open(providers_path, 'r') as f: + reader = csv.DictReader(f) + return [row['provider.name'] for row in reader] + +@pytest.fixture +def valid_platforms(): + with open(servers_patch, 'r') as f: + reader = csv.DictReader(f) + return {row['id'] for row in reader} + + +def test_platform_exists_in_server_csv(providers, valid_platforms): + for provider_name in providers: + provider_csv_path = f"{cloud_path}/{provider_name}.csv" + + try: + with open(provider_csv_path, 'r') as f: + reader = csv.DictReader(f) + for row in reader: + platform = row.get('platform', '').strip() + if platform not in valid_platforms: + pytest.fail(f"Platform '{platform}' for provider '{provider_name}' not found in server.csv") + except FileNotFoundError: + pytest.fail(f"CSV file for provider '{provider_name}' not found: {provider_csv_path}") \ No newline at end of file