def test_create_project_param(qtbot): """ Create a single Project parameter. Does not user the overarching application due to mouseClick failing """ assert bw.projects.current == "pytest_project" assert ProjectParameter.select().count() == 0 project_db_tab = ParameterDefinitionTab() qtbot.addWidget(project_db_tab) project_db_tab.build_tables() table = project_db_tab.project_table signal_list = [ signals.parameters_changed, signals.parameters_changed, signals.parameters_changed ] with qtbot.waitSignals(signal_list, timeout=1000): qtbot.mouseClick(project_db_tab.new_project_param, QtCore.Qt.LeftButton) qtbot.mouseClick(project_db_tab.new_project_param, QtCore.Qt.LeftButton) qtbot.mouseClick(project_db_tab.new_project_param, QtCore.Qt.LeftButton) assert table.rowCount() == 3 # New parameter is named 'param_1' assert table.model.index(0, 0).data() == "param_1" assert ProjectParameter.select().count() == 3 assert ProjectParameter.get_or_none(name="param_1") is not None
def test_edit_project_param(qtbot, monkeypatch): """ Edit the existing parameter to have new values. """ table = ProjectParameterTable() qtbot.addWidget(table) table.model.sync() # Edit both the name and the amount of the first parameter. monkeypatch.setattr( QtWidgets.QInputDialog, "getText", staticmethod(lambda *args, **kwargs: ("test_project", True))) table.model.handle_parameter_rename(table.proxy_model.index(0, 0)) table.model.setData(table.model.index(0, 1), 2.5) # Check that parameter is correctly stored in brightway. assert ProjectParameter.get(name="test_project").amount == 2.5 # Now edit the formula directly (without delegate) with qtbot.waitSignal(signals.parameters_changed, timeout=1000): table.model.setData(table.model.index(0, 2), "2 + 3") assert ProjectParameter.get(name="test_project").amount == 5 # Now edit the formula of the 3rd param to use the 2nd param with qtbot.waitSignal(signals.parameters_changed, timeout=1000): table.model.setData(table.model.index(2, 2), "param_2 + 3") assert ProjectParameter.get(name="param_3").amount == 4
def resetParams(db_name): """Reset project and activity parameters""" _param_registry().clear() ProjectParameter.delete().execute() ActivityParameter.delete().execute() DatabaseParameter.delete().execute() Group.delete().execute()
def test_update_alter_mean(qtbot, monkeypatch, ab_app): param = ProjectParameter.create(name="uc2", amount=1) wizard = UncertaintyWizard(param, None) qtbot.addWidget(wizard) wizard.show() # Select the lognormal distribution and set 'loc' and 'scale' fields. wizard.type.distribution.setCurrentIndex(LognormalUncertainty.id) wizard.type.loc.setText("1") wizard.type.scale.setText("0.3") wizard.type.generate_plot() assert wizard.type.complete # Now, monkeypatch Qt to ensure a 'yes' is selected for updating. monkeypatch.setattr(QMessageBox, "question", staticmethod(lambda *args: QMessageBox.Yes)) # Now trigger a 'finish' action with qtbot.waitSignal(signals.parameters_changed, timeout=100): wizard.button(QWizard.FinishButton).click() # Reload param and check that the amount is changed. param = ProjectParameter.get(name="uc2") assert "loc" in param.data and param.amount != 1 loc = param.data["loc"] assert loc == 1 assert np.isclose(np.log(param.amount), loc)
def test_create_project_param(qtbot): """ Create a single Project parameter. Does not user the overarching application due to mouseClick failing """ assert bw.projects.current == "pytest_project" assert ProjectParameter.select().count() == 0 project_db_tab = ParameterDefinitionTab() qtbot.addWidget(project_db_tab) project_db_tab.build_tables() table = project_db_tab.project_table bw.parameters.new_project_parameters([ { "name": "param_1", "amount": 1.0 }, { "name": "param_2", "amount": 1.0 }, { "name": "param_3", "amount": 1.0 }, ]) table.model.sync() assert table.rowCount() == 3 # New parameter is named 'param_1' assert table.model.index(0, 0).data() == "param_1" assert ProjectParameter.select().count() == 3 assert ProjectParameter.select().where( ProjectParameter.name == "param_1").exists()
def test_append_package(): ProjectParameter.create( name="p1", amount=1, ) ProjectParameter.create( name="p2", amount=1, ) parameters.recalculate() pbm = ParameterizedBrightwayModel("project") pbm.load_parameter_data() pbm.calculate_static() for obj in pbm.data.values(): obj['amount'] = 10 _, dirpath = pbm.save_presample('project-test') pp = PresamplesPackage(dirpath) assert len(pp) == 1 assert pp.parameters['project__p1'] == 10 Database("db").register() Group.create(name="D", order=[]) DatabaseParameter.create( database="db", name="db1", formula="2 * p1", amount=2, ) DatabaseParameter.create( database="db", name="db2", amount=2, ) ActivityParameter.create( group="D", database="db", code="D1", name="d1", formula="p1 + db1", amount=3, ) parameters.recalculate() pbm = ParameterizedBrightwayModel("D") pbm.load_existing(dirpath) expected = {'project__p1': 10, 'project__p2': 10} assert pbm.global_params == expected pbm.load_parameter_data() pbm.data = {'D__d1': pbm.data['D__d1']} pbm.data['D__d1']['amount'] = 12 _, dp = pbm.append_presample(dirpath, 'D-test') assert dp == dirpath pp = PresamplesPackage(dirpath) assert len(pp) == 2 assert pp.parameters['D__d1'] == 12 assert pp.parameters['project__p1'] == 10
def test_calculate_stochastic(): Database("db").register() ProjectParameter.create( name="p1", amount=1, ) ProjectParameter.create( name="p2", amount=1, ) parameters.recalculate() pbm = ParameterizedBrightwayModel("project") pbm.load_parameter_data() pbm.data['project__p1']['amount'] = np.ones(10) + 100 pbm.data['project__p2']['amount'] = np.ones(10) + 10 _, dirpath_project = pbm.save_presample('project-test') pp = PresamplesPackage(dirpath_project) assert len(pp) == 1 assert np.allclose(pp.parameters['project__p1'], np.ones(10) + 100) # Create rest of parameters Group.create(name="E", order=[]) ActivityParameter.create( group="E", database="db", code="E1", name="e1", formula="p1 + p2 + e2", amount=4, ) ActivityParameter.create(group="E", database="db", code="E2", name="e2", amount=1, data={ 'uncertainty type': 4, 'minimum': 1000, 'maximum': 1100 }) parameters.recalculate() pbm = ParameterizedBrightwayModel("E") pbm.load_existing(dirpath_project) pbm.load_parameter_data() result = pbm.calculate_stochastic(10) assert np.allclose(result['project__p1'], [101] * 10) assert np.allclose(result['project__p2'], [11] * 10) assert all(result['E__e2'] >= 1000) assert all(result['E__e2'] <= 1100) assert np.allclose(result['E__e1'], result['E__e2'] + 101 + 11)
def resetParams(db_name=None): """Clear parameters in live memory (registry) and on disk. Clear either all params (project and all db params) or db params from a single database (if db_name provided)""" _param_registry().clear(db_name) if db_name is None: ProjectParameter.delete().execute() ActivityParameter.delete().execute() DatabaseParameter.delete().execute() else: ActivityParameter.delete().where( ActivityParameter.database == db_name).execute() DatabaseParameter.delete().where( DatabaseParameter.database == db_name).execute() Group.delete().execute()
def test_graph_rebuild(qtbot, bw2test): """Test that the graph is correctly built and rebuilt, ensure that the 'finish' button is enabled and disabled at the correct times. """ param = ProjectParameter.create(name="test1", amount=3) wizard = UncertaintyWizard(param, None) qtbot.addWidget(wizard) wizard.show() # Check that the graph exists and distribution is 'unknown' assert wizard.type.plot.isVisible() assert wizard.type.distribution.currentIndex() == UndefinedUncertainty.id assert wizard.button(QWizard.FinishButton).isEnabled() # Select an uncertainty distribution, fill out numbers. with qtbot.waitSignal(wizard.type.distribution.currentIndexChanged, timeout=100): wizard.type.distribution.setCurrentIndex(UniformUncertainty.id) assert not wizard.type.complete # Missing values for valid uncertainty. assert not wizard.button(QWizard.FinishButton).isEnabled() # When programmatically changing values, no textEdited signal is emitted. with qtbot.assertNotEmitted(wizard.type.minimum.textEdited): wizard.type.minimum.setText("1") wizard.type.generate_plot() assert not wizard.type.complete # Still missing 'maximum' assert not wizard.button(QWizard.FinishButton).isEnabled() with qtbot.assertNotEmitted(wizard.type.minimum.textEdited): wizard.type.maximum.setText("5") wizard.type.generate_plot() assert wizard.type.complete assert wizard.button(QWizard.FinishButton).isEnabled()
def test_lognormal_mean_balance(qtbot, bw2test): uncertain = { "loc": 2, "scale": 0.2, "uncertainty type": 2, } param = ProjectParameter.create(name="uc1", amount=3, data=uncertain) wizard = UncertaintyWizard(param, None) qtbot.addWidget(wizard) wizard.show() # Compare loc with mean, loc, mean = float(wizard.type.loc.text()), float(wizard.type.mean.text()) assert np.isclose(np.exp(loc), mean) wizard.type.check_negative() assert not wizard.field("negative") # Alter mean and loc fields in turn to show balancing methods with qtbot.assertNotEmitted(wizard.type.mean.textEdited): wizard.type.mean.setText("") wizard.type.balance_loc_with_mean() wizard.type.check_negative() assert wizard.type.loc.text() == "nan" # Setting the mean to a negative number will still return the same loc # value, but it will alter the 'negative' field. with qtbot.assertNotEmitted(wizard.type.mean.textEdited): wizard.type.mean.setText("-5") wizard.type.balance_loc_with_mean() wizard.type.check_negative() assert np.isclose(np.exp(float(wizard.type.loc.text())), 5) assert wizard.field("negative")
def get_usable_parameters(self): """ Use the `key` set for the table to determine the database and group of the activity, using that information to constrain the usable parameters. """ project = ([k, v, "project"] for k, v in ProjectParameter.static().items()) if self.key is None: return project database = ([k, v, "database"] for k, v in DatabaseParameter.static(self.key[0]).items()) # Determine if the activity is already part of a parameter group. query = (Group.select().join( ActivityParameter, on=(Group.name == ActivityParameter.group)).where( ActivityParameter.database == self.key[0], ActivityParameter.code == self.key[1]).distinct()) if query.exists(): group = query.get() # First, build a list for parameters in the same group activity = ([p.name, p.amount, "activity"] for p in ActivityParameter.select().where( ActivityParameter.group == group.name)) # Then extend the list with parameters from groups in the `order` # field additions = ([p.name, p.amount, "activity"] for p in ActivityParameter.select().where( ActivityParameter.group << group.order)) activity = itertools.chain(activity, additions) else: activity = [] return itertools.chain(project, database, activity)
def process_brightway_parameters() -> Iterable[tuple]: """ Converts brightway parameters of all types into a simple structure in order of possible dependency. """ return itertools.chain( ((p.name, "project", p.amount) for p in ProjectParameter.select()), ((p.name, p.database, p.amount) for p in DatabaseParameter.select()), ((p.name, p.group, p.amount) for p in ActivityParameter.select()))
def build_df(cls): """ Build a dataframe using the ProjectParameters set in brightway """ data = [ cls.parse_parameter(p) for p in ProjectParameter.select() ] df = pd.DataFrame(data, columns=cls.combine_columns()) return df
def __init__(self, seed: Optional[int] = None): super().__init__() parameters = itertools.chain(ProjectParameter.select(), DatabaseParameter.select(), ActivityParameter.select()) self.uncertainties = UncertaintyBase.from_dicts( *[getattr(p, "data", {}) for p in parameters]) self.mc_generator = MCRandomNumberGenerator(self.uncertainties, seed=seed)
def from_bw_parameters(cls) -> 'Parameters': """Construct a Parameters list from brightway2 parameters.""" return cls(chain( (Parameter(p.name, "project", p.amount, "project") for p in ProjectParameter.select()), (Parameter(p.name, p.database, p.amount, "database") for p in DatabaseParameter.select()), (Parameter(p.name, p.group, p.amount, "activity") for p in ActivityParameter.select()), ))
def test_update_uncertainty(qtbot, ab_app): """Using the signal/controller setup, update the uncertainty of a parameter""" param = ProjectParameter.create(name="uc1", amount=3) wizard = UncertaintyWizard(param, None) qtbot.addWidget(wizard) wizard.show() wizard.type.distribution.setCurrentIndex(TriangularUncertainty.id) wizard.type.minimum.setText("1") wizard.type.maximum.setText("5") wizard.type.generate_plot() assert wizard.type.complete # Now trigger a 'finish' action with qtbot.waitSignal(signals.parameters_changed, timeout=100): wizard.button(QWizard.FinishButton).click() # Reload param param = ProjectParameter.get(name="uc1") assert "loc" in param.data and param.data["loc"] == 3
def sync(self, *args, **kwargs) -> None: self.beginResetModel() self.root.clear() self.endResetModel() self._data.update({ "project": ProjectParameter.select().iterator(), "database": DatabaseParameter.select().iterator(), "activity": ActivityParameter.select().iterator(), }) self.setup_model_data() self.updated.emit()
def recalculate_project_parameters(self) -> Optional[dict]: new_values = self.get_altered_values("project") data = ProjectParameter.load() if not data: return for name, amount in new_values.items(): data[name]["amount"] = amount ParameterSet(data).evaluate_and_set_amount_field() return self._prune_result_data(data)
def add_parameter(self) -> None: """ Build a new parameter and immediately store it. """ counter = (ProjectParameter.select().count() + DatabaseParameter.select().count()) try: bw.parameters.new_project_parameters([ {"name": "param_{}".format(counter + 1), "amount": 1.0} ], False) signals.parameters_changed.emit() except ValueError as e: simple_warning_box(self, "Name already in use!", str(e))
def write_project_parameters(self, data=None, delete_existing=True): """Write global parameters to ``ProjectParameter`` database table. ``delete_existing`` controls whether new parameters will delete_existing existing parameters, or just update values. The ``name`` field is used to determine if a parameter exists. ``data`` should be a list of dictionaries (``self.project_parameters`` is used by default): .. code-block:: python [{ 'name': name of variable (unique), 'amount': numeric value of variable (optional), 'formula': formula in Python as string (optional), optional keys like uncertainty, etc. (no limitations) }] """ if (data or self.project_parameters) is None: return if delete_existing: ProjectParameter.delete().execute() parameters.new_project_parameters(data or self.project_parameters)
def test_edit_project_param(qtbot): """ Edit the existing parameter to have new values. """ table = ProjectParameterTable() qtbot.addWidget(table) table.sync(table.build_df()) # Edit both the name and the amount of the first parameter. table.rename_parameter(table.proxy_model.index(0, 0), "test_project") table.model.setData(table.model.index(0, 1), 2.5) # Check that parameter is correctly stored in brightway. assert ProjectParameter.get(name="test_project").amount == 2.5 # Now edit the formula directly (without delegate) with qtbot.waitSignal(signals.parameters_changed, timeout=1000): table.model.setData(table.model.index(0, 2), "2 + 3") assert ProjectParameter.get(name="test_project").amount == 5 # Now edit the formula of the 3rd param to use the 2nd param with qtbot.waitSignal(signals.parameters_changed, timeout=1000): table.model.setData(table.model.index(2, 2), "param_2 + 3") assert ProjectParameter.get(name="param_3").amount == 4
def test_pedigree(qtbot, bw2test): """Configure uncertainty using the pedigree page of the wizard.""" uncertain = { "loc": 2, "scale": 0.2, "uncertainty type": 2, "pedigree": { "reliability": 1, "completeness": 2, "temporal correlation": 2, "geographical correlation": 2, "further technological correlation": 3 }, } param = ProjectParameter.create(name="uc1", amount=3, data=uncertain) wizard = UncertaintyWizard(param, None) qtbot.addWidget(wizard) wizard.show() # Uncertainty data has pedigree in it. assert "pedigree" in wizard.obj.uncertainty # Go to the pedigree page with qtbot.waitSignal(wizard.currentIdChanged, timeout=100): wizard.type.pedigree.click() assert wizard.using_pedigree # Uncertainty/Pedigree data is valid loc, mean = float(wizard.pedigree.loc.text()), float( wizard.pedigree.mean.text()) assert np.isclose(np.exp(loc), mean) # The uncertainty should be positive assert not wizard.field("negative") wizard.pedigree.check_negative() assert not wizard.field("negative") # Alter mean and loc fields in turn to show balancing methods with qtbot.assertNotEmitted(wizard.pedigree.mean.textEdited): wizard.pedigree.mean.setText("") wizard.pedigree.balance_loc_with_mean() wizard.pedigree.check_negative() assert wizard.pedigree.loc.text() == "nan" # Setting the mean to a negative number will still return the same loc # value, but it will alter the 'negative' field. with qtbot.assertNotEmitted(wizard.pedigree.mean.textEdited): wizard.pedigree.mean.setText("-5") wizard.pedigree.balance_loc_with_mean() wizard.pedigree.check_negative() assert np.isclose(np.exp(float(wizard.pedigree.loc.text())), 5) assert wizard.field("negative")
def add_parameter(self) -> None: """ Add a new database parameter to the dataframe NOTE: The new parameter uses the first database it can find. """ counter = (ProjectParameter.select().count() + DatabaseParameter.select().count()) database = next(iter(bw.databases)) try: bw.parameters.new_database_parameters([ {"name": "param_{}".format(counter + 1), "amount": 1.0} ], database, False) signals.parameters_changed.emit() except ValueError as e: simple_warning_box(self, "Name already in use!", str(e))
def get_interpreter(self) -> Interpreter: """ Use the activity key to determine which symbols are added to the formula interpreter. """ interpreter = Interpreter() act = ActivityParameter.get_or_none(database=self.key[0], code=self.key[1]) if act: interpreter.symtable.update( ActivityParameter.static(act.group, full=True)) else: print("No parameter found for {}, creating one on formula save". format(self.key)) interpreter.symtable.update(ProjectParameter.static()) interpreter.symtable.update(DatabaseParameter.static(self.key[0])) return interpreter
def __init__(self): self._project_params = ProjectParameter.load() self._db_params = { p.database: DatabaseParameter.load(p.database) for p in DatabaseParameter.select(DatabaseParameter.database).distinct() } self._act_params = { p.group: ActivityParameter.load(p.group) for p in ActivityParameter.select(ActivityParameter.group).distinct() } self._distinct_act_params = [ p for p in (ActivityParameter .select(ActivityParameter.group, ActivityParameter.database) .distinct()) ] self._exc_params = [p for p in ParameterizedExchange.select()]
def export_db(db_name, filename) : """Export Db and linked parameters""" db = bw.Database(db_name) db_params = DatabaseParameter.select().where(DatabaseParameter.database == db_name) # Export Db params db.metadata["database_parameters"] = [param_data(param) for param in db_params] # List of all project params used in this dataset used_project_params = list(param.name for param in _listParams(db_name) if param.dbname is None) if len(used_project_params) > 0 : error('Warning : this DB uses project parameters that are exported as well and might override project params at import time : ', used_project_params) proj_params = list(ProjectParameter.get(ProjectParameter.name==name) for name in used_project_params) db.metadata["project_parameters"] = [param_data(param) for param in proj_params] BW2Package._write_file(filename, [BW2Package._prepare_obj(db, False)])
def test_uncertainty_wizard_simple(qtbot, bw2test, capsys): """Use extremely simple text to open the wizard and go to all the pages.""" param = ProjectParameter.create(name="test1", amount=3) wizard = UncertaintyWizard(param, None) qtbot.addWidget(wizard) wizard.show() assert "uncertainty type" in wizard.uncertainty_info wizard.extract_uncertainty() wizard.extract_lognormal_loc() # Go to the pedigree page with qtbot.waitSignal(wizard.currentIdChanged, timeout=100): wizard.type.pedigree.click() # Pedigree is empty, so complaint is issued. captured = capsys.readouterr() assert "Could not extract pedigree data" in captured.out # Now go back for giggles. with qtbot.waitSignal(wizard.currentIdChanged, timeout=100): wizard.button(QWizard.BackButton).click() assert not wizard.using_pedigree
def sync(self) -> None: self.data.update({ "project": ProjectParameter.select().iterator(), "database": DatabaseParameter.select().iterator(), "activity": ActivityParameter.select().iterator(), })
def get_interpreter() -> Interpreter: interpreter = Interpreter() interpreter.symtable.update(ProjectParameter.static()) return interpreter
def get_usable_parameters(): return ([k, v, "project"] for k, v in ProjectParameter.static().items())