def test_timestep_after(self): sos_model = SosModel('test') sos_model.timesteps = [2010, 2011, 2012] assert sos_model.timestep_after(2010) == 2011 assert sos_model.timestep_after(2011) == 2012 assert sos_model.timestep_after(2012) is None assert sos_model.timestep_after(2013) is None
def test_timestep_before(self): sos_model = SosModel('test') sos_model.timesteps = [2010, 2011, 2012] assert sos_model.timestep_before(2010) is None assert sos_model.timestep_before(2011) == 2010 assert sos_model.timestep_before(2012) == 2011 assert sos_model.timestep_before(2013) is None
def test_data_not_present(self, sos_model_dict, sector_model): """Raise a NotImplementedError if an input is defined but no dependency links it to a data source """ sos_model_dict['scenario_dependencies'] = [] sos_model_dict['model_dependencies'] = [] with raises(NotImplementedError): SosModel.from_dict(sos_model_dict, [sector_model])
def test_scenario_model_not_provided(self, sos_model_dict, sector_model, economic_model): """Error on scenario not provided """ with raises(SmifDataMismatchError) as ex: SosModel.from_dict(sos_model_dict, [sector_model, economic_model]) assert "SectorModel or ScenarioModel source `climate` required by " + \ "dependency `climate (precipitation) - water_supply (precipitation)` " + \ "was not provided by the builder" in str(ex.value)
def test_scenario_variant_not_present(self, sos_model_dict, scenario_model, economic_model): """Error on sector model not provided """ with raises(SmifDataMismatchError) as ex: SosModel.from_dict(sos_model_dict, [scenario_model, economic_model]) assert "SectorModel or ScenarioModel sink `water_supply` required " + \ "by dependency `economic_model (gva) - water_supply (rGVA)` " + \ "was not provided by the builder" in str(ex.value)
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 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_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_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_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_undefined_unit_conversion(self, sos_model_dict, sector_model, scenario_model, economic_model): """Error on invalid dependency """ sector_model.inputs['precipitation'] = Spec(name='precipitation', dims=['LSOA'], coords={'LSOA': [1, 2, 3]}, dtype='float', unit='incompatible') with raises(ValueError) as ex: SosModel.from_dict(sos_model_dict, [sector_model, scenario_model, economic_model]) assert "ml!=incompatible" in str(ex.value)
def test_construct(self, sos_model_dict, scenario_model, sector_model, economic_model): """Constructing from config of the form:: { 'name': 'sos_model_name', 'description': 'friendly description of the sos model', 'dependencies': [ { 'source': str (Model.name), 'source_output': str (Metadata.name), 'sink': str (Model.name), 'sink_output': str (Metadata.name) } ] } With list of child SectorModel/ScenarioModel instances passed in alongside. """ sos_model = SosModel.from_dict( sos_model_dict, [scenario_model, sector_model, economic_model]) assert isinstance(sos_model, SosModel) assert list(sos_model.scenario_models) == [scenario_model] assert isinstance(sos_model.get_model('climate'), ScenarioModel) assert sos_model.sector_models == [sector_model, economic_model] or \ sos_model.sector_models == [economic_model, sector_model] assert isinstance(sos_model.get_model('economic_model'), SectorModel) assert isinstance(sos_model.get_model('water_supply'), SectorModel)
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_parameter_passing_into_models(self, get_empty_sector_model): """Tests that policy values for global parameters are passed to all models, and policy 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) sector_model.add_parameter({ 'name': 'sector_model_param', 'description': 'Some meaningful text', 'absolute_range': (0, 100), 'suggested_range': (3, 10), 'default_value': 3, 'units': '%' }) sector_model_2 = get_empty_sector_model('another') sector_model_2.simulate = lambda _, y: {'sm2': y} sos_model.add_model(sector_model_2) data = { 'global': { 'sos_model_param': 999 }, 'source_model': { 'sector_model_param': 123 } } assert sos_model.simulate(2010, data) == \ {'sm1': {'sector_model_param': 123, 'sos_model_param': 999}, 'sm2': {'sos_model_param': 999}}
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_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 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) sos_model.timesteps = scenario_model.timesteps sector_model.add_dependency(scenario_model, 'raininess', 'raininess') return sos_model
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 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_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): 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_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_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): 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_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_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 test_dependencies_fields(self, sos_model_dict, scenario_model, sector_model, economic_model): """Compose dependencies from scenario- and model- fields """ sos_model_dict['scenario_dependencies'] = [{ 'source': 'climate', 'source_output': 'precipitation', 'sink_input': 'precipitation', 'sink': 'water_supply', 'timestep': 'CURRENT' }, { 'source': 'climate', 'source_output': 'reservoir_level', 'sink_input': 'reservoir_level', 'sink': 'water_supply', 'timestep': 'CURRENT' }] sos_model_dict['model_dependencies'] = [{ 'source': 'economic_model', 'source_output': 'gva', 'sink_input': 'rGVA', 'sink': 'water_supply', 'timestep': 'CURRENT' }, { 'source': 'water_supply', 'source_output': 'reservoir_level', 'sink_input': 'reservoir_level', 'sink': 'water_supply', 'timestep': 'PREVIOUS' }] sos_model = SosModel.from_dict( sos_model_dict, [scenario_model, sector_model, economic_model]) actual = sos_model.as_dict() TestCase().assertCountEqual(actual['scenario_dependencies'], sos_model_dict['scenario_dependencies']) TestCase().assertCountEqual(actual['model_dependencies'], sos_model_dict['model_dependencies'])
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_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