def test_simple_graph(self, get_sector_model): regions = Mock() regions.name = 'test_regions' intervals = Mock() intervals.name = 'test_intervals' SectorModel = get_sector_model elec_scenario = ScenarioModel('scenario') elec_scenario.add_output('output', regions, intervals, 'unit') energy_model = SectorModel('model') energy_model.add_input('input', regions, intervals, 'unit') energy_model.add_dependency(elec_scenario, 'output', 'input') sos_model = SosModel('energy_sos_model') sos_model.add_model(energy_model) sos_model.add_model(elec_scenario) # Builds the dependency graph sos_model.check_dependencies() graph = sos_model.dependency_graph assert energy_model in graph assert elec_scenario in graph assert list(graph.edges()) == [(elec_scenario, energy_model)]
def test_get_model_sets(self, get_sector_model): regions = Mock() regions.name = 'test_regions' intervals = Mock() intervals.name = 'test_intervals' elec_scenario = ScenarioModel('scenario') elec_scenario.add_output('output', regions, intervals, 'unit') SectorModel = get_sector_model energy_model = SectorModel('model') energy_model.add_input('input', regions, intervals, 'unit') energy_model.add_dependency(elec_scenario, 'output', 'input') sos_model = SosModel('energy_sos_model') sos_model.add_model(energy_model) sos_model.add_model(elec_scenario) sos_model.check_dependencies() actual = sos_model._get_model_sets_in_run_order() expected = ['scenario', 'model'] for model, name in zip(actual, expected): assert model.name == name
def test_hanging_inputs(self, get_sector_model): """ sos_model_high sos_model_lo -> em """ SectorModel = get_sector_model energy_model = SectorModel('energy_sector_model') input_metadata = { 'name': 'electricity_demand_input', 'spatial_resolution': Mock(), 'temporal_resolution': Mock(), 'units': 'unit' } energy_model._inputs = MetadataSet([input_metadata]) sos_model_lo = SosModel('lower') sos_model_lo.add_model(energy_model) expected = Metadata(input_metadata['name'], input_metadata['spatial_resolution'], input_metadata['temporal_resolution'], input_metadata['units']) assert energy_model.free_inputs.names == ['electricity_demand_input'] assert sos_model_lo.free_inputs.names == ['electricity_demand_input'] sos_model_high = SosModel('higher') sos_model_high.add_model(sos_model_lo) actual = sos_model_high.free_inputs['electricity_demand_input'] assert actual == expected
def test_add_parameters(self, get_empty_sector_model): sos_model = SosModel('global') sos_model_param = { 'name': 'sos_model_param', 'description': 'A global parameter passed to all contained models', 'absolute_range': (0, 100), 'suggested_range': (3, 10), 'default_value': 3, 'units': '%' } sos_model.add_parameter(sos_model_param) expected = sos_model_param assert sos_model.parameters['sos_model_param'].as_dict() == expected assert sos_model.parameters.names == ['sos_model_param'] sector_model = get_empty_sector_model('source_model') sector_model.add_parameter({ 'name': 'sector_model_param', 'description': 'Required for the sectormodel', 'absolute_range': (0, 100), 'suggested_range': (3, 10), 'default_value': 3, 'units': '%' }) sos_model.add_model(sector_model) # SosModel contains only its own parameters assert 'sos_model_param' in sos_model.parameters.names # SectorModel has its own ParameterList, gettable by param name assert 'sector_model_param' in sector_model.parameters.names
def test_simplest_case(self, get_scenario): """One scenario only """ elec_scenario = get_scenario sos_model = SosModel('simple') sos_model.add_model(elec_scenario) data_handle = get_data_handle(sos_model) sos_model.simulate(data_handle)
def get_sos_model_object(get_sector_model_object, get_scenario_model_object): sos_model = SosModel('test_sos_model') sector_model = get_sector_model_object scenario_model = get_scenario_model_object sos_model.add_model(sector_model) sos_model.add_model(scenario_model) sector_model.add_dependency(scenario_model, 'raininess', 'raininess') return sos_model
def sos_model(sector_model, scenario_model, economic_model): """SosModel with one scenario and one sector model """ model = SosModel('test_sos_model') model.add_model(scenario_model) model.add_model(economic_model) model.add_model(sector_model) model.add_dependency(scenario_model, 'precipitation', sector_model, 'precipitation') model.add_dependency(economic_model, 'gva', sector_model, 'rGVA') return model
def test_default_parameter_passing_(self, get_empty_sector_model): """Tests that default values for global parameters are passed to all models, and others default values only passed into the intended models. """ sos_model_param = { 'name': 'sos_model_param', 'description': 'A global parameter passed to all contained models', 'absolute_range': (0, 100), 'suggested_range': (3, 10), 'default_value': 3, 'units': '%' } sos_model = SosModel('global') sos_model.add_parameter(sos_model_param) sector_model = get_empty_sector_model('source_model') # Patch the sector model so that it returns the calling arguments sector_model.simulate = lambda _, y: {'sm1': y} sos_model.add_model(sector_model) assert sos_model.simulate(2010) == {'sm1': {'sos_model_param': 3}} sector_model.add_parameter({ 'name': 'sector_model_param', 'description': 'Some meaningful text', 'absolute_range': (0, 100), 'suggested_range': (3, 10), 'default_value': 3, 'units': '%' }) assert sos_model.simulate(2010) == { 'sm1': { 'sector_model_param': 3, 'sos_model_param': 3 } } sector_model_2 = get_empty_sector_model('another') sector_model_2.simulate = lambda _, y: {'sm2': y} sos_model.add_model(sector_model_2) assert sos_model.simulate(2010) == { 'sm1': { 'sector_model_param': 3, 'sos_model_param': 3 }, 'sm2': { 'sos_model_param': 3 } }
def test_simplest_case(self, get_scenario): """One scenario only """ elec_scenario = get_scenario sos_model = SosModel('simple') sos_model.add_model(elec_scenario) actual = sos_model.simulate(2010) expected = { 'electricity_demand_scenario': { 'electricity_demand_output': np.array([[123]]) } } assert actual == expected
def test_simulate_data_not_present(self, get_sector_model): SectorModel = get_sector_model """Raise a NotImplementedError if an input is defined but no dependency links it to a data source """ sos_model = SosModel('test') model = SectorModel('test_model') model.add_input('input', Mock(), Mock(), 'units') sos_model.add_model(model) data_handle = get_data_handle(sos_model) with raises(NotImplementedError): sos_model.simulate(data_handle)
def test_nested_graph(self, get_sector_model): """If we add a nested model, all Sectormodel and ScenarioModel objects are added as nodes in the graph with edges along dependencies. SosModel objects are not included, as they are just containers for the SectorModel and ScenarioModel objects, passing up inputs for deferred linkages to dependencies. Not implemented yet: """ SectorModel = get_sector_model energy_model = SectorModel('energy_sector_model') input_metadata = { 'name': 'electricity_demand_input', 'spatial_resolution': Mock(), 'temporal_resolution': Mock(), 'units': 'unit' } energy_model._model_inputs = MetadataSet([input_metadata]) sos_model_lo = SosModel('lower') sos_model_lo.add_model(energy_model) sos_model_high = SosModel('higher') sos_model_high.add_model(sos_model_lo) with raises(NotImplementedError): sos_model_high.check_dependencies() graph = sos_model_high.dependency_graph assert graph.edges() == [] expected = networkx.DiGraph() expected.add_node(sos_model_lo) expected.add_node(energy_model) assert energy_model in graph.nodes() scenario = ScenarioModel('electricity_demand') scenario.add_output('elec_demand_output', Mock(), Mock(), 'kWh') sos_model_high.add_dependency(scenario, 'elec_demand_output', 'electricity_demand_input') sos_model_high.check_dependencies() assert graph.edges() == [(scenario, sos_model_high)]
def test_add_parameters(self, get_empty_sector_model): sos_model_param = { 'name': 'sos_model_param', 'description': 'A global parameter passed to all contained models', 'absolute_range': (0, 100), 'suggested_range': (3, 10), 'default_value': 3, 'units': '%' } sos_model = SosModel('global') sos_model.add_parameter(sos_model_param) expected = dict(sos_model_param, **{'parent': sos_model}) assert sos_model.parameters == { 'global': { 'sos_model_param': expected } } assert sos_model.parameters['global'].names == ['sos_model_param'] sector_model = get_empty_sector_model('source_model') sector_model.add_parameter({ 'name': 'sector_model_param', 'description': 'Required for the sectormodel', 'absolute_range': (0, 100), 'suggested_range': (3, 10), 'default_value': 3, 'units': '%' }) sos_model.add_model(sector_model) # SosModel contains ParameterList objects in a nested dict by model name assert 'global' in sos_model.parameters assert 'sos_model_param' in sos_model.parameters['global'].names # SosModel parameter attribute holds references to contained # model parameters keyed by model name assert 'source_model' in sos_model.parameters assert 'sector_model_param' in sos_model.parameters['source_model'] # SectorModel has a ParameterList, gettable by param name assert 'sector_model_param' in sector_model.parameters.names assert 'source_model' in sos_model.parameters assert 'sector_model_param' in sos_model.parameters['source_model']
def get_sos_model_with_model_dependency(): sos_model = SosModel('test_sos_model') ws = WaterSupplySectorModel('water_supply') ws.inputs = [{ 'name': 'raininess', 'spatial_resolution': 'LSOA', 'temporal_resolution': 'annual', 'units': 'ml' }] ws.outputs = [{ 'name': 'water', 'spatial_resolution': 'LSOA', 'temporal_resolution': 'annual', 'units': 'Ml' }, { 'name': 'cost', 'spatial_resolution': 'LSOA', 'temporal_resolution': 'annual', 'units': 'million GBP' }] ws.interventions = [{ "name": "water_asset_a", "location": "oxford" }, { "name": "water_asset_b", "location": "oxford" }, { "name": "water_asset_c", "location": "oxford" }] sos_model.add_model(ws) ws2 = WaterSupplySectorModel('water_supply_2') ws2.inputs = [{ 'name': 'water', 'spatial_resolution': 'LSOA', 'temporal_resolution': 'annual', 'units': 'Ml' }] ws2.add_dependency(ws, 'water', 'water') sos_model.add_model(ws2) return sos_model
def test_add_dependency(self, get_empty_sector_model): regions = Mock() regions.name = 'test_regions' intervals = Mock() intervals.name = 'test_intervals' units = 'test_units' sink_model = get_empty_sector_model('sink_model') sink_model.add_input('input_name', regions, intervals, units) source_model = get_empty_sector_model('source_model') source_model.add_output('output_name', regions, intervals, units) sink_model.add_dependency(source_model, 'output_name', 'input_name') sos_model = SosModel('test') sos_model.add_model(source_model) sos_model.add_model(sink_model)
def test_loop(self, get_energy_sector_model, get_water_sector_model): """Fails because no functionality to deal with loops """ energy_model = get_energy_sector_model water_model = get_water_sector_model sos_model = SosModel('energy_water_model') water_model.add_dependency(energy_model, 'fluffiness', 'fluffyness') energy_model.add_dependency(water_model, 'electricity_demand', 'electricity_demand_input') sos_model.add_model(water_model) sos_model.add_model(energy_model) assert energy_model.model_inputs.names == ['electricity_demand_input'] assert water_model.model_inputs.names == ['fluffyness'] assert sos_model.model_inputs.names == [] assert energy_model.free_inputs.names == [] assert water_model.free_inputs.names == [] assert sos_model.free_inputs.names == [] sos_model.check_dependencies() graph = sos_model.dependency_graph assert (water_model, energy_model) in graph.edges() assert (energy_model, water_model) in graph.edges() modelset = ModelSet([water_model, energy_model], sos_model) actual = modelset.guess_results(water_model, 2010, {}) expected = {'electricity_demand': np.array([1.])} # assert actual == expected sos_model.max_iterations = 100 results = sos_model.simulate(2010) expected = np.array([[0.13488114]], dtype=np.float) actual = results['energy_sector_model']['fluffiness'] np.testing.assert_allclose(actual, expected, rtol=1e-5) expected = np.array([[0.16469004]], dtype=np.float) actual = results['water_supply_model']['electricity_demand'] np.testing.assert_allclose(actual, expected, rtol=1e-5)
def test_add_dependency(self, empty_sector_model): """Add models, connect via dependency """ spec = Spec(name='hourly_value', dims=['hours'], coords={'hours': range(24)}, dtype='int') sink_model = copy(empty_sector_model) sink_model.add_input(spec) source_model = copy(empty_sector_model) source_model.add_output(spec) sos_model = SosModel('test') sos_model.add_model(source_model) sos_model.add_model(sink_model) sos_model.add_dependency(source_model, 'hourly_value', sink_model, 'hourly_value')
def test_sector_model_one_input(self, get_energy_sector_model, get_scenario): elec_scenario = get_scenario energy_model = get_energy_sector_model energy_model.add_dependency(elec_scenario, 'electricity_demand_output', 'electricity_demand_input') sos_model = SosModel('blobby') sos_model.add_model(elec_scenario) sos_model.add_model(energy_model) data_handle = get_data_handle(sos_model) results = sos_model.simulate(data_handle) expected = np.array([[100.737]]) actual = results.get_results('fluffiness', 'energy_sector_model') np.testing.assert_allclose(actual, expected, rtol=1e-5)
def test_topological_sort(self, get_sector_model): SectorModel = get_sector_model elec_scenario = ScenarioModel('scenario') elec_scenario.add_output('output', Mock(), Mock(), 'unit') elec_scenario.add_data(np.array([[[123]]]), [2010]) energy_model = SectorModel('model') energy_model.add_input('input', Mock(), Mock(), 'unit') energy_model.add_dependency(elec_scenario, 'output', 'input') sos_model = SosModel('energy_sos_model') sos_model.add_model(energy_model) sos_model.add_model(elec_scenario) sos_model.check_dependencies() graph = sos_model.dependency_graph actual = networkx.topological_sort(graph, reverse=False) assert actual == [elec_scenario, energy_model]
def test_loop(self, get_energy_sector_model, get_water_sector_model): """Fails because no functionality to deal with loops """ energy_model = get_energy_sector_model water_model = get_water_sector_model sos_model = SosModel('energy_water_model') water_model.add_dependency(energy_model, 'fluffiness', 'fluffyness') energy_model.add_dependency(water_model, 'electricity_demand', 'electricity_demand_input') sos_model.add_model(water_model) sos_model.add_model(energy_model) assert energy_model.inputs.names == ['electricity_demand_input'] assert water_model.inputs.names == ['fluffyness'] assert sos_model.inputs.names == [] assert energy_model.free_inputs.names == [] assert water_model.free_inputs.names == [] assert sos_model.free_inputs.names == [] sos_model.check_dependencies() graph = sos_model.dependency_graph assert (water_model, energy_model) in graph.edges() assert (energy_model, water_model) in graph.edges() sos_model.max_iterations = 100 data_handle = get_data_handle(sos_model) results = sos_model.simulate(data_handle) expected = np.array([[0.13488114]], dtype=np.float) actual = results.get_results('fluffiness', model_name='energy_sector_model', modelset_iteration=35) np.testing.assert_allclose(actual, expected, rtol=1e-5) expected = np.array([[0.16469004]], dtype=np.float) actual = results.get_results('electricity_demand', model_name='water_supply_model', modelset_iteration=35) np.testing.assert_allclose(actual, expected, rtol=1e-5)
def test_simple_graph(self, get_sector_model): SectorModel = get_sector_model elec_scenario = ScenarioModel('scenario') elec_scenario.add_output('output', Mock(), Mock(), 'unit') elec_scenario.add_data(np.array([[[123]]]), [2010]) energy_model = SectorModel('model') energy_model.add_input('input', Mock(), Mock(), 'unit') energy_model.add_dependency(elec_scenario, 'output', 'input') sos_model = SosModel('energy_sos_model') sos_model.add_model(energy_model) sos_model.add_model(elec_scenario) # Builds the dependency graph sos_model.check_dependencies() graph = sos_model.dependency_graph assert energy_model in graph assert elec_scenario in graph assert graph.edges() == [(elec_scenario, energy_model)]
def test_sector_model_one_input(self, get_energy_sector_model, get_scenario): elec_scenario = get_scenario energy_model = get_energy_sector_model energy_model.add_dependency(elec_scenario, 'electricity_demand_output', 'electricity_demand_input') sos_model = SosModel('blobby') sos_model.add_model(elec_scenario) sos_model.add_model(energy_model) actual = sos_model.simulate(2010) expected = { 'energy_sector_model': { 'fluffiness': np.array([[100.737]]) }, 'electricity_demand_scenario': { 'electricity_demand_output': np.array([[123]]) } } assert actual == expected
def test_get_model_sets(self, get_sector_model): SectorModel = get_sector_model elec_scenario = ScenarioModel('scenario') elec_scenario.add_output('output', Mock(), Mock(), 'unit') elec_scenario.add_data(np.array([[[123]]]), [2010]) energy_model = SectorModel('model') energy_model.add_input('input', Mock(), Mock(), 'unit') energy_model.add_dependency(elec_scenario, 'output', 'input') sos_model = SosModel('energy_sos_model') sos_model.add_model(energy_model) sos_model.add_model(elec_scenario) sos_model.check_dependencies() actual = sos_model._get_model_sets_in_run_order() expected = ['scenario', 'model'] for model, name in zip(actual, expected): assert model.name == name
def test_topological_sort(self, get_sector_model): regions = Mock() regions.name = 'test_regions' intervals = Mock() intervals.name = 'test_intervals' SectorModel = get_sector_model elec_scenario = ScenarioModel('scenario') elec_scenario.add_output('output', regions, intervals, 'unit') energy_model = SectorModel('model') energy_model.add_input('input', regions, intervals, 'unit') energy_model.add_dependency(elec_scenario, 'output', 'input') sos_model = SosModel('energy_sos_model') sos_model.add_model(energy_model) sos_model.add_model(elec_scenario) sos_model.check_dependencies() graph = sos_model.dependency_graph actual = list(networkx.topological_sort(graph)) assert actual == [elec_scenario, energy_model]
def test_composite_nested_sos_model(self, get_sector_model): """System of systems example with two nested SosModels, two Scenarios and one SectorModel. One dependency is defined at the SectorModel level, another at the lower SosModel level """ SectorModel = get_sector_model elec_scenario = ScenarioModel('electricity_demand_scenario') elec_scenario.add_output('electricity_demand_output', Mock(), Mock(), 'unit') energy_model = SectorModel('energy_sector_model') energy_model.add_input( 'electricity_demand_input', Mock(), Mock(), 'unit') energy_model.add_input('fluffiness_input', Mock(), Mock(), 'unit') energy_model.add_output('cost', Mock(), Mock(), 'unit') energy_model.add_output('fluffyness', Mock(), Mock(), 'unit') def energy_function(timestep, input_data): """Mimics the running of a sector model """ results = {} demand = input_data['electricity_demand_input'] fluff = input_data['fluffiness_input'] results['cost'] = demand * 1.2894 results['fluffyness'] = fluff * 22 return results energy_model.simulate = energy_function energy_model.add_dependency(elec_scenario, 'electricity_demand_output', 'electricity_demand_input') sos_model_lo = SosModel('lower') sos_model_lo.add_model(elec_scenario) sos_model_lo.add_model(energy_model) fluf_scenario = ScenarioModel('fluffiness_scenario') fluf_scenario.add_output('fluffiness', Mock(), Mock(), 'unit') # fluf_scenario.add_data('fluffiness', np.array([[[12]]]), [2010]) assert sos_model_lo.free_inputs.names == ['fluffiness_input'] sos_model_lo.add_dependency(fluf_scenario, 'fluffiness', 'fluffiness_input') assert sos_model_lo.inputs.names == [] sos_model_high = SosModel('higher') sos_model_high.add_model(sos_model_lo) sos_model_high.add_model(fluf_scenario) data_handle = get_data_handle(sos_model_high) actual = sos_model_high.simulate(data_handle) expected = { 'fluffiness_scenario': { 'fluffiness': 12 }, 'lower': { 'electricity_demand_scenario': { 'electricity_demand_output': 123 }, 'energy_sector_model': { 'cost': 158.5962, 'fluffyness': 264 } } } assert actual == expected