Source code for digital_comms.mobile_network.interventions

"""Decide on interventions
"""
# pylint: disable=C0103
from digital_comms.mobile_network.model import PostcodeSector

import copy
import math

################################################################
# EXAMPLE COST LOOKUP TABLE
# - TODO come back to net present value or total cost of ownership for costs
################################################################

# Postcode-sector level individual interventions
INTERVENTIONS = {
    'upgrade_to_lte': {
        'name': 'Upgrade site to LTE',
        'description': 'If a site has only 2G/3G',
        'result': '800 and 2600 bands available',
        'cost': 142446,
        'assets_to_build': [
            {
                # site_ngr to match upgraded
                'site_ngr': None,
                'frequency': '800',
                'technology': 'LTE',
                'type': 'macrocell_site',
                'bandwidth': '2x10MHz',
                # set build date when deciding
                'build_date': None,
            },
            {
                # site_ngr to match upgraded
                'site_ngr': None,
                'frequency': '2600',
                'technology': 'LTE',
                'type': 'macrocell_site',
                'bandwidth': '2x10MHz',
                # set build date when deciding
                'build_date': None,
            },
        ]
    },
    'carrier_700': {
        'name': 'Build 700 MHz carrier',
        'description': 'Available if a site has LTE',
        'result': '700 band available',
        'cost': 50917,
        'assets_to_build': [
            {
                # site_ngr to match upgraded
                'site_ngr': None,
                'frequency': ['700'],
                'technology': '5G',
                'type': 'macrocell_site',
                'bandwidth': '2x10MHz',
                # set build date when deciding
                'build_date': None,
            },
        ]
    },
    'carrier_3500': {
        'name': 'Build 3500 MHz carrier',
        'description': 'Available if a site has LTE',
        'result': '3500 band available',
        'cost': 50917,
        'assets_to_build': [
            {
                # site_ngr to match upgraded
                'site_ngr': None,
                'frequency': ['3500'],
                'technology': '5G',
                'type': 'macrocell_site',
                'bandwidth': '2x10MHz',
                # set build date when deciding
                'build_date': None,
            },
        ]
    },
    'small_cell': {
        'name': 'Build a small cell',
        'description': 'Must be deployed at preset densities to be modelled',
        'result': '2x25 MHz small cells available at given density',
        'cost': 40220,
        'assets_to_build': [
            {
                # site_ngr not used
                'site_ngr': 'small_cell_site',
                'frequency': '3700',
                'technology': '5G',
                'type': 'small_cell',
                'bandwidth': '2x25MHz',
                # set build date when deciding
                'build_date': None,
            },
        ]
    },
}

AVAILABLE_STRATEGY_INTERVENTIONS = {
    # Intervention Strategy 1
    # Minimal Intervention 'Do Nothing Scenario'
    # Build no more additional sites -> will lead to a capacity margin deficit
    # The cost will be the replacement of existing units annually based on the
    # (decommissioning rate of 10%) common asset lifetime of 10 years
    # Capacity will be the sum of 800 and 2600 MHz
    'minimal': (),

    'upgrade_to_lte': ('upgrade_to_lte'),

    # Intervention Strategy 2
    # Integrate 700 and 3500 MHz on to the macrocellular layer
    # The cost will be the addtion of another carrier on each basestation ~£15k
    # (providing thre is 4G already)
    # If 4G isn't present, the site will need major upgrades.
    'macrocell': ('upgrade_to_lte', 'carrier_700',
                  'carrier_3500'),
     # Intervention Strategy 2.
     # Integrate 700
    'macrocell_700': ('upgrade_to_lte', 'carrier_700'),

    # Intervention Strategy 3
    # Deploy a small cell layer at 3700 MHz
    # The cost will include the small cell unit and the civil works per cell
    'small-cell': ('upgrade_to_lte', 'small_cell'),

    # Intervention Strategy 4
    # Deploy a small cell layer at 3700 MHz
    # The cost will include the small cell unit and the civil works per cell
    'small-cell-and-spectrum': ('upgrade_to_lte', 'carrier_700',
                   'carrier_3500', 'small_cell'),
}


[docs]def decide_interventions(strategy, budget, service_obligation_capacity, system, timestep, simulation_parameters): """Given strategy parameters and a system return some next best intervention Params ====== strategy : str One of 'minimal', 'macrocell', 'small_cell' intervention strategies budget : int Annual budget in GBP service_obligation_capacity : float Threshold for universal mobile service, in Mbps/km^2 system : NetworkManager Gives areas (postcode sectors) with population density, demand """ available_interventions = AVAILABLE_STRATEGY_INTERVENTIONS[strategy] if service_obligation_capacity > 0: service_built, budget, service_spend = meet_service_obligation(budget, available_interventions, timestep, service_obligation_capacity, system, simulation_parameters) else: service_built = [] service_spend = [] # Build to meet demand built, budget, spend = meet_demand( budget, available_interventions, timestep, system, simulation_parameters) print("Service", len(service_built)) print("Demand", len(built)) return built + service_built, budget, spend + service_spend
[docs]def meet_service_obligation(budget, available_interventions, timestep, service_obligation_capacity, system, simulation_parameters): areas = _suggest_target_postcodes(system, service_obligation_capacity) return _suggest_interventions(budget, available_interventions, areas, timestep, simulation_parameters, service_obligation_capacity)
[docs]def meet_demand(budget, available_interventions, timestep, system, simulation_parameters): areas = _suggest_target_postcodes(system) return _suggest_interventions(budget, available_interventions, areas, timestep, simulation_parameters)
def _suggest_interventions(budget, available_interventions, areas, timestep, simulation_parameters, threshold=None): built_interventions = [] spend = [] for area in areas: area_interventions = [] if budget <= 0: break if _area_satisfied(area, area_interventions, threshold, simulation_parameters): continue # group assets by site assets_by_site = {} for asset in area.assets: if asset['site_ngr'] not in assets_by_site: assets_by_site[asset['site_ngr']] = [asset] else: assets_by_site[asset['site_ngr']].append(asset) # integrate_800 and integrate_2.6 if 'upgrade_to_lte' in available_interventions: build_option = INTERVENTIONS['upgrade_to_lte']['assets_to_build'] cost = INTERVENTIONS['upgrade_to_lte']['cost'] for site_ngr, site_assets in assets_by_site.items(): if site_ngr == 'small_cell_site': continue if 'LTE' not in [asset['technology'] for asset in site_assets]: # set both assets to this site_ngr for option in build_option: to_build = copy.copy(option) to_build['site_ngr'] = site_ngr to_build['pcd_sector'] = area.id to_build['build_date'] = timestep area_interventions.append(to_build) built_interventions.append(to_build) budget -= cost spend.append((area.id, area.lad_id, 'upgrade_to_lte', cost)) if budget <= 0: break if budget <= 0: break # integrate_700 if 'carrier_700' in available_interventions and timestep >= 2020: if _area_satisfied(area, area_interventions, threshold, simulation_parameters): continue build_option = INTERVENTIONS['carrier_700']['assets_to_build'] cost = INTERVENTIONS['carrier_700']['cost'] for site_ngr, site_assets in assets_by_site.items(): if site_ngr == 'small_cell_site': continue if 'LTE' in [asset['technology'] for asset in site_assets] and \ '700' not in [asset['frequency'] for asset in site_assets]: # set both assets to this site_ngr for option in build_option: to_build = copy.copy(option) to_build['site_ngr'] = site_ngr to_build['pcd_sector'] = area.id to_build['build_date'] = timestep area_interventions.append(to_build) built_interventions.append(to_build) spend.append((area.id, area.lad_id, 'carrier_700', cost)) budget -= cost if budget <= 0: break if budget <= 0: break # integrate_3.5 if 'carrier_3500' in available_interventions and timestep >= 2020: if _area_satisfied(area, area_interventions, threshold, simulation_parameters): continue build_option = INTERVENTIONS['carrier_3500']['assets_to_build'] cost = INTERVENTIONS['carrier_3500']['cost'] for site_ngr, site_assets in assets_by_site.items(): if site_ngr == 'small_cell_site': continue if 'LTE' in [asset['technology'] for asset in site_assets] and \ '3500' not in [asset['frequency'] for asset in site_assets]: # set both assets to this site_ngr for option in build_option: to_build = copy.copy(option) to_build['site_ngr'] = site_ngr to_build['pcd_sector'] = area.id to_build['build_date'] = timestep area_interventions.append(to_build) built_interventions.append(to_build) spend.append((area.id, area.lad_id, 'carrier_3500', cost)) budget -= cost if budget <= 0: break if budget <= 0: break # build small cells to next density if 'small_cell' in available_interventions and timestep >= 2020: if area.clutter_environment == 'rural': continue if _area_satisfied(area, area_interventions, threshold, simulation_parameters): continue build_option = INTERVENTIONS['small_cell']['assets_to_build'] cost = INTERVENTIONS['small_cell']['cost'] loop_number = 0 while True: to_build = copy.deepcopy(build_option) to_build[0]['build_date'] = timestep to_build[0]['pcd_sector'] = area.id area_interventions += to_build built_interventions += to_build spend.append((area.id, area.lad_id, 'small_cells', cost)) budget -= cost loop_number += 1 if _area_satisfied(area, area_interventions, threshold, simulation_parameters): break if budget <= 0: break return built_interventions, budget, spend def _suggest_target_postcodes(system, threshold=None): """Sort postcodes by population density (descending) - if considering threshold, filter out any with capacity above threshold """ postcodes = system.postcode_sectors.values() if threshold is not None: considered_postcodes = [pcd for pcd in postcodes if pcd.capacity < threshold] else: considered_postcodes = [p for p in postcodes] return sorted(considered_postcodes, key=lambda pcd: -pcd.population_density) def _area_satisfied(area, built_interventions, threshold, simulation_parameters): if threshold is None: target_capacity = area.demand else: target_capacity = threshold data = { "id": area.id, "lad_id": area.lad_id, "population": area.population, "area_km2": area.area, "user_throughput": area.user_throughput, } assets = area.assets + built_interventions test_area = PostcodeSector( data, assets, area._capacity_lookup_table, area._clutter_lookup, simulation_parameters, 1 ) reached_capacity = test_area.capacity return reached_capacity >= target_capacity