def test_heat_conservation_simple(self):
        # Dimensions of the cubes
        cubes_dimensions = Dimensions(x=3, z=3, y=3)

        # Cube material
        cuboid_material = SMSilicon()

        # Cuboid initial temperature
        system_initial_temperature = 273.15 + 45

        # Definition of the CPU shape and materials
        scene_definition = {
            # Cube
            0: (cuboid_material,
                Cuboid(location=Location(x=0, z=0, y=0),
                       dimensions=cubes_dimensions))
        }

        # Edge size pf 1 mm
        cube_edge_size = 0.001

        cubed_space = Model(material_cubes=scene_definition,
                            cube_edge_size=cube_edge_size,
                            environment_properties=None,
                            simulation_precision="HIGH")

        initial_state = cubed_space.create_initial_state(
            default_temperature=system_initial_temperature,
            environment_temperature=None)

        max_temperature_vector = []
        min_temperature_vector = []

        number_of_iterations = 10000

        for i in range(number_of_iterations):
            # Apply energy over the cubed space
            initial_state = cubed_space.apply_energy(
                actual_state=initial_state, amount_of_time=0.001)
            temperature_over_energy_application = cubed_space.obtain_temperature(
                actual_state=initial_state)

            min_temperature_one = min(
                obtain_min_temperature(
                    temperature_over_energy_application).values())
            max_temperature_one = max(
                obtain_max_temperature(
                    temperature_over_energy_application).values())

            min_temperature_vector.append(min_temperature_one)
            max_temperature_vector.append(max_temperature_one)

        assert (self.float_equal(min(min_temperature_vector),
                                 system_initial_temperature,
                                 error=0.1))
        assert (self.float_equal(max(max_temperature_vector),
                                 system_initial_temperature,
                                 error=0.1))
Esempio n. 2
0
def generate_board_temperature_evolution_2d_video(schedule_result: RawSimulationResult,
                                                  title: Optional[str] = None) -> FuncAnimation:
    """
    Generate a 2D animation of the evolution in the temperature of the processor

    :param schedule_result: Result of the simulation
    :param title: Plot title
    :return: plot
    """
    min_simulation_value = min(min(obtain_min_temperature(i).values())
                               for i in schedule_result.temperature_measures.values())
    max_simulation_value = max(max(obtain_max_temperature(i).values())
                               for i in schedule_result.temperature_measures.values())

    return generate_video_2d_heat_map(
        schedule_result.temperature_measures,
        min_temperature=min_simulation_value,
        max_temperature=max_simulation_value, axis="Z", location_in_axis=1, delay_between_frames_ms=100)
Esempio n. 3
0
def generate_component_hotspots_plot(
        schedule_result: RawSimulationResult,
        start_time: float = 0,
        end_time: Optional[float] = None,
        title: Optional[str] = None,
        units: Literal["KELVIN", "CELSIUS"] = "KELVIN") -> Figure:
    """
    Generate a graphic with the temperature of the hotspots in the components (cores and board) over time

    :param units: Units where the temperature will be expressed
    :param schedule_result: Result of the simulation
    :param start_time: Plot start time in seconds
    :param end_time: Plot end time in seconds
    :param title: Plot title
    :return: plot
    """
    cpu_ids: List[int] = sorted(schedule_result.job_sections_execution.keys())

    fig, ax = pyplot.subplots(nrows=len(cpu_ids) + 1)

    measures_times = list(sorted(schedule_result.temperature_measures.keys()))
    max_temperature_list = [
        obtain_max_temperature(schedule_result.temperature_measures[i])
        for i in measures_times
    ]

    # min_temperature = 0

    for cpu_id in cpu_ids + [len(cpu_ids)]:
        max_temperature_list_cpu_i = [
            i[cpu_id] - (0 if units == "KELVIN" else 273.15)
            for i in max_temperature_list
        ]
        ax[cpu_id].plot(measures_times, max_temperature_list_cpu_i)

        ax[cpu_id].set_xlim(start_time, end_time)

        # Set label
        ax[cpu_id].set_xlabel(f'Time (s)')

        if cpu_id != len(cpu_ids):
            ax[cpu_id].set_ylabel(
                f'CPU {cpu_id} \n Temperature ({"K" if units == "KELVIN" else "ÂşC"})'
            )
        else:
            ax[cpu_id].set_ylabel(
                f'Board \n Temperature ({"K" if units == "KELVIN" else "ÂşC"})')

    # Set uniform y_lim
    max_y = max(ax[i].get_ylim()[1] for i in range(len(cpu_ids) + 1))
    min_y = min(ax[i].get_ylim()[0] for i in range(len(cpu_ids) + 1))
    for i in range(len(cpu_ids) + 1):
        ax[i].set_ylim(min_y, max_y)

    # Set title
    if title is not None:
        fig.suptitle(title)

    # Adjust layout
    fig.tight_layout()

    return fig
    def test_external_conduction_plot_3d_stacked(self):
        # Configuration
        # Size of the simulation step
        simulation_step = 0.1

        # Major cycle in seconds
        major_cycle = 1.0

        # Temperature of the system (Celsius degrees)
        system_temperature = 45

        number_of_cores = 1

        # Dimensions of the core
        core_dimensions = Dimensions(x=10, z=2, y=10)

        # Material of the core
        core_material = SMSilicon()

        # Material of the board
        board_material = SMCooper()

        # Environment initial temperature
        environment_temperature = 273.15 + system_temperature

        # CPU distribution
        # XXXXXXX
        # XX0X3XX
        # XX1X4XX
        # XX2X5XX
        # XXXXXXX

        # Definition of the CPU shape and materials
        cpu_definition = {
            # Cores
            0: (core_material,
                Cuboid(location=Location(x=20, z=2, y=10),
                       dimensions=core_dimensions)),

            # Board
            1: (board_material,
                Cuboid(location=Location(x=0, z=0, y=0),
                       dimensions=Dimensions(x=70, z=2, y=70)))
        }

        # Edge size pf 0.5 mm
        cube_edge_size = 0.001

        # Environment properties
        environment_properties = FEAirFree()

        external_heat_generators_dynamic_power = {
            0:
            create_energy_applicator(
                (core_material,
                 Cuboid(location=Location(x=20, z=0, y=10),
                        dimensions=Dimensions(x=10, z=2, y=10))),
                watts_to_apply=10,
                cube_edge_size=cube_edge_size)
        }

        # Generate cubed space
        cubed_space = Model(material_cubes=cpu_definition,
                            cube_edge_size=cube_edge_size,
                            external_temperature_booster_points=
                            external_heat_generators_dynamic_power,
                            internal_temperature_booster_points={},
                            environment_properties=environment_properties,
                            simulation_precision="HIGH")

        initial_state = cubed_space.create_initial_state(
            default_temperature=environment_temperature,
            environment_temperature=environment_temperature)

        # Initial temperatures
        temperature_measures: Dict[float, Dict[int, PhysicalCuboid]] = {}
        temperature_over_before_zero_seconds = cubed_space.obtain_temperature(
            actual_state=initial_state)

        temperature_measures[0.0] = temperature_over_before_zero_seconds

        actual_applied_externally_energy_points = {0}

        # Apply energy over the cubed space
        for i in range(round(major_cycle / simulation_step)):
            initial_state = cubed_space.apply_energy(
                actual_state=initial_state,
                external_energy_application_points=
                actual_applied_externally_energy_points,
                internal_energy_application_points=set(),
                amount_of_time=simulation_step)
            temperature = cubed_space.obtain_temperature(
                actual_state=initial_state)
            temperature_measures[i * simulation_step] = temperature

        # Display temperatures
        for i in range(round(major_cycle / simulation_step)):
            time_to_display = i * simulation_step

            min_temperature = min(
                obtain_min_temperature(
                    temperature_measures[time_to_display]).values())
            max_temperature = max(
                obtain_max_temperature(
                    temperature_measures[time_to_display]).values())

            plot_3d = plot_3d_heat_map_temperature(
                temperature_measures[time_to_display],
                min_temperature=min_temperature,
                max_temperature=max_temperature)

            plot_3d.show()
    def test_internal_conduction_plot(self):
        # Dimensions of the cubes
        cubes_dimensions = Dimensions(x=2, z=2, y=2)

        # Cube 0 material
        cube_0_material = SMCooper()

        # Core initial temperature
        cube_0_initial_temperature = 273.15 + 65

        # Board initial temperature
        environment_temperature = 273.15 + 25

        # Min simulation value
        min_simulation_value = cube_0_initial_temperature - 10
        max_simulation_value = cube_0_initial_temperature + 10

        # Definition of the CPU shape and materials
        scene_definition = {
            # Cores
            0: (cube_0_material,
                Cuboid(location=Location(x=0, z=0, y=0),
                       dimensions=cubes_dimensions))
        }

        # Edge size pf 1 mm
        cube_edge_size = 0.001

        # Environment properties
        environment_properties = FEAirForced()

        cubed_space = Model(material_cubes=scene_definition,
                            cube_edge_size=cube_edge_size,
                            environment_properties=environment_properties,
                            simulation_precision="HIGH")

        initial_state = cubed_space.create_initial_state(
            default_temperature=environment_temperature,
            material_cubes_temperatures={0: cube_0_initial_temperature},
            environment_temperature=environment_temperature)

        # Initial temperatures
        temperature_over_before_zero_seconds = cubed_space.obtain_temperature(
            actual_state=initial_state)

        # Apply energy over the cubed space
        initial_state = cubed_space.apply_energy(actual_state=initial_state,
                                                 amount_of_time=0.9)
        temperature_over_before_half_second = cubed_space.obtain_temperature(
            actual_state=initial_state)

        # Apply energy over the cubed space
        initial_state = cubed_space.apply_energy(actual_state=initial_state,
                                                 amount_of_time=0.9)
        temperature_over_before_one_second = cubed_space.obtain_temperature(
            actual_state=initial_state)

        # Zero seconds
        plot_3d_heat_map_temperature(temperature_over_before_zero_seconds,
                                     min_temperature=min_simulation_value,
                                     max_temperature=max_simulation_value)

        min_temperature = obtain_min_temperature(
            temperature_over_before_zero_seconds)
        max_temperature = obtain_max_temperature(
            temperature_over_before_zero_seconds)

        print("Temperature before 0 seconds: min", min_temperature, ", max",
              max_temperature)

        # Half second
        plot_3d_heat_map_temperature(temperature_over_before_half_second,
                                     min_temperature=min_simulation_value,
                                     max_temperature=max_simulation_value)

        min_temperature = obtain_min_temperature(
            temperature_over_before_half_second)
        max_temperature = obtain_max_temperature(
            temperature_over_before_half_second)

        print("Temperature before 0.5 seconds: min", min_temperature, ", max",
              max_temperature)

        # One second
        plot_3d_heat_map_temperature(temperature_over_before_one_second,
                                     min_temperature=min_simulation_value,
                                     max_temperature=max_simulation_value)

        min_temperature = obtain_min_temperature(
            temperature_over_before_one_second)
        max_temperature = obtain_max_temperature(
            temperature_over_before_one_second)

        print("Temperature before 1 second: min", min_temperature, ", max",
              max_temperature)
    def test_processor_heat_generation_plot(self):
        # Dimensions of the core
        core_dimensions = Dimensions(x=10, z=2, y=10)

        # Material of the core
        core_material = SMSilicon()

        # Material of the board
        board_material = SMCooper()

        # Core initial temperature
        # core_initial_temperature = 273.15 + 65
        core_0_initial_temperature = 273.15 + 25
        core_1_initial_temperature = 273.15 + 25
        core_2_initial_temperature = 273.15 + 25
        core_3_initial_temperature = 273.15 + 25

        # Board initial temperature
        board_initial_temperature = 273.15 + 25

        # Environment initial temperature
        environment_temperature = 273.15 + 25

        # Min simulation value
        max_simulation_value = 273.15 + 80
        min_simulation_value = 273.15 + 20

        # Definition of the CPU shape and materials
        cpu_definition = {
            # Cores
            0: (core_material,
                Cuboid(location=Location(x=10, z=2, y=10),
                       dimensions=core_dimensions)),
            1: (core_material,
                Cuboid(location=Location(x=30, z=2, y=10),
                       dimensions=core_dimensions)),
            2: (core_material,
                Cuboid(location=Location(x=10, z=2, y=30),
                       dimensions=core_dimensions)),
            3: (core_material,
                Cuboid(location=Location(x=30, z=2, y=30),
                       dimensions=core_dimensions)),

            # Board
            4: (board_material,
                Cuboid(location=Location(x=0, z=0, y=0),
                       dimensions=Dimensions(x=50, z=2, y=50)))
        }

        # Edge size pf 1 mm
        cube_edge_size = 0.001

        # Environment properties
        environment_properties = FEAirFree()

        # CPU energy consumption configuration
        #  Dynamic power = dynamic_alpha * F^3 + dynamic_beta
        #  Leakage power = current temperature * 2 * leakage_delta + leakage_alpha
        leakage_alpha: float = 0.001
        leakage_delta: float = 0.1
        dynamic_alpha: float = 19 * 1000**-3
        dynamic_beta: float = 2

        cpu_frequency: float = 1000

        watts_to_apply = dynamic_alpha * (cpu_frequency**3) + dynamic_beta

        # External heat generators
        external_heat_generators = {
            # Dynamic power
            0:
            create_energy_applicator(
                (core_material,
                 Cuboid(location=Location(x=10, z=2, y=10),
                        dimensions=core_dimensions)),
                watts_to_apply=watts_to_apply,
                cube_edge_size=cube_edge_size),

            # Leakage power
            1:
            create_energy_applicator(
                (core_material,
                 Cuboid(location=Location(x=10, z=2, y=10),
                        dimensions=core_dimensions)),
                watts_to_apply=leakage_alpha,
                cube_edge_size=cube_edge_size),
            2:
            create_energy_applicator(
                (core_material,
                 Cuboid(location=Location(x=30, z=2, y=10),
                        dimensions=core_dimensions)),
                watts_to_apply=leakage_alpha,
                cube_edge_size=cube_edge_size),
            3:
            create_energy_applicator(
                (core_material,
                 Cuboid(location=Location(x=10, z=2, y=30),
                        dimensions=core_dimensions)),
                watts_to_apply=leakage_alpha,
                cube_edge_size=cube_edge_size),
            4:
            create_energy_applicator(
                (core_material,
                 Cuboid(location=Location(x=30, z=2, y=30),
                        dimensions=core_dimensions)),
                watts_to_apply=leakage_alpha,
                cube_edge_size=cube_edge_size)
        }

        # Internal heat generators
        internal_heat_generators = {
            0:
            TMInternal(cuboid=Cuboid(location=Location(x=10, z=2, y=10),
                                     dimensions=core_dimensions),
                       boostRateMultiplier=leakage_delta),
            1:
            TMInternal(cuboid=Cuboid(location=Location(x=30, z=2, y=10),
                                     dimensions=core_dimensions),
                       boostRateMultiplier=leakage_delta),
            2:
            TMInternal(cuboid=Cuboid(location=Location(x=10, z=2, y=30),
                                     dimensions=core_dimensions),
                       boostRateMultiplier=leakage_delta),
            3:
            TMInternal(cuboid=Cuboid(location=Location(x=30, z=2, y=30),
                                     dimensions=core_dimensions),
                       boostRateMultiplier=leakage_delta)
        }

        # Generate cubed space
        cubed_space = Model(
            material_cubes=cpu_definition,
            cube_edge_size=cube_edge_size,
            external_temperature_booster_points=external_heat_generators,
            internal_temperature_booster_points=internal_heat_generators,
            environment_properties=environment_properties,
            simulation_precision="HIGH")

        initial_state = cubed_space.create_initial_state(
            default_temperature=environment_temperature,
            material_cubes_temperatures={
                0: core_0_initial_temperature,
                1: core_1_initial_temperature,
                2: core_2_initial_temperature,
                3: core_3_initial_temperature,
                4: board_initial_temperature
            },
            environment_temperature=environment_temperature)

        temperatures_vector = []

        # Initial temperatures
        temperature_over_before_zero_seconds = cubed_space.obtain_temperature(
            actual_state=initial_state)

        temperatures_vector.append(temperature_over_before_zero_seconds)

        # Apply energy over the cubed space
        number_of_iterations = 10
        for i in range(number_of_iterations):
            initial_state = cubed_space.apply_energy(
                actual_state=initial_state,
                external_energy_application_points={0},  # {0, 1, 2, 3, 4},
                # internal_energy_application_points={0, 1, 2, 3},
                amount_of_time=0.5)
            temperature = cubed_space.obtain_temperature(
                actual_state=initial_state)

            temperatures_vector.append(temperature)

        for i, temperature in enumerate(temperatures_vector):
            # Zero seconds
            plot_3d_heat_map_temperature(
                temperature,
                min_temperature=min_simulation_value,
                max_temperature=max_simulation_value).show()

            plot_2d_heat_map(temperature,
                             min_temperature=min_simulation_value,
                             max_temperature=max_simulation_value,
                             axis="Z",
                             location_in_axis=1).show()

            min_temperature = obtain_min_temperature(temperature)
            max_temperature = obtain_max_temperature(temperature)

            print("Temperature before", i * 0.5, "seconds: min",
                  min_temperature, ", max", max_temperature)
    def test_internal_conduction_with_convection(self):
        # Dimensions of the cubes
        cubes_dimensions = Dimensions(x=3, z=3, y=3)

        # Cube 0 material
        cuboid_material = SMSilicon()

        # Core initial temperature
        cuboid_initial_temperature = 273.15 + 65

        # Board initial temperature
        environment_temperature = 273.15 + 25

        # Definition of the CPU shape and materials
        scene_definition = {
            # Cores
            0: (cuboid_material,
                Cuboid(location=Location(x=0, z=0, y=0),
                       dimensions=cubes_dimensions))
        }

        # Edge size pf 1 mm
        cube_edge_size = 0.001

        # Environment properties
        environment_properties = FEAirForced()

        cubed_space = Model(material_cubes=scene_definition,
                            cube_edge_size=cube_edge_size,
                            environment_properties=environment_properties,
                            simulation_precision="HIGH")

        initial_state = cubed_space.create_initial_state(
            default_temperature=environment_temperature,
            material_cubes_temperatures={0: cuboid_initial_temperature},
            environment_temperature=environment_temperature)

        # Apply energy over the cubed space
        initial_state = cubed_space.apply_energy(actual_state=initial_state,
                                                 amount_of_time=0.5)
        temperature_over_before_half_second = cubed_space.obtain_temperature(
            actual_state=initial_state)

        # Apply energy over the cubed space
        initial_state = cubed_space.apply_energy(actual_state=initial_state,
                                                 amount_of_time=0.5)
        temperature_over_before_one_second = cubed_space.obtain_temperature(
            actual_state=initial_state)

        # Half second
        min_temperature_half = obtain_min_temperature(
            temperature_over_before_half_second)
        max_temperature_half = obtain_max_temperature(
            temperature_over_before_half_second)

        min_temperature_half = min(min_temperature_half.values())
        max_temperature_half = max(max_temperature_half.values())

        # One second
        min_temperature_one = obtain_min_temperature(
            temperature_over_before_one_second)
        max_temperature_one = obtain_max_temperature(
            temperature_over_before_one_second)

        min_temperature_one = min(min_temperature_one.values())
        max_temperature_one = max(max_temperature_one.values())

        assert (environment_temperature <= min_temperature_half <=
                cuboid_initial_temperature
                and max_temperature_half <= cuboid_initial_temperature)

        assert (environment_temperature <= min_temperature_one <=
                cuboid_initial_temperature
                and max_temperature_one <= cuboid_initial_temperature)

        assert (min_temperature_one <= min_temperature_half
                and max_temperature_one <= max_temperature_half)
    def test_processor_heat_conservation(self):
        # Dimensions of the core
        core_dimensions = Dimensions(x=10, z=2, y=10)

        # Material of the core
        core_material = SMSilicon()

        # Material of the board
        board_material = SMCooper()

        # System initial temperature
        system_initial_temperature = 273.15 + 25

        # Core initial temperature
        core_initial_temperature = system_initial_temperature

        # Board initial temperature
        board_initial_temperature = system_initial_temperature

        # Environment initial temperature
        environment_temperature = system_initial_temperature

        # Definition of the CPU shape and materials
        cpu_definition = {
            # Cores
            0: (core_material,
                Cuboid(location=Location(x=10, z=2, y=10),
                       dimensions=core_dimensions)),
            1: (core_material,
                Cuboid(location=Location(x=30, z=2, y=10),
                       dimensions=core_dimensions)),
            2: (core_material,
                Cuboid(location=Location(x=10, z=2, y=30),
                       dimensions=core_dimensions)),
            3: (core_material,
                Cuboid(location=Location(x=30, z=2, y=30),
                       dimensions=core_dimensions)),

            # Board
            4: (board_material,
                Cuboid(location=Location(x=0, z=0, y=0),
                       dimensions=Dimensions(x=50, z=2, y=50)))
        }

        # Edge size pf 1 mm
        cube_edge_size = 0.001

        # Environment properties
        environment_properties = FEAirFree()

        # Generate cubed space
        cubed_space = Model(material_cubes=cpu_definition,
                            cube_edge_size=cube_edge_size,
                            external_temperature_booster_points={},
                            internal_temperature_booster_points={},
                            environment_properties=environment_properties,
                            simulation_precision="HIGH")

        initial_state = cubed_space.create_initial_state(
            default_temperature=environment_temperature,
            material_cubes_temperatures={
                0: core_initial_temperature,
                1: core_initial_temperature,
                2: core_initial_temperature,
                3: core_initial_temperature,
                4: board_initial_temperature
            },
            environment_temperature=environment_temperature)

        min_max_temperatures_vector: List[Tuple[float, float, float]] = [
            (0.0, core_initial_temperature, core_initial_temperature)
        ]

        # Initial temperatures
        temperature_over_before_zero_seconds = cubed_space.obtain_temperature(
            actual_state=initial_state)
        min_temperature = obtain_min_temperature(
            temperature_over_before_zero_seconds)
        max_temperature = obtain_max_temperature(
            temperature_over_before_zero_seconds)
        min_max_temperatures_vector.append((0.0, min(min_temperature.values()),
                                            max(max_temperature.values())))

        # Apply energy over the cubed space
        number_of_iterations = 100
        for i in range(number_of_iterations):
            initial_state = cubed_space.apply_energy(
                actual_state=initial_state, amount_of_time=0.001)
            temperature = cubed_space.obtain_temperature(
                actual_state=initial_state)
            min_temperature = obtain_min_temperature(temperature)
            max_temperature = obtain_max_temperature(temperature)
            min_max_temperatures_vector.append(
                (0.0, min(min_temperature.values()),
                 max(max_temperature.values())))

        assert all(i > system_initial_temperature - 0.1
                   for _, i, _ in min_max_temperatures_vector)
        assert all(i < system_initial_temperature + 0.1
                   for _, _, i in min_max_temperatures_vector)
Esempio n. 9
0
def _execute_centralized_scheduler_simulation(
        jobs: List[Job], tasks: TaskSet, processor_definition: Processor,
        environment_specification: Environment,
        scheduler: CentralizedScheduler,
        simulation_options: SimulationConfiguration,
        simulation_start_time: float,
        simulation_end_time: float) -> RawSimulationResult:
    """
    Run a simulation using a centralized scheduler

    :param jobs: Jobs in the system
    :param simulation_start_time: Time in seconds where the system start to make decisions. Time 0 is the start of the
     first major cycle
    :param simulation_end_time: Time in seconds since the start of the first major cycle where the simulation ends.
    :param tasks: Group of tasks in the system
    :param processor_definition: Definition of the CPU to use
    :param environment_specification: Specification of the environment
    :param scheduler: Centralized scheduler to use
    :param simulation_options: Options of the simulation
    :return: Simulation result
    """
    # Possible frequencies
    # As we are simulating with a centralized scheduler, only frequencies possibles in all cores are available
    available_frequencies = Set.intersection(*[
        i.core_type.available_frequencies
        for i in processor_definition.cores_definition.values()
    ])

    if len(available_frequencies) == 0:
        return RawSimulationResult(
            have_been_scheduled=False,
            scheduler_acceptance_error_message=
            "at least one frequency must be shared by all" +
            " cores in a centralized scheduler simulation",
            job_sections_execution={},
            cpus_frequencies={},
            scheduling_points=[],
            temperature_measures={},
            hard_real_time_deadline_missed_stack_trace=None,
            memory_usage_record=None)

    # Unit mesh division
    if simulation_options.processor_mesh_division < 1:
        return RawSimulationResult(
            have_been_scheduled=False,
            scheduler_acceptance_error_message=
            "mesh division must be greater than 0",
            job_sections_execution={},
            cpus_frequencies={},
            scheduling_points=[],
            temperature_measures={},
            hard_real_time_deadline_missed_stack_trace=None,
            memory_usage_record=None)

    # Number of cpus
    number_of_cpus = len(processor_definition.cores_definition)

    # Check if CPU ids go from 0 to number_of_tasks - 1
    cpus_ids_corrects: bool = all(
        0 <= i < number_of_cpus
        for i in processor_definition.cores_definition.keys())

    if not cpus_ids_corrects:
        return RawSimulationResult(
            have_been_scheduled=False,
            scheduler_acceptance_error_message=
            "Processors id must go from 0 to the number of" + " CPUS - 1",
            job_sections_execution={},
            cpus_frequencies={},
            scheduling_points=[],
            temperature_measures={},
            hard_real_time_deadline_missed_stack_trace=None,
            memory_usage_record=None)

    # Check if scheduler is capable of execute task set
    can_schedule, error_message = scheduler.check_schedulability(
        processor_definition, environment_specification, tasks)

    if not can_schedule:
        return RawSimulationResult(
            have_been_scheduled=False,
            scheduler_acceptance_error_message="the scheduler can't schedule"
            if error_message is None else error_message,
            job_sections_execution={},
            cpus_frequencies={},
            scheduling_points=[],
            temperature_measures={},
            hard_real_time_deadline_missed_stack_trace=None,
            memory_usage_record=None)

    # Run scheduler offline phase
    cpu_frequency = scheduler.offline_stage(processor_definition,
                                            environment_specification, tasks)

    # Create data structures for the simulation
    # Max frequency
    lcm_frequency = list_int_lcm(list(available_frequencies))

    # Dict with activation and deadlines
    activation_dict, deadlines_dict = _create_deadline_arrive_dict(
        lcm_frequency, jobs)

    # Jobs CC dict by id (this value is constant and only should be used for fast access to the original cc)
    jobs_cc_dict: Dict[int,
                       int] = {i.identifier: i.execution_time
                               for i in jobs}

    # Remaining jobs CC dict by id
    remaining_cc_dict: Dict[int, int] = jobs_cc_dict.copy()

    # Simulation step
    actual_lcm_cycle: int = round(simulation_start_time * lcm_frequency)
    final_lcm_cycle: int = round(simulation_end_time * lcm_frequency)

    # Major cycle
    major_cycle_lcm = list_int_lcm(
        [round(i.period * lcm_frequency) for i in tasks.periodic_tasks])

    # Jobs to task dict
    jobs_to_task_dict = {i.identifier: i.task.identifier for i in jobs}

    # Activate jobs set
    active_jobs = set()

    # Hard deadline task miss deadline
    hard_rt_task_miss_deadline = False

    # Only must take value if a hard real time is missed
    hard_real_time_deadline_missed_stack_trace: Optional[
        SimulationStackTraceHardRTDeadlineMissed] = None

    # Jobs type dict
    hard_real_time_jobs: Set[int] = {
        i.identifier
        for i in jobs if i.task.deadline_criteria == Criticality.HARD
    }
    firm_real_time_jobs: Set[int] = {
        i.identifier
        for i in jobs if i.task.deadline_criteria == Criticality.FIRM
    }
    non_preemptive_jobs: Set[int] = {
        i.identifier
        for i in jobs
        if i.task.preemptive_execution == PreemptiveExecution.NON_PREEMPTIVE
    }

    # When is set the next scheduling point by quantum
    next_scheduling_point = None

    # Jobs being executed
    jobs_being_executed_id: Dict[int, int] = {}

    #  Raw execution result tables
    job_sections_execution: Dict[int, List[JobSectionExecution]] = {
        i: []
        for i in range(number_of_cpus)
    }  # List of jobs executed by each core
    cpus_frequencies: Dict[int, List[CPUUsedFrequency]] = {
        i: []
        for i in range(number_of_cpus)
    }  # List of CPU frequencies used by each core
    scheduling_points: List[float] = [
    ]  # Points where the scheduler have made an scheduling
    temperature_measures: Dict[float, Dict[int, PhysicalCuboid]] = {
    }  # Measures of temperature

    # Jobs being executed extra information [CPU, [start time]]
    jobs_last_section_start_time: Dict[int, float] = {
        i.identifier: -1
        for i in jobs
    }
    jobs_last_cpu_used: Dict[int, int] = {i.identifier: -1 for i in jobs}
    jobs_last_preemption_remaining_cycles: Dict[int, int] = {
        i.identifier: -1
        for i in jobs
    }

    # Last time frequency was set
    last_frequency_set_time = simulation_start_time

    # Board id
    board_thermal_id: int = number_of_cpus

    # Available memory
    jobs_memory_consumption: Dict[int, int] = {
        i.identifier:
        i.task.memory_footprint if i.task.memory_footprint is not None else 0
        for i in jobs
    }
    memory_usage: int = 0
    memory_usage_record: Dict[float, int] = {}

    # Energy management objects
    cubed_space: Optional[Model] = None
    initial_state: Optional[SimulationState] = None
    core_frequency_energy_activator: Optional[Dict[Tuple[int, int],
                                                   int]] = None
    core_task_energy_activator: Optional[Dict[Tuple[int, int], int]] = None

    # Thermal options
    if simulation_options.simulate_thermal_behaviour:
        cubed_space, initial_state, core_frequency_energy_activator, core_task_energy_activator = _generate_cubed_space(
            tasks, processor_definition, environment_specification,
            simulation_options, board_thermal_id)

    # Main control loop
    while actual_lcm_cycle < final_lcm_cycle and not hard_rt_task_miss_deadline and \
            len(active_jobs) + len(activation_dict) > 0:
        # Actual time in seconds
        actual_time_seconds = actual_lcm_cycle / lcm_frequency

        # Record temperature
        if simulation_options.simulate_thermal_behaviour:
            cubes_temperatures = cubed_space.obtain_temperature(initial_state)
            temperature_measures[actual_time_seconds] = cubes_temperatures
            cores_max_temperature = obtain_max_temperature(cubes_temperatures)
            cores_max_temperature.pop(board_thermal_id)

        # Record memory usage
        if simulation_options.simulate_memory_footprint:
            memory_usage_record[actual_time_seconds] = memory_usage

        # Major cycle start event
        major_cycle_event_require_scheduling = scheduler.on_major_cycle_start(actual_time_seconds) \
            if actual_lcm_cycle % major_cycle_lcm == 0 else False

        # Job activation events
        activated_this_cycle = [(i, j) for i, j in activation_dict.items()
                                if i <= actual_lcm_cycle]

        for i, j in activated_this_cycle:
            activation_dict.pop(i)
            for k in j:
                active_jobs.add(k)

        activation_event_require_scheduling_list = [
            scheduler.on_jobs_activation(actual_time_seconds,
                                         i / lcm_frequency,
                                         [(k, jobs_to_task_dict[k])
                                          for k in j])
            for i, j in activated_this_cycle
        ]

        activation_event_require_scheduling = any(
            activation_event_require_scheduling_list)

        # Job end event
        jobs_that_have_end = [
            i for i in active_jobs if remaining_cc_dict[i] == 0
        ]

        for i in jobs_that_have_end:
            active_jobs.remove(i)

            # Update RawSimulationResult tables in case that a task end by cc
            # Remove it from executed tasks
            job_cpu_used = jobs_last_cpu_used[i]
            jobs_being_executed_id.pop(job_cpu_used)
            job_sections_execution[job_cpu_used].append((JobSectionExecution(
                i, jobs_to_task_dict[i], jobs_last_section_start_time[i],
                actual_time_seconds, jobs_last_preemption_remaining_cycles[i] -
                remaining_cc_dict[i])))

            # Remove job from memory
            if simulation_options.simulate_memory_footprint:
                memory_usage = memory_usage - jobs_memory_consumption[i]

        end_event_require_scheduling = scheduler.on_job_execution_finished(actual_time_seconds, jobs_that_have_end) \
            if len(jobs_that_have_end) > 0 else False

        # Job missed deadline events
        deadline_this_cycle = [(i, j) for i, j in deadlines_dict.items()
                               if i <= actual_lcm_cycle]

        for i, _ in deadline_this_cycle:
            deadlines_dict.pop(i)

        jobs_deadline_this_cycle: List[int] = list(
            itertools.chain(*[j for _, j in deadline_this_cycle]))

        deadline_missed_this_cycle = [
            i for i in jobs_deadline_this_cycle if i in active_jobs
        ]

        for i in (j for j in deadline_missed_this_cycle
                  if j in firm_real_time_jobs):
            active_jobs.remove(i)  # Remove firm real time from active set

            # Update RawSimulationResult tables in case that a task reach deadline and are firm
            job_cpu_used = jobs_last_cpu_used[i]

            if jobs_being_executed_id.__contains__(
                    job_cpu_used
            ) and jobs_being_executed_id[job_cpu_used] == i:
                jobs_being_executed_id.pop(job_cpu_used)
                job_sections_execution[job_cpu_used].append(
                    (JobSectionExecution(
                        i, jobs_to_task_dict[i],
                        jobs_last_section_start_time[i], actual_time_seconds,
                        jobs_last_preemption_remaining_cycles[i] -
                        remaining_cc_dict[i])))

                # Remove job from memory
                if simulation_options.simulate_memory_footprint:
                    memory_usage = memory_usage - jobs_memory_consumption[i]

        hard_rt_task_miss_deadline = any(
            (i in hard_real_time_jobs for i in deadline_missed_this_cycle
             ))  # If some jab is hard real time set the flag

        deadline_missed_event_require_scheduling = False

        if hard_rt_task_miss_deadline:
            hard_real_time_deadline_missed_stack_trace = SimulationStackTraceHardRTDeadlineMissed(
                actual_time_seconds, {
                    j: remaining_cc_dict[j]
                    for j in deadline_missed_this_cycle
                    if j in hard_real_time_jobs
                })
        else:
            # Check if a deadline missed require rescheduling
            deadline_missed_event_require_scheduling = scheduler.on_jobs_deadline_missed(actual_time_seconds,
                                                                                         deadline_missed_this_cycle) \
                if len(deadline_missed_this_cycle) > 0 else False

        # Do scheduling if required
        if not hard_rt_task_miss_deadline and (
                major_cycle_event_require_scheduling
                or activation_event_require_scheduling
                or end_event_require_scheduling
                or deadline_missed_event_require_scheduling
                or next_scheduling_point
                == actual_lcm_cycle) and len(active_jobs) > 0:
            # Call scheduler
            jobs_being_executed_id_next, cycles_until_next_scheduler_invocation, cores_frequency_next = \
                scheduler.schedule_policy(actual_time_seconds, active_jobs, jobs_being_executed_id, cpu_frequency, None)

            if cores_frequency_next is None:
                cores_frequency_next = cpu_frequency

            # Scheduler result checks
            if simulation_options.scheduler_selections_check:
                bad_scheduler_behaviour = not (
                    available_frequencies.__contains__(cores_frequency_next)
                    and all(
                        (0 <= i < number_of_cpus
                         for i in jobs_being_executed_id_next.keys())) and all(
                             (i in active_jobs
                              for i in jobs_being_executed_id_next.values()))
                    and (cycles_until_next_scheduler_invocation is None
                         or cycles_until_next_scheduler_invocation > 0))

                if bad_scheduler_behaviour:
                    exception_message = "Error due to bad scheduler behaviour\n" + \
                                        "\t Jobs to CPU assignation: " + str(jobs_being_executed_id_next) + "\n" + \
                                        "\t Active jobs: " + str(active_jobs) + "\n" + \
                                        "\t Selected frequency: " + str(cores_frequency_next) + "\n" + \
                                        "\t Available frequencies: " + \
                                        str(available_frequencies) + "\n" + \
                                        "\t Actual time: " + str(actual_time_seconds)
                    raise Exception(exception_message)

            # Check if none preemptive task is preempted
            for i, j in jobs_being_executed_id.items():
                if j in non_preemptive_jobs and remaining_cc_dict[j] > 0 and (
                        not jobs_being_executed_id_next.__contains__(i)
                        or jobs_being_executed_id_next[i] != j):
                    # If a non preemptive task have been preempted, its execution time must be restarted
                    remaining_cc_dict[j] = jobs_cc_dict[j]

            # Check if a task is preempted
            for i, j in jobs_being_executed_id.items():
                if not jobs_being_executed_id_next.__contains__(
                        i) or jobs_being_executed_id_next[i] != j:
                    job_sections_execution[i].append((JobSectionExecution(
                        j, jobs_to_task_dict[j],
                        jobs_last_section_start_time[j], actual_time_seconds,
                        jobs_last_preemption_remaining_cycles[j] -
                        remaining_cc_dict[j])))

            # Check new tasks in execution
            for i, j in jobs_being_executed_id_next.items():
                if not jobs_being_executed_id.__contains__(
                        i) or jobs_being_executed_id[i] != j:
                    jobs_last_preemption_remaining_cycles[
                        j] = remaining_cc_dict[j]
                    jobs_last_section_start_time[j] = actual_time_seconds

            # Check if frequency have changed
            if cores_frequency_next != cpu_frequency:
                for i in range(number_of_cpus):
                    cpus_frequencies[i].append(
                        CPUUsedFrequency(cores_frequency_next,
                                         last_frequency_set_time,
                                         actual_time_seconds))

                last_frequency_set_time = actual_time_seconds

            # Update memory usage
            if simulation_options.simulate_memory_footprint:
                memory_usage = memory_usage - sum(jobs_memory_consumption[i] for i in jobs_being_executed_id.values()) \
                               + sum(jobs_memory_consumption[i] for i in jobs_being_executed_id_next.values())

            # Update RawSimulationResult tables
            scheduling_points.append(actual_time_seconds)

            # Update frequency and executed tasks
            cpu_frequency = cores_frequency_next
            jobs_being_executed_id = jobs_being_executed_id_next
            next_scheduling_point = (
                (lcm_frequency // cpu_frequency) *
                cycles_until_next_scheduler_invocation + actual_lcm_cycle
            ) if cycles_until_next_scheduler_invocation is not None else None

            for i, j in jobs_being_executed_id.items():
                jobs_last_cpu_used[j] = i

        # In case that it has been missed the state of the variables must keep without alteration
        if not hard_rt_task_miss_deadline:
            # Next cycle == min(keys(activation_dict), keys(deadline_dict), remaining cycles)
            next_major_cycle: int = major_cycle_lcm * (
                (actual_lcm_cycle // major_cycle_lcm) + 1)

            next_job_end: int = min([
                remaining_cc_dict[i] for i in jobs_being_executed_id.values()
            ]) * (lcm_frequency // cpu_frequency) + actual_lcm_cycle if len(
                jobs_being_executed_id) > 0 else next_major_cycle

            next_job_deadline: int = min(deadlines_dict.keys(
            )) if len(deadlines_dict) != 0 else next_major_cycle

            next_job_activation: int = min(activation_dict.keys(
            )) if len(activation_dict) != 0 else next_major_cycle

            next_lcm_cycle: int = min([
                next_major_cycle, next_job_end, next_job_deadline,
                next_job_activation
            ] + ([next_scheduling_point]
                 if next_scheduling_point is not None else []))

            # This is just ceil((next_lcm_cycle - actual_lcm_cycle) / cpu_frequency) to advance an integer number
            # of cycles.
            # But with this formulation avoid floating point errors
            cc_to_advance = (((next_lcm_cycle - actual_lcm_cycle) //
                              (lcm_frequency // cpu_frequency)) +
                             (0 if (next_lcm_cycle - actual_lcm_cycle) %
                              (lcm_frequency // cpu_frequency) == 0 else 1))

            # Calculated update CC tables
            for i in jobs_being_executed_id.values():
                remaining_cc_dict[i] -= cc_to_advance

            # Obtain temperature in the next simulation point
            if simulation_options.simulate_thermal_behaviour:
                if simulation_options.thermal_simulation_type == "DVFS":
                    external_energy_point_execution = {
                        core_frequency_energy_activator[(used_cpu,
                                                         cpu_frequency)]
                        for used_cpu in jobs_being_executed_id.keys()
                    }

                elif simulation_options.thermal_simulation_type == "TASK_CONSUMPTION_MEASURED":
                    external_energy_point_execution = {
                        core_task_energy_activator[(used_cpu, task_executed)]
                        for used_cpu, task_executed in
                        jobs_being_executed_id.keys()
                    }
                else:
                    external_energy_point_execution = set()

                # Apply energy
                initial_state = cubed_space.apply_energy(
                    actual_state=initial_state,
                    amount_of_time=cc_to_advance / cpu_frequency,
                    external_energy_application_points=Set.union(
                        external_energy_point_execution,
                        {i
                         for i in range(number_of_cpus)}),
                    internal_energy_application_points={
                        i
                        for i in range(number_of_cpus)
                    })

            # Update actual_lcm_cycle
            actual_lcm_cycle += (lcm_frequency //
                                 cpu_frequency) * cc_to_advance

    # In the last cycle update RawSimulationResult tables (All jobs being executed)
    for i, j in jobs_being_executed_id.items():
        job_sections_execution[i].append((JobSectionExecution(
            j, jobs_to_task_dict[j], jobs_last_section_start_time[j],
            actual_lcm_cycle / lcm_frequency,
            jobs_last_preemption_remaining_cycles[j] - remaining_cc_dict[j])))

    # Record temperature
    if simulation_options.simulate_thermal_behaviour:
        cubes_temperatures = cubed_space.obtain_temperature(initial_state)
        temperature_measures[actual_lcm_cycle /
                             lcm_frequency] = cubes_temperatures

    # In the last cycle update RawSimulationResult tables (Used frequencies)
    for i in range(number_of_cpus):
        cpus_frequencies[i].append(
            CPUUsedFrequency(cpu_frequency, last_frequency_set_time,
                             simulation_end_time))

    return RawSimulationResult(
        have_been_scheduled=True,
        scheduler_acceptance_error_message=None,
        job_sections_execution=job_sections_execution,
        cpus_frequencies=cpus_frequencies,
        scheduling_points=scheduling_points,
        temperature_measures=temperature_measures,
        hard_real_time_deadline_missed_stack_trace=
        hard_real_time_deadline_missed_stack_trace,
        memory_usage_record=memory_usage_record
        if simulation_options.simulate_memory_footprint else None)