Exemplo n.º 1
0
    def serialize(self, save_unit_scores=False):
        if self.is_null:
            return {}
        if self.is_context:
            j = {'origin': self._term_flow.origin, 'context': self._term.name}
        else:
            j = {
                'origin': self._term.origin,
                'externalId': self._term.external_ref
            }
        # saving term_flow: for subfragments, we save it only it it's specified
        if self.is_frag:
            if self._term_flow is not None:
                j['termFlow'] = self._term_flow_block()
        elif self.term_flow != self._parent.flow:
            j['termFlow'] = self._term_flow_block()

        if self.direction != comp_dir(self._parent.direction):
            j['direction'] = self.direction
        if self._descend is False:
            j['descend'] = False
        if self._parent.is_background and save_unit_scores and len(
                self._score_cache) > 0:
            j['scoreCache'] = self._serialize_score_cache()
        return j
Exemplo n.º 2
0
 def test_fg(self):
     frag = self._frag_with_child()
     z = next(frag.child_flows)
     self.assertFalse(z.term.is_fg)
     self.assertTrue(z.term.is_context)
     self.assertIs(z.flow, z.term.term_flow)
     self.assertEqual(z.direction, comp_dir(z.term.direction))
Exemplo n.º 3
0
 def _load_factor(self, ns, factor, lcia, load_all_flows=False):
     f_uuid, f_uri, f_dir = get_flow_ref(factor, ns=ns)
     if self[f_uuid] is None:
         if not load_all_flows:
             # don't bother loading factors for flows that don't exist
             return
     cf = float(find_tag(factor, 'meanValue', ns=ns))
     loc = str(find_tag(factor, 'location', ns=ns))
     if loc == '':
         loc = None
     flow = self._check_or_retrieve_child(f_uuid, f_uri)
     cx = self.tm[flow.context]
     if cx.sense is None:
         cx.sense = {'Input': 'Source', 'Output': 'Sink'}[f_dir]
     else:
         if comp_dir(cx.sense) != f_dir:
             print(
                 'flow %s: context %s sense %s conflicts with direction %s; negating factor'
                 % (f_uuid, cx, cx.sense, f_dir))
             cf *= -1
     return self.tm.add_characterization(flow.link,
                                         flow.reference_entity,
                                         lcia,
                                         cf,
                                         context=flow.context,
                                         location=loc)
Exemplo n.º 4
0
    def sys_lci(self, demand, quiet=None, **kwargs):
        """

        :param demand: an iterable of exchanges, each of which must be mapped to a foreground, interior, or exterior
        TermRef
        :return:
        """
        data = defaultdict(list)
        for x in demand:
            if isinstance(x.termination, Context):
                key = ('; '.join(x.termination.as_list()), x.flow.external_ref,
                       comp_dir(x.direction))
                try:
                    ind = self._ex_index[key]
                    data['ex_ind'].append(ind)
                    data['ex_val'].append(x.value *
                                          self._check_dirn(self._ex[ind], x))
                except KeyError:
                    data['missed'].append(x)
            elif x.termination is None:
                data['missed'].append(x)
            else:
                key = (x.termination, x.flow.external_ref)
                if key in self._fg_index:
                    ind = self._fg_index[key]
                    data['fg_ind'].append(ind)
                    data['fg_val'].append(x.value *
                                          self._check_dirn(self._fg[ind], x))
                elif key in self._bg_index:
                    ind = self._bg_index[key]
                    data['bg_ind'].append(ind)
                    data['bg_val'].append(x.value *
                                          self._check_dirn(self._bg[ind], x))
                else:
                    data['missed'].append(x)

        # compute ad_tilde  # csr_matrix(((1,), ((inx,), (0,))), shape=(dim, 1))
        x_dmd = csr_matrix(
            (data['fg_val'], (data['fg_ind'], [0] * len(data['fg_ind']))),
            shape=(self.pdim, 1))
        x_tilde = _iterate_a_matrix(self._af, x_dmd, quiet=True, **kwargs)
        ad_tilde = self._ad.dot(x_tilde).todense()
        bf_tilde = self._bf.dot(x_tilde).todense()

        # consolidate bg dependencies
        for i in range(len(data['bg_ind'])):
            ad_tilde[data['bg_ind'][i]] += data['bg_val'][i]

        # compute b
        bx = self._compute_bg_lci(ad_tilde, quiet=quiet, **kwargs) + bf_tilde

        # consolidate direct emissions
        for i in range(len(data['ex_ind'])):
            bx[data['ex_ind'][i]] += data['ex_val'][i]

        for x in self._generate_em_defs(None, csr_matrix(bx)):
            yield x

        for x in data['missed']:
            yield ExchDef(None, x.flow, x.direction, x.termination, x.value)
Exemplo n.º 5
0
        def _make_term_ext(em):
            """
            Here we decide to store contexts as '; '-concatenated strings -- which we must do bc it is serializable

            gets undone in generate_em_defs which indicates we should use a method of TermRef to produce the ExchDef
            middleman

            Note also the directionality here: comp_dir(em.direction)  em is coming from the BackgroundEngine so it
            is an Emission type, which is created from an exterior exchange using its native flow and direction [w/r/t
            the parent].  We take direction w.r.t. the context so the declaration is self-consistent, but that is not
            really sensible. But it's serialized. Thus we take comp-dir.

            Not investigated: whether problems arise when an exchange with a non-complementary context is used
            as the source for a BackgroundEngine emission, the BackgroundEngine is flattened, serialized to .mat, and
            deserialized for computation.  Not sure what the problem would be, but we should probably test it.
            [LciaResult negates value when it detects a conflicting exchange-context pairing.]

            :param em:
            :return:
            """
            ''' # <<< master
            try:
                comp = em.compartment[-1]
            except IndexError:
                comp = None
            return em.flow.external_ref, comp_dir(em.direction), comp, 0
            >>>>>>> preferred_product
            '''
            return em.flow.external_ref, comp_dir(em.direction), '; '.join(
                em.context.as_list()), 0
Exemplo n.º 6
0
 def exterior_flows(self, direction=None, search=None, **kwargs):
     """
     Exterior flows are all flows that do not have interior terminations (i.e. not found in the index targets)
     Since contexts are still in limbo, we need a default directionality (or some way to establish directionality
     for compartments..) but for now let's just use default 'output' for all exterior flows
     :param direction:
     :param search:
     :param kwargs:
     :return:
     """
     self.check_bg()
     for f in self._index.flows():
         if search_skip(f, search):
             continue
         try:
             next(self._index.targets(f.external_ref, direction=direction))
         except StopIteration:
             cx = self._index.get_context(f.context)
             dir = comp_dir(cx.sense)
             '''
             if self.is_elementary(f):
                 yield ExteriorFlow(self._archive.ref, f, 'Output', f['Compartment'])
             else:
                 yield ExteriorFlow(self._archive.ref, f, 'Output', None)
             '''
             yield ExteriorFlow(self._archive.ref, f, dir, cx)
Exemplo n.º 7
0
 def direction(self, value):
     if value is None:
         # this is the default: should set the direction by the reference.  Only non-none if from_json
         if self.is_process and self.valid:
             rx = self.term_node.reference(self.term_flow)
             value = rx.direction
         else:
             # for fg, invert direction doesn't make sense. for subfragments, direction is ignored
             value = comp_dir(self._parent.direction)
     self._direction = check_direction(value)
Exemplo n.º 8
0
 def test_create_term(self):
     frag = self._petro_frag()
     term = FlowTermination(frag, self.petro, term_flow=frag.flow)
     self.assertIs(frag.flow, term.term_flow)
     self.assertEqual(comp_dir(frag.direction), term.direction)
     self.assertTrue(term.is_process)
     self.assertFalse(term.is_null)
     self.assertFalse(term.is_null)
     self.assertFalse(term.is_subfrag)
     self.assertFalse(term.is_bg)
     self.assertFalse(term.term_is_bg)
Exemplo n.º 9
0
 def _generate_exch_defs(node_ref, data_vec, enumeration):
     rows, cols = data_vec.nonzero()
     assert all(cols == 0)
     for i in range(len(rows)):
         term = enumeration[rows[i]]
         dat = data_vec.data[i]
         if dat < 0:
             dat *= -1
             dirn = term.direction
         else:
             dirn = comp_dir(term.direction)
         yield ExchDef(node_ref, term.flow_ref, dirn, term.term_ref, dat)
Exemplo n.º 10
0
    def fragment_from_fragment_flow(self, qi, ff):
        """
        :param qi: query interface for entity lookup
        :param ff: should be a decorated entry from self._ff
        :return:
        """
        if ff['FragmentFlowID'] in self._frags:
            self._print('Already loaded: %s' % ff['FragmentFlowID'])
            return
        flow = qi.get(ff['FlowUUID'])
        direction = direction_map(ff['DirectionID'])
        stage = self.stage(ff['FragmentStageID'])
        name = ff['Name']

        # create the fragment, with or without a parent
        if ff['ParentFragmentFlowID'] == '':
            # must be a reference flow
            frag_uuid = ff['Fragment']['FragmentUUID']
            frag = create_fragment(flow,
                                   comp_dir(direction),
                                   uuid=frag_uuid,
                                   StageName=stage,
                                   Name=name,
                                   FragmentFlowID=ff['FragmentFlowID'],
                                   origin=self._origin)
            self._fragments[ff['Fragment']['FragmentID']] = frag
        else:
            try:
                parent = self._frags[ff['ParentFragmentFlowID']]
            except KeyError:
                self._print('Recursing to %s' % ff['ParentFragmentFlowID'])
                self.fragment_from_fragment_flow(
                    qi,
                    self.fragment_flow_by_index(ff['ParentFragmentFlowID']))
                parent = self._frags[ff['ParentFragmentFlowID']]

            frag_uuid = ff['FragmentUUID']
            frag = create_fragment(flow,
                                   direction,
                                   uuid=frag_uuid,
                                   Name=name,
                                   StageName=stage,
                                   parent=parent,
                                   FragmentFlowID=ff['FragmentFlowID'])

        # save the fragment
        if frag is None:
            raise TypeError
        self._frags[ff['FragmentFlowID']] = frag
Exemplo n.º 11
0
def create_fragment(flow, direction, parent=None, **kwargs):
    """
    User-facing pass-through function. Identical to internal function except that reference fragment direction is
    reversed for UX purposes- to give direction with respect to newly created reference fragment (internal direction
    is always given with respect to parent)
    :param flow:
    :param direction:
    :param parent:
    :param kwargs: uuid, name, comment, value, balance=False, units, ...
    :return:
    """
    if parent is None:
        return _create_fragment(flow, comp_dir(direction), parent=parent, **kwargs)
    else:
        return _create_fragment(flow, direction, parent=parent, **kwargs)
Exemplo n.º 12
0
 def _generate_em_defs(self, node_ref, data_vec):
     """
     Emissions have a natural direction which should not be changed.
     :param node_ref:
     :param data_vec:
     :return:
     """
     rows, cols = data_vec.nonzero()
     assert all(cols == 0)
     for i in range(len(rows)):
         term = self._ex[rows[i]]
         dat = data_vec.data[i]
         dirn = comp_dir(term.direction)
         if CONTEXT_STATUS_ == 'compat':
             _term = None
         else:
             _term = tuple(
                 term.term_ref.split('; '))  # here we undo the '; '-join
         yield ExchDef(node_ref, term.flow_ref, dirn, _term, dat)
Exemplo n.º 13
0
    def make_fragment_flow(self, ff):
        """
            A fragment traversal generates an array of FragmentFlow objects.

    X    "fragmentID": 8, - added by antelope
    X    "fragmentStageID": 80,

    f    "fragmentFlowID": 167,
    f    "name": "UO Local Collection",
    f    "shortName": "Scenario",
    f    "flowID": 371,
    f    "direction": "Output",
    f    "parentFragmentFlowID": 168,
    f    "isBackground": false,

    w    "nodeWeight": 1.0,

    t    "nodeType": "Process",
    t    "processID": 62,

    *    "isConserved": true,
    *    "flowPropertyMagnitudes": [
      {
        "flowPropertyID": 23,
        "unit": "kg",
        "magnitude": 1.0
      }
    ]

        Need to:
         * create a termination
         * create a fragment ref
         * extract node weight
         * extract magnitude
         * extract is_conserved

        :param ff: .from_antelope_v1(ff, self.query)
         JSON-formatted fragmentflow, from a v1 .NET antelope instance.  Must be modified to include StageName
         instead of fragmentStageID
        :return:
        """
        """
        This needs to be in-house because it uses the query mechanism
        :param ff:
        :return:
        """
        fpms = ff['flowPropertyMagnitudes']
        ref_mag = fpms[0]
        magnitude = ref_mag['magnitude']
        flow = self.query.get('flows/%s' % ff['flowID'])
        for fpm in fpms[1:]:
            mag_qty = self.query.get('flowproperties/%s' %
                                     fpm['flowPropertyID'])
            if fpm['magnitude'] == 0:
                val = 0
            else:
                val = fpm['magnitude'] / magnitude
            try:
                flow.characterize(mag_qty, value=val)
            except DuplicateCharacterizationError:
                if not isclose(mag_qty.cf(flow), val):
                    raise ValueError(
                        'Characterizations do not match: %g vs %g' %
                        (mag_qty.cf(flow), val))

        dirn = ff['direction']

        if 'parentFragmentFlowID' in ff:
            parent = 'fragments/%s/fragmentflows/%s' % (
                ff['fragmentID'], ff['parentFragmentFlowID'])
            frag = GhostFragment(parent, flow,
                                 dirn)  # distinctly not reference

        else:
            frag = self.query.get('fragments/%s' % ff['fragmentID'])

        node_type = ff['nodeType']
        nw = ff['nodeWeight']
        if magnitude == 0:
            inbound_ev = 0
        else:
            inbound_ev = magnitude / nw

        if node_type == 'Process':
            term_node = self.query.get('processes/%s' % ff['processID'])
            term = FlowTermination(
                frag,
                term_node,
                term_flow=flow,
                inbound_ev=inbound_ev,
                _direction=comp_dir(dirn)
            )  # antelope v1 processes do not have reference flows!
        elif node_type == 'Fragment':
            term_node = self.query.get('fragments/%s' % ff['subFragmentID'])
            term = FlowTermination(frag,
                                   term_node,
                                   term_flow=flow,
                                   inbound_ev=inbound_ev)
        else:
            term = FlowTermination.null(frag)
        if 'isConserved' in ff:
            conserved = ff['isConserved']
        else:
            conserved = False

        return FragmentFlow(frag, magnitude, nw, term, conserved)
Exemplo n.º 14
0
 def _check_dirn(term_ref, exch):
     if comp_dir(exch.direction) == term_ref.direction:
         return 1
     return -1
Exemplo n.º 15
0
    def foreground(self, process, ref_flow, traverse=False, exterior=False):
        """
        Most of the way toward making exchanges. yields a sequence of 5-tuples defining terminated exchanges.

        NOTE: traverse=True differs from the prior implementation because the old BackgroundEngine returned an Af
        matrix and the foreground routine generated one exchange per matrix entry.

        In contrast, the current implementation traverses the foreground and creates one exchange per traversal link.
        If a fragment references the same subfragment multiple times, this will result in redundant entries for the
        same fragment.  At the moment this is by design but it may be undesirable.

        An easy solution would be to keep a log of nonzero Af indices and 'continue' if one is encountered.
        :param process:
        :param ref_flow:
        :param traverse: [False] if True, generate one exchange for every traversal link. Default is to create one
        exchange for every matrix entry.  traverse=True will produce duplicate exchanges in cases where sub-fragments
        are traversed multiple times.
        :param exterior: [False] return entries for exterior flows
        :return:
        """
        if _FLATTEN_AF is False and traverse is True:
            print('Warning: traversal of foreground SCC will never terminate')

        index = self._fg_index[process, ref_flow]
        yield ExchDef(process, ref_flow, self._fg[index].direction, None, 1.0)

        cols_seen = set()
        cols_seen.add(index)

        q = [index]
        while len(q) > 0:
            current = q.pop(0)
            node = self._fg[current]
            fg_deps = self._af[:, current]
            rows, cols = fg_deps.nonzero()
            for i in range(len(rows)):
                assert cols[i] == 0  # 1-column slice
                if _FLATTEN_AF:
                    assert rows[i] > current  # well-ordered and flattened
                if rows[i] in cols_seen:
                    if traverse:
                        q.append(
                            rows[i]
                        )  # allow fragment to be traversed multiple times
                else:
                    cols_seen.add(rows[i])
                    q.append(rows[i])
                term = self._fg[rows[i]]
                dat = fg_deps.data[i]
                if dat < 0:
                    dat *= -1
                    dirn = term.direction  # comp directions w.r.t. parent node
                else:
                    dirn = comp_dir(
                        term.direction)  # comp directions w.r.t. parent node
                yield ExchDef(node.term_ref, term.flow_ref, dirn,
                              term.term_ref, dat)

            bg_deps = self._ad[:, current]
            for dep in self._generate_exch_defs(node.term_ref, bg_deps,
                                                self._bg):
                yield dep

            if exterior:
                ems = self._bf[:, current]
                for ext in self._generate_em_defs(node.term_ref, ems):
                    yield ext
Exemplo n.º 16
0
    def _traverse_term_exchanges(self, parent, multi_term, default_allocation):
        """
        Implements the Tarjan traversal
        :param parent: a ProductFlow
        :param default_allocation:
        :return:
        """
        rx = parent.process.reference(parent.flow)

        if not rx.is_reference:
            print(
                '### Nonreference RX found!\nterm: %s\nflow: %s\next_id: %s' %
                (rx.process, rx.flow, rx.process.external_ref))
            rx = parent.process.reference()
            print('    using ref %s\n' % rx)

        exchs = parent.process.inventory(ref_flow=rx)  # allocated exchanges

        for exch in exchs:
            if exch.is_reference:  # in parent.process.reference_entity:
                # we're done with the exchange
                raise TypeError(
                    'Reference exchange encountered in bg inventory %s' % exch)
            val = pval = exch.value  # allocated exchange
            if val is None or val == 0:
                # don't add zero entries (or descendants) to sparse matrix
                continue
            if exch.flow == rx.flow and exch.direction == comp_dir(
                    rx.direction) and val == 1.0 and exch.type == 'cutoff':
                # skip pass-thru flows
                print('Skipping pass-thru exchange: %s' % exch)
                continue

            # interior flow-- enforce normative direction
            if exch.direction == 'Output':
                pval *= -1
            # normal non-reference exchange. Either a dependency (if interior) or a cutoff (if exterior).
            term = self.terminate(exch, multi_term)
            if term is None:
                # cutoff -- add the exchange value to the exterior matrix
                emission = self._add_emission(
                    exch.flow, exch.direction,
                    exch.termination)  # check, create, and add all at once
                self.add_cutoff(parent, emission, val)
                continue

            # so it's interior-- does it exist already?
            i = self.check_product_flow(exch.flow, term)
            if i is None:
                # not visited -- need to visit
                i = self._create_product_flow(exch.flow, term)
                if i is None:
                    print('Cutting off at Parent process: %s\n%s %s\n' %
                          (parent.process.external_ref, exch.flow.link, term))
                    continue
                if i.debug:
                    print('Parent: %s' % parent.process)
                try:
                    self._traverse_term_exchanges(i, multi_term,
                                                  default_allocation)
                except TerminationError:
                    self._rm_product_flow_children(i)
                    raise

                # carry back lowlink, if lower
                self._set_lowlink(parent, self._lowlink(i))
            elif self.tstack.check_stack(i):
                # visited and currently on stack - carry back index if lower
                self._set_lowlink(parent, self.index(i))
            else:
                # visited, not on stack- nothing to do
                pass
            # add the exchange value to the interior matrix
            self.add_interior(parent, i, pval)

        # name an SCC if we've found one
        if self._lowlink(parent) == self.index(parent):
            self.tstack.label_scc(self.index(parent), parent.key)
Exemplo n.º 17
0
 def comp_dir(self):
     return comp_dir(self.direction)
Exemplo n.º 18
0
 def _dirn_adjust(self):
     if self._qr.context.sense is None:
         return 1.0
     elif comp_dir(self._qr.context.sense) == self._exchange.direction:
         return 1.0
     return -1.0
Exemplo n.º 19
0
 def exterior_flows(self, search=None, **kwargs):
     self.check_bg()
     for ex in self._flat.ex:
         c = ex.term_ref
         f = self[ex.flow_ref]
         yield ExteriorFlow(self.origin, f, comp_dir(ex.direction), c)  # serialization is opposite sense from API spec