def test_delete_parameterized_exchange(): db = DatabaseChooser("example") db.register() a = db.new_activity(code="A", name="An activity") a.save() b = db.new_activity(code="B", name="Another activity") b.save() exc = a.new_exchange(amount=0, input=b, type="technosphere", formula="foo * bar + 4") exc.save() activity_data = [{ 'name': 'reference_me', 'formula': 'sqrt(25)', 'database': 'example', 'code': "B", }, { 'name': 'bar', 'formula': 'reference_me + 2', 'database': 'example', 'code': "A", }] parameters.new_activity_parameters(activity_data, "my group") parameters.add_exchanges_to_group("my group", a) assert ActivityParameter.select().count() == 2 assert ParameterizedExchange.select().count() == 1 exc.delete() assert ActivityParameter.select().count() == 2 assert not ParameterizedExchange.select().count()
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_delete_activity_parameters(): db = DatabaseChooser("example") db.register() a = db.new_activity(code="A", name="An activity") a.save() b = db.new_activity(code="B", name="Another activity") b.save() a.new_exchange( amount=0, input=b, type="technosphere", formula="foo * bar + 4" ).save() activity_data = [ { "name": "reference_me", "formula": "sqrt(25)", "database": "example", "code": "B", }, { "name": "bar", "formula": "reference_me + 2", "database": "example", "code": "A", }, ] parameters.new_activity_parameters(activity_data, "my group") parameters.add_exchanges_to_group("my group", a) assert ActivityParameter.select().count() == 2 assert ParameterizedExchange.select().count() == 1 a.delete() assert ActivityParameter.select().count() == 1 assert not ParameterizedExchange.select().count()
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 test_delete_activity_param(qtbot): """ Remove activity parameters. """ project_db_tab = ParameterDefinitionTab() qtbot.addWidget(project_db_tab) project_db_tab.build_tables() table = project_db_tab.activity_table rect = table.visualRect(table.proxy_model.index(0, 0)) qtbot.mouseClick(table.viewport(), QtCore.Qt.LeftButton, pos=rect.center()) group = table.get_current_group() # Now delete the parameter for the selected row. table.delete_parameter(table.currentIndex()) assert table.rowCount() == 2 assert ActivityParameter.select().count() == 2 assert Group.get_or_none(name=group) # And delete the other two parameters one by one. table.delete_parameter(table.proxy_model.index(0, 0)) table.delete_parameter(table.proxy_model.index(0, 0)) assert table.rowCount() == 0 assert ActivityParameter.select().count() == 0 # Group is automatically removed with the last parameter gone assert Group.get_or_none(name=group) is None
def delete_parameter(self, proxy) -> None: """ Override the base method to include additional logic. If there are multiple `ActivityParameters` for a single activity, only delete the selected instance, otherwise use `bw.parameters.remove_from_group` to clear out the `ParameterizedExchanges` as well. """ key = self.get_key(proxy) query = (ActivityParameter.select().where( ActivityParameter.database == key[0], ActivityParameter.code == key[1])) if query.count() > 1: super().delete_parameter(proxy) else: act = bw.get_activity(key) group = self.get_current_group(proxy) bw.parameters.remove_from_group(group, act) # Also clear the group if there are no more parameters in it if ActivityParameter.get_or_none(group=group) is None: with bw.parameters.db.atomic(): Group.get(name=group).delete_instance() bw.parameters.recalculate() signals.parameters_changed.emit()
def delete_parameter(self, parameter: ParameterBase) -> None: """ Remove the given parameter from the project. If there are multiple `ActivityParameters` for a single activity, only delete the selected instance, otherwise use `bw.parameters.remove_from_group` to clear out the `ParameterizedExchanges` as well. """ if isinstance(parameter, ActivityParameter): db = parameter.database code = parameter.code amount = (ActivityParameter.select() .where((ActivityParameter.database == db) & (ActivityParameter.code == code)) .count()) if amount > 1: with bw.parameters.db.atomic(): parameter.delete_instance() else: group = parameter.group act = bw.get_activity((db, code)) bw.parameters.remove_from_group(group, act) # Also clear the group if there are no more parameters in it exists = (ActivityParameter.select() .where(ActivityParameter.group == group).exists()) if not exists: with bw.parameters.db.atomic(): Group.delete().where(Group.name == group).execute() else: with bw.parameters.db.atomic(): parameter.delete_instance() # After deleting things, recalculate and signal changes bw.parameters.recalculate() signals.parameters_changed.emit()
def recalculate_exchanges(): """ Will iterate through all activity parameters and rerun the formula interpretation for all exchanges. """ for param in ActivityParameter.select().iterator(): act = bw.get_activity((param.database, param.code)) bw.parameters.add_exchanges_to_group(param.group, act) ActivityParameter.recalculate_exchanges(param.group) signals.parameters_changed.emit()
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 get_activity_parameters(self, act): data = [ o.dict for o in ActivityParameter.select().where( ActivityParameter.database == act[0], ActivityParameter.code == act[1], ) ] if not data: return {} dct = self.order_dicts(data, "parameter") dct["group"] = ActivityParameter.get(database=act[0], code=act[1],).group return dct
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 parameterize_exchanges(self, key: tuple) -> None: """ Used whenever a formula is set on an exchange in an activity. If no `ActivityParameter` exists for the key, generate one immediately """ if ActivityParameter.get_or_none(database=key[0], code=key[1]) is None: signals.add_activity_parameter.emit(key) param = ActivityParameter.get(database=key[0], code=key[1]) act = bw.get_activity(key) bw.parameters.remove_exchanges_from_group(param.group, act) bw.parameters.add_exchanges_to_group(param.group, act) ActivityParameter.recalculate_exchanges(param.group) signals.parameters_changed.emit()
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_create_activity_param(qtbot): """ Create several activity parameters. TODO: Figure out some way of performing a drag action between tables. Use method calls for now. Until the above is implemented, take shortcuts and don't check db validity """ project_db_tab = ParameterDefinitionTab() qtbot.addWidget(project_db_tab) project_db_tab.build_tables() table = project_db_tab.activity_table # Open the order column just because we can col = table.COLUMNS.index("order") assert table.isColumnHidden(col) with qtbot.waitSignal(project_db_tab.show_order.stateChanged, timeout=1000): qtbot.mouseClick(project_db_tab.show_order, QtCore.Qt.LeftButton) assert not table.isColumnHidden(col) # Create multiple parameters for a single activity act_key = ("testdb", "act1") for _ in range(3): table.add_parameter(act_key) # Test created parameters assert ActivityParameter.select().count() == 3 # First of the multiple parameters assert table.proxy_model.index(0, 0).data() == "act_1" # Second of the multiple parameters assert table.proxy_model.index(1, 0).data() == "act_2" # The group name for the `testdb` parameters is the same. loc = table.visualRect(table.proxy_model.index(0, 0)) qtbot.mouseClick(table.viewport(), QtCore.Qt.LeftButton, pos=loc.center()) group = table.get_current_group() assert table.proxy_model.index(2, table.COLUMNS.index("group")).data() == group
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 parameterize_exchanges(self, key: tuple) -> None: """ Used whenever a formula is set on an exchange in an activity. If no `ActivityParameter` exists for the key, generate one immediately """ group = bc.build_activity_group_name(key) if not (ActivityParameter.select() .where(ActivityParameter.group == group).count()): ActivityParameterTable.add_parameter(key) act = bw.get_activity(key) with bw.parameters.db.atomic(): bw.parameters.remove_exchanges_from_group(group, act) bw.parameters.add_exchanges_to_group(group, act) ActivityParameter.recalculate_exchanges(group) signals.parameters_changed.emit()
def delete_activity(self, key): act = bw.get_activity(key) nu = len(act.upstream()) if nu: text = "activities consume" if nu > 1 else "activity consumes" QtWidgets.QMessageBox.information( None, "Not possible.", """Can't delete {}. {} upstream {} its reference product. Upstream exchanges must be modified or deleted.""".format(act, nu, text) ) else: # Check if the activity is parameterized: query = ActivityParameter.select().where( ActivityParameter.database == act[0], ActivityParameter.code == act[1] ) if query.exists(): # Remove all activity parameters Controller.delete_activity_parameter(act.key) act.delete() bw.databases.set_modified(act["database"]) signals.metadata_changed.emit(act.key) signals.database_changed.emit(act["database"]) signals.databases_changed.emit() signals.calculation_setup_changed.emit()
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 recalculate_activity_parameters(self, group: str, global_params: dict = None ) -> Optional[dict]: new_values = self.get_altered_values(group) if global_params is None: global_params = {} data = ActivityParameter.load(group) if not data: return for name, amount in new_values.items(): data[name]["amount"] = amount new_symbols = get_new_symbols(data.values(), set(data)) missing = new_symbols.difference(global_params) if missing: raise MissingName( "The following variables aren't defined:\n{}".format( "|".join(missing))) glo = self._static(global_params, needed=new_symbols) if new_symbols else None ParameterSet(data, glo).evaluate_and_set_amount_field() return self._prune_result_data(data)
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 test_edit_activity_param(qtbot): """ Alter names, amounts and formulas. Introduce dependencies through formulas """ table = ActivityParameterTable() qtbot.addWidget(table) table.sync(table.build_df()) # Fill rows with new variables table.rename_parameter(table.proxy_model.index(0, 0), "edit_act_1", True) table.model.setData(table.model.index(0, 2), "test_db3 * 3") table.rename_parameter(table.proxy_model.index(1, 0), "edit_act_2", True) table.model.setData(table.model.index(1, 2), "edit_act_1 - 3") # Test updated values assert ActivityParameter.get(name="edit_act_1").amount == 25.5 assert ActivityParameter.get(name="edit_act_2").amount == 22.5
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 clear_broken_activity_parameter(database: str, code: str, group: str) -> None: """Take the given information and attempt to remove all of the downstream parameter information. """ with bw.parameters.db.atomic() as txn: bw.parameters.remove_exchanges_from_group(group, None, False) ActivityParameter.delete().where( ActivityParameter.database == database, ActivityParameter.code == code ).execute() # Do commit to ensure .exists() call does not include deleted params txn.commit() exists = (ActivityParameter.select() .where(ActivityParameter.group == group) .exists()) if not exists: # Also clear Group if it is not in use anymore Group.delete().where(Group.name == group).execute()
def recalculate_scenario( self, scenario_values: Iterable[float]) -> (np.ndarray, np.ndarray): """ Convenience function that takes new parameter values and returns a fully-formed set of exchange amounts and indices. All parameter types are recalculated in turn before interpreting the ParameterizedExchange formulas into amounts. """ self.param_values = replace_amounts(self.param_values, scenario_values) global_project = self.recalculate_project_parameters() all_db = {} for p in DatabaseParameter.select( DatabaseParameter.database).distinct(): db = self.recalculate_database_parameters(p.database, global_project) all_db[p.database] = {x: y for x, y in db.items()} if db else {} complete_data = [] complete_indices = [] for p in ActivityParameter.select( ActivityParameter.group, ActivityParameter.database).distinct(): combination = {x: y for x, y in global_project.items() } if global_project else {} combination.update(all_db.get(p.database, {})) act = self.recalculate_activity_parameters(p.group, combination) combination.update(act) recalculated = self.recalculate_exchanges( p.group, global_params=combination) # If the parameter group contains no ParameterizedExchanges, skip. if not recalculated: continue # `data` contains the recalculated amounts for the exchanges. ids, data = zip(*recalculated) indices = [] for pk in ids: exc = ExchangeDataset.get_by_id(pk) input_key = (exc.input_database, exc.input_code) output_key = (exc.output_database, exc.output_code) if exc.input_database == bw.config.biosphere: indices.append((input_key, output_key, "biosphere")) else: indices.append((input_key, output_key, "technosphere")) complete_data.extend(data) complete_indices.extend(indices) # After recalculating all the exchanges and adding all samples and indices # to lists, format them according to presamples requirements: # eg: samples as a column of floats and indices as a row of tuples. samples = np.array(complete_data) samples = samples.reshape(1, -1).T indices = np.array(complete_indices) return samples, indices
def get_activity_groups(self, proxy, ignore_groups: list = None): """ Helper method to look into the Group and determine which if any other groups the current activity can depend on """ db = self.get_key(proxy)[0] ignore_groups = [] if ignore_groups is None else ignore_groups return (param.group for param in ( ActivityParameter.select(ActivityParameter.group).where( ActivityParameter.database == db).distinct()) if param.group not in ignore_groups)
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 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 get_usable_parameters(): """ Include all types of parameters. NOTE: This method does not take into account which formula is being edited, and therefore does not restrict which database or activity parameters are returned. """ database = DataBaseParameterTable.get_usable_parameters() activity = ([p.name, p.amount, "activity ({})".format(p.group)] for p in ActivityParameter.select()) return itertools.chain(database, activity)
def delete_activity_parameter(key: tuple) -> None: """Remove all activity parameters and underlying exchange parameters for the given key. """ query = ActivityParameter.select(ActivityParameter.group).where( ActivityParameter.database == key[0], ActivityParameter.code == key[1], ) for p in query.iterator(): bw.parameters.remove_from_group(p.group, key) bw.parameters.recalculate() signals.parameters_changed.emit()
def parse_parameter(cls, parameter) -> dict: """ Override the base method to add more steps. """ row = super().parse_parameter(parameter) # Combine the 'database' and 'code' fields of the parameter into a 'key' row["key"] = (parameter.database, parameter.code) act = bw.get_activity(row["key"]) row["product"] = act.get("reference product") or act.get("name") row["activity"] = act.get("name") row["location"] = act.get("location", "unknown") # Replace the namedtuple with the actual ActivityParameter row["parameter"] = ActivityParameter.get_by_id(parameter.id) return row