2
2
import functools
3
3
import itertools
4
4
import warnings
5
- from typing import Optional
5
+ from typing import Optional , Tuple
6
6
7
- from bw2data import Database , config , databases , parameters , labels
7
+ from bw2data import Database , config , databases , labels , parameters
8
+ from bw2data .data_store import ProcessedDataStore
8
9
from bw2data .parameters import (
9
10
ActivityParameter ,
10
11
DatabaseParameter ,
@@ -56,7 +57,11 @@ def __init__(self, db_name):
56
57
def all_linked (self ):
57
58
return self .statistics ()[2 ] == 0
58
59
59
- def statistics (self , print_stats = True ):
60
+ @property
61
+ def needs_multifunctional_database (self ):
62
+ return any (ds .get ("type" ) == "multifunctional" for ds in self .data )
63
+
64
+ def statistics (self , print_stats : bool = True ) -> Tuple [int , int , int , int ]:
60
65
num_datasets = len (self .data )
61
66
num_exchanges = sum ([len (ds .get ("exchanges" , [])) for ds in self .data ])
62
67
num_unlinked = len (
@@ -65,8 +70,12 @@ def statistics(self, print_stats=True):
65
70
for ds in self .data
66
71
for exc in ds .get ("exchanges" , [])
67
72
if not exc .get ("input" )
73
+ and not (ds .get ("type" ) == "multifunctional" and exc .get ("functional" ))
68
74
]
69
75
)
76
+ num_multifunctional = sum (
77
+ 1 for ds in self .data if ds .get ("type" ) == "multifunctional"
78
+ )
70
79
if print_stats :
71
80
unique_unlinked = collections .defaultdict (set )
72
81
for ds in self .data :
@@ -75,19 +84,28 @@ def statistics(self, print_stats=True):
75
84
unique_unlinked = sorted (
76
85
[(k , len (v )) for k , v in list (unique_unlinked .items ())]
77
86
)
78
-
79
- print (
80
- (
81
- "{} datasets\n {} exchanges\n {} unlinked exchanges\n "
82
- + "\n " .join (
83
- [
84
- "Type {}: {} unique unlinked exchanges" .format (* o )
85
- for o in unique_unlinked
86
- ]
87
- )
88
- ).format (num_datasets , num_exchanges , num_unlinked )
87
+ uu = "\n \t " .join (
88
+ [
89
+ "Type {}: {} unique unlinked exchanges" .format (* o )
90
+ for o in unique_unlinked
91
+ ]
89
92
)
90
- return num_datasets , num_exchanges , num_unlinked
93
+
94
+ if num_multifunctional :
95
+ print (
96
+ f"""{ num_datasets } datasets, including { num_multifunctional } multifunctional datasets
97
+ \t { num_exchanges } exchanges
98
+ \t { num_unlinked } unlinked exchanges ({ len (unique_unlinked )} unique)
99
+ \t { uu } """
100
+ )
101
+ else :
102
+ print (
103
+ f"""{ num_datasets } datasets
104
+ \t { num_exchanges } exchanges
105
+ \t { num_unlinked } unlinked exchanges ({ len (unique_unlinked )} unique)
106
+ \t { uu } """
107
+ )
108
+ return num_datasets , num_exchanges , num_unlinked , num_multifunctional
91
109
92
110
def write_project_parameters (self , data = None , delete_existing = True ):
93
111
"""Write global parameters to ``ProjectParameter`` database table.
@@ -201,6 +219,16 @@ def _write_activity_parameters(self, activity_parameters):
201
219
for key in keys :
202
220
parameters .add_exchanges_to_group (group , key )
203
221
222
+ def database_class (
223
+ self , db_name : str , requested_backend : str = "sqlite"
224
+ ) -> ProcessedDataStore :
225
+ from multifunctional import MultifunctionalDatabase
226
+
227
+ if self .needs_multifunctional_database :
228
+ return MultifunctionalDatabase (db_name )
229
+ else :
230
+ return Database (db_name , backend = requested_backend )
231
+
204
232
def write_database (
205
233
self ,
206
234
data : Optional [dict ] = None ,
@@ -260,7 +288,7 @@ def write_database(
260
288
261
289
if db_name in databases :
262
290
# TODO: Raise error if unlinked exchanges?
263
- db = Database (db_name )
291
+ db = self . database_class (db_name )
264
292
if delete_existing :
265
293
existing = {}
266
294
else :
@@ -269,9 +297,14 @@ def write_database(
269
297
existing = {}
270
298
if "format" not in self .metadata :
271
299
self .metadata ["format" ] = self .format
300
+ if (
301
+ self .needs_multifunctional_database
302
+ and "default_allocation" not in self .metadata
303
+ ):
304
+ self .metadata ["default_allocation" ] = "manual_allocation"
272
305
with warnings .catch_warnings ():
273
306
warnings .simplefilter ("ignore" )
274
- db = Database (db_name , backend = backend )
307
+ db = self . database_class (db_name )
275
308
db .register (** self .metadata )
276
309
277
310
self .write_database_parameters (activate_parameters , delete_existing )
@@ -346,27 +379,39 @@ def create_new_biosphere(self, biosphere_name: str):
346
379
raise ValueError (f"{ biosphere_name } database already exists" )
347
380
348
381
def reformat (exc ):
349
- return exc | {"type" : labels .biosphere_node_default , "exchanges" : [], "database" : biosphere_name , "code" : activity_hash (exc )}
382
+ return exc | {
383
+ "type" : labels .biosphere_node_default ,
384
+ "exchanges" : [],
385
+ "database" : biosphere_name ,
386
+ "code" : activity_hash (exc ),
387
+ }
350
388
351
- bio_data = {(flow ["database" ], flow ["code" ]): flow for flow in [
352
- reformat (exc )
353
- for ds in self .data
354
- for exc in ds .get ("exchanges" , [])
355
- if exc ["type" ] in labels .biosphere_edge_types
356
- and not exc .get ("input" )
357
- ]}
389
+ bio_data = {
390
+ (flow ["database" ], flow ["code" ]): flow
391
+ for flow in [
392
+ reformat (exc )
393
+ for ds in self .data
394
+ for exc in ds .get ("exchanges" , [])
395
+ if exc ["type" ] in labels .biosphere_edge_types and not exc .get ("input" )
396
+ ]
397
+ }
358
398
359
399
if not bio_data :
360
- print ("Skipping biosphere database creation as all biosphere flows are linked" )
400
+ print (
401
+ "Skipping biosphere database creation as all biosphere flows are linked"
402
+ )
361
403
return
362
404
363
- print (f"Creating new biosphere database { biosphere_name } with { len (bio_data )} flows" )
405
+ print (
406
+ f"Creating new biosphere database { biosphere_name } with { len (bio_data )} flows"
407
+ )
364
408
365
409
with warnings .catch_warnings ():
366
410
warnings .simplefilter ("ignore" )
367
411
new_bio = Database (biosphere_name )
368
412
new_bio .register (
369
- format = self .format , comment = f"Database for unlinked biosphere flows from { self .db_name } "
413
+ format = self .format ,
414
+ comment = f"Database for unlinked biosphere flows from { self .db_name } " ,
370
415
)
371
416
372
417
new_bio .write (bio_data )
0 commit comments