edges 1.0.0__py3-none-any.whl → 1.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of edges might be problematic. Click here for more details.

edges/edgelcia.py CHANGED
@@ -6,7 +6,6 @@ LCIA class.
6
6
 
7
7
  import math
8
8
  from collections import defaultdict
9
- import logging
10
9
  import json
11
10
  from typing import Optional
12
11
  from pathlib import Path
@@ -47,9 +46,9 @@ from .georesolver import GeoResolver
47
46
  from .uncertainty import sample_cf_distribution, make_distribution_key, get_rng_for_key
48
47
  from .filesystem_constants import DATA_DIR
49
48
 
50
- # delete the logs
51
- with open("edgelcia.log", "w", encoding="utf-8"):
52
- pass
49
+ import logging
50
+
51
+ logger = logging.getLogger(__name__)
53
52
 
54
53
 
55
54
  def add_cf_entry(
@@ -333,6 +332,8 @@ class EdgeLCIA:
333
332
  # Accept both "parameters" and "scenarios" for flexibility
334
333
  self.parameters = parameters or {}
335
334
 
335
+ self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
336
+
336
337
  self.scenario = scenario # New: store default scenario
337
338
  self.scenario_length = validate_parameter_lengths(parameters=self.parameters)
338
339
  self.use_distributions = use_distributions
@@ -340,9 +341,6 @@ class EdgeLCIA:
340
341
  self.random_seed = random_seed if random_seed is not None else 42
341
342
  self.random_state = np.random.default_rng(self.random_seed)
342
343
 
343
- self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
344
- self.logger.setLevel(logging.INFO)
345
-
346
344
  self.lca = bw2calc.LCA(demand=self.demand)
347
345
  self._load_raw_lcia_data()
348
346
  self.cfs_mapping = []
@@ -509,8 +507,10 @@ class EdgeLCIA:
509
507
  for pos in cf["positions"]
510
508
  }
511
509
 
512
- print(
513
- f"Processed edges: {len(self.processed_biosphere_edges) + len(self.processed_technosphere_edges)}"
510
+ logger.info(
511
+ "Processed edges: %d",
512
+ len(self.processed_biosphere_edges)
513
+ + len(self.processed_technosphere_edges),
514
514
  )
515
515
 
516
516
  self.unprocessed_biosphere_edges = [
@@ -749,8 +749,10 @@ class EdgeLCIA:
749
749
  Leaves near-misses due to 'location' for later geo steps.
750
750
  """
751
751
 
752
+ log = self.logger.getChild("map") # edges.edgelcia.EdgeLCIA.map
753
+
752
754
  self._initialize_weights()
753
- self._preprocess_lookups() # will populate *_bio and *_tech lookups (see patch below)
755
+ self._preprocess_lookups() # populates lookups and prefix indexes
754
756
 
755
757
  # ---- Build direction-specific bundles -----------------------------------
756
758
  DIR_BIO = "biosphere-technosphere"
@@ -806,13 +808,57 @@ class EdgeLCIA:
806
808
  self.reversed_supplier_lookup_tech,
807
809
  )
808
810
 
809
- classification_match_cache = {}
810
- entries_before = len(self.cfs_mapping)
811
+ # --- helpers for concise logging -----------------------------------------
812
+ def _short(d, limit=180):
813
+ try:
814
+ s = str(d)
815
+ except Exception:
816
+ s = repr(d)
817
+ return s if len(s) <= limit else s[: limit - 1] + "…"
818
+
819
+ def _count_none(x):
820
+ return 0 if x is None else (len(x) if hasattr(x, "__len__") else 1)
821
+
822
+ # High-level preamble
823
+ log.debug(
824
+ "START map_exchanges | biosphere_edges=%d | technosphere_edges=%d | CFs=%d | req_supplier=%s | req_consumer=%s",
825
+ len(self.biosphere_edges),
826
+ len(self.technosphere_edges),
827
+ len(self.raw_cfs_data),
828
+ sorted(self.required_supplier_fields),
829
+ sorted(self.required_consumer_fields),
830
+ )
831
+ log.debug(
832
+ "Lookups | supplier_bio=%d keys | supplier_tech=%d keys | consumer=%d keys",
833
+ len(self.supplier_lookup_bio),
834
+ len(self.supplier_lookup_tech),
835
+ len(self.consumer_lookup),
836
+ )
837
+
838
+ matched_positions_total = 0
839
+ allow_bio_added = 0
840
+ allow_tec_added = 0
811
841
 
812
842
  # Bind hot locals (micro-optimization)
813
843
  consumer_lookup = self.consumer_lookup
814
844
  reversed_consumer_lookup = self.reversed_consumer_lookup
815
845
 
846
+ # ---- Precompute required field tuples (no 'classifications') once
847
+ req_sup_nc = getattr(self, "_req_sup_nc", None)
848
+ if req_sup_nc is None:
849
+ self._req_sup_nc = tuple(
850
+ sorted(
851
+ k for k in self.required_supplier_fields if k != "classifications"
852
+ )
853
+ )
854
+ self._req_con_nc = tuple(
855
+ sorted(
856
+ k for k in self.required_consumer_fields if k != "classifications"
857
+ )
858
+ )
859
+ req_sup_nc = self._req_sup_nc
860
+ req_con_nc = self._req_con_nc
861
+
816
862
  # Iterate CFs
817
863
  for i, cf in enumerate(tqdm(self.raw_cfs_data, desc="Mapping exchanges")):
818
864
  s_crit = cf["supplier"]
@@ -825,18 +871,16 @@ class EdgeLCIA:
825
871
 
826
872
  if not rem:
827
873
  # This direction already fully characterized
874
+ log.debug("CF[%d] dir=%s skipped: no remaining edges.", i, dir_name)
828
875
  continue
829
876
 
830
- # ---------- SUPPLIER side ----------
831
- # Classifications prefilter (only across current adjacency keys)
832
877
  # ---------- SUPPLIER side ----------
833
878
  if "classifications" in s_crit:
834
- # prefix-indexed fetch; intersect with current adjacency supplier keys
835
879
  s_class_hits = _cls_candidates_from_cf(
836
880
  s_crit["classifications"],
837
881
  (
838
882
  self.cls_prefidx_supplier_bio
839
- if dir_name == "biosphere-technosphere"
883
+ if dir_name == DIR_BIO
840
884
  else self.cls_prefidx_supplier_tech
841
885
  ),
842
886
  adjacency_keys=set(ebs.keys()),
@@ -844,51 +888,28 @@ class EdgeLCIA:
844
888
  else:
845
889
  s_class_hits = None
846
890
 
847
- # Non-class matches via cached index (location tested last internally)
848
891
  cached_match_with_index.index = s_index
849
892
  cached_match_with_index.lookup_mapping = s_lookup
850
893
  cached_match_with_index.reversed_lookup = s_reversed
851
894
 
852
- # ---- Precompute required field tuples (no 'classifications')
853
- req_sup_nc = getattr(self, "_req_sup_nc", None)
854
- if req_sup_nc is None:
855
- self._req_sup_nc = tuple(
856
- sorted(
857
- k
858
- for k in self.required_supplier_fields
859
- if k != "classifications"
860
- )
861
- )
862
- self._req_con_nc = tuple(
863
- sorted(
864
- k
865
- for k in self.required_consumer_fields
866
- if k != "classifications"
867
- )
868
- )
869
- req_sup_nc = self._req_sup_nc
870
- req_con_nc = self._req_con_nc
871
-
872
895
  s_nonclass = {k: v for k, v in s_crit.items() if k != "classifications"}
873
896
  s_out = cached_match_with_index(make_hashable(s_nonclass), req_sup_nc)
874
897
 
875
- # Full supplier candidates (filter by adjacency & classifications)
876
- s_cands = s_out.matches
898
+ s_matches_raw = list(s_out.matches) # before adjacency & class refinement
877
899
  if s_class_hits is not None:
878
- s_cands = list(set(s_cands) & set(s_class_hits))
879
- s_cands = [s for s in s_cands if s in ebs] # must still have consumers
900
+ s_cands = list(set(s_out.matches) & set(s_class_hits))
901
+ else:
902
+ s_cands = list(s_out.matches)
903
+ # must still have consumers in adjacency
904
+ s_cands = [s for s in s_cands if s in ebs]
880
905
 
881
- # near-miss on location only (also filtered by classifications if present)
882
906
  s_loc_only = set(s_out.location_only_rejects)
883
907
  if s_class_hits is not None:
884
908
  s_loc_only &= set(s_class_hits)
885
-
886
909
  s_loc_required = ("location" in s_crit) and (
887
910
  s_crit.get("location") is not None
888
911
  )
889
912
 
890
- # ---------- CONSUMER side ----------
891
- # Classifications prefilter (use current direction’s consumer adjacency keys)
892
913
  # ---------- CONSUMER side ----------
893
914
  if "classifications" in c_crit:
894
915
  c_class_hits = _cls_candidates_from_cf(
@@ -906,28 +927,82 @@ class EdgeLCIA:
906
927
  c_nonclass = {k: v for k, v in c_crit.items() if k != "classifications"}
907
928
  c_out = cached_match_with_index(make_hashable(c_nonclass), req_con_nc)
908
929
 
909
- c_cands = c_out.matches
930
+ c_matches_raw = list(c_out.matches)
910
931
  if c_class_hits is not None:
911
- c_cands = list(set(c_cands) & set(c_class_hits))
912
- c_cands = [c for c in c_cands if c in ebc] # must still have suppliers
932
+ c_cands = list(set(c_out.matches) & set(c_class_hits))
933
+ else:
934
+ c_cands = list(c_out.matches)
935
+ c_cands = [c for c in c_cands if c in ebc]
913
936
 
914
937
  c_loc_only = set(c_out.location_only_rejects)
915
938
  if c_class_hits is not None:
916
939
  c_loc_only &= set(c_class_hits)
917
-
918
940
  c_loc_required = ("location" in c_crit) and (
919
941
  c_crit.get("location") is not None
920
942
  )
921
943
 
944
+ # ---- DEBUG: explain empty candidate sets
945
+ if not s_cands:
946
+ reason = []
947
+ if not s_matches_raw:
948
+ reason.append("no-index-match")
949
+ else:
950
+ reason.append(f"raw-matches={len(s_matches_raw)}")
951
+ if s_class_hits is not None and not (
952
+ set(s_matches_raw) & set(s_class_hits)
953
+ ):
954
+ reason.append("class-filtered-out")
955
+ if s_class_hits is None:
956
+ reason.append("no-class-filter")
957
+ # check adjacency pruning
958
+ pruned = [s for s in s_matches_raw if s not in ebs]
959
+ if pruned and len(pruned) == len(s_matches_raw):
960
+ reason.append("all-pruned-by-adjacency")
961
+ log.debug(
962
+ "CF[%d] dir=%s supplier candidates empty | reasons=%s | s_crit=%s | raw=%d class_hits=%s ebs_keys=%d",
963
+ i,
964
+ dir_name,
965
+ ",".join(reason),
966
+ _short(s_crit),
967
+ len(s_matches_raw),
968
+ _count_none(s_class_hits),
969
+ len(ebs),
970
+ )
971
+
972
+ if not c_cands:
973
+ reason = []
974
+ if not c_matches_raw:
975
+ reason.append("no-index-match")
976
+ else:
977
+ reason.append(f"raw-matches={len(c_matches_raw)}")
978
+ if c_class_hits is not None and not (
979
+ set(c_matches_raw) & set(c_class_hits)
980
+ ):
981
+ reason.append("class-filtered-out")
982
+ if c_class_hits is None:
983
+ reason.append("no-class-filter")
984
+ pruned = [c for c in c_matches_raw if c not in ebc]
985
+ if pruned and len(pruned) == len(c_matches_raw):
986
+ reason.append("all-pruned-by-adjacency")
987
+ log.debug(
988
+ "CF[%d] dir=%s consumer candidates empty | reasons=%s | c_crit=%s | raw=%d class_hits=%s ebc_keys=%d",
989
+ i,
990
+ dir_name,
991
+ ",".join(reason),
992
+ _short(c_crit),
993
+ len(c_matches_raw),
994
+ _count_none(c_class_hits),
995
+ len(ebc),
996
+ )
997
+
922
998
  # ---------- Combine full matches using adjacency intersections ----------
923
999
  positions = []
924
1000
  if s_cands and c_cands:
925
- cset = set(c_cands) # build once; reuse later for allowlists too
1001
+ cset = set(c_cands)
926
1002
  for s in s_cands:
927
1003
  cs = ebs.get(s)
928
1004
  if not cs:
929
1005
  continue
930
- # no generator, no rem check here – pruning below already guards it
931
1006
  for c in cs:
932
1007
  if c in cset:
933
1008
  positions.append((s, c))
@@ -942,6 +1017,17 @@ class EdgeLCIA:
942
1017
  value=cf["value"],
943
1018
  uncertainty=cf.get("uncertainty"),
944
1019
  )
1020
+ matched_positions_total += len(positions)
1021
+ log.debug(
1022
+ "CF[%d] dir=%s MATCH | positions=%d | s_cands=%d c_cands=%d | s_loc_only=%d c_loc_only=%d",
1023
+ i,
1024
+ dir_name,
1025
+ len(positions),
1026
+ len(s_cands),
1027
+ len(c_cands),
1028
+ len(s_loc_only),
1029
+ len(c_loc_only),
1030
+ )
945
1031
 
946
1032
  # prune matched edges from this direction
947
1033
  for s, c in positions:
@@ -953,12 +1039,24 @@ class EdgeLCIA:
953
1039
  ebc[c].discard(s)
954
1040
  if not ebc[c]:
955
1041
  del ebc[c]
1042
+ else:
1043
+ log.debug(
1044
+ "CF[%d] dir=%s NO-MATCH | s_cands=%d c_cands=%d | s_loc_only=%d c_loc_only=%d | rem=%d",
1045
+ i,
1046
+ dir_name,
1047
+ len(s_cands),
1048
+ len(c_cands),
1049
+ len(s_loc_only),
1050
+ len(c_loc_only),
1051
+ len(rem),
1052
+ )
956
1053
 
957
1054
  # ---------- Build near-miss allowlists (location-only) ----------
958
1055
  # supplier near-miss with consumer full matches
959
1056
  if s_loc_required and s_loc_only and c_cands:
960
1057
  cset = set(c_cands)
961
1058
  bucket = allow_bio if dir_name == DIR_BIO else allow_tec
1059
+ added = 0
962
1060
  for s in s_loc_only:
963
1061
  cs = ebs.get(s)
964
1062
  if not cs:
@@ -968,11 +1066,24 @@ class EdgeLCIA:
968
1066
  for c in hit:
969
1067
  if (s, c) in rem:
970
1068
  bucket.add((s, c))
1069
+ added += 1
1070
+ if added:
1071
+ if dir_name == DIR_BIO:
1072
+ allow_bio_added += added
1073
+ else:
1074
+ allow_tec_added += added
1075
+ log.debug(
1076
+ "CF[%d] dir=%s allowlist add (supplier loc-only) | added=%d",
1077
+ i,
1078
+ dir_name,
1079
+ added,
1080
+ )
971
1081
 
972
1082
  # consumer near-miss with supplier full matches
973
1083
  if c_loc_required and c_loc_only and s_cands:
974
1084
  sset = set(s_cands)
975
1085
  bucket = allow_bio if dir_name == DIR_BIO else allow_tec
1086
+ added = 0
976
1087
  for c in c_loc_only:
977
1088
  ss = ebc.get(c)
978
1089
  if not ss:
@@ -982,11 +1093,24 @@ class EdgeLCIA:
982
1093
  for s in hit:
983
1094
  if (s, c) in rem:
984
1095
  bucket.add((s, c))
1096
+ added += 1
1097
+ if added:
1098
+ if dir_name == DIR_BIO:
1099
+ allow_bio_added += added
1100
+ else:
1101
+ allow_tec_added += added
1102
+ log.debug(
1103
+ "CF[%d] dir=%s allowlist add (consumer loc-only) | added=%d",
1104
+ i,
1105
+ dir_name,
1106
+ added,
1107
+ )
985
1108
 
986
1109
  # both sides near-miss (rare but useful)
987
1110
  if s_loc_required and c_loc_required and s_loc_only and c_loc_only:
988
1111
  cset = set(c_loc_only)
989
1112
  bucket = allow_bio if dir_name == DIR_BIO else allow_tec
1113
+ added = 0
990
1114
  for s in s_loc_only:
991
1115
  cs = ebs.get(s)
992
1116
  if not cs:
@@ -996,6 +1120,18 @@ class EdgeLCIA:
996
1120
  for c in hit:
997
1121
  if (s, c) in rem:
998
1122
  bucket.add((s, c))
1123
+ added += 1
1124
+ if added:
1125
+ if dir_name == DIR_BIO:
1126
+ allow_bio_added += added
1127
+ else:
1128
+ allow_tec_added += added
1129
+ log.debug(
1130
+ "CF[%d] dir=%s allowlist add (both loc-only) | added=%d",
1131
+ i,
1132
+ dir_name,
1133
+ added,
1134
+ )
999
1135
 
1000
1136
  self._update_unprocessed_edges()
1001
1137
 
@@ -1003,6 +1139,17 @@ class EdgeLCIA:
1003
1139
  self.eligible_edges_for_next_bio = allow_bio
1004
1140
  self.eligible_edges_for_next_tech = allow_tec
1005
1141
 
1142
+ log.debug(
1143
+ "END map_exchanges | matched_positions=%d | allow_bio=%d | allow_tec=%d | processed_bio=%d | processed_tech=%d | unprocessed_bio=%d | unprocessed_tech=%d",
1144
+ matched_positions_total,
1145
+ len(allow_bio),
1146
+ len(allow_tec),
1147
+ len(self.processed_biosphere_edges),
1148
+ len(self.processed_technosphere_edges),
1149
+ len(self.unprocessed_biosphere_edges),
1150
+ len(self.unprocessed_technosphere_edges),
1151
+ )
1152
+
1006
1153
  def map_aggregate_locations(self) -> None:
1007
1154
  """
1008
1155
  Map unmatched exchanges using CFs from broader (aggregated) regions.
@@ -1034,7 +1181,7 @@ class EdgeLCIA:
1034
1181
  """
1035
1182
 
1036
1183
  self._initialize_weights()
1037
- print("Handling static regions...")
1184
+ logger.info("Handling static regions")
1038
1185
 
1039
1186
  cf_operators = {
1040
1187
  cf["supplier"].get("operator", "equals") for cf in self.raw_cfs_data
@@ -1181,47 +1328,48 @@ class EdgeLCIA:
1181
1328
  )
1182
1329
 
1183
1330
  # Pass 1
1184
- for sig, group_edges in tqdm(
1185
- prefiltered_groups.items(), desc="Processing static groups (pass 1)"
1186
- ):
1187
- supplier_info = group_edges[0][2]
1188
- consumer_info = group_edges[0][3]
1189
- candidate_supplier_locations = group_edges[0][-2]
1190
- candidate_consumer_locations = group_edges[0][-1]
1191
-
1192
- new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
1193
- candidate_suppliers=candidate_supplier_locations,
1194
- candidate_consumers=candidate_consumer_locations,
1195
- supplier_info=supplier_info,
1196
- consumer_info=consumer_info,
1197
- required_supplier_fields=self.required_supplier_fields,
1198
- required_consumer_fields=self.required_consumer_fields,
1199
- cf_index=self.cf_index,
1200
- )
1331
+ if len(prefiltered_groups) > 0:
1332
+ for sig, group_edges in tqdm(
1333
+ prefiltered_groups.items(), desc="Processing static groups (pass 1)"
1334
+ ):
1335
+ supplier_info = group_edges[0][2]
1336
+ consumer_info = group_edges[0][3]
1337
+ candidate_supplier_locations = group_edges[0][-2]
1338
+ candidate_consumer_locations = group_edges[0][-1]
1339
+
1340
+ new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
1341
+ candidate_suppliers=candidate_supplier_locations,
1342
+ candidate_consumers=candidate_consumer_locations,
1343
+ supplier_info=supplier_info,
1344
+ consumer_info=consumer_info,
1345
+ required_supplier_fields=self.required_supplier_fields,
1346
+ required_consumer_fields=self.required_consumer_fields,
1347
+ cf_index=self.cf_index,
1348
+ )
1201
1349
 
1202
- if new_cf != 0:
1203
- for (
1204
- supplier_idx,
1205
- consumer_idx,
1206
- supplier_info,
1207
- consumer_info,
1208
- _,
1209
- _,
1210
- ) in group_edges:
1211
- add_cf_entry(
1212
- cfs_mapping=self.cfs_mapping,
1213
- supplier_info=supplier_info,
1214
- consumer_info=consumer_info,
1215
- direction=direction,
1216
- indices=[(supplier_idx, consumer_idx)],
1217
- value=new_cf,
1218
- uncertainty=agg_uncertainty,
1350
+ if new_cf != 0:
1351
+ for (
1352
+ supplier_idx,
1353
+ consumer_idx,
1354
+ supplier_info,
1355
+ consumer_info,
1356
+ _,
1357
+ _,
1358
+ ) in group_edges:
1359
+ add_cf_entry(
1360
+ cfs_mapping=self.cfs_mapping,
1361
+ supplier_info=supplier_info,
1362
+ consumer_info=consumer_info,
1363
+ direction=direction,
1364
+ indices=[(supplier_idx, consumer_idx)],
1365
+ value=new_cf,
1366
+ uncertainty=agg_uncertainty,
1367
+ )
1368
+ else:
1369
+ self.logger.warning(
1370
+ f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
1371
+ f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1219
1372
  )
1220
- else:
1221
- self.logger.warning(
1222
- f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
1223
- f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1224
- )
1225
1373
 
1226
1374
  # Pass 2
1227
1375
  compute_cf_memoized = compute_cf_memoized_factory(
@@ -1237,33 +1385,34 @@ class EdgeLCIA:
1237
1385
  required_consumer_fields=self.required_consumer_fields,
1238
1386
  )
1239
1387
 
1240
- for (
1241
- s_key,
1242
- c_key,
1243
- (candidate_suppliers, candidate_consumers),
1244
- ), edge_group in tqdm(
1245
- grouped_edges.items(), desc="Processing static groups (pass 2)"
1246
- ):
1247
- new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
1248
- s_key, c_key, candidate_suppliers, candidate_consumers
1249
- )
1388
+ if len(grouped_edges) > 0:
1389
+ for (
1390
+ s_key,
1391
+ c_key,
1392
+ (candidate_suppliers, candidate_consumers),
1393
+ ), edge_group in tqdm(
1394
+ grouped_edges.items(), desc="Processing static groups (pass 2)"
1395
+ ):
1396
+ new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
1397
+ s_key, c_key, candidate_suppliers, candidate_consumers
1398
+ )
1250
1399
 
1251
- if new_cf != 0:
1252
- for supplier_idx, consumer_idx in edge_group:
1253
- add_cf_entry(
1254
- cfs_mapping=self.cfs_mapping,
1255
- supplier_info=dict(s_key),
1256
- consumer_info=dict(c_key),
1257
- direction=direction,
1258
- indices=[(supplier_idx, consumer_idx)],
1259
- value=new_cf,
1260
- uncertainty=agg_uncertainty,
1400
+ if new_cf != 0:
1401
+ for supplier_idx, consumer_idx in edge_group:
1402
+ add_cf_entry(
1403
+ cfs_mapping=self.cfs_mapping,
1404
+ supplier_info=dict(s_key),
1405
+ consumer_info=dict(c_key),
1406
+ direction=direction,
1407
+ indices=[(supplier_idx, consumer_idx)],
1408
+ value=new_cf,
1409
+ uncertainty=agg_uncertainty,
1410
+ )
1411
+ else:
1412
+ self.logger.warning(
1413
+ f"Fallback CF could not be computed for supplier={s_key}, consumer={c_key} "
1414
+ f"with candidate suppliers={candidate_suppliers} and consumers={candidate_consumers}"
1261
1415
  )
1262
- else:
1263
- self.logger.warning(
1264
- f"Fallback CF could not be computed for supplier={s_key}, consumer={c_key} "
1265
- f"with candidate suppliers={candidate_suppliers} and consumers={candidate_consumers}"
1266
- )
1267
1416
 
1268
1417
  self._update_unprocessed_edges()
1269
1418
 
@@ -1300,7 +1449,7 @@ class EdgeLCIA:
1300
1449
  """
1301
1450
 
1302
1451
  self._initialize_weights()
1303
- print("Handling dynamic regions...")
1452
+ logger.info("Handling dynamic regions")
1304
1453
 
1305
1454
  cf_operators = {
1306
1455
  cf["supplier"].get("operator", "equals") for cf in self.raw_cfs_data
@@ -1450,47 +1599,49 @@ class EdgeLCIA:
1450
1599
  )
1451
1600
 
1452
1601
  # Pass 1
1453
- for sig, group_edges in tqdm(
1454
- prefiltered_groups.items(), desc="Processing dynamic groups (pass 1)"
1455
- ):
1456
- rep_supplier = group_edges[0][2]
1457
- rep_consumer = group_edges[0][3]
1458
- candidate_supplier_locations = group_edges[0][-2]
1459
- candidate_consumer_locations = group_edges[0][-1]
1460
-
1461
- new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
1462
- candidate_suppliers=candidate_supplier_locations,
1463
- candidate_consumers=candidate_consumer_locations,
1464
- supplier_info=rep_supplier,
1465
- consumer_info=rep_consumer,
1466
- required_supplier_fields=self.required_supplier_fields,
1467
- required_consumer_fields=self.required_consumer_fields,
1468
- cf_index=self.cf_index,
1469
- )
1602
+ if len(prefiltered_groups) > 0:
1603
+ for sig, group_edges in tqdm(
1604
+ prefiltered_groups.items(),
1605
+ desc="Processing dynamic groups (pass 1)",
1606
+ ):
1607
+ rep_supplier = group_edges[0][2]
1608
+ rep_consumer = group_edges[0][3]
1609
+ candidate_supplier_locations = group_edges[0][-2]
1610
+ candidate_consumer_locations = group_edges[0][-1]
1611
+
1612
+ new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
1613
+ candidate_suppliers=candidate_supplier_locations,
1614
+ candidate_consumers=candidate_consumer_locations,
1615
+ supplier_info=rep_supplier,
1616
+ consumer_info=rep_consumer,
1617
+ required_supplier_fields=self.required_supplier_fields,
1618
+ required_consumer_fields=self.required_consumer_fields,
1619
+ cf_index=self.cf_index,
1620
+ )
1470
1621
 
1471
- if new_cf:
1472
- for (
1473
- supplier_idx,
1474
- consumer_idx,
1475
- supplier_info,
1476
- consumer_info,
1477
- _,
1478
- _,
1479
- ) in group_edges:
1480
- add_cf_entry(
1481
- cfs_mapping=self.cfs_mapping,
1482
- supplier_info=supplier_info,
1483
- consumer_info=consumer_info,
1484
- direction=direction,
1485
- indices=[(supplier_idx, consumer_idx)],
1486
- value=new_cf,
1487
- uncertainty=agg_uncertainty,
1622
+ if new_cf:
1623
+ for (
1624
+ supplier_idx,
1625
+ consumer_idx,
1626
+ supplier_info,
1627
+ consumer_info,
1628
+ _,
1629
+ _,
1630
+ ) in group_edges:
1631
+ add_cf_entry(
1632
+ cfs_mapping=self.cfs_mapping,
1633
+ supplier_info=supplier_info,
1634
+ consumer_info=consumer_info,
1635
+ direction=direction,
1636
+ indices=[(supplier_idx, consumer_idx)],
1637
+ value=new_cf,
1638
+ uncertainty=agg_uncertainty,
1639
+ )
1640
+ else:
1641
+ self.logger.warning(
1642
+ f"Fallback CF could not be computed for supplier={rep_supplier}, consumer={rep_consumer} "
1643
+ f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1488
1644
  )
1489
- else:
1490
- self.logger.warning(
1491
- f"Fallback CF could not be computed for supplier={rep_supplier}, consumer={rep_consumer} "
1492
- f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1493
- )
1494
1645
 
1495
1646
  # Pass 2
1496
1647
  compute_cf_memoized = compute_cf_memoized_factory(
@@ -1506,36 +1657,37 @@ class EdgeLCIA:
1506
1657
  required_consumer_fields=self.required_consumer_fields,
1507
1658
  )
1508
1659
 
1509
- for (
1510
- s_key,
1511
- c_key,
1512
- (candidate_supplier_locations, candidate_consumer_locations),
1513
- ), edge_group in tqdm(
1514
- grouped_edges.items(), desc="Processing dynamic groups (pass 2)"
1515
- ):
1516
- new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
1660
+ if len(grouped_edges) > 0:
1661
+ for (
1517
1662
  s_key,
1518
1663
  c_key,
1519
- candidate_supplier_locations,
1520
- candidate_consumer_locations,
1521
- )
1664
+ (candidate_supplier_locations, candidate_consumer_locations),
1665
+ ), edge_group in tqdm(
1666
+ grouped_edges.items(), desc="Processing dynamic groups (pass 2)"
1667
+ ):
1668
+ new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
1669
+ s_key,
1670
+ c_key,
1671
+ candidate_supplier_locations,
1672
+ candidate_consumer_locations,
1673
+ )
1522
1674
 
1523
- if new_cf:
1524
- for supplier_idx, consumer_idx in edge_group:
1525
- add_cf_entry(
1526
- cfs_mapping=self.cfs_mapping,
1527
- supplier_info=dict(s_key),
1528
- consumer_info=dict(c_key),
1529
- direction=direction,
1530
- indices=[(supplier_idx, consumer_idx)],
1531
- value=new_cf,
1532
- uncertainty=agg_uncertainty,
1675
+ if new_cf:
1676
+ for supplier_idx, consumer_idx in edge_group:
1677
+ add_cf_entry(
1678
+ cfs_mapping=self.cfs_mapping,
1679
+ supplier_info=dict(s_key),
1680
+ consumer_info=dict(c_key),
1681
+ direction=direction,
1682
+ indices=[(supplier_idx, consumer_idx)],
1683
+ value=new_cf,
1684
+ uncertainty=agg_uncertainty,
1685
+ )
1686
+ else:
1687
+ self.logger.warning(
1688
+ f"Fallback CF could not be computed for supplier={s_key}, consumer={c_key} "
1689
+ f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1533
1690
  )
1534
- else:
1535
- self.logger.warning(
1536
- f"Fallback CF could not be computed for supplier={s_key}, consumer={c_key} "
1537
- f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1538
- )
1539
1691
 
1540
1692
  self._update_unprocessed_edges()
1541
1693
 
@@ -1568,7 +1720,7 @@ class EdgeLCIA:
1568
1720
  """
1569
1721
 
1570
1722
  self._initialize_weights()
1571
- print("Handling contained locations...")
1723
+ logger.info("Handling contained locations")
1572
1724
 
1573
1725
  cf_operators = {
1574
1726
  cf["supplier"].get("operator", "equals") for cf in self.raw_cfs_data
@@ -1704,47 +1856,49 @@ class EdgeLCIA:
1704
1856
  )
1705
1857
 
1706
1858
  # Pass 1
1707
- for sig, group_edges in tqdm(
1708
- prefiltered_groups.items(), desc="Processing contained groups (pass 1)"
1709
- ):
1710
- supplier_info = group_edges[0][2]
1711
- consumer_info = group_edges[0][3]
1712
- candidate_supplier_locations = group_edges[0][-2]
1713
- candidate_consumer_locations = group_edges[0][-1]
1714
-
1715
- new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
1716
- candidate_suppliers=candidate_supplier_locations,
1717
- candidate_consumers=candidate_consumer_locations,
1718
- supplier_info=supplier_info,
1719
- consumer_info=consumer_info,
1720
- required_supplier_fields=self.required_supplier_fields,
1721
- required_consumer_fields=self.required_consumer_fields,
1722
- cf_index=self.cf_index,
1723
- )
1859
+ if len(prefiltered_groups) > 0:
1860
+ for sig, group_edges in tqdm(
1861
+ prefiltered_groups.items(),
1862
+ desc="Processing contained groups (pass 1)",
1863
+ ):
1864
+ supplier_info = group_edges[0][2]
1865
+ consumer_info = group_edges[0][3]
1866
+ candidate_supplier_locations = group_edges[0][-2]
1867
+ candidate_consumer_locations = group_edges[0][-1]
1868
+
1869
+ new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
1870
+ candidate_suppliers=candidate_supplier_locations,
1871
+ candidate_consumers=candidate_consumer_locations,
1872
+ supplier_info=supplier_info,
1873
+ consumer_info=consumer_info,
1874
+ required_supplier_fields=self.required_supplier_fields,
1875
+ required_consumer_fields=self.required_consumer_fields,
1876
+ cf_index=self.cf_index,
1877
+ )
1724
1878
 
1725
- if new_cf:
1726
- for (
1727
- supplier_idx,
1728
- consumer_idx,
1729
- supplier_info,
1730
- consumer_info,
1731
- _,
1732
- _,
1733
- ) in group_edges:
1734
- add_cf_entry(
1735
- cfs_mapping=self.cfs_mapping,
1736
- supplier_info=supplier_info,
1737
- consumer_info=consumer_info,
1738
- direction=direction,
1739
- indices=[(supplier_idx, consumer_idx)],
1740
- value=new_cf,
1741
- uncertainty=agg_uncertainty,
1879
+ if new_cf:
1880
+ for (
1881
+ supplier_idx,
1882
+ consumer_idx,
1883
+ supplier_info,
1884
+ consumer_info,
1885
+ _,
1886
+ _,
1887
+ ) in group_edges:
1888
+ add_cf_entry(
1889
+ cfs_mapping=self.cfs_mapping,
1890
+ supplier_info=supplier_info,
1891
+ consumer_info=consumer_info,
1892
+ direction=direction,
1893
+ indices=[(supplier_idx, consumer_idx)],
1894
+ value=new_cf,
1895
+ uncertainty=agg_uncertainty,
1896
+ )
1897
+ else:
1898
+ self.logger.warning(
1899
+ f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
1900
+ f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1742
1901
  )
1743
- else:
1744
- self.logger.warning(
1745
- f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
1746
- f"with candidate suppliers={candidate_supplier_locations} and consumers={candidate_consumer_locations}"
1747
- )
1748
1902
 
1749
1903
  # Pass 2
1750
1904
  compute_cf_memoized = compute_cf_memoized_factory(
@@ -1760,35 +1914,36 @@ class EdgeLCIA:
1760
1914
  required_consumer_fields=self.required_consumer_fields,
1761
1915
  )
1762
1916
 
1763
- for (
1764
- supplier_info,
1765
- consumer_info,
1766
- (candidate_suppliers, candidate_consumers),
1767
- ), edge_group in tqdm(
1768
- grouped_edges.items(), desc="Processing contained groups (pass 2)"
1769
- ):
1770
- new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
1917
+ if len(grouped_edges) > 0:
1918
+ for (
1771
1919
  supplier_info,
1772
1920
  consumer_info,
1773
- candidate_suppliers,
1774
- candidate_consumers,
1775
- )
1776
- if new_cf:
1777
- for supplier_idx, consumer_idx in edge_group:
1778
- add_cf_entry(
1779
- cfs_mapping=self.cfs_mapping,
1780
- supplier_info=dict(supplier_info),
1781
- consumer_info=dict(consumer_info),
1782
- direction=direction,
1783
- indices=[(supplier_idx, consumer_idx)],
1784
- value=new_cf,
1785
- uncertainty=agg_uncertainty,
1786
- )
1787
- else:
1788
- self.logger.warning(
1789
- f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
1790
- f"with candidate suppliers={candidate_suppliers} and consumers={candidate_consumers}"
1921
+ (candidate_suppliers, candidate_consumers),
1922
+ ), edge_group in tqdm(
1923
+ grouped_edges.items(), desc="Processing contained groups (pass 2)"
1924
+ ):
1925
+ new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
1926
+ supplier_info,
1927
+ consumer_info,
1928
+ candidate_suppliers,
1929
+ candidate_consumers,
1791
1930
  )
1931
+ if new_cf:
1932
+ for supplier_idx, consumer_idx in edge_group:
1933
+ add_cf_entry(
1934
+ cfs_mapping=self.cfs_mapping,
1935
+ supplier_info=dict(supplier_info),
1936
+ consumer_info=dict(consumer_info),
1937
+ direction=direction,
1938
+ indices=[(supplier_idx, consumer_idx)],
1939
+ value=new_cf,
1940
+ uncertainty=agg_uncertainty,
1941
+ )
1942
+ else:
1943
+ self.logger.warning(
1944
+ f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
1945
+ f"with candidate suppliers={candidate_suppliers} and consumers={candidate_consumers}"
1946
+ )
1792
1947
 
1793
1948
  self._update_unprocessed_edges()
1794
1949
 
@@ -1820,7 +1975,7 @@ class EdgeLCIA:
1820
1975
  """
1821
1976
 
1822
1977
  self._initialize_weights()
1823
- print("Handling remaining exchanges...")
1978
+ logger.info("Handling remaining exchanges")
1824
1979
 
1825
1980
  cf_operators = {
1826
1981
  cf["supplier"].get("operator", "equals") for cf in self.raw_cfs_data
@@ -1891,11 +2046,6 @@ class EdgeLCIA:
1891
2046
  remaining_edges = []
1892
2047
 
1893
2048
  for (consumer_location, supplier_location), edges in edges_index.items():
1894
- if any(
1895
- x in ("RoW", "RoE", "GLO")
1896
- for x in (consumer_location, supplier_location)
1897
- ):
1898
- continue
1899
2049
 
1900
2050
  if supplier_location is None:
1901
2051
  candidate_suppliers_locations = [
@@ -1945,51 +2095,56 @@ class EdgeLCIA:
1945
2095
  )
1946
2096
 
1947
2097
  # Pass 1
1948
- for sig, group_edges in tqdm(
1949
- prefiltered_groups.items(), desc="Processing global groups (pass 1)"
1950
- ):
1951
- supplier_info = group_edges[0][2]
1952
- consumer_info = group_edges[0][3]
1953
-
1954
- new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
1955
- candidate_suppliers=global_locations,
1956
- candidate_consumers=global_locations,
1957
- supplier_info=supplier_info,
1958
- consumer_info=consumer_info,
1959
- required_supplier_fields=self.required_supplier_fields,
1960
- required_consumer_fields=self.required_consumer_fields,
1961
- cf_index=self.cf_index,
1962
- )
1963
- unc = (
1964
- agg_uncertainty
1965
- if agg_uncertainty is not None
1966
- else (matched_cf_obj.get("uncertainty") if matched_cf_obj else None)
1967
- )
1968
-
1969
- if new_cf:
1970
- for (
1971
- supplier_idx,
1972
- consumer_idx,
1973
- supplier_info,
1974
- consumer_info,
1975
- _,
1976
- _,
1977
- ) in group_edges:
1978
- add_cf_entry(
1979
- cfs_mapping=self.cfs_mapping,
1980
- supplier_info=supplier_info,
1981
- consumer_info=consumer_info,
1982
- direction=direction,
1983
- indices=[(supplier_idx, consumer_idx)],
1984
- value=new_cf,
1985
- uncertainty=unc,
2098
+ if len(prefiltered_groups) > 0:
2099
+ for sig, group_edges in tqdm(
2100
+ prefiltered_groups.items(), desc="Processing global groups (pass 1)"
2101
+ ):
2102
+ supplier_info = group_edges[0][2]
2103
+ consumer_info = group_edges[0][3]
2104
+
2105
+ new_cf, matched_cf_obj, agg_uncertainty = compute_average_cf(
2106
+ candidate_suppliers=global_locations,
2107
+ candidate_consumers=global_locations,
2108
+ supplier_info=supplier_info,
2109
+ consumer_info=consumer_info,
2110
+ required_supplier_fields=self.required_supplier_fields,
2111
+ required_consumer_fields=self.required_consumer_fields,
2112
+ cf_index=self.cf_index,
2113
+ )
2114
+ unc = (
2115
+ agg_uncertainty
2116
+ if agg_uncertainty is not None
2117
+ else (
2118
+ matched_cf_obj.get("uncertainty")
2119
+ if matched_cf_obj
2120
+ else None
1986
2121
  )
1987
- else:
1988
- self.logger.warning(
1989
- f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
1990
- f"with candidate suppliers={global_locations} and consumers={global_locations}"
1991
2122
  )
1992
2123
 
2124
+ if new_cf:
2125
+ for (
2126
+ supplier_idx,
2127
+ consumer_idx,
2128
+ supplier_info,
2129
+ consumer_info,
2130
+ _,
2131
+ _,
2132
+ ) in group_edges:
2133
+ add_cf_entry(
2134
+ cfs_mapping=self.cfs_mapping,
2135
+ supplier_info=supplier_info,
2136
+ consumer_info=consumer_info,
2137
+ direction=direction,
2138
+ indices=[(supplier_idx, consumer_idx)],
2139
+ value=new_cf,
2140
+ uncertainty=unc,
2141
+ )
2142
+ else:
2143
+ self.logger.warning(
2144
+ f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
2145
+ f"with candidate suppliers={global_locations} and consumers={global_locations}"
2146
+ )
2147
+
1993
2148
  # Pass 2
1994
2149
  compute_cf_memoized = compute_cf_memoized_factory(
1995
2150
  cf_index=self.cf_index,
@@ -2004,40 +2159,45 @@ class EdgeLCIA:
2004
2159
  required_consumer_fields=self.required_consumer_fields,
2005
2160
  )
2006
2161
 
2007
- for (
2008
- supplier_info,
2009
- consumer_info,
2010
- (candidate_suppliers, candidate_consumers),
2011
- ), edge_group in tqdm(
2012
- grouped_edges.items(), desc="Processing global groups (pass 2)"
2013
- ):
2014
- new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
2162
+ if len(grouped_edges) > 0:
2163
+ for (
2015
2164
  supplier_info,
2016
2165
  consumer_info,
2017
- candidate_suppliers,
2018
- candidate_consumers,
2019
- )
2020
- unc = (
2021
- agg_uncertainty
2022
- if agg_uncertainty is not None
2023
- else (matched_cf_obj.get("uncertainty") if matched_cf_obj else None)
2024
- )
2025
- if new_cf:
2026
- for supplier_idx, consumer_idx in edge_group:
2027
- add_cf_entry(
2028
- cfs_mapping=self.cfs_mapping,
2029
- supplier_info=dict(supplier_info),
2030
- consumer_info=dict(consumer_info),
2031
- direction=direction,
2032
- indices=[(supplier_idx, consumer_idx)],
2033
- value=new_cf,
2034
- uncertainty=unc,
2166
+ (candidate_suppliers, candidate_consumers),
2167
+ ), edge_group in tqdm(
2168
+ grouped_edges.items(), desc="Processing global groups (pass 2)"
2169
+ ):
2170
+ new_cf, matched_cf_obj, agg_uncertainty = compute_cf_memoized(
2171
+ supplier_info,
2172
+ consumer_info,
2173
+ candidate_suppliers,
2174
+ candidate_consumers,
2175
+ )
2176
+ unc = (
2177
+ agg_uncertainty
2178
+ if agg_uncertainty is not None
2179
+ else (
2180
+ matched_cf_obj.get("uncertainty")
2181
+ if matched_cf_obj
2182
+ else None
2035
2183
  )
2036
- else:
2037
- self.logger.warning(
2038
- f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
2039
- f"with candidate suppliers={candidate_suppliers} and consumers={candidate_consumers}"
2040
2184
  )
2185
+ if new_cf:
2186
+ for supplier_idx, consumer_idx in edge_group:
2187
+ add_cf_entry(
2188
+ cfs_mapping=self.cfs_mapping,
2189
+ supplier_info=dict(supplier_info),
2190
+ consumer_info=dict(consumer_info),
2191
+ direction=direction,
2192
+ indices=[(supplier_idx, consumer_idx)],
2193
+ value=new_cf,
2194
+ uncertainty=unc,
2195
+ )
2196
+ else:
2197
+ self.logger.warning(
2198
+ f"Fallback CF could not be computed for supplier={supplier_info}, consumer={consumer_info} "
2199
+ f"with candidate suppliers={candidate_suppliers} and consumers={candidate_consumers}"
2200
+ )
2041
2201
 
2042
2202
  self._update_unprocessed_edges()
2043
2203