def fragment_lcia(self, fragment, quantity_ref, scenario=None, refresh=False, **kwargs): if scenario is None: scenario = '1' lcia_q = self.get_lcia_quantity(quantity_ref) endpoint = 'scenarios/%s/%s/%s/lciaresults' % (scenario, fragment, lcia_q.external_ref) lcia_r = self._archive.get_endpoint(endpoint, cache=False) if lcia_r is None or (isinstance(lcia_r, list) and all(i is None for i in lcia_r)): res = LciaResult(lcia_q, scenario=scenario) return res res = LciaResult(lcia_q, scenario=lcia_r.pop('scenarioID')) total = lcia_r.pop('total') for component in lcia_r['lciaScore']: self.add_lcia_component(res, component) self.check_total(res.total(), total) return res
def test_catalog_ref(self): """ Create an LCIA result using a catalog ref as quantity :return: """ qty = CatalogRef('fictitious.origin', 'dummy_ext_ref', entity_type='quantity', Name='Test Quantity') res = LciaResult(qty) self.assertEqual(res.total(), 0.0)
def retrieve_lcia_scores(self, process_uuid, rf_uuid, quantities=None): """ This function retrieves LCIA scores from an Ecospold02 file and stores them as characterizations in an LcFlow entity corresponding to the *first* (and presumably, only) reference intermediate flow Only stores cfs for quantities that exist locally. :param process_uuid: :param rf_uuid: :param quantities: list of quantity entities to look for (defaults to all local lcia_methods) :return: a dict of quantity uuid to score """ if quantities is None: quantities = [l for l in self.entities_by_type('quantity') if l.is_lcia_method()] import time start_time = time.time() print('Loading LCIA results for %s_%s' % (process_uuid, rf_uuid)) o = self.objectify(process_uuid, rf_uuid) self._print('%30.30s -- %5f' % ('Objectified', time.time() - start_time)) p = self._create_process_entity(o) rf = self._grab_reference_flow(o, rf_uuid) exch = ExchangeValue(p, rf, 'Output', value=1.0) tags = dict() for q in quantities: if 'Method' in q.keys(): if q['Name'] in tags: raise KeyError('Name collision %s' % q['Name']) tags[q['Name']] = q results = LciaResults(p) for char in find_tag(o, 'flowData').getchildren(): if 'impactIndicator' in char.tag: m = char.impactMethodName.text c = char.impactCategoryName.text i = char.name.text v = float(char.get('amount')) my_tag = ', '.join([m, c, i]) if my_tag in tags: q = tags[my_tag] result = LciaResult(q) cf = Characterization(rf, q, value=v, location=p['SpatialScope']) result.add_score(p.get_uuid(), exch, cf, p['SpatialScope']) results[q.get_uuid()] = result self._print('%30.30s -- %5f' % ('Impact scores collected', time.time() - start_time)) return results
def retrieve_lcia_scores(self, filename, quantities=None): """ This function retrieves LCIA scores from an Ecospold02 file and stores them as characterizations in an LcFlow entity corresponding to the *first* (and presumably, only) reference intermediate flow Only stores cfs for quantities that exist locally. :param filename: :param quantities: list of quantity entities to look for (defaults to self.quantities()) :return: a dict of quantity uuid to score """ if quantities is None: quantities = self.quantities() import time start_time = time.time() print('Loading LCIA results from %s' % filename) o = self.objectify(filename) self._print('%30.30s -- %5f' % ('Objectified', time.time() - start_time)) p = self._create_process_entity(o) rf = self._grab_reference_flow(o, spold_reference_flow(filename)) exch = ExchangeValue(p, rf, 'Output', value=1.0) tags = dict() for q in quantities: if 'Method' in q.keys(): if q['Name'] in tags: raise KeyError('Name collision %s' % q['Name']) tags[q['Name']] = q results = LciaResults(p) for char in find_tag(o, 'flowData')[0].getchildren(): if 'impactIndicator' in char.tag: m = char.impactMethodName.text c = char.impactCategoryName.text i = char.name.text v = float(char.get('amount')) my_tag = ', '.join([m, c, i]) if my_tag in tags: q = tags[my_tag] result = LciaResult(q) cf = Characterization(rf, q, value=v, location=p['SpatialScope']) result.add_score(p.get_uuid(), exch, cf, p['SpatialScope']) results[q.get_uuid()] = result self._print('%30.30s -- %5f' % ('Impact scores collected', time.time() - start_time)) return results
def lcia(self, quantity, ref_flow=None, scenario=None, flowdb=None): result = LciaResult(quantity, scenario) result.add_component(self.get_uuid(), entity=self) for ex in self.allocated_exchanges(scenario or ref_flow): if not ex.flow.has_characterization(quantity): if flowdb is not None: if quantity in flowdb.known_quantities(): factor = flowdb.lookup_single_cf(ex.flow, quantity, self['SpatialScope']) if factor is None: ex.flow.add_characterization(quantity) else: ex.flow.add_characterization(factor) factor = ex.flow.factor(quantity) result.add_score(self.get_uuid(), ex, factor, self['SpatialScope']) return result
def frag_flow_lcia(fragmentflows, quantity_ref, scenario=None, refresh=False, ignore_uncached=True): """ Recursive function to compute LCIA of a traversal record contained in a set of Fragment Flows. :param fragmentflows: :param quantity_ref: :param scenario: necessary if any remote traversals are required :param refresh: whether to refresh the LCIA CFs :param ignore_uncached: [True] whether to allow zero scores for un-cached, un-computable fragments :return: """ result = LciaResult(quantity_ref) for ff in fragmentflows: if ff.term.is_null: continue node_weight = ff.node_weight if node_weight == 0: continue try: v = ff.term.score_cache(quantity=quantity_ref, refresh=refresh, ignore_uncached=ignore_uncached) except SubFragmentAggregation: # if we were given interior fragments, recurse on them. otherwise ask remote. if len(ff.subfragments) == 0: v = ff.term.term_node.fragment_lcia(quantity_ref, scenario=scenario, refresh=refresh) else: v = frag_flow_lcia(ff.subfragments, quantity_ref, refresh=refresh) if v.is_null: continue if ff.term.direction == ff.fragment.direction: # if the directions collide (rather than complement), the term is getting run in reverse node_weight *= -1 result.add_summary(ff.fragment.uuid, ff, node_weight, v) return result
def lcia(self, process, ref_flow, quantity_ref, refresh=False, **kwargs): """ Antelope v1 doesn't support or even have any knowledge of process reference-flows. this is a somewhat significant design flaw. well, no matter. each antelopev1 process must therefore represent an allocated single operation process that has an unambiguous reference flow. This is a problem to solve on the server side; for now we just ignore the ref_flow argument. If the quantity ref is one of the ones natively known by the antelope server-- i.e. if it is a catalog ref whose origin matches the origin of the current archive-- then it is trivially used. Otherwise, the lcia call reduces to obtaining the inventory and computing LCIA locally. :param process: :param ref_flow: :param quantity_ref: :param refresh: :param kwargs: :return: """ lcia_q = self.get_lcia_quantity(quantity_ref) endpoint = '%s/%s/lciaresults' % (process, lcia_q.external_ref) lcia_r = self._archive.get_endpoint(endpoint, cache=False) res = LciaResult(lcia_q, scenario=lcia_r.pop('scenarioID')) total = lcia_r.pop('total') if len(lcia_r['lciaScore']) > 1: raise AntelopeV1Error( 'Process LCIA result contains too many components\n%s' % process) component = lcia_r['lciaScore'][0] cum = component['cumulativeResult'] self.check_total(cum, total) if 'processes/%s' % component['processID'] != process: raise AntelopeV1Error('Reference mismatch: %s begat %s' % (process, component['processID'])) self.add_lcia_component(res, component) self.check_total(res.total(), total) return res
def fg_lcia(self, process_ref, quantities=None, dist=3, scenario=None, **kwargs): """ :param process_ref: :param quantities: defaults to foreground lcia quantities :param dist: [1] how far afield to search for cfs (see CLookup.find() from flowdb) :param scenario: (not presently used) - some day the flow-quantity database will be scenario-sensitive :return: """ if self._catalog.fg is None: print('Missing a foreground!') return None if not self._catalog.is_loaded(0): self._catalog.load(0) if not self._catalog.is_loaded(process_ref.index): self._catalog.load(process_ref.index) exch = self.db.filter_exch(process_ref, elem=True, **kwargs) qs = self._prep_quantities(quantities) results = LciaResults(process_ref.entity()) for q in qs: q_result = LciaResult(q) if q in self.db.known_quantities(): for x in exch: if not x.flow.has_characterization(q): # look locally local = self[0][x.flow.get_uuid()] if local is not None and local.has_characterization(q): cf = local.factor(q) x.flow.add_characterization(cf) else: cf = self.db.lookup_single_cf(x.flow, q, dist=dist, location=process_ref['SpatialScope']) if cf is None: x.flow.add_characterization(q) else: x.flow.add_characterization(cf) self.add_to_foreground(x.flow) fac = x.flow.factor(q) q_result.add_score(process_ref.id, x, fac, process_ref['SpatialScope']) results[q.get_uuid()] = q_result return results
def lcia(self, quantity, ref_flow=None): if not quantity.is_entity: # only works for quantity refs-- in other words, always works return quantity.do_lcia(self.inventory(ref_flow=ref_flow), locale=self['SpatialScope']) else: result = LciaResult(quantity) result.add_component(self.get_uuid(), entity=self) for ex in self.inventory(ref_flow): factor = ex.flow.factor(quantity) result.add_score(self.get_uuid(), ex, factor, self['SpatialScope']) return result
def score_cache(self, quantity=None, ignore_uncached=False, refresh=False, **kwargs): if quantity is None: return self._score_cache if quantity.uuid in self._score_cache and refresh is False: return self._score_cache[quantity.uuid] else: try: res = self.compute_unit_score(quantity, refresh=refresh, **kwargs) except UnCachedScore: if ignore_uncached: res = LciaResult(quantity) else: raise self._score_cache[quantity.uuid] = res return res
def do_lcia(self, quantity, inventory, locale='GLO', refresh=False, debug=False, **kwargs): """ takes a quantity and an exchanges generator; returns an LciaResult for the given quantity. For now, does NOT pre-load quantity LCIA methods. that is a catalog action. the Qdb doesn't do catalog stuff. I am open to re-thinking it, though. :param quantity: :param inventory: generates exchanges :param locale: ['GLO'] :param refresh: [False] whether to rewrite characterization factors from the database :param debug: [False] print extra information to screen :param kwargs: just quell_biogenic_co2 for the moment :return: an LciaResult whose components are the flows of the exchanges """ q = self[quantity.link] q_ind = self._get_q_ind(q) _is_quiet = self._quiet if debug: self._quiet = False self._print('q_ind: %d' % q_ind) r = LciaResult(q) for x in inventory: if refresh or not x.flow.has_characterization(q): try: factor = self.convert(flow=x.flow, query_q_ind=q_ind, locale=locale, **kwargs) except MissingCompartment: self._print('Missing compartment %s; abandoning this exchange' % x.flow['Compartment']) continue except ConversionReferenceMismatch: print('Mismatch %s' % x) factor = None if factor is not None: self._print('factor %g %s' % (factor, x)) x.flow.add_characterization(q, value=factor, overwrite=refresh) else: self._print('factor NONE %s' % x) x.flow.add_characterization(q) if x.flow.cf(q) is not None: r.add_component(x.flow.external_ref, entity=x.flow) fac = x.flow.factor(q) fac.set_natural_direction(self.c_mgr) r.add_score(x.flow.external_ref, x, fac, locale) self._quiet = _is_quiet return r
def lcia(x, y, z): local = self[0][y.get_uuid()] if local is not y: raise AmbiguousReference('!!! this should be in foreground') return LciaResult.from_cfs(x, self.db.factors_for_flow(local, z))
def add_lcia_score(self, quantity, score, scenario=None): res = LciaResult(quantity, scenario=scenario) res.add_summary(self._parent.uuid, self._parent, 1.0, score) self._score_cache.add(res)
def compute_unit_score(self, quantity_ref, **kwargs): """ four different ways to do this. 0- we are a subfragment-- throw exception: use subfragment traversal results contained in the FragmentFlow 1- parent is bg: ask catalog to give us bg_lcia (process or fragment) 2- get fg lcia for unobserved exchanges If :param quantity_ref: :return: """ if self.is_subfrag: if self.descend: return LciaResult( quantity_ref ) # null result for subfragments that are explicitly followed else: raise SubFragmentAggregation # to be caught if self.is_bg: if self.is_fg: # surprisingly not inconsistent! in the current pre-ContextRefactor world, this is how we are handling # cached-LCIA-score nodes raise UnCachedScore('fragment: %s\nquantity: %s' % (self._parent, quantity_ref)) elif self.is_frag: # need bg_lcia method for FragmentRefs # this is probably not currently supported return self.term_node.bg_lcia( lcia_qty=quantity_ref, ref_flow=self.term_flow.external_ref, **kwargs) try: locale = self.term_node['SpatialScope'] except KeyError: locale = 'GLO' try: res = quantity_ref.do_lcia(self._unobserved_exchanges(), locale=locale, **kwargs) except PrivateArchive: if self.is_bg: print( 'terminations.compute_unit_score UNTESTED for private bg archives!' ) res = self.term_node.bg_lcia( lcia_qty=quantity_ref, ref_flow=self.term_flow.external_ref, **kwargs) else: res = self.term_node.fg_lcia( quantity_ref, ref_flow=self.term_flow.external_ref, **kwargs) print( 'terminations.compute_unit_score UNTESTED for private fg archives!' ) # res.set_scale(self.inbound_exchange_value) return res