From d3f327869ecec10641469e9bac052fc741d2c759 Mon Sep 17 00:00:00 2001 From: Michael <23102087+michaelweinold@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:49:40 +0100 Subject: [PATCH] #14 updated configuration to use autoapi --- setup/conda_environment.yml | 1 + setup/pip_requirements.txt | 3 +- sphinx/_autoapi_templates/index.rst | 15 + .../_autoapi_templates/python/attribute.rst | 1 + sphinx/_autoapi_templates/python/class.rst | 58 + sphinx/_autoapi_templates/python/data.rst | 32 + .../_autoapi_templates/python/exception.rst | 1 + sphinx/_autoapi_templates/python/function.rst | 15 + sphinx/_autoapi_templates/python/method.rst | 19 + sphinx/_autoapi_templates/python/module.rst | 114 + sphinx/_autoapi_templates/python/package.rst | 1 + sphinx/_autoapi_templates/python/property.rst | 15 + sphinx/conf.py | 18 +- sphinx/source/reference/bw2analyzer.rst | 44 - sphinx/source/reference/bw2calc.rst | 73 - sphinx/source/reference/bw2data.rst | 292 - sphinx/source/reference/bw2io.rst | 284 - .../reference/images/import-database.graffle | 1808 ------ .../reference/images/import-database.png | Bin 82310 -> 0 bytes .../reference/images/import-method.graffle | 673 --- .../source/reference/images/import-method.png | Bin 39233 -> 0 bytes .../reference/images/import-simapro.graffle | 1509 ----- .../reference/images/import-simapro.png | Bin 88088 -> 0 bytes .../reference/images/simapro-options.png | Bin 19274 -> 0 bytes .../reference/images/tagged traversal.graffle | 4898 ----------------- .../reference/images/tagged-traversal-2.png | Bin 41516 -> 0 bytes .../reference/images/tagged-traversal.png | Bin 40337 -> 0 bytes sphinx/source/reference/index.rst | 7 +- sphinx/technical/api/blank_lines/index.rst | 41 + .../api/bw2analyzer/comparisons/index.rst | 125 + .../api/bw2analyzer/contribution/index.rst | 153 + .../technical/api/bw2analyzer/econ/index.rst | 85 + .../api/bw2analyzer/health_check/index.rst | 46 + sphinx/technical/api/bw2analyzer/index.rst | 463 ++ .../technical/api/bw2analyzer/lci/index.rst | 29 + .../api/bw2analyzer/matrix_grapher/index.rst | 31 + .../api/bw2analyzer/page_rank/index.rst | 64 + .../api/bw2analyzer/report/index.rst | 57 + .../api/bw2analyzer/sc_graph/index.rst | 75 + .../api/bw2analyzer/tagged/index.rst | 224 + .../technical/api/bw2analyzer/utils/index.rst | 117 + .../api/bw2analyzer/version/index.rst | 14 + .../technical/api/bw2calc/dense_lca/index.rst | 43 + .../api/bw2calc/dictionary_manager/index.rst | 137 + sphinx/technical/api/bw2calc/errors/index.rst | 86 + .../api/bw2calc/graph_traversal/index.rst | 184 + sphinx/technical/api/bw2calc/index.rst | 224 + sphinx/technical/api/bw2calc/lca/index.rst | 352 ++ .../api/bw2calc/least_squares/index.rst | 63 + .../technical/api/bw2calc/log_utils/index.rst | 128 + .../api/bw2calc/monte_carlo/index.rst | 140 + .../technical/api/bw2calc/multi_lca/index.rst | 61 + .../single_value_diagonal_matrix/index.rst | 46 + sphinx/technical/api/bw2calc/utils/index.rst | 39 + .../technical/api/bw2calc/version/index.rst | 14 + .../api/bw2data/backends/base/index.rst | 572 ++ .../technical/api/bw2data/backends/index.rst | 831 +++ .../api/bw2data/backends/iotable/index.rst | 154 + .../api/bw2data/backends/proxies/index.rst | 214 + .../api/bw2data/backends/schema/index.rst | 109 + .../api/bw2data/backends/utils/index.rst | 54 + .../backends/wurst_extraction/index.rst | 64 + .../api/bw2data/bin/bw2_uptodate/index.rst | 58 + sphinx/technical/api/bw2data/bin/index.rst | 15 + sphinx/technical/api/bw2data/compat/index.rst | 114 + .../api/bw2data/configuration/index.rst | 79 + .../api/bw2data/data_store/index.rst | 189 + .../technical/api/bw2data/database/index.rst | 22 + sphinx/technical/api/bw2data/errors/index.rst | 114 + .../technical/api/bw2data/fatomic/index.rst | 57 + .../api/bw2data/filesystem/index.rst | 49 + .../api/bw2data/ia_data_store/index.rst | 97 + sphinx/technical/api/bw2data/index.rst | 967 ++++ sphinx/technical/api/bw2data/logs/index.rst | 74 + sphinx/technical/api/bw2data/meta/index.rst | 235 + sphinx/technical/api/bw2data/method/index.rst | 96 + .../api/bw2data/parameters/index.rst | 1055 ++++ .../technical/api/bw2data/project/index.rst | 250 + .../technical/api/bw2data/proxies/index.rst | 224 + sphinx/technical/api/bw2data/query/index.rst | 178 + sphinx/technical/api/bw2data/search/index.rst | 70 + .../api/bw2data/search/indices/index.rst | 46 + .../api/bw2data/search/schema/index.rst | 14 + .../api/bw2data/search/search/index.rst | 44 + .../api/bw2data/serialization/index.rst | 210 + sphinx/technical/api/bw2data/sqlite/index.rst | 80 + sphinx/technical/api/bw2data/tests/index.rst | 82 + .../technical/api/bw2data/updates/index.rst | 221 + sphinx/technical/api/bw2data/utils/index.rst | 207 + .../technical/api/bw2data/validate/index.rst | 77 + .../technical/api/bw2data/version/index.rst | 14 + .../bw2data/weighting_normalization/index.rst | 102 + sphinx/technical/api/bw2io/backup/index.rst | 54 + .../technical/api/bw2io/chemidplus/index.rst | 94 + .../api/bw2io/compatibility/index.rst | 29 + .../api/bw2io/data/exiopol/index.rst | 22 + sphinx/technical/api/bw2io/data/index.rst | 222 + .../api/bw2io/download_utils/index.rst | 26 + sphinx/technical/api/bw2io/errors/index.rst | 72 + .../technical/api/bw2io/export/csv/index.rst | 142 + .../api/bw2io/export/excel/index.rst | 60 + .../technical/api/bw2io/export/gexf/index.rst | 81 + sphinx/technical/api/bw2io/export/index.rst | 135 + .../api/bw2io/export/matlab/index.rst | 22 + .../api/bw2io/extractors/csv/index.rst | 28 + .../api/bw2io/extractors/ecospold1/index.rst | 87 + .../bw2io/extractors/ecospold1_lcia/index.rst | 49 + .../api/bw2io/extractors/ecospold2/index.rst | 149 + .../api/bw2io/extractors/excel/index.rst | 43 + .../api/bw2io/extractors/exiobase/index.rst | 65 + .../technical/api/bw2io/extractors/index.rst | 448 ++ .../api/bw2io/extractors/json_ld/index.rst | 47 + .../bw2io/extractors/simapro_csv/index.rst | 262 + .../extractors/simapro_lcia_csv/index.rst | 126 + .../api/bw2io/importers/base/index.rst | 74 + .../api/bw2io/importers/base_lci/index.rst | 156 + .../api/bw2io/importers/base_lcia/index.rst | 51 + .../bw2io/importers/ecoinvent_lcia/index.rst | 32 + .../api/bw2io/importers/ecospold1/index.rst | 77 + .../bw2io/importers/ecospold1_lcia/index.rst | 29 + .../api/bw2io/importers/ecospold2/index.rst | 36 + .../importers/ecospold2_biosphere/index.rst | 52 + .../api/bw2io/importers/excel/index.rst | 165 + .../api/bw2io/importers/excel_lcia/index.rst | 68 + .../importers/exiobase3_hybrid/index.rst | 32 + .../importers/exiobase3_monetary/index.rst | 72 + .../technical/api/bw2io/importers/index.rst | 442 ++ .../api/bw2io/importers/json_ld/index.rst | 56 + .../bw2io/importers/json_ld_lcia/index.rst | 39 + .../api/bw2io/importers/simapro_csv/index.rst | 62 + .../importers/simapro_lcia_csv/index.rst | 29 + sphinx/technical/api/bw2io/index.rst | 806 +++ .../api/bw2io/modified_database/index.rst | 87 + sphinx/technical/api/bw2io/package/index.rst | 166 + .../api/bw2io/strategies/biosphere/index.rst | 53 + .../api/bw2io/strategies/csv/index.rst | 46 + .../api/bw2io/strategies/ecospold1/index.rst | 6 + .../strategies/ecospold1_allocation/index.rst | 64 + .../api/bw2io/strategies/ecospold2/index.rst | 151 + .../api/bw2io/strategies/exiobase/index.rst | 50 + .../api/bw2io/strategies/generic/index.rst | 163 + .../technical/api/bw2io/strategies/index.rst | 609 ++ .../api/bw2io/strategies/json_ld/index.rst | 102 + .../strategies/json_ld_allocation/index.rst | 74 + .../bw2io/strategies/json_ld_lcia/index.rst | 34 + .../api/bw2io/strategies/lcia/index.rst | 56 + .../api/bw2io/strategies/locations/index.rst | 37 + .../api/bw2io/strategies/simapro/index.rst | 139 + .../api/bw2io/strategies/special/index.rst | 24 + sphinx/technical/api/bw2io/tests/index.rst | 72 + sphinx/technical/api/bw2io/units/index.rst | 59 + .../api/bw2io/unlinked_data/index.rst | 73 + sphinx/technical/api/bw2io/utils/index.rst | 94 + .../technical/api/bw2io/validation/index.rst | 14 + sphinx/technical/api/bw2io/version/index.rst | 14 + .../api/bw2regional/base_data/index.rst | 43 + .../api/bw2regional/databases/index.rst | 26 + .../api/bw2regional/density/index.rst | 40 + .../api/bw2regional/errors/index.rst | 72 + .../api/bw2regional/export/index.rst | 71 + .../api/bw2regional/gis_tasks/index.rst | 47 + .../api/bw2regional/hashing/index.rst | 24 + sphinx/technical/api/bw2regional/index.rst | 688 +++ .../api/bw2regional/intersection/index.rst | 63 + .../api/bw2regional/lca/base_class/index.rst | 131 + .../lca/extension_tables/index.rst | 119 + .../technical/api/bw2regional/lca/index.rst | 271 + .../lca/one_spatial_scale/index.rst | 57 + .../lca/two_spatial_scales/index.rst | 68 + .../two_spatial_scales_weighting/index.rst | 68 + .../api/bw2regional/loading/index.rst | 60 + .../technical/api/bw2regional/meta/index.rst | 126 + .../api/bw2regional/pandarus/index.rst | 77 + .../api/bw2regional/pandarus_remote/index.rst | 129 + .../api/bw2regional/topography/index.rst | 87 + .../technical/api/bw2regional/utils/index.rst | 120 + .../api/bw2regional/validate/index.rst | 67 + .../api/bw2regional/version/index.rst | 14 + .../api/bw2regional/xtables/index.rst | 50 + sphinx/technical/api/complicated/index.rst | 35 + sphinx/technical/api/conf/index.rst | 173 + .../api/csv_excel_exporters/index.rst | 89 + sphinx/technical/api/csv_lcia/index.rst | 43 + sphinx/technical/api/default_data/index.rst | 22 + .../api/ecospold1_importer/index.rst | 47 + .../api/ecospold2_extractor/index.rst | 39 + .../api/ecospold2_importer/index.rst | 43 + sphinx/technical/api/extraction/index.rst | 38 + sphinx/technical/api/fixtures/index.rst | 41 + .../technical/api/formula_parsing/index.rst | 35 + .../technical/api/generate_fixture/index.rst | 22 + sphinx/technical/api/generic_excel/index.rst | 71 + sphinx/technical/api/index.rst | 52 + sphinx/technical/api/json_ld/index.rst | 44 + sphinx/technical/api/lca_old/index.rst | 117 + sphinx/technical/api/lcia/index.rst | 43 + sphinx/technical/api/noxfile/index.rst | 22 + sphinx/technical/api/parameterized/index.rst | 43 + sphinx/technical/api/setup/index.rst | 24 + sphinx/technical/api/simple/index.rst | 45 + sphinx/technical/api/subcomponents/index.rst | 38 + sphinx/technical/api/test_density/index.rst | 45 + .../api/test_intersections/index.rst | 26 + sphinx/technical/api/test_lca/index.rst | 26 + sphinx/technical/api/test_loading/index.rst | 34 + sphinx/technical/api/test_meta/index.rst | 71 + .../api/test_one_spatial_scale/index.rst | 50 + sphinx/technical/api/test_pandarus/index.rst | 43 + sphinx/technical/api/test_setup/index.rst | 42 + .../technical/api/test_topography/index.rst | 14 + .../api/test_two_spatial_scales/index.rst | 58 + .../index.rst | 62 + sphinx/technical/api/test_utils/index.rst | 46 + sphinx/technical/api/test_xtables/index.rst | 22 + .../api/tests/activity_proxy/index.rst | 126 + sphinx/technical/api/tests/base/index.rst | 22 + .../api/tests/base_lci_importer/index.rst | 123 + .../api/tests/base_lcia_importer/index.rst | 26 + .../technical/api/tests/chemidplus/index.rst | 6 + .../technical/api/tests/comparisons/index.rst | 82 + .../api/tests/compatibility/index.rst | 50 + sphinx/technical/api/tests/config/index.rst | 26 + .../api/tests/contribution/index.rst | 88 + sphinx/technical/api/tests/data/index.rst | 15 + .../api/tests/data/init_file/index.rst | 22 + .../technical/api/tests/data_store/index.rst | 124 + sphinx/technical/api/tests/database/index.rst | 238 + .../api/tests/database_querying/index.rst | 72 + sphinx/technical/api/tests/dict_man/index.rst | 102 + sphinx/technical/api/tests/econ/index.rst | 73 + .../api/tests/exchange_proxy/index.rst | 123 + .../tests/fixtures/create_fixtures/index.rst | 67 + sphinx/technical/api/tests/fixtures/index.rst | 35 + .../tests/fixtures/presamples_basic/index.rst | 57 + .../fixtures/simapro_reference/index.rst | 14 + sphinx/technical/api/tests/geo/index.rst | 54 + .../api/tests/health_check/index.rst | 36 + sphinx/technical/api/tests/ia/index.rst | 162 + sphinx/technical/api/tests/index.rst | 66 + sphinx/technical/api/tests/iotable/index.rst | 108 + sphinx/technical/api/tests/json_ld/index.rst | 6 + sphinx/technical/api/tests/lca/index.rst | 235 + sphinx/technical/api/tests/lci/index.rst | 26 + .../api/tests/matrix_grapher/index.rst | 39 + .../technical/api/tests/monte_carlo/index.rst | 6 + .../multifunctional_graph_traversal/index.rst | 50 + .../technical/api/tests/packaging/index.rst | 85 + .../technical/api/tests/parameters/index.rst | 536 ++ .../technical/api/tests/presamples/index.rst | 6 + sphinx/technical/api/tests/projects/index.rst | 158 + sphinx/technical/api/tests/sc_graph/index.rst | 192 + sphinx/technical/api/tests/search/index.rst | 110 + .../api/tests/serialization/index.rst | 64 + sphinx/technical/api/tests/simapro/index.rst | 95 + .../api/tests/single_matrix/index.rst | 6 + sphinx/technical/api/tests/sqlite/index.rst | 22 + .../api/tests/strategies/allocation/index.rst | 58 + .../api/tests/strategies/biosphere/index.rst | 241 + .../api/tests/strategies/ecospold2/index.rst | 42 + .../api/tests/strategies/generic/index.rst | 102 + .../technical/api/tests/strategies/index.rst | 26 + .../api/tests/strategies/json_ld/index.rst | 87 + .../api/tests/strategies/lcia/index.rst | 146 + .../tests/strategies/link_iterable/index.rst | 88 + .../api/tests/strategies/locations/index.rst | 26 + .../api/tests/strategies/simapro/index.rst | 34 + .../simapro_flip_sign_on_waste/index.rst | 22 + .../simapro_name_splitting/index.rst | 67 + .../simapro_normalization/index.rst | 58 + sphinx/technical/api/tests/svdm/index.rst | 63 + sphinx/technical/api/tests/tagged/index.rst | 62 + .../api/tests/to_dataframe/index.rst | 79 + sphinx/technical/api/tests/updates/index.rst | 33 + sphinx/technical/api/tests/utils/index.rst | 78 + .../technical/api/tests/validation/index.rst | 82 + sphinx/technical/api/with_products/index.rst | 39 + 276 files changed, 26265 insertions(+), 9592 deletions(-) create mode 100644 sphinx/_autoapi_templates/index.rst create mode 100644 sphinx/_autoapi_templates/python/attribute.rst create mode 100644 sphinx/_autoapi_templates/python/class.rst create mode 100644 sphinx/_autoapi_templates/python/data.rst create mode 100644 sphinx/_autoapi_templates/python/exception.rst create mode 100644 sphinx/_autoapi_templates/python/function.rst create mode 100644 sphinx/_autoapi_templates/python/method.rst create mode 100644 sphinx/_autoapi_templates/python/module.rst create mode 100644 sphinx/_autoapi_templates/python/package.rst create mode 100644 sphinx/_autoapi_templates/python/property.rst delete mode 100644 sphinx/source/reference/bw2analyzer.rst delete mode 100644 sphinx/source/reference/bw2calc.rst delete mode 100644 sphinx/source/reference/bw2data.rst delete mode 100644 sphinx/source/reference/bw2io.rst delete mode 100644 sphinx/source/reference/images/import-database.graffle delete mode 100644 sphinx/source/reference/images/import-database.png delete mode 100644 sphinx/source/reference/images/import-method.graffle delete mode 100644 sphinx/source/reference/images/import-method.png delete mode 100644 sphinx/source/reference/images/import-simapro.graffle delete mode 100644 sphinx/source/reference/images/import-simapro.png delete mode 100644 sphinx/source/reference/images/simapro-options.png delete mode 100644 sphinx/source/reference/images/tagged traversal.graffle delete mode 100644 sphinx/source/reference/images/tagged-traversal-2.png delete mode 100644 sphinx/source/reference/images/tagged-traversal.png create mode 100644 sphinx/technical/api/blank_lines/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/comparisons/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/contribution/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/econ/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/health_check/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/lci/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/matrix_grapher/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/page_rank/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/report/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/sc_graph/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/tagged/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/utils/index.rst create mode 100644 sphinx/technical/api/bw2analyzer/version/index.rst create mode 100644 sphinx/technical/api/bw2calc/dense_lca/index.rst create mode 100644 sphinx/technical/api/bw2calc/dictionary_manager/index.rst create mode 100644 sphinx/technical/api/bw2calc/errors/index.rst create mode 100644 sphinx/technical/api/bw2calc/graph_traversal/index.rst create mode 100644 sphinx/technical/api/bw2calc/index.rst create mode 100644 sphinx/technical/api/bw2calc/lca/index.rst create mode 100644 sphinx/technical/api/bw2calc/least_squares/index.rst create mode 100644 sphinx/technical/api/bw2calc/log_utils/index.rst create mode 100644 sphinx/technical/api/bw2calc/monte_carlo/index.rst create mode 100644 sphinx/technical/api/bw2calc/multi_lca/index.rst create mode 100644 sphinx/technical/api/bw2calc/single_value_diagonal_matrix/index.rst create mode 100644 sphinx/technical/api/bw2calc/utils/index.rst create mode 100644 sphinx/technical/api/bw2calc/version/index.rst create mode 100644 sphinx/technical/api/bw2data/backends/base/index.rst create mode 100644 sphinx/technical/api/bw2data/backends/index.rst create mode 100644 sphinx/technical/api/bw2data/backends/iotable/index.rst create mode 100644 sphinx/technical/api/bw2data/backends/proxies/index.rst create mode 100644 sphinx/technical/api/bw2data/backends/schema/index.rst create mode 100644 sphinx/technical/api/bw2data/backends/utils/index.rst create mode 100644 sphinx/technical/api/bw2data/backends/wurst_extraction/index.rst create mode 100644 sphinx/technical/api/bw2data/bin/bw2_uptodate/index.rst create mode 100644 sphinx/technical/api/bw2data/bin/index.rst create mode 100644 sphinx/technical/api/bw2data/compat/index.rst create mode 100644 sphinx/technical/api/bw2data/configuration/index.rst create mode 100644 sphinx/technical/api/bw2data/data_store/index.rst create mode 100644 sphinx/technical/api/bw2data/database/index.rst create mode 100644 sphinx/technical/api/bw2data/errors/index.rst create mode 100644 sphinx/technical/api/bw2data/fatomic/index.rst create mode 100644 sphinx/technical/api/bw2data/filesystem/index.rst create mode 100644 sphinx/technical/api/bw2data/ia_data_store/index.rst create mode 100644 sphinx/technical/api/bw2data/index.rst create mode 100644 sphinx/technical/api/bw2data/logs/index.rst create mode 100644 sphinx/technical/api/bw2data/meta/index.rst create mode 100644 sphinx/technical/api/bw2data/method/index.rst create mode 100644 sphinx/technical/api/bw2data/parameters/index.rst create mode 100644 sphinx/technical/api/bw2data/project/index.rst create mode 100644 sphinx/technical/api/bw2data/proxies/index.rst create mode 100644 sphinx/technical/api/bw2data/query/index.rst create mode 100644 sphinx/technical/api/bw2data/search/index.rst create mode 100644 sphinx/technical/api/bw2data/search/indices/index.rst create mode 100644 sphinx/technical/api/bw2data/search/schema/index.rst create mode 100644 sphinx/technical/api/bw2data/search/search/index.rst create mode 100644 sphinx/technical/api/bw2data/serialization/index.rst create mode 100644 sphinx/technical/api/bw2data/sqlite/index.rst create mode 100644 sphinx/technical/api/bw2data/tests/index.rst create mode 100644 sphinx/technical/api/bw2data/updates/index.rst create mode 100644 sphinx/technical/api/bw2data/utils/index.rst create mode 100644 sphinx/technical/api/bw2data/validate/index.rst create mode 100644 sphinx/technical/api/bw2data/version/index.rst create mode 100644 sphinx/technical/api/bw2data/weighting_normalization/index.rst create mode 100644 sphinx/technical/api/bw2io/backup/index.rst create mode 100644 sphinx/technical/api/bw2io/chemidplus/index.rst create mode 100644 sphinx/technical/api/bw2io/compatibility/index.rst create mode 100644 sphinx/technical/api/bw2io/data/exiopol/index.rst create mode 100644 sphinx/technical/api/bw2io/data/index.rst create mode 100644 sphinx/technical/api/bw2io/download_utils/index.rst create mode 100644 sphinx/technical/api/bw2io/errors/index.rst create mode 100644 sphinx/technical/api/bw2io/export/csv/index.rst create mode 100644 sphinx/technical/api/bw2io/export/excel/index.rst create mode 100644 sphinx/technical/api/bw2io/export/gexf/index.rst create mode 100644 sphinx/technical/api/bw2io/export/index.rst create mode 100644 sphinx/technical/api/bw2io/export/matlab/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/csv/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/ecospold1/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/ecospold1_lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/ecospold2/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/excel/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/exiobase/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/json_ld/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/simapro_csv/index.rst create mode 100644 sphinx/technical/api/bw2io/extractors/simapro_lcia_csv/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/base/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/base_lci/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/base_lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/ecoinvent_lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/ecospold1/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/ecospold1_lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/ecospold2/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/ecospold2_biosphere/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/excel/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/excel_lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/exiobase3_hybrid/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/exiobase3_monetary/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/json_ld/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/json_ld_lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/simapro_csv/index.rst create mode 100644 sphinx/technical/api/bw2io/importers/simapro_lcia_csv/index.rst create mode 100644 sphinx/technical/api/bw2io/index.rst create mode 100644 sphinx/technical/api/bw2io/modified_database/index.rst create mode 100644 sphinx/technical/api/bw2io/package/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/biosphere/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/csv/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/ecospold1/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/ecospold1_allocation/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/ecospold2/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/exiobase/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/generic/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/json_ld/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/json_ld_allocation/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/json_ld_lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/lcia/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/locations/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/simapro/index.rst create mode 100644 sphinx/technical/api/bw2io/strategies/special/index.rst create mode 100644 sphinx/technical/api/bw2io/tests/index.rst create mode 100644 sphinx/technical/api/bw2io/units/index.rst create mode 100644 sphinx/technical/api/bw2io/unlinked_data/index.rst create mode 100644 sphinx/technical/api/bw2io/utils/index.rst create mode 100644 sphinx/technical/api/bw2io/validation/index.rst create mode 100644 sphinx/technical/api/bw2io/version/index.rst create mode 100644 sphinx/technical/api/bw2regional/base_data/index.rst create mode 100644 sphinx/technical/api/bw2regional/databases/index.rst create mode 100644 sphinx/technical/api/bw2regional/density/index.rst create mode 100644 sphinx/technical/api/bw2regional/errors/index.rst create mode 100644 sphinx/technical/api/bw2regional/export/index.rst create mode 100644 sphinx/technical/api/bw2regional/gis_tasks/index.rst create mode 100644 sphinx/technical/api/bw2regional/hashing/index.rst create mode 100644 sphinx/technical/api/bw2regional/index.rst create mode 100644 sphinx/technical/api/bw2regional/intersection/index.rst create mode 100644 sphinx/technical/api/bw2regional/lca/base_class/index.rst create mode 100644 sphinx/technical/api/bw2regional/lca/extension_tables/index.rst create mode 100644 sphinx/technical/api/bw2regional/lca/index.rst create mode 100644 sphinx/technical/api/bw2regional/lca/one_spatial_scale/index.rst create mode 100644 sphinx/technical/api/bw2regional/lca/two_spatial_scales/index.rst create mode 100644 sphinx/technical/api/bw2regional/lca/two_spatial_scales_weighting/index.rst create mode 100644 sphinx/technical/api/bw2regional/loading/index.rst create mode 100644 sphinx/technical/api/bw2regional/meta/index.rst create mode 100644 sphinx/technical/api/bw2regional/pandarus/index.rst create mode 100644 sphinx/technical/api/bw2regional/pandarus_remote/index.rst create mode 100644 sphinx/technical/api/bw2regional/topography/index.rst create mode 100644 sphinx/technical/api/bw2regional/utils/index.rst create mode 100644 sphinx/technical/api/bw2regional/validate/index.rst create mode 100644 sphinx/technical/api/bw2regional/version/index.rst create mode 100644 sphinx/technical/api/bw2regional/xtables/index.rst create mode 100644 sphinx/technical/api/complicated/index.rst create mode 100644 sphinx/technical/api/conf/index.rst create mode 100644 sphinx/technical/api/csv_excel_exporters/index.rst create mode 100644 sphinx/technical/api/csv_lcia/index.rst create mode 100644 sphinx/technical/api/default_data/index.rst create mode 100644 sphinx/technical/api/ecospold1_importer/index.rst create mode 100644 sphinx/technical/api/ecospold2_extractor/index.rst create mode 100644 sphinx/technical/api/ecospold2_importer/index.rst create mode 100644 sphinx/technical/api/extraction/index.rst create mode 100644 sphinx/technical/api/fixtures/index.rst create mode 100644 sphinx/technical/api/formula_parsing/index.rst create mode 100644 sphinx/technical/api/generate_fixture/index.rst create mode 100644 sphinx/technical/api/generic_excel/index.rst create mode 100644 sphinx/technical/api/index.rst create mode 100644 sphinx/technical/api/json_ld/index.rst create mode 100644 sphinx/technical/api/lca_old/index.rst create mode 100644 sphinx/technical/api/lcia/index.rst create mode 100644 sphinx/technical/api/noxfile/index.rst create mode 100644 sphinx/technical/api/parameterized/index.rst create mode 100644 sphinx/technical/api/setup/index.rst create mode 100644 sphinx/technical/api/simple/index.rst create mode 100644 sphinx/technical/api/subcomponents/index.rst create mode 100644 sphinx/technical/api/test_density/index.rst create mode 100644 sphinx/technical/api/test_intersections/index.rst create mode 100644 sphinx/technical/api/test_lca/index.rst create mode 100644 sphinx/technical/api/test_loading/index.rst create mode 100644 sphinx/technical/api/test_meta/index.rst create mode 100644 sphinx/technical/api/test_one_spatial_scale/index.rst create mode 100644 sphinx/technical/api/test_pandarus/index.rst create mode 100644 sphinx/technical/api/test_setup/index.rst create mode 100644 sphinx/technical/api/test_topography/index.rst create mode 100644 sphinx/technical/api/test_two_spatial_scales/index.rst create mode 100644 sphinx/technical/api/test_two_spatial_scales_weighting/index.rst create mode 100644 sphinx/technical/api/test_utils/index.rst create mode 100644 sphinx/technical/api/test_xtables/index.rst create mode 100644 sphinx/technical/api/tests/activity_proxy/index.rst create mode 100644 sphinx/technical/api/tests/base/index.rst create mode 100644 sphinx/technical/api/tests/base_lci_importer/index.rst create mode 100644 sphinx/technical/api/tests/base_lcia_importer/index.rst create mode 100644 sphinx/technical/api/tests/chemidplus/index.rst create mode 100644 sphinx/technical/api/tests/comparisons/index.rst create mode 100644 sphinx/technical/api/tests/compatibility/index.rst create mode 100644 sphinx/technical/api/tests/config/index.rst create mode 100644 sphinx/technical/api/tests/contribution/index.rst create mode 100644 sphinx/technical/api/tests/data/index.rst create mode 100644 sphinx/technical/api/tests/data/init_file/index.rst create mode 100644 sphinx/technical/api/tests/data_store/index.rst create mode 100644 sphinx/technical/api/tests/database/index.rst create mode 100644 sphinx/technical/api/tests/database_querying/index.rst create mode 100644 sphinx/technical/api/tests/dict_man/index.rst create mode 100644 sphinx/technical/api/tests/econ/index.rst create mode 100644 sphinx/technical/api/tests/exchange_proxy/index.rst create mode 100644 sphinx/technical/api/tests/fixtures/create_fixtures/index.rst create mode 100644 sphinx/technical/api/tests/fixtures/index.rst create mode 100644 sphinx/technical/api/tests/fixtures/presamples_basic/index.rst create mode 100644 sphinx/technical/api/tests/fixtures/simapro_reference/index.rst create mode 100644 sphinx/technical/api/tests/geo/index.rst create mode 100644 sphinx/technical/api/tests/health_check/index.rst create mode 100644 sphinx/technical/api/tests/ia/index.rst create mode 100644 sphinx/technical/api/tests/index.rst create mode 100644 sphinx/technical/api/tests/iotable/index.rst create mode 100644 sphinx/technical/api/tests/json_ld/index.rst create mode 100644 sphinx/technical/api/tests/lca/index.rst create mode 100644 sphinx/technical/api/tests/lci/index.rst create mode 100644 sphinx/technical/api/tests/matrix_grapher/index.rst create mode 100644 sphinx/technical/api/tests/monte_carlo/index.rst create mode 100644 sphinx/technical/api/tests/multifunctional_graph_traversal/index.rst create mode 100644 sphinx/technical/api/tests/packaging/index.rst create mode 100644 sphinx/technical/api/tests/parameters/index.rst create mode 100644 sphinx/technical/api/tests/presamples/index.rst create mode 100644 sphinx/technical/api/tests/projects/index.rst create mode 100644 sphinx/technical/api/tests/sc_graph/index.rst create mode 100644 sphinx/technical/api/tests/search/index.rst create mode 100644 sphinx/technical/api/tests/serialization/index.rst create mode 100644 sphinx/technical/api/tests/simapro/index.rst create mode 100644 sphinx/technical/api/tests/single_matrix/index.rst create mode 100644 sphinx/technical/api/tests/sqlite/index.rst create mode 100644 sphinx/technical/api/tests/strategies/allocation/index.rst create mode 100644 sphinx/technical/api/tests/strategies/biosphere/index.rst create mode 100644 sphinx/technical/api/tests/strategies/ecospold2/index.rst create mode 100644 sphinx/technical/api/tests/strategies/generic/index.rst create mode 100644 sphinx/technical/api/tests/strategies/index.rst create mode 100644 sphinx/technical/api/tests/strategies/json_ld/index.rst create mode 100644 sphinx/technical/api/tests/strategies/lcia/index.rst create mode 100644 sphinx/technical/api/tests/strategies/link_iterable/index.rst create mode 100644 sphinx/technical/api/tests/strategies/locations/index.rst create mode 100644 sphinx/technical/api/tests/strategies/simapro/index.rst create mode 100644 sphinx/technical/api/tests/strategies/simapro_flip_sign_on_waste/index.rst create mode 100644 sphinx/technical/api/tests/strategies/simapro_name_splitting/index.rst create mode 100644 sphinx/technical/api/tests/strategies/simapro_normalization/index.rst create mode 100644 sphinx/technical/api/tests/svdm/index.rst create mode 100644 sphinx/technical/api/tests/tagged/index.rst create mode 100644 sphinx/technical/api/tests/to_dataframe/index.rst create mode 100644 sphinx/technical/api/tests/updates/index.rst create mode 100644 sphinx/technical/api/tests/utils/index.rst create mode 100644 sphinx/technical/api/tests/validation/index.rst create mode 100644 sphinx/technical/api/with_products/index.rst diff --git a/setup/conda_environment.yml b/setup/conda_environment.yml index d85da67..7b2425c 100644 --- a/setup/conda_environment.yml +++ b/setup/conda_environment.yml @@ -17,5 +17,6 @@ dependencies: - sphinx-gallery # support for a specific part of the website theme - myst-parser # Markdown support - sphinx-autobuild # live-html support + - sphinx-autoapi # to build docs from source code instead of package import # pip packages - pip \ No newline at end of file diff --git a/setup/pip_requirements.txt b/setup/pip_requirements.txt index 2f63eec..a3dc544 100644 --- a/setup/pip_requirements.txt +++ b/setup/pip_requirements.txt @@ -2,4 +2,5 @@ ipython nbsphinx sphinx-gallery pydata-sphinx-theme -myst-parser \ No newline at end of file +myst-parser +sphinx-autoapi \ No newline at end of file diff --git a/sphinx/_autoapi_templates/index.rst b/sphinx/_autoapi_templates/index.rst new file mode 100644 index 0000000..95d0ad8 --- /dev/null +++ b/sphinx/_autoapi_templates/index.rst @@ -0,0 +1,15 @@ +API Reference +============= + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + {% for page in pages %} + {% if page.top_level_object and page.display %} + {{ page.include_path }} + {% endif %} + {% endfor %} + +.. [#f1] Created with `sphinx-autoapi `_ diff --git a/sphinx/_autoapi_templates/python/attribute.rst b/sphinx/_autoapi_templates/python/attribute.rst new file mode 100644 index 0000000..ebaba55 --- /dev/null +++ b/sphinx/_autoapi_templates/python/attribute.rst @@ -0,0 +1 @@ +{% extends "python/data.rst" %} diff --git a/sphinx/_autoapi_templates/python/class.rst b/sphinx/_autoapi_templates/python/class.rst new file mode 100644 index 0000000..df5edff --- /dev/null +++ b/sphinx/_autoapi_templates/python/class.rst @@ -0,0 +1,58 @@ +{% if obj.display %} +.. py:{{ obj.type }}:: {{ obj.short_name }}{% if obj.args %}({{ obj.args }}){% endif %} +{% for (args, return_annotation) in obj.overloads %} + {{ " " * (obj.type | length) }} {{ obj.short_name }}{% if args %}({{ args }}){% endif %} +{% endfor %} + + + {% if obj.bases %} + {% if "show-inheritance" in autoapi_options %} + Bases: {% for base in obj.bases %}{{ base|link_objs }}{% if not loop.last %}, {% endif %}{% endfor %} + {% endif %} + + + {% if "show-inheritance-diagram" in autoapi_options and obj.bases != ["object"] %} + .. autoapi-inheritance-diagram:: {{ obj.obj["full_name"] }} + :parts: 1 + {% if "private-members" in autoapi_options %} + :private-bases: + {% endif %} + + {% endif %} + {% endif %} + {% if obj.docstring %} + {{ obj.docstring|indent(3) }} + {% endif %} + {% if "inherited-members" in autoapi_options %} + {% set visible_classes = obj.classes|selectattr("display")|list %} + {% else %} + {% set visible_classes = obj.classes|rejectattr("inherited")|selectattr("display")|list %} + {% endif %} + {% for klass in visible_classes %} + {{ klass.render()|indent(3) }} + {% endfor %} + {% if "inherited-members" in autoapi_options %} + {% set visible_properties = obj.properties|selectattr("display")|list %} + {% else %} + {% set visible_properties = obj.properties|rejectattr("inherited")|selectattr("display")|list %} + {% endif %} + {% for property in visible_properties %} + {{ property.render()|indent(3) }} + {% endfor %} + {% if "inherited-members" in autoapi_options %} + {% set visible_attributes = obj.attributes|selectattr("display")|list %} + {% else %} + {% set visible_attributes = obj.attributes|rejectattr("inherited")|selectattr("display")|list %} + {% endif %} + {% for attribute in visible_attributes %} + {{ attribute.render()|indent(3) }} + {% endfor %} + {% if "inherited-members" in autoapi_options %} + {% set visible_methods = obj.methods|selectattr("display")|list %} + {% else %} + {% set visible_methods = obj.methods|rejectattr("inherited")|selectattr("display")|list %} + {% endif %} + {% for method in visible_methods %} + {{ method.render()|indent(3) }} + {% endfor %} +{% endif %} diff --git a/sphinx/_autoapi_templates/python/data.rst b/sphinx/_autoapi_templates/python/data.rst new file mode 100644 index 0000000..89417f1 --- /dev/null +++ b/sphinx/_autoapi_templates/python/data.rst @@ -0,0 +1,32 @@ +{% if obj.display %} +.. py:{{ obj.type }}:: {{ obj.name }} + {%+ if obj.value is not none or obj.annotation is not none -%} + :annotation: + {%- if obj.annotation %} :{{ obj.annotation }} + {%- endif %} + {%- if obj.value is not none %} = {% + if obj.value is string and obj.value.splitlines()|count > 1 -%} + Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + {{ obj.value|indent(width=8) }} + + .. raw:: html + +
+ + {%- else -%} + {{ obj.value|string|truncate(100) }} + {%- endif %} + {%- endif %} + {% endif %} + + + {{ obj.docstring|indent(3) }} +{% endif %} diff --git a/sphinx/_autoapi_templates/python/exception.rst b/sphinx/_autoapi_templates/python/exception.rst new file mode 100644 index 0000000..92f3d38 --- /dev/null +++ b/sphinx/_autoapi_templates/python/exception.rst @@ -0,0 +1 @@ +{% extends "python/class.rst" %} diff --git a/sphinx/_autoapi_templates/python/function.rst b/sphinx/_autoapi_templates/python/function.rst new file mode 100644 index 0000000..b00d5c2 --- /dev/null +++ b/sphinx/_autoapi_templates/python/function.rst @@ -0,0 +1,15 @@ +{% if obj.display %} +.. py:function:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %} + +{% for (args, return_annotation) in obj.overloads %} + {{ obj.short_name }}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %} + +{% endfor %} + {% for property in obj.properties %} + :{{ property }}: + {% endfor %} + + {% if obj.docstring %} + {{ obj.docstring|indent(3) }} + {% endif %} +{% endif %} diff --git a/sphinx/_autoapi_templates/python/method.rst b/sphinx/_autoapi_templates/python/method.rst new file mode 100644 index 0000000..723cb7b --- /dev/null +++ b/sphinx/_autoapi_templates/python/method.rst @@ -0,0 +1,19 @@ +{%- if obj.display %} +.. py:method:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %} + +{% for (args, return_annotation) in obj.overloads %} + {{ obj.short_name }}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %} + +{% endfor %} + {% if obj.properties %} + {% for property in obj.properties %} + :{{ property }}: + {% endfor %} + + {% else %} + + {% endif %} + {% if obj.docstring %} + {{ obj.docstring|indent(3) }} + {% endif %} +{% endif %} diff --git a/sphinx/_autoapi_templates/python/module.rst b/sphinx/_autoapi_templates/python/module.rst new file mode 100644 index 0000000..d2714f6 --- /dev/null +++ b/sphinx/_autoapi_templates/python/module.rst @@ -0,0 +1,114 @@ +{% if not obj.display %} +:orphan: + +{% endif %} +:py:mod:`{{ obj.name }}` +=========={{ "=" * obj.name|length }} + +.. py:module:: {{ obj.name }} + +{% if obj.docstring %} +.. autoapi-nested-parse:: + + {{ obj.docstring|indent(3) }} + +{% endif %} + +{% block subpackages %} +{% set visible_subpackages = obj.subpackages|selectattr("display")|list %} +{% if visible_subpackages %} +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + +{% for subpackage in visible_subpackages %} + {{ subpackage.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block submodules %} +{% set visible_submodules = obj.submodules|selectattr("display")|list %} +{% if visible_submodules %} +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + +{% for submodule in visible_submodules %} + {{ submodule.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block content %} +{% if obj.all is not none %} +{% set visible_children = obj.children|selectattr("short_name", "in", obj.all)|list %} +{% elif obj.type is equalto("package") %} +{% set visible_children = obj.children|selectattr("display")|list %} +{% else %} +{% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %} +{% endif %} +{% if visible_children %} +{{ obj.type|title }} Contents +{{ "-" * obj.type|length }}--------- + +{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %} +{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %} +{% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %} +{% if "show-module-summary" in autoapi_options and (visible_classes or visible_functions) %} +{% block classes scoped %} +{% if visible_classes %} +Classes +~~~~~~~ + +.. autoapisummary:: + +{% for klass in visible_classes %} + {{ klass.id }} +{% endfor %} + + +{% endif %} +{% endblock %} + +{% block functions scoped %} +{% if visible_functions %} +Functions +~~~~~~~~~ + +.. autoapisummary:: + +{% for function in visible_functions %} + {{ function.id }} +{% endfor %} + + +{% endif %} +{% endblock %} + +{% block attributes scoped %} +{% if visible_attributes %} +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + +{% for attribute in visible_attributes %} + {{ attribute.id }} +{% endfor %} + + +{% endif %} +{% endblock %} +{% endif %} +{% for obj_item in visible_children %} +{{ obj_item.render()|indent(0) }} +{% endfor %} +{% endif %} +{% endblock %} diff --git a/sphinx/_autoapi_templates/python/package.rst b/sphinx/_autoapi_templates/python/package.rst new file mode 100644 index 0000000..fb9a649 --- /dev/null +++ b/sphinx/_autoapi_templates/python/package.rst @@ -0,0 +1 @@ +{% extends "python/module.rst" %} diff --git a/sphinx/_autoapi_templates/python/property.rst b/sphinx/_autoapi_templates/python/property.rst new file mode 100644 index 0000000..70af242 --- /dev/null +++ b/sphinx/_autoapi_templates/python/property.rst @@ -0,0 +1,15 @@ +{%- if obj.display %} +.. py:property:: {{ obj.short_name }} + {% if obj.annotation %} + :type: {{ obj.annotation }} + {% endif %} + {% if obj.properties %} + {% for property in obj.properties %} + :{{ property }}: + {% endfor %} + {% endif %} + + {% if obj.docstring %} + {{ obj.docstring|indent(3) }} + {% endif %} +{% endif %} diff --git a/sphinx/conf.py b/sphinx/conf.py index 85c3d16..43e6980 100644 --- a/sphinx/conf.py +++ b/sphinx/conf.py @@ -16,25 +16,35 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', 'sphinx.ext.mathjax', - 'sphinx.ext.todo', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx', 'sphinx.ext.extlinks', 'sphinx.ext.napoleon', - 'sphinx.ext.autosummary', 'IPython.sphinxext.ipython_directive', 'IPython.sphinxext.ipython_console_highlighting', - 'sphinx.ext.doctest', 'nbsphinx', 'sphinx_gallery.load_style', - 'myst_parser' + 'myst_parser', + 'autoapi' ] templates_path = ['source/_templates'] html_static_path = ["source/_static"] exclude_patterns = ['_build'] +autoapi_dirs = [ + '../brightway2-analyzer', + '../brightway2-calc', + '../brightway2-data', + '../brightway2-io', + '../brightway2-regional' +] + +autoapi_template_dir = '_autoapi_templates' +autoapi_root = 'technical/api' + # The suffix of source filenames. source_suffix = { '.rst': 'restructuredtext', diff --git a/sphinx/source/reference/bw2analyzer.rst b/sphinx/source/reference/bw2analyzer.rst deleted file mode 100644 index 05b9fa9..0000000 --- a/sphinx/source/reference/bw2analyzer.rst +++ /dev/null @@ -1,44 +0,0 @@ -Brightway2-analyzer -******************* - -Contribution analysis -===================== - -.. autoclass:: bw2analyzer.contribution.ContributionAnalysis - :members: - -Supply chain traversal -====================== - -.. autofunction:: bw2analyzer.traverse_tagged_databases - -This function uses the following help functions: - -.. autofunction:: bw2analyzer.tagged.aggregate_tagged_graph - -.. autofunction:: bw2analyzer.tagged.recurse_tagged_database - -LCA reports -=========== - -.. autoclass:: bw2analyzer.report.SerializedLCAReport - :members: - -PageRank algorithm -================== - -.. autoclass:: bw2analyzer.page_rank.PageRank - :members: - -Comparison functions -==================== - -.. autofunction:: bw2analyzer.comparisons.compare_activities_by_grouped_leaves - -.. autofunction:: bw2analyzer.utils.print_recursive_calculation - -.. autofunction:: bw2analyzer.utils.print_recursive_supply_chain - -.. autofunction:: bw2analyzer.comparisons.find_differences_in_inputs - -.. autofunction:: bw2analyzer.comparisons.compare_activities_by_lcia_score diff --git a/sphinx/source/reference/bw2calc.rst b/sphinx/source/reference/bw2calc.rst deleted file mode 100644 index 0459104..0000000 --- a/sphinx/source/reference/bw2calc.rst +++ /dev/null @@ -1,73 +0,0 @@ -Brightway2-calc -*************** - -.. _matrixbuilders: - -Matrix builders -=============== - -One of the most basic and most important components of ``bw2calc`` is the ability to build a sparse matrix from a processed parameter array. - -Matrix Builder --------------- - -.. autoclass:: bw2calc.MatrixBuilder - :members: - -Technosphere Biosphere Matrix Builder -------------------------------------- - -.. autoclass:: bw2calc.TechnosphereBiosphereMatrixBuilder - :members: - -Indexing --------- - -.. autofunction:: bw2calc.indexing.index_with_arrays - -.. autofunction:: bw2calc.indexing.index_with_searchsorted - -.. _lca: - -Static life cycle assessment -============================ - -.. autoclass:: bw2calc.LCA - :members: - -.. autoclass:: bw2calc.least_squares.LeastSquaresLCA - :members: - -Graph Traversal -=============== - -.. autoclass:: bw2calc.GraphTraversal - :members: - -.. _montecarlo: - -Stochastic Life Cycle Assessment -================================ - -Monte Carlo LCA ---------------- - -.. autoclass:: bw2calc.MonteCarloLCA - :members: - -Vector Monte Carlo LCA ----------------------- - -.. autoclass:: bw2calc.ParameterVectorLCA - :members: - -Parallel Monte Carlo LCA ------------------------- - -.. autoclass:: bw2calc.ParallelMonteCarlo - :members: - -Utilities -========= - -.. autofunction:: bw2calc.utils.load_arrays diff --git a/sphinx/source/reference/bw2data.rst b/sphinx/source/reference/bw2data.rst deleted file mode 100644 index 94549a1..0000000 --- a/sphinx/source/reference/bw2data.rst +++ /dev/null @@ -1,292 +0,0 @@ -Brightway2-data -*************** - -.. _configuration-technical: - -Configuration -============= - -The configuration for brightway2 is implemented as a singleton class that is created when ``brightway2`` is imported. - -.. autoclass:: bw2data.configuration.Config - :members: - -Projects -======== - -.. autoclass:: bw2data.project.ProjectManager - :members: - -Base classes for metadata -========================= - -.. _serialized-dict: - -Serialized Dictionary ---------------------- - -.. autoclass:: bw2data.serialization.SerializedDict - :members: - -.. _compound-json: - -Compound JSON dictionary ------------------------- - -JSON hash tables don't support keys like ``("biosphere", "an emission")``, so the ``pack`` and ``unpack`` methods are used to transform data from Python to JSON and back. - -.. autoclass:: bw2data.serialization.CompoundJSONDict - :members: - -.. _pickled-dict: - -Pickled Dictionary ------------------- - -.. autoclass:: bw2data.serialization.PickledDict - :members: - -Metadata stores -=============== - -.. _databases: - -databases ---------- - -.. autoclass:: bw2data.meta.Databases - :members: - :inherited-members: - -.. _methods: - -methods -------- - -.. autoclass:: bw2data.meta.Methods - :members: - :inherited-members: - -.. _normalizations: - -normalizations --------------- - -.. autoclass:: bw2data.meta.NormalizationMeta - :members: - -.. _weightings: - -weightings ----------- - -.. autoclass:: bw2data.meta.WeightingMeta - :members: - -Mappings -======== - -.. _mapping: - -mapping -------- - -.. autoclass:: bw2data.meta.Mapping - :members: - -.. _geomapping: - -geomapping ----------- - -.. autoclass:: bw2data.meta.GeoMapping - :members: - -Data stores -=========== - -.. _datastore: - -DataStore ---------- - -.. autoclass:: bw2data.DataStore - :members: - -Inventory data backends -======================= - -.. _database: - -DatabaseChooser ---------------- - -The function :func:`bw2data.database.DatabaseChooser` will choose the correct ``Database`` class depending on the database backend registered for the database in its metadata. ``bw2data`` comes with two backends (see :ref:`database-backends`). - -The function ``bw2data.database.Database`` is an alias for :func:`bw2data.database.DatabaseChooser`, provided for backwards compatibility. - -.. autofunction:: bw2data.database.DatabaseChooser - -.. _switching-backends: - -Switching backends ------------------- - -.. autofunction:: bw2data.backends.utils.convert_backend - -.. _custom-backends: - -Custom database backends ------------------------- - -New database backends should inherit from ``bw2data.backends.base.LCIBackend``: - -.. autoclass:: bw2data.backends.base.LCIBackend - :members: - -.. _single-file-database: - -Default backend - databases stored in a SQLite database -------------------------------------------------------- - -This backend is a hybrid between SQLite and a document database. See also :ref:`whysqlite` - -The LCI data is stored in two tables, `ActivityDataset` and `ExchangeDataset`. Interactions with the database are mostly done using the `Peewee ORM `__, although some raw SQL queries are used for performance reasons. The table have the following schemas: - -.. code:: sql - - CREATE TABLE "activitydataset" ( - "id" INTEGER NOT NULL PRIMARY KEY, - "data" BLOB NOT NULL, - "code" TEXT NOT NULL, - "database" TEXT NOT NULL, - "location" TEXT, - "name" TEXT, - "product" TEXT, - "type" TEXT - ) - - CREATE TABLE "exchangedataset" ( - "id" INTEGER NOT NULL PRIMARY KEY, - "data" BLOB NOT NULL, - "input_code" TEXT NOT NULL, - "input_database" TEXT NOT NULL, - "output_code" TEXT NOT NULL, - "output_database" TEXT NOT NULL, - "type" TEXT NOT NULL - ) - -As one of the fundamental principles of Brightway2 is to have a document-based backend, we incude most data in the column `data`, which is stored as a binary pickle. Serializing and deserializing is handled automatically by the Peewee interface. However, some attributes can be changed in the database. The following columns are canonical, i.e. they are included when constructing the `Activity` and `Exchange` objects: - -* ActivityDataset.database -* ActivityDataset.code -* ExchangeDataset.input_database -* ExchangeDataset.input_code -* ExchangeDataset.output_database -* ExchangeDataset.output_code - -All other columns are only used to querying and filtering datasets. - -.. autoclass:: bw2data.backends.peewee.database.SQLiteBackend - :members: - -Single file - each database in a single file --------------------------------------------- - -.. autoclass:: bw2data.backends.single_file.database.SingleFileDatabase - :members: - -.. _json-database: - -Version-control friendly - each database is a JSON file -------------------------------------------------------- - -.. autoclass:: bw2data.backends.json.database.JSONDatabase - :members: - -.. autoclass:: bw2data.backends.json.sync_json_dict.SynchronousJSONDict - :members: - -.. autoclass:: bw2data.backends.json.sync_json_dict.frozendict - :members: - -Impact Assessment data stores -============================= - -.. _ia-datastore: - -ImpactAssessmentDataStore -------------------------- - -.. autofunction:: bw2data.ia_data_store.abbreviate - -.. autoclass:: bw2data.ia_data_store.ImpactAssessmentDataStore - :members: - -.. _method: - -Method ------- - -.. autoclass:: bw2data.method.Method - :members: - -.. _normalization: - -Normalization -------------- - -.. autoclass:: bw2data.weighting_normalization.Normalization - :members: - -.. _weighting: - -Weighting ---------- - -.. autoclass:: bw2data.weighting_normalization.Weighting - :members: - -.. _user-preferences: - -User preferences -================ - -.. autoclass:: bw2data.meta.Preferences - :members: - -.. _parameters: - -Parameters -========== - -.. autoclass:: bw2data.parameters.ParameterManager - :members: - -.. autoclass:: bw2data.parameters.Group - :members: - -.. autoclass:: bw2data.parameters.ProjectParameter - :members: - -.. autoclass:: bw2data.parameters.DatabaseParameter - :members: - -.. autoclass:: bw2data.parameters.ActivityParameter - :members: - -Utilities -========= - -.. automethod:: bw2data.utils.combine_methods - -.. automethod:: bw2data.utils.download_file - -.. automethod:: bw2data.utils.natural_sort - -.. automethod:: bw2data.utils.random_string - -.. automethod:: bw2data.utils.recursive_str_to_unicode - -.. automethod:: bw2data.utils.uncertainify - diff --git a/sphinx/source/reference/bw2io.rst b/sphinx/source/reference/bw2io.rst deleted file mode 100644 index 3bc1657..0000000 --- a/sphinx/source/reference/bw2io.rst +++ /dev/null @@ -1,284 +0,0 @@ -Brightway2-io -************* - -BW2Package -========== - -Brightway2 has its own data format for archiving data which is both efficient and compatible across operating systems and programming languages. This is the default backup format for Brightway2 :ref:`datastore` objects. - -.. note:: **imports** and **exports** are supported. - -.. autoclass:: bw2io.package.BW2Package - :members: - -Migrations -========== - -.. autoclass:: bw2io.migrations.Migration - :members: - -.. autofunction:: bw2io.migrations.create_core_migrations - -Extractors -========== - -Ecospold 1 -`````````` - -.. autoclass:: bw2io.extractors.ecospold1.Ecospold1DataExtractor - :members: - -.. autoclass:: bw2io.extractors.ecospold1_lcia.Ecospold1LCIAExtractor - :members: - -Ecospold 2 -`````````` - -.. autoclass:: bw2io.extractors.ecospold2.Ecospold2DataExtractor - :members: - -Simapro CSV -``````````` - -.. autoclass:: bw2io.extractors.simapro_csv.SimaProCSVExtractor - :members: - -.. autoclass:: bw2io.extractors.simapro_lcia_csv.SimaProLCIACSVExtractor - :members: - -Importers -========= - -Base -```` - -.. autoclass:: bw2io.importers.base.ImportBase - :members: - -.. autoclass:: bw2io.importers.base_lci.LCIImporter - :members: - -.. autoclass:: bw2io.importers.base_lcia.LCIAImporter - :members: - -Ecospold 1 -`````````` - -Ecospold version 1 is the data format of ecoinvent versions 1 and 2, and the US LCI. It is an XML data format with reasonable defaults. - -.. note:: only **imports** are supported. - -.. autoclass:: bw2io.importers.ecospold1.SingleOutputEcospold1Importer - :members: - -.. autoclass:: bw2io.importers.ecospold1.MultiOutputEcospold1Importer - :members: - -.. autoclass:: bw2io.importers.ecospold1_lcia.Ecospold1LCIAImporter - :members: - -Ecospold 2 -`````````` - -Ecospold version 2 is the data format of ecoinvent version 3. - -.. note:: only **imports** are supported. - -.. autoclass:: bw2io.importers.ecospold2.SingleOutputEcospold2Importer - :members: - -.. autoclass:: bw2io.importers.ecospold2_biosphere.Ecospold2BiosphereImporter - :members: - -Ecoinvent -````````` - -.. autoclass:: bw2io.importers.ecoinvent_lcia.EcoinventLCIAImporter - :members: - -Simapro -``````` - -Import a `SimaPro `_ text file. - -.. note:: only **imports** are supported. - -.. autoclass:: bw2io.importers.simapro_csv.SimaProCSVImporter - :members: - -.. autoclass:: bw2io.importers.simapro_lcia_csv.SimaProLCIACSVImporter - :members: - -Excel -````` - -Import an inventory in an Excel spreadsheet which follows the generic `Excel example `__. - -.. note:: both imports and exports are supported. - -.. autoclass:: bw2io.importers.excel.ExcelImporter - -CSV -``` - -Import an inventory in a CSV file which follows the generic `CSV example `__. - -.. note:: both imports and exports are supported. - -.. autoclass:: bw2io.importers.CSVImporter - :members: - -Strategies -========== - -Migrations -`````````` - -.. autofunction:: bw2io.strategies.migrations.migrate_datasets - -.. autofunction:: bw2io.strategies.migrations.migrate_exchanges - -Generic -``````` - -.. autofunction:: bw2io.strategies.generic.link_iterable_by_fields - -.. autofunction:: bw2io.strategies.generic.assign_only_product_as_production - -.. autofunction:: bw2io.strategies.generic.link_technosphere_by_activity_hash - -.. autofunction:: bw2io.strategies.generic.set_code_by_activity_hash - -.. autofunction:: bw2io.strategies.generic.set_code_by_activity_hash - -.. autofunction:: bw2io.strategies.generic.tupleize_categories - -.. autofunction:: bw2io.strategies.generic.drop_unlinked - -.. autofunction:: bw2io.strategies.generic.normalize_units - -Biosphere -````````` - -.. autofunction:: bw2io.strategies.biosphere.drop_unspecified_subcategories - -.. autofunction:: bw2io.strategies.biosphere.normalize_biosphere_names - -.. autofunction:: bw2io.strategies.biosphere.normalize_biosphere_categories - -.. autofunction:: bw2io.strategies.biosphere.strip_biosphere_exc_locations - -LCIA -```` - -.. autofunction:: bw2io.strategies.lcia.add_activity_hash_code - -.. autofunction:: bw2io.strategies.lcia.drop_unlinked_cfs - -.. autofunction:: bw2io.strategies.lcia.set_biosphere_type - -.. autofunction:: bw2io.strategies.lcia.match_subcategories - -Ecospold 1 -`````````` - -.. autofunction:: bw2io.strategies.ecospold1_allocation.clean_integer_codes - -.. autofunction:: bw2io.strategies.ecospold1_allocation.es1_allocate_multioutput - -.. autofunction:: bw2io.strategies.ecospold1_allocation.allocate_exchanges - -Ecospold 2 -`````````` - -.. autofunction:: bw2io.strategies.ecospold2.link_biosphere_by_flow_uuid - -.. autofunction:: bw2io.strategies.ecospold2.remove_zero_amount_coproducts - -.. autofunction:: bw2io.strategies.ecospold2.remove_zero_amount_inputs_with_no_activity - -.. autofunction:: bw2io.strategies.ecospold2.es2_assign_only_product_with_amount_as_reference_product - -.. autofunction:: bw2io.strategies.ecospold2.assign_single_product_as_activity - -.. autofunction:: bw2io.strategies.ecospold2.create_composite_code - -.. autofunction:: bw2io.strategies.ecospold2.link_internal_technosphere_by_composite_code - -.. autofunction:: bw2io.strategies.ecospold2.delete_exchanges_missing_activity - -.. autofunction:: bw2io.strategies.ecospold2.delete_ghost_exchanges - -Simapro -``````` - -.. autofunction:: bw2io.strategies.simapro.sp_allocate_products - -.. autofunction:: bw2io.strategies.simapro.link_technosphere_based_on_name_unit_location - -.. autofunction:: bw2io.strategies.simapro.split_simapro_name_geo - -.. autofunction:: bw2io.strategies.simapro.normalize_simapro_biosphere_categories - -.. autofunction:: bw2io.strategies.simapro.normalize_simapro_biosphere_names - -.. autofunction:: bw2io.strategies.simapro.normalize_simapro_formulae - -Special -``````` - -.. autofunction:: bw2io.strategies.special.add_dummy_processes_and_rename_exchanges - -Export -====== - -.. autofunction:: bw2io.export.excel.write_lci_excel - -.. autofunction:: bw2io.export.csv.write_lci_csv - -.. autofunction:: bw2io.export.excel.lci_matrices_to_excel - -.. autofunction:: bw2io.export.excel.write_lci_matching - -.. autofunction:: bw2io.export.excel.write_lcia_matching - -`Gephi `_ is an open-source graph visualization and analysis program. - -.. note:: only **exports** are supported. - -.. autoclass:: bw2io.export.gexf.DatabaseToGEXF - :members: - -.. autofunction:: bw2io.export.matlab.lci_matrices_to_matlab - -.. _backup-data-directory: - -Backups -======= - -.. autofunction:: bw2io.backup.backup_data_directory - -.. autofunction:: bw2io.backup.backup_project_directory - -Data -==== - -.. autofunction:: bw2io.data.write_json_file - -.. autofunction:: bw2io.data.get_ecoinvent_301_31_migration_data - -.. autofunction:: bw2io.data.get_ecoinvent_2_301_migration_data - -.. autofunction:: bw2io.data.get_biosphere_2_3_category_migration_data - -.. autofunction:: bw2io.data.get_biosphere_2_3_name_migration_data - -.. autofunction:: bw2io.data.get_us_lci_migration_data - -.. autofunction:: bw2io.data.convert_simapro_ecoinvent_elementary_flows - -.. autofunction:: bw2io.data.get_simapro_ecoinvent_3_migration_data - -.. autofunction:: bw2io.data.convert_ecoinvent_2_301 - -.. autofunction:: bw2io.data.convert_lcia_methods_data diff --git a/sphinx/source/reference/images/import-database.graffle b/sphinx/source/reference/images/import-database.graffle deleted file mode 100644 index 67ab263..0000000 --- a/sphinx/source/reference/images/import-database.graffle +++ /dev/null @@ -1,1808 +0,0 @@ - - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGraffle - 139.16.0.171715 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {1612, 536}} - Class - SolidGraphic - ID - 2 - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - BaseZoom - 0 - CanvasOrigin - {0, 0} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2013-01-12 09:42:41 +0100 - Creator - Chris Mutel - DisplayScale - 1.000 cm = 1.000 cm - GraphDocumentVersion - 8 - GraphicsList - - - Class - LineGraphic - Head - - ID - 28 - - ID - 53 - Points - - {584.542, 236.656} - {612.892, 236.997} - - Style - - stroke - - Color - - b - 1 - g - 0.469854 - r - 0.980195 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 18 - - - - Class - LineGraphic - Head - - ID - 3 - - ID - 52 - Points - - {125.229, 129.159} - {192.297, 129.159} - - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 51 - - - - Bounds - {{41, 84.1865}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 51 - Shape - Pentagon - Style - - stroke - - Color - - b - 0.814088 - g - 0.223006 - r - 1 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 For each exchange} - - - - Class - LineGraphic - Head - - ID - 32 - - ID - 50 - Points - - {584.542, 162.656} - {612.892, 162.997} - - Style - - stroke - - Color - - b - 1 - g - 0.469854 - r - 0.980195 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 48 - - - - Bounds - {{498.505, 133.785}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 48 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Log info} - - - - Bounds - {{450.543, 150.328}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 45 - Line - - ID - 43 - Position - 0.41691452264785767 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 no} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 48 - - ID - 43 - Points - - {437.544, 162.256} - {497.505, 162.428} - - Style - - stroke - - Color - - b - 0.109804 - g - 0.0980392 - r - 0.843137 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 4 - - - - Class - LineGraphic - Head - - ID - 22 - - ID - 41 - Points - - {696.07, 184.044} - {758.88, 216.548} - - Style - - stroke - - Color - - b - 1 - g - 0.469854 - r - 0.980195 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 32 - - - - Class - LineGraphic - Head - - ID - 51 - - ID - 40 - Points - - {85.9726, 208.175} - {85.9726, 166.543} - - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 39 - - - - Bounds - {{41, 209.175}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 39 - Shape - Circle - Style - - stroke - - Color - - b - 0.913725 - g - 0.85098 - r - 0.670588 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Apply name and category transforms} - - - - Bounds - {{613.89, 135.175}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 32 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Add biosphere input} - - - - Class - LineGraphic - Head - - ID - 22 - - ID - 31 - Points - - {699.929, 237.521} - {736.292, 237.521} - - Style - - stroke - - Color - - b - 1 - g - 0.469854 - r - 0.980195 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 28 - - - - Bounds - {{608.942, 95.6031}, {29, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 30 - Line - - ID - 29 - Position - 0.49612370133399963 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 yes} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 22 - - ID - 29 - Points - - {437.461, 143.187} - {488.194, 121.06} - {568.653, 107.853} - {650.126, 111.424} - {725.63, 142.2} - {776.857, 208.384} - - Style - - stroke - - Color - - b - 0.25054 - g - 0.728261 - r - 0.148018 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 4 - - - - Bounds - {{613.89, 209.175}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 28 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Add database input} - - - - Bounds - {{592.484, 272.578}, {29, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 27 - Line - - ID - 26 - Position - 0.52294832468032837 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 yes} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 22 - - ID - 26 - Points - - {443.01, 271.645} - {493.604, 280.438} - {572.166, 283.886} - {671.025, 281.64} - {753.337, 253.353} - - Style - - stroke - - Color - - b - 0.25054 - g - 0.728261 - r - 0.148018 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 10 - - - - Bounds - {{737.292, 209.175}, {124.236, 56.6929}} - Class - ShapedGraphic - ID - 22 - Shape - Hexagon - Style - - stroke - - Color - - b - 0.259778 - g - 0.344928 - r - 0.229471 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Add exchange} - - - - Bounds - {{455.81, 237.817}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 21 - Line - - ID - 20 - Position - 0.4506455659866333 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 no} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 18 - - ID - 20 - Points - - {443.009, 254.293} - {498.044, 244.361} - - Style - - stroke - - Color - - b - 0.109804 - g - 0.0980392 - r - 0.843137 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 10 - - - - Bounds - {{498.505, 207.785}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 18 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Log warning} - - - - Bounds - {{297.691, 252.536}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 17 - Line - - ID - 16 - Position - 0.47210600972175598 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 no} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 10 - - ID - 16 - Points - - {278.091, 265.063} - {345.025, 263.948} - - Style - - stroke - - Color - - b - 0.109804 - g - 0.0980392 - r - 0.843137 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 7 - - - - Bounds - {{504.285, 325.537}, {29, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 12 - Line - - ID - 11 - Position - 0.48065617680549622 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 yes} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 22 - - ID - 11 - Points - - {277.924, 294.659} - {336.223, 333.486} - {427.674, 337.537} - {519.215, 337.537} - {610.74, 337.537} - {701.649, 327.243} - {767.787, 266.544} - - Style - - stroke - - Color - - b - 0.25054 - g - 0.728261 - r - 0.148018 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 7 - - - - Bounds - {{346.025, 232.13}, {96, 62.004}} - Class - ShapedGraphic - ID - 10 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Is hash in dependent database} - - - - Bounds - {{224.313, 189.455}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 9 - Line - - ID - 8 - Position - 0.42934837937355042 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 no} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 7 - - ID - 8 - Points - - {237.025, 175.131} - {235.366, 236.441} - - Style - - stroke - - Color - - b - 0.109804 - g - 0.0980392 - r - 0.843137 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 3 - - - - Bounds - {{192.052, 237.441}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 7 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Is hash in database?} - - - - Bounds - {{300.232, 133.482}, {29, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 6 - Line - - ID - 5 - Position - 0.46016854047775269 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 yes} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 4 - - ID - 5 - Points - - {284.221, 138.968} - {350.526, 153.123} - - Style - - stroke - - Color - - b - 0.25054 - g - 0.728261 - r - 0.148018 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 3 - - - - Bounds - {{351.505, 133.785}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 4 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Is hash in biosphere?} - - - - Bounds - {{193.297, 84.1865}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 3 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Is input from biosphere?} - - - - GridInfo - - GuidesLocked - NO - GuidesVisible - YES - HPages - 2 - ImageCounter - 1 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - HierarchicalOrientation - 0 - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - dot - neatoOverlap - - neatoSeparation - 1 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2013-02-08 00:49:08 +0100 - Modifier - Chris Mutel - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - PageBreaks - YES - PrintInfo - - NSBottomMargin - - float - 41 - - NSLeftMargin - - float - 18 - - NSOrientation - - int - 1 - - NSPaperSize - - size - {842, 595} - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 1 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 1 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - - name - Canvas 1 - - - Frame - {{99, 155}, {1136, 873}} - ListView - - OutlineWidth - 142 - RightSidebar - - ShowRuler - - Sidebar - - SidebarWidth - 120 - VisibleRegion - {{0, 0}, {987, 720}} - Zoom - 1 - ZoomValues - - - Canvas 1 - 1 - 1 - - - - - diff --git a/sphinx/source/reference/images/import-database.png b/sphinx/source/reference/images/import-database.png deleted file mode 100644 index c5366f2d7056cde091a196ea198c37b5a4b33cca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82310 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 prsUuEMhkktD(T - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGraffle - 139.16.0.171715 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {559, 783}} - Class - SolidGraphic - ID - 2 - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - BaseZoom - 0 - CanvasOrigin - {0, 0} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2013-01-12 09:42:41 +0100 - Creator - Chris Mutel - DisplayScale - 1.000 cm = 1.000 cm - GraphDocumentVersion - 8 - GraphicsList - - - Class - LineGraphic - Head - - ID - 22 - - ID - 37 - Points - - {402.495, 404.215} - {443.817, 391.684} - - Style - - stroke - - Color - - b - 1 - g - 0.469854 - r - 0.980195 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 35 - - - - Class - LineGraphic - Head - - ID - 35 - - ID - 36 - Points - - {284.667, 417.002} - {316.804, 417} - - Style - - stroke - - Color - - b - 1 - g - 0.469854 - r - 0.980195 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 32 - - - - Bounds - {{317.804, 388.658}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 35 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Add new flow} - - - - Bounds - {{198.627, 388.658}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 32 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.713726 - g - 0.482353 - r - 0.172549 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Log info} - - - - Bounds - {{436.98, 352.658}, {85.0394, 56.6929}} - Class - ShapedGraphic - ID - 22 - Shape - Hexagon - Style - - stroke - - Color - - b - 0.259778 - g - 0.344928 - r - 0.229471 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Add CF data} - - - - Bounds - {{145.308, 387.146}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 9 - Line - - ID - 8 - Position - 0.42934837937355042 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 no} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 32 - - ID - 8 - Points - - {126.47, 392.58} - {198.295, 407.873} - - Style - - stroke - - Color - - b - 0.109804 - g - 0.0980392 - r - 0.843137 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 3 - - - - Bounds - {{264.406, 340.683}, {29, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 6 - Line - - ID - 5 - Position - 0.48569253087043762 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 yes} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 22 - - ID - 5 - Points - - {126.958, 376.124} - {282, 352.658} - {440.123, 375.352} - - Style - - stroke - - Color - - b - 0.25054 - g - 0.728261 - r - 0.148018 - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - Width - 2 - - - Tail - - ID - 3 - - - - Bounds - {{36.5273, 338.031}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 3 - Shape - Circle - Style - - stroke - - Color - - b - 0.913725 - g - 0.85098 - r - 0.670588 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 Is hash in biosphere?} - - - - GridInfo - - GuidesLocked - NO - GuidesVisible - YES - HPages - 1 - ImageCounter - 1 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - HierarchicalOrientation - 0 - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - dot - neatoOverlap - - neatoSeparation - 1 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2013-02-08 00:31:25 +0100 - Modifier - Chris Mutel - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - PageBreaks - YES - PrintInfo - - NSBottomMargin - - float - 41 - - NSLeftMargin - - float - 18 - - NSPaperSize - - size - {595, 842} - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 1 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 1 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - - name - Canvas 1 - - - Frame - {{99, 135}, {1136, 873}} - ListView - - OutlineWidth - 142 - RightSidebar - - ShowRuler - - Sidebar - - SidebarWidth - 120 - VisibleRegion - {{-214, 1}, {987, 735}} - Zoom - 1 - ZoomValues - - - Canvas 1 - 1 - 1 - - - - - diff --git a/sphinx/source/reference/images/import-method.png b/sphinx/source/reference/images/import-method.png deleted file mode 100644 index bad1092b066b0f12882be1376385610394d9566c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39233 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

q5i}B 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 diff --git a/sphinx/source/reference/images/import-simapro.graffle b/sphinx/source/reference/images/import-simapro.graffle deleted file mode 100644 index b7269bc..0000000 --- a/sphinx/source/reference/images/import-simapro.graffle +++ /dev/null @@ -1,1509 +0,0 @@ - - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGraffle - 139.16.0.171715 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {806, 536}} - Class - SolidGraphic - ID - 2 - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - BaseZoom - 0 - CanvasOrigin - {0, 0} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2013-01-12 09:42:41 +0100 - Creator - Chris Mutel - DisplayScale - 1.000 cm = 1.000 cm - GraphDocumentVersion - 8 - GraphicsList - - - Class - LineGraphic - Head - - ID - 85 - - ID - 87 - Points - - {404.639, 262.984} - {404.639, 281.615} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 67 - - - - Bounds - {{366.139, 282.615}, {77, 49.0629}} - Class - ShapedGraphic - ID - 85 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 load background\ -datasets} - - - - Class - LineGraphic - Head - - ID - 79 - - ID - 84 - Points - - {591.794, 286.686} - {661.088, 317.758} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 75 - - - - Class - LineGraphic - Head - - ID - 78 - - ID - 83 - Points - - {595.127, 276.395} - {652.149, 284.11} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 75 - - - - Class - LineGraphic - Head - - ID - 77 - - ID - 82 - Points - - {599.077, 263.908} - {652.155, 254.468} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 75 - - - - Class - LineGraphic - Head - - ID - 73 - - ID - 81 - Points - - {444.134, 311.062} - {474.811, 314.103} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 85 - - - - Class - LineGraphic - Head - - ID - 66 - - ID - 80 - Points - - {479.536, 382.829} - {444.558, 394.781} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 73 - - - - Bounds - {{653.14, 318.372}, {102.641, 37.6696}} - Class - ShapedGraphic - ID - 79 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.0739918 - g - 0.0857814 - r - 1 - - Width - 3 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 or raise error} - - - - Bounds - {{653.14, 272.352}, {102.641, 37.6696}} - Class - ShapedGraphic - ID - 78 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 or link to background} - - - - Bounds - {{653.14, 226.331}, {102.641, 37.6696}} - Class - ShapedGraphic - ID - 77 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 link to foreground} - - - - Class - LineGraphic - Head - - ID - 75 - - ID - 76 - Points - - {557.491, 339.529} - {557.491, 308.687} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 74 - - - - Bounds - {{512.518, 226.331}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 75 - Shape - Pentagon - Style - - stroke - - Color - - b - 0.814088 - g - 0.223006 - r - 1 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\i\fs24 \cf0 For each exchange} - - - - Bounds - {{512.518, 340.529}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 74 - Shape - Pentagon - Style - - stroke - - Color - - b - 0.814088 - g - 0.223006 - r - 1 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\i\fs24 \cf0 For each dataset} - - - - Bounds - {{474.877, 212.921}, {319, 234}} - Class - ShapedGraphic - ID - 73 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.25054 - g - 0.728261 - r - 0.148018 - - - - - - Class - LineGraphic - Head - - ID - 67 - - ID - 72 - Points - - {318.94, 270.744} - {365.208, 252.9} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 70 - - - - Class - LineGraphic - Head - - ID - 70 - - ID - 71 - Points - - {206.963, 173.518} - {196.645, 212.438} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 55 - - - - Class - LineGraphic - Head - - ID - 55 - - ID - 69 - Points - - {295.444, 134.051} - {256.926, 134.051} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 68 - - - - Bounds - {{296.444, 95.5513}, {77, 77}} - Class - ShapedGraphic - ID - 68 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 verify data} - - - - Bounds - {{366.139, 212.921}, {77, 49.0629}} - Class - ShapedGraphic - ID - 67 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 catalogue foreground\ -datasets} - - - - Bounds - {{366.139, 369.921}, {77, 77}} - Class - ShapedGraphic - ID - 66 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.913725 - g - 0.214944 - r - 0.165968 - - Width - 3 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 write and process database} - - - - Class - LineGraphic - Head - - ID - 63 - - ID - 65 - Points - - {103.778, 343.771} - {192.41, 379.039} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 51 - - - - Class - LineGraphic - Head - - ID - 62 - - ID - 64 - Points - - {106.639, 334.918} - {188.361, 345.757} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 51 - - - - Bounds - {{189.352, 379.409}, {102.641, 37.6696}} - Class - ShapedGraphic - ID - 63 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 get production exchange} - - - - Bounds - {{189.352, 333.861}, {102.641, 37.6696}} - Class - ShapedGraphic - ID - 62 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 get exchanges} - - - - Class - LineGraphic - Head - - ID - 59 - - ID - 61 - Points - - {109.943, 324.487} - {188.361, 314.086} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 51 - - - - Class - LineGraphic - Head - - ID - 58 - - ID - 60 - Points - - {109.897, 313.637} - {192.41, 280.803} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 51 - - - - Bounds - {{189.352, 288.312}, {102.641, 37.6696}} - Class - ShapedGraphic - ID - 59 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 get dataset metdata} - - - - Bounds - {{189.352, 242.764}, {102.641, 37.6696}} - Class - ShapedGraphic - ID - 58 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 define dataset} - - - - Class - LineGraphic - Head - - ID - 68 - - ID - 56 - Points - - {417.426, 134.051} - {374.444, 134.051} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 3 - - - - Bounds - {{178.926, 95.5513}, {77, 77}} - Class - ShapedGraphic - ID - 55 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 clean data} - - - - Class - LineGraphic - Head - - ID - 3 - - ID - 54 - Points - - {538.907, 134.051} - {496.426, 134.051} - - Style - - stroke - - HeadArrow - FilledArrow - HopLines - - HopType - 1 - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 39 - - - - Bounds - {{24, 284.949}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 51 - Shape - Pentagon - Style - - stroke - - Color - - b - 0.814088 - g - 0.223006 - r - 1 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\i\fs24 \cf0 For each dataset} - - - - Bounds - {{540.407, 89.0787}, {89.9453, 89.9453}} - Class - ShapedGraphic - ID - 39 - Shape - Circle - Style - - stroke - - Color - - b - 0.913725 - g - 0.85098 - r - 0.670588 - - Width - 3 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 instantiate SimaPro Importer} - - - - Bounds - {{418.426, 95.5513}, {77, 77}} - Class - ShapedGraphic - ID - 3 - Shape - Rectangle - Style - - stroke - - Color - - b - 0.272319 - g - 0.527387 - r - 0.913725 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg65001\cocoartf1038\cocoasubrtf360 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural - -\f0\fs24 \cf0 load file} - - - - Bounds - {{6, 212.921}, {319, 234}} - Class - ShapedGraphic - ID - 70 - Shape - RoundRect - Style - - stroke - - Color - - b - 0.25054 - g - 0.728261 - r - 0.148018 - - - - - - GridInfo - - GuidesLocked - NO - GuidesVisible - YES - HPages - 1 - ImageCounter - 1 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - HierarchicalOrientation - 0 - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - dot - neatoOverlap - - neatoSeparation - 1 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2013-09-03 07:27:14 +0200 - Modifier - Chris Mutel - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - PageBreaks - YES - PrintInfo - - NSBottomMargin - - float - 41 - - NSLeftMargin - - float - 18 - - NSOrientation - - int - 1 - - NSPaperSize - - size - {842, 595} - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 3 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 3 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - Frame - {{99, 155}, {1136, 873}} - ListView - - OutlineWidth - 142 - RightSidebar - - ShowRuler - - Sidebar - - SidebarWidth - 120 - VisibleRegion - {{-98, -99}, {1001, 735}} - Zoom - 1 - ZoomValues - - - Canvas 3 - 1 - 1 - - - - - diff --git a/sphinx/source/reference/images/import-simapro.png b/sphinx/source/reference/images/import-simapro.png deleted file mode 100644 index 681091025aea3c1617b5bc0207c514925c9b95ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88088 zcmagGby(Eh_XSELjWkM#QUcOQHz*Yd8FpUiGB87YwdL=;)U9C0^EDJC@3fdiVAWXC@ARKC@83@cd)>BEV_ni z!GAbT3c3&!6e3gP2Wqioku?g+0~AF$>DQhadrR*;UvJJ`93Fhn(5?Fz@*s%V-Gcls zF}d7hy|PbA4O+$X9aZzC4f96u(NeeVNIf{Bw69Tv&8Fhvy3valD<`h_t;Wq;Id^9}F!3}NBX@2N0}^W(+1VSd%vFUQ@N&yO(}-V@8* zVR-*dMl`?$M!WxgFYNEv#4_fTIF1czw}@rJ`9G{G{r&6?!w;z-_J1!| zK>z>$<-rre{~lppXiayD0F5 zUY+>I(WaZ}y@KSw`TSrM$WbCFbTu5{#!ur9PHTRazn?c4fB`irs_16f^rP(-yFhoq zm*GtC!NesK#EiV(rLBnDEJmkZM$O3}=cVApgj_`DXpOK7;o!%HGvfro=%xLE?-e`N z9Acw>7}U?9q^VG<;>;#;qh=a3sYT4lm&~Y+?e=UOD&#Q9v3&e*x$}bc@Jd|`pHJ}P z_vSDsnHYch40X*<<(!8q1+Kft=X>6HOeYh0zYi|s-13{|YKB{tpOn8>PEhsr(u4Bt z_X6mef_jL<&i7;+x@;`aH4qowNY&=T}|aKOrE zd**`LuDsk+to=P)S!dJKcf;JP_ZXONKKM4&wfQ_QGO~ZFivEZ~__Io_&}-Yx^d$!m z4N(tR*@At8@6K(+bOGVry7^_cLwj-N^oFMx+;nmIyCSMK8JW(-eBz3u&MgKslt(=L zk_w$O6a7hTYJuvx-9_Aa555(tvSsr>i?GHH<{*_&Zl28^qy9OueV_aHrB6y>DnEUh zs6RC17biG%9n%bEn{KolbD}v89)%ti1Kh7}8lIn@OWr+rH{+HL)wa=34-M?Ra<|@n z^l2XIRE_3(OZpPp(YS#5Lk*g%UuZo9Ye(0^^%aCPc37t=?y_3sPs)&MnSiaGt=M_J zO^5%n&I=uKh0lJ6LX>27?+f$j)k)CSHjx$<_jT0L_Nl%AS}PW;89ot zM-uUK=^*xT+X6pb=6i%W3SsQxXLbzn3af|w#uX#x1MF@_?+dMesS5m<-mbR9ODkk* z;B(F9cxG!~C7`vD*eL0ay|A2?ZKj#I%W0p@Iekxa8u4vNOa65toNcnmHUG~=NA!Mz zY#`ThYH6=CDj3}f(dA{c#21zrp6FoeMX%nGKh-@>UDEN^%oa7R3owK5{byi6?803f z6+Fj!567!SGS_Zur36avch*T0fqJSL(B z$7~cP`|?xys1~j>2Xb#eLc&u{L#U&k;ph=LdEA!8E`8i+woXgpSCb@_Rn%bjvQB%( zLGkEr)Lk2k?7q7$G<=$x@a{RiPxYb-v_l2EA~ibEo*d6&Pajw#@?k=64?Q6)i<>TU z)Ev9#Sy0^a@`lnq(|~b|#6qHpLzW~A9~IU?@j~96CF$7pw=ULnqW8?~N~30V%f&-w zJ}a7;S2INwv5qV~PYq-Gnu{D@5?{jIsJ<7P0uybYG#|#_5f&D9S6or?Mb*#zEcZ}a zjPALn=6a3JvoogqeQ#<$KRxplqaM~MbZP7;Egg~kab`^_>d8^jD2SiGfH6b6+5W%` zQ)}A0h^?@0VCrM4OPfJ7>m6Qx$yq3ccUI3k)JQa=WzjLKWJkyD-sdlaU%nN4++Dk6 zsu;i|hj+;zZZtTgXPx%eqq%p@snCPHOlM%~UXq2IYum?DuW5Deh31 zv}tK*VljB`B~)k3Fc6}5vvtRTEsr^dXX@s=ZQ5JQ@DSn=!FpQWq;Ye#=Y~nqX@y@s!sR_53XBk)qJ6IJVs^T62Od_MEt~S zbWZvX_O5bw>yd-u62#dqFuB|KHI6x z`CA3u14Ecqs18TJB~K=p)O4ZH>Slc{nvZz9ll*i1VItl})|o72>Vc7@=5!}^R4sKM zqC4?wBnA;iBdOyGj3lYcndKk71Vl-Vn@ggwMXPF~PO4+A3%8~km>S@jy07Eupl0*0 z_$Z*=Lqj$LZ+Oo7`Om!cwq9)_$e0ti*0z0}6XEX-B=c#g%TtDX2b?M6t@t00?W@61 z8F-PoioL+Sy*#!_f!=bOErFH`miT(N@q3kW_T*Z?Qq8MGk>qEe!=}#0`0BJuhWT}l z9c|5=B_K^0S`2-?KRYj3mvYeF77`5NaesaJ_0gWjEVVkP-9+Xnri?4*-%3J5H~Xrp z-CcVLjyFv$r>tM3?s0qdxzdd(wd};%C}YPu1IEnWti(w2KA(rp5LJju0U2>8BL|1J zIt8zG-V6$G(jzoSIGxtpS0Bnbep)JUcyA?dOY3*}P@!wzORVixcQS#VXp)PMcU;!| zw7n))IWHN%<1ktasq&GR=3(R`fli_1>cUq=Pc4JW?htF_dy3I{OFJhIPuWkmljgAx zQp=twLCcjqhO5mJROna0ynjG5AGsMBdIu9v`q7*xFS>->lc!FDUTwX5UciwtM zg&kIPDdwQY3AK0RThF;+pht)34^3%%2fg3Gd94YjPigZ<2FodlCi2UVz6Y%!Tfn2jQc>M#+@lzR?s^#UH z92EI_=pzdxDlwcxgR4eo1}s1SJpSE4c00REIJ+AvT&Ml9homF$zXkbb11(Q%FmVDe z>RI{NAR;lKPM`f6d#`xhR}XEXdA*2pnU~}5*#3i`;pLm(^@NqHW_F+GvL_GsRr{0> zdUE`^Adf^hLY&()Nm`qlrg}4Z;r)lu;v44EY&8#T2kTD=JO@v`c$<^wOp6~$#aznp ze@%~Ta@(tDBz~CwA6^VE|CH9C#F#exKFp2u$ry9gr`#sp0r_5PB8gb-KSe{erd!d^+Sz-#$K1r>RgdL79{mU8 zYd`W;|8UFvTMdfFCp5n1#jPmQ`wwjCiU00KkERuE>_wb+qZ3uX>5Bw^V723Xd1EWx z5zFM)ST2Wd?a$(@1P9q8z3Gm~OGvnIq~5nh_VN4QzN;chi#d}mRT^J)WZ#>wc!-5K zVTq!Rf|(WX192>TDk%;Mv+W-R-_r!5B(JyCQLaXBuZUPOT}e=OXR2TkELkS!`%B+b z`NG~EZyo+n=Bn|#_F`jaPZe^0SZ6u96(hK8a(T2V_wa6^dN#Bm*$B5=nXBXd0{rdm z&2_m!%g3W57l+nuW*ZwDopw*2Y~M5LF2vG7ilo29LHjWwpCuH-%FVmY9j@dBjq^Ks zgp$N%426t}KUY-5(KdR=^yqo)-Wbm|N_x2GRlkgQE^x~BG#R)~~sr)I+1m)B`{T+N1@v3QpvSNG0W{3S)*_e>6!I~Ls+ z?5|d_B~6AtJ+_;x4Z-7VMhQquyT{qQiq;=TKWbf+*^eBIk=HiUpVQsvrlt(ne@^qu z%cFe}XNL_{zA@6lSUhI^_>A=Y8|_&e`5vucJRgKj3Lo@*kRj5IjnkUIIaG(=Qkj*C($E)iRaj<>kX-BL*-n zdXmiMe-g|dSC$J@F+!MXQ_cU9(|9*lb3%^wtICgGyBp6Y*Y9w3Bi+_&Mm-KfOyoMraNl(zBTk39LPi|Ygbm0YuN$)B*gYFsI?53=Q*@vA+n zN)!BK@;&^jgGLeMjH!S5v^lMN z|FXEM%5u6~H^XE39sd2N(GzN-BXrZrrS0t*G4};s`l&6s&A$<6`Tg1cj6YiZGJkKO zudk1vpTE9MGHfBsVxiIgeXptC)doG^yCZXE^{ns5qmrX%@C8vXR#pP9!&V}99z%yO z%|wsw80OgHE5M2)xqtab5yr8hl;TjF&Lm+irk)c_Z94Vcf<;Zu%^qy}pZjeGTVTeU z$z4}Wr>CdLh*FG0OjY^y?N)U`!5UEUyuoN%@e>vG*caP7TX1QH>&oO3gRSU`qFm@P zeHbsaGXrr!?L!R_j~7CD+4P2JxV|<56mmPJe=CzX9g5Mv;heXhB<}!er@OMgM>3l= zK<6Dj%-I&bxM;xH`s?A=NcwXN3nrAvJmQ#Vfm?Z$An7o$WO+VRRaNCM?z{>|BaRy| zbL=hYfD32yS&cmiCAi-oNzTst_;C;Hn8*LdCyCS0OU2)#rfXA&#fPEoO%C~Qr2wYd z#DCdW1j-Po6;N;~dv+@-dYifZQJK?pqEHo|iaUT0DimCl?U#1np&2z`uFev&;(P9V z&~fepZ8+j$KYJ67Rr`AK5rb11J>X)w2c#-f@g=j$sD%Ydl;xQ5%0UeYuz zjC^ELZoa)iP)qm-p=05jtab+)Kt==xPCO_v^+ z9(FBfEG;c+chw1QTu8NxCKT%b+>C;E-VxFJ+xp4Gm$ z7RiSFlwB9{Ac06^$I*(qztGhk9LtqOz1a8YQg(3Qtb046G=H|(0!bD1;6$;ou<-e_ zT?7K?==fOJX%*1(=7t9G>`_ZeY-INEIT~cKTW(MN@j+yhRF6QHi2oR*xg!RJ}`NV<{@|${mH( zp)*)ju(3+DZUj@UjbnQ!i-7!9=CSwlBJ$Ptvd4utLp?e^pQkO#ahL#Y=L%pSyS7vugd>= zw^zaeKHuPTGX9*_FVn2S<6t?u!`)JJ^V2KO(<$wV){#buuBxsZkw?#>S7DO3!s-&2 zjG!=KtyZ?}i)`&UWEPYyET`Ue#?lRKV~Qf#`^pfgoWx-|UvC|KPatxA{S9aPZ<%G^ zb3>G;cC+09sOUfbc+D6<2zpG$ZXUCwE;P;koS zw18d}uJ$I4xkiU$qRm;y`1a4tT$7`5a_3uldCVC@588r)0R6*5J;%m)5?r6?;6&AxuJdiDw z{Z51f@-XeUAFH8w2Tt!lq#-<>>AFcAOkm-2UjNGJ@5^3YD{|CYY1XXQkIGgFr6#>t z3!^`(aP*B5J!ntZ*|7l=l(M#dZ2ad(G76Q5D=SzB#a&xw5*%bEDJ?B6E-ifnb^;mC z**7Oe*1nmsFKEoT3?1UcX~u?(+@@g9!Ydgq^xi83PnBsOe|r@7Avt-V{oS!1T)_d9 zQTZr}@v*UPOiF|95K1Gf+m-u{?L9}E!x3^1Rk9^A0WKg5NH+4q0SEr(8ll_b!nC=$ zx#&8fih&j=+t_70!$}8L=idGMaF{-T81JqQ2TF>IS3kW{TLZwOz;UJXYD=(7ddAJ~ zde<1TVBhr8z~H-CoOu7vR2en52?}KEyXlY5iq^u<1Ox=cu1_Ye)`Np_*RkWzK_G1Jo3$ORt-qRJxzz{dSR)t<4 z@&#Gm`@$eMtvLymT)947=<=6D!ll_(H};MGn2j*WD~5OP-bpQF%UazY+v;|B2{?9M zG6zZpkh5#OFfvjDaaWY(wZjl+f8aBrt1|KPbjH|nJWn3E8la}9rKPcQa_*nZs4Wq( zn_URaZ@!tWHsvsA{&3)T-ZnWiLmDCH3Cr|I5r!}W zsJ0&eoNlfC7`nmbe~C9h>p`P!=te#&c8p)9UG;YTYlP1&Vr5{{rQvd}5d#esAw)?@ z2{|4Sgxp-hNaF_HU7yap)J(MESJKoZM;;Q}naWM@#N3jS2moS!gBsfKDmH9g?7M?t ze@}p*KpU&v(`{k#`wXom{s3?JY%3eT0mm1gR^*d1XUlta$!q>?p8J9hKco4eC?;b$ z(gx0b{d2O0XMBMc6B9!#@ieWl#%7AG^C+kPUsIrPU8W^8nwdn3?14w7w(Ly%@S$dgP;! z;e*5?NQejUXZp3jeS>kA-!&@-$7k^Wr^LhtMAmLGNSp7CuhuD%O9haAjolpY`eAcN ztePWngj|Z)J6>{fat@v9fH=vUlvF;;`OK?EiLfxlm0o02)MHlG9DqPOeGwWjU-qo` zMG?I-@P?&B0jNW!aNAIP>MzKoK&mblSXxR=?O^9NSrHvhuCv`5JM7%~QluoM@3UgU z#mR(v%X%cc=UNCQfP6*j*%DU(NEYSfAkXUWg_Ro$D>}}1AmE%p2=UoWveJ3)@$|>i z4g)}wBI3r@b+yT*+v@sw?98%FAk_5t5POx=(Bq=j<4c`3_a`$(-cOK#43zY8y@tq9 z5KCm_(SVOm!LV5HS%p^B3o4#5J|8YVz*bv zQjoUf#KanxO(m$vZuwKmFu1Q@CNd<#?>=H>XRo95wO0k9=vrD;1p_c-6+m|TCb+Gw zZCg1!Gy5GZ&Xg)T+nrhRPnRV6>h@>TKqMMD-T6CX_R8iUu%2p?*RDtg3uYZ8OGh8` zOcxx|1ZGv4mt+p@htH5S2`vx>9FKDSrr67482#De)px$p+2dAF!ya>Ssf)l;3J1aF z5Oo6@-E!D}zvXf(9TgyO)BVMkOx|ejQ=wff#YD8+%VQuM)#Eq|0d>YX`l-WW%1Ob= zh@&5AfNJ{FxpVv3YE(_lRB#r@GR6VkCK|W;1lToz)q^0?pdg=&@y9@*!UKN${~s0v z(i&I$Z!`dFYyBkw78x8F!R$Ube+3c}0J(F`&W|GV>gRCo0a}Pm6`mV0g5h!xUu$c7 zTnw#8Q}YR3{!DlW!txzJx{8b>VdgxbJP9tjzM2pHTu{rb{yhKXmo;vX?p3Co5}zETGm=E?U=(PaCD#=C@Jmxpow(Yq$k->h`{Dn>bHfj|pe zy1BYMZh<#06ql8OSh~PQCex6R5XHE6=Sv>rZkY+{5_6r7Ti;X9;E(LPdOKjT&wxUJ zN6Bdp&>`|zfF)XUZzC%C=njBnjo0Z8VaK!QI*<=}P2uc0boC1_3Z-#*oXNp z$NBp050`SeFT#;g?C7&`U+OqnNr;A-_>L1O#S6z|&!VY%kYNMO6otd+2FW}8uKzG; zQk0dH)S#y#SyPqICOe>z8OVxgHbB@mk!S9HFB#Bqg6hrWed$=}u4|Ae)KWwT>$ifc6dn zmcHjJfR8T!QluhN-^{K0QT!c1bHD1>#;?}c1^9k#V-p3wmgZDvj>fB3 zvJZ$K+{2%Xn-DEA^GA}GE`NW&v$|~55$SRIo8Q<->^EIyJPK5rk973-Snw!bsUP-5 zy}pgg%F6ng-j>c;!J$O-&_HzTy_F{Wg?^Cae$eTlT0I_^8zWH+;rN^r^6eQfOex&# zWRuH3^*O!or@}&JZ~{OI=5cd#Q;bU$u%+PYyvP&ZzCePvW7sGFYDh^B$%53x4&}fS z0w{d>(s(>w(2))Rx<+Mt$)j(N&YmJirpS`{6_lDgAu=3B?LyWFfIa|@Gc=@KXmW(z zU%9{n85;mYQ9vOssp8c50MZ904B@PIThwR!OT%A6@TkSTT+1Mlt#!yn0|H-2*Uf1a z5^+l%XC#^ShSt34!&OpJ0u9{n*^(<+?9TiY96H?q%ndZ!F9?B*Pjq>U6yF>cg#bm7 zG)-d$7NYM?CtG&CA(l{E~GpxPUTb8FZ{90Dk zIh6E79|Z|&1GWg=y|a7IB)VwFmoE$XXaNVk0*~GV!j{=osU{dlw$y_U;|#{o4Xa;i zzM>5i*_=p#2I~A9ATFKxBDn!5{1%JN&b0GyTSrHZw?_VAEpe88pw6uWKqwQ|G()$P z+RCf?Jy_P2$@V`j!+f2qDd775J*d;Fq)v2D^QGw;o5|7KTuC87>OBeTk3UWLB`Leg8-ZCU?!=(iT zJ{AC&LV&(q9o*hH&NnU@W;%VrK>|;3o+H5dbLzpQbOJ;T1@2F{fVgs(1J%9KW!}1| z#`EwE5ScxH#y@X8X?OxvYqw5I(NlIRD2mlqPH^0i(DX(g*}s|g3?l4)d{^P-W~P}Pk}NYuyO#}!Vlxa#qqxi zPy2yt39ukI8v$D%HvsMu&6^Q?n*+$~GOX#*yvPbo)Z}RM)EVe{zn5VE!+XZ32{jD_srF247#*8% z?AIuhqugqy{rvfp7$57>hm)gB+#sM?yFK zOy=y-WPX<1;^Jai1hO&n^meqxw`(Pv3`1mKUVnFAN951u(t>Bl^5=&X&vzwnFNu(X z5=*v^OIskhl!Jpq`ft6{Om z>ZDFPA6p=zPwdka$11>D=HTvpZ@z|`Efw@eGU~g0e}b`;`aM}o|6=MFJ!JtYaSAouQbuo}?q+Brom%k% z8g&od8!;5#>+k#)WESzT$_eO~Q@(qbkD?D4rOuSh#s785$Tj#LX<06B9P)EH7;4)~ zHa=4|w-NLX34corF|Q2gpM$UASA(Yh9I%i4?KqKJnpj)|r-t4{t*3)qIV7k|5~wji z2I_d#=*u`=X=q!OnxqI_60~-uRbt`@bvN8vs z`rcP){7yak*8+>~=q%8)rzR<$-h2L%%z%?JGLO-s>ixH;8e_;dDlzD39x_@yFDLAv zj>;+FA@c{0yONgzf3QRxV+sKCJ_PEO)ABpmArSCGHey9%%PLzVEwkQ*0%Z&6X>y%F z-~kz_dAG6++4)cxJH$XLACc^p)qEy})<=NN9nRZUjRWDQvSsUDf3&qx-7c~z0GOWz zXo!LI4GN|@(B?^5)XYI0`uc`vzw6eIjg2jZ+Z1gDv3!09D;PA$NHjGgp5?#R)N}?} zx8T&#<1H3x!_>u+L%UHG?Jp%D9w+{5{|ID2-T_TGGH>*J8C4&ySg~!&~J=}FEqW<)C|dE^*K&o`RsptQ*PKsVFeBE0Fd4KHr*p+;|%_- zbE(;tK>j6wCGJw28U9RiPvgJ;^?74~XxefHp+9oF0Xs1Rz!Lg^htY*k-cvTL2fxC@B{L$-8H3%;s7k zMb`+>P+Quub^MR&-FJB^Dw^VZ8_E3vg3R z9fu}YS^2a78(bY)CZE~nsn;Rm+Ebivhg~w)>X!nFL{u>_)Va;yUHMLX-6Xp}S#*fNa$m94;1X}8<6|i}H309Ebr*+A1 zvC}Fj93ozb@3mJmfHDho&QS>j$3&63CD7ob?+eC)HW~UzFTODlRXcx&aMibh0kOtpaljjjjwba`l_W&i@}OZ z6)ohX?-<`>(>L%~a&y-`20SNqsZGC>loLQ9OgxI&u3I35D|+pVpD)U0OZWmmFEWJA zv%U9QkcRy#L`3uUE`wOY{9Kx9{v@n7Re$>y=U<-qdL=hxbBDFVT@(Ls4Z zRSD~8-r5JBwhfEw>+7cw!95Q^zzgNsgKErpZ3Gm{uMwni?^fnXrwH7i>(y#arAvMV z0XwjecFr7__{|+SIR5U^K+AGsU5@+%Vk@Z0rO)2NtVZZnLMYWsaR{WUvi(R27Z;cJ z?9YJBD|?Jye$;S*$=v+~@Y@fl@aLtIAWJ;Rw$ncou`Y{SCcCXr&dLD`6+Q_*R>?{y zroiHz0EaZY@)(-8Wtp%}#J)1(O{d|e6i1K4^UKp~@?D$|nKNz`Tb&%xg{#y0+l>7; z?bxZnCSE2j*fO=jB^goTL3u-V9ZR0egQRb(M(1M8jF<&6-X3?C46W&5QV&eDt?SW@ zO2}AiQ`_4@kcBp}d@r1@xi+*E^Dn1W)f6pW(6p9><M@)9qz1Smn5>7|C!Cw-;==iJ+7=O26#N z@hm+@y+i*rLzDg_l}~l5(o6B%Sz-nEfXPH-AzV5HzsS19zv_nEZl^5fq3*U*$!oYG3J<%#FXiv=YvFTt!%u z&(m+Ymb4R)xV!uXUl4SkEU~qrms-%4zk9{7duvGcikeC0ri1iCQT0`PpT4&94=tP7l3 z|Jz(xInA%{M!G$kz?R?I0YCN6!;n3p<)sNwzn9OblHe>aeA39h2133K``$PqCrV+T zCR>&_Zr|9qgNXq;J!|qiHEg_I9Y$pG@#TrXG|hE#ki+)Ev~&WMI`iAFBpvwJ$^BsI?d&!A=0%T{TkN#v?USdXhO zgdP?!GmIF{UagCvpI<1}ByF!LinY-kUN!#lj(_532fC_VIl)37`=k@;fkX>NMV(!j(z1(~B6Z|~*=&BQYt8XTTha&_8 zcwLY9QQoBWbe#)ub4Y#*4>DoC&8RPpvMrY!larf(Bfd0DmHYqU@uB)jN%jH|acOC3 z<>PzS%*w)gnqf<{Y7bAnL(|io*bM;iY8(G5)`Vs~u zcT=dk8$((3ck|D2xB;RffkCGg{OF%0P3JUrx9}KdDc=E`Q9?tlEu#{ezlY~ib z3md{01y*Uqac|$ucu5R6L)?v8hm*@2)X#1d^gUFRM2oP-mP$G@$=5$BEO@94@$3## za+mvRecG!0V4Vh-sAx6Xoi0n z=~PuK>#=R6Uv-dSX=+^a)YW>|?59}eQv9(mt9`an3108pKXuD@9h&aPvg&2ZMb_T zzoB>@W@|fJy{<@d+CWA@K}TXBj9zBK9S$t~-AduX$Z>tFleNOs!eLT~VNZToSE76_ zKS9uW2%FKps-V_A4yj+!ejC?zKxkYKeK5aqXmSB5VGeuqg?gzf%b}}^jKlDVv-`LESJFB?W~BQem<-jwHh3UCP}6PE7t$ z?BBIR{r+W|njc|g)8*w|)uOz@h$p9av0#{Bk6C^V8myY4 zct3J>=kF_yp#9@p=dnQXw9*Gv{D(QVI@GNkpp895W?& zD3MJh|MFu{nTpT0NL6`}7S=JCi;OU%Yp;WCAFQb?*zKAAIe!VQU0Ei2F3nZX2l0W2 zr%2a*rYU=K?T`aD#3jO31dDSql5%rZp3EBYR!hpX!P#a3aqZ_#mBQD(QzEV9jjFXH z{MAcRcUK6YN+9MI`obVy_lkmB|Btfm0Z4gmlFK7>@E=7U>u<)yFTwHM`+_N_QN@(m1 zddNkgg=s46;`Qk5GE(7x1Q$OBbhYi=3=is8ZAP=iGrV?6fC9qucSSDAp=I+>t|fqx z;(9#%w%?X+SisAssH@MWN-?$ouSHxllHiDbpcu)}-p+*EJ)zjKgY8eYxbKINqv%Mx zv69vDy`J_KqKi{rD5{3WPh0j6842T2=?pZAzU7S5vI*EcM;vS%+*#sn( zT-R52W22$AeXRwe2f(wvU)B#ji!jH<6CqYPnH?gt{nLqd>`~)}Ti~8ad3xpU#r4hS z(VE1*p^*`AphzO*iU_XZg8&c=8R=$2c4Zc)Q$7zBgc z{PQrU+0fIVGWa&gG{_;%sxZPKrMOjEhwqN9$l7}~or&@2#+BX(o#;|k`r6|q|M7Z% zE)Y4u8P}H4%J-p`0=v<#9M5{Xj$S{d|C zC9>6!BOMF`2PcqX<}$RMD@+roHB~`1sI+@qnwC;GSzBoB{(xXavzts_T2m9Wi6Z3I zKgi3DB?||O z*7~F#7ea!12|OsnCe)0SFbuFmx%`)JYN)b15l z+>=M!h?gQTwDV?aD~YD1HvPl`dqDSNy8($uw*cFe+M^t`$ei z^3U*vqjk!lYzZxa zrF-um@rmA$jN{!5i^PiPbg-2uPh`>bll7>+v~rtTRJ7V|4YhDVPd&)w!2u$@gR$2Q z$U-BhfY}BI{`yr%8v%}L@}vb;`ElxLr}_v$yGnorlHtzHv9ot;Xf|BFiM?kr`uzUv z>6$l4CqbCL-=1(X6cwi|=4l)$DdB8UuAW}Lyz*<8kff_>x(v!L&0|`(vcVT1$u04c zRBf2z*Q9@7@Je+fu)~wE^VU~_z9l!Q)DL>u2d>Se$y!@5Rz$H(L&{e*rfW#nfByvl z5PWeHY@KOtX;`8i>_whyX=UYGU_C<`-$3_h+TH`x(v;XTVDtN1;TiEkbEi$a-N{^W zsA;wq@srFwiW0aTt*E89XqCA~k(##6f9vtLEZitHD#XYf7p{j1sFwKTK1qx=d0v0yHVwQ zA_l-=BAE*8HNebhz2dIz3@vbZE0{rhr~ThA);Xx-6dyF}vW>fjPenXWct#R61K+g7`F zg4(L`0MP z;DR-=g>9Cj?OfS(?I2X@TWU=$8ar%E9!66$IiTzR+%}bm;cGmwy`Ff1ldDl>;-U52 z76_yUZc~~dTlY#f-&el=0k6!oQ*f}gG$HtrD?wuK*6fXDjWC-?SdduCp!}pVAveLl z=+Jas)!>kZR3WsrLfPozSS%`#hEV*rfRCCDlepFML!}~NzeLbEU_J_#Jp`6KB(QcJ zn3pCrkCqqXMR;riaHf(O-lefyXp8~oQfAe($%^23J7(IjW#xIJ&%u!Jn$xz1%Cv^i zJWZiK+~SNW#R_`c7ygIx_23>#_@R+M55@!y$eKH%##)ckz_Pv{@&5a)nnr=tH zdKLvb$`)Q8px5DwX32nSY3&AUwSg{_K+5O!uowlZYxk8a_L5u4LNQ*;xJDwIMTCO} zIOBat>0V7O7GSR?OCogqKRB*;mL9WP)ssQ(5klyChWdhKu zpq|wF4f#Bb@i}RLQpS}C*}v!fT*?NngCO1IC-4QqiMB_`b`67w1NLJ`$@|>Z9QZ?S+?h6-a+x(I#)2jFq3rObSoDYvH{(f1T4q7v=cmL}LG5q&c;Lwl~ z==P2L98BQrU`MJO#CZvS*Fy)a2QYX^z5Jwd5Q!Z6`(KQ})ijO+>T0~g*Bir745VXS z#vJ+H1HUkU4;I;MR!m?A0520T-l75}fqGf_@64R>-BC87qR8nbAT=Apu!MaG@NCkGCIPdh z_Led72As@^ou~p%B~sx7>pknRfLX|ULGS3tg6#+3~@J4PtJCb(D-MaczSUH<$1 za40jWB=`X^6n6tf+NI@UK=MW!$hf>6%Kxn*siKqH*h?){vh`}@1tc*#7}8w`R`kR9 zERR!*0kGm5Jv#MeJ6MQS^c@}YZr=DQ&MJ4#1Z$K%EnOXY7v!+pNWA@{%2*O37xE(Mt(9JY!`PO1~=s^H%H34*8X(F={!D$pq_~hRwV? ze4K7!;j}4+v)f9@?{TkOP3G0Z{yz##^m3)MGz+pZ6oozE&Oue-Vjd4(wLPhgCz`!9 zEj)k7-fTeZa{28TgY|fePh>LtWsl#5`0{6HxjC*#KQ}(Vsx4t9KS5R-p7u3e+fF=7 zzV{4Ys?>V(%Vx=QU(iYeH;Hf0rq2tG5p#dk9B_W6qnD1vFZmE(M-x5DuF)?mGK$M> zUi^em;%$b>gr8k3(j6_L;)+Q>!x*L_-BXX?8oe=U?96{vhnQn63o4r|tD45qpK<%r zI+QSVP@8vyzI?~gqPpunP76A9vg8L1M>?bAFnG=Nw+0@D)~f2tTG^`2kx=+zD@^#T z|9(o6;3o(~-P~b(5FeKeIBhZmwHmX&#wAYWPPx(cas#Q*DrO2}=iJ#4lEb9)sLY%B8{X2?cLmLyu=hPA-hP~!ot9H+9}WN7GxE(?6f zO5ps!JEq`tHzfsvW-RW&7Dwir?zeoUa!^aWPBmp^YE)XWI=4fx*s{&^_9r3Co5|9} zeJuj`6@UtPJXUU$^+bqfcQI9g5lrH%W+FUW|Mi&rinhqwcM;rHhin zYtY!)MJ+I2J3r|mo!a%x`Qt|T%aoHVy+47)ps%E5^CT<(k(~Pvo=ffCr&(q1vTe(5 zVu9LKcHvsdDjXwYtoFmBp4`qNijLkuSbZKPtuS|2#O;vV z@vB*7zm)jlNaQCPPWH?%76q@0C-SeXNADXLo?--_Q8+)p6;>I#A3ZQ3mNSyd^Q|!9 zqNbMfnROcQOaQ^wKvSP?H3>D4c3sG!MhE|a^|Gep*t~%Rc+V65m}DFFX19am3vnYq zkqa44w$$*4%8t140%FZV;{H@cFrtmhp5qJ4rRP_jHcTFldVMhk*&7QMmg|D;pkof+ zTw<;~tMWNew(Sz#eyA~$v}+*l&cNSAzH-4n%uaB8D_0G!o3OqAWge3CK#)c<-S$uR z$nCyYBA31Y2fX6}mzhJz_Qq&Koi>RH?GSDyxLBvD`DWaz(Bp9#xJ3oxo-PjCaK>BS z6b}}nzAGvcuTagRH4x`6KH^=YhY8Na;Vg`B?@;x5O6714}Dled-w8%^oy5w?^bJtq8*{}`r&@lVoTE0 z(k+N;{3P>yzWiyIq)e>!<2HK@q3qqA0!6hf2#iq-WUh0O#S8o8|A(=+0P6DJ`b7~z zNkJL}QKY0x5Gf@@N~F8HB&AU#ln@Z<29fR#>F(}s5RjBol)Ik&zVDnn=bSrp_srh2 zx7+^@e$VrbwLZ15wxq7|7C8mnu-)Q4ac+o}j3xKTW)P`GM^wJd63ZI3r(^fM8`D7R z>$wu&zb+BhF~O@OF5VX8+Cz1b)9fmoGoO+cxOl9w%~q~y!x?U(@%zG}!<@&EgG}~9 zwQ5s2yc9kJbMj1zR4v+P{?dbK$UHWs)|ao)X6fVabPW<79Lb!d3OVB2w@YXqnmm>qUGW5X~FMIy;MUE6i73$u-7Eoi;kz?8mONGr^p%Z#l0RzT^5ZG2bRP zARr)Qs8QPSpY`Rf;c3oKGU_3kS+^ay`)lH(-k`EG_X+rmKEq}X$b%{Xc;>f@tDp{* ztgwt?ey3VANje+lMIhlNWgDd-Yf8Gt$#rwtc=5jfBW{s zXPbqdns0!czL6N$=`42BoBF>_Eg+tS&H)IMfCdwslUogGiYdNDnH<0rcA!|KfA6=d)cFSYavK7YgA>M z;VshVT#x{V%< zPUoQ`w5JYssWgxWAbLPQKeXivP%a&S1Dgvhyb}dlH=kNsGASr1eEasT7+f7FU%q@% zhk~T_Bi?2(d&9j457NgRsh&6mfPxn>CKNmWoG~n-5_J6xs=g5Lutb6yfRKs`w+X<2 zsZx_*ySc@8SN*vvd48RThljEWydn0h9VK7@>8vI3UaH_KGLk3Az+|r19Xz?T{>@O3 zn%fi7e?Q!iEHxmYONe~^FD3JKh~cY|v)x*^=NPy1KP~)|eo)BbAN4ZJ=&Ju%R#tED z!&2a{Q;f38KzLkHC<1|>PMQfZ?DBh<)zrQ`a4!z>(skS$U)A-+B&08PKe0p#5m8Z8 zu@5-c;OqFNiG5tz-iY@Qa~a@ae03~Oge*xkrBfily+YtBgMgK^4Rm718}eU zRl)Br`8c<wKX|a*s!?t}Y@~x%Hn}bJ*G2=2Iq79*!KLH~V>;%oov)~5a5;C666d4qeij9ppty~%OLlnkM2r$gaU%CCz2T)v=z*SPsM zUjqVO(20KV%l>_}oH44$_2f{p;fy-;dg?1i*`nU$fjiHRTTZGMqRPv85SuzkB|%(s zi@ouZ3(Q#$goKhleZm3B$@9rVoonadGM+BCAOtV!2FT#e0Ne5V_%VIl^2c!c&G7JW z6nF|4`z455LLcOpqK^%tm{hjlF(CE_P=pwPW~hY>mg&^g)SsCv@EpS~0jmWziuq_J zZc*J4mQtpy+nSZ4#%qkbB)z%yrUn@kL>zlFPXv_*#K);aWO2qxZ{K#qsI7ivY%=uX zLuZj;x-n0P&ZU1Paa6{1v7AStv?}`8&IxTUH+eiszhuSl;m)$a3OSD_ySbyl|L&jQz`(#*j?ztz6T&~Iv$lt)r}P{guMTiP zFB`1)_9A1<@nMuS>hK`*LnZ`eP}f7jyL2$Q7#4=dH=~L z&bzK^39RjFhpU|zpi4KOE~NwCt}&=jzGY-6y!ueznC!Qx)}8C^UVk^_d8>ehR#(K2 zX?#HiYb+KTnxhxL4y{`lDt>cwxDf|l#I-vVGqqk?lqKhV5#svgF2s*+`8=z@B;btu z)1igS)iZJU*Pf4buD(^*9iHH%)}Wn_WI87`hDB=8RHdcgSV#(*nx67d;6guo(YV}uT9Hai!W&gSO#zkWF_Kk8kTeml|DCcb<{MM+5pzGqNv5rit`jZe+aimVWG zDonR%?)hgFbE%<^ne&Lxp9k01%Czx%P)8{n*l_p8$e=Ifp=B`MD+s&o6qVQ2uN0EB z{r=V+*!D44Tx7|Y*BfuVY$7i%<6eK=W6YJHn_{D=t+Dq_llNX?awVp`hnaBUN$G#y zwHvBkj6m_DHQbrx^MK#dwllTp#5*`wJXrai?Sq41Lqo5=e*KCFTqMKpnn2b?TW6=^ z?i8o{*`6Gv8*~i}Ao?Xg|2PLz6Rx-=DtM7(&CShDdvoO8pw&{8kP;V16Zv=k07Q4~ z-Q9s8Ga7*{jxtkeBje=6qf_ey>1)McH;a^})9=a9ta?oaB>RgDMbLGTqN1XrY|Yhh zBe@MNewdG+JSo+0M&-3#=mR}Y@1GGBtiDCx_22oUKORSVy$^G6E@QW*Pj--G*3t-F zBWWo$4(PweE)ej`_X(bJ8KdGYvJi9LzORhT?K9BhzpdB;3Xj_Hbw z#P!rUjxyXSW5s4km~-HipNW=Zoiy$^a53N#;Y++1tpz*kAveVn<<0M@w?EZXMq8ot z`QN=0%@%^m#uWeirA_5p4PHh_!d;Os05Y#6(9H4z~2qe28g zOFw_g+1YWxljgA)5yw{p87dtsYlycuie`nS#LmQ(YJwN2tzR)kjpb`Zz@L?v_EUi3 z<$KU~*C>?Z-`2Y6qhow-LRN$_J2-TG!;>=`QPf%E9LGLV>esXT4%`ZGIaCB2kXR3rdp`;B|d%-9SJ47SHm0P>c^>&=>^8(EZzx{3S@>bW@uB@#1 zh^8T_C5eJ=;N6Ty$*iE~=O@?GTU2aZgv|>qmj(D=OXf#zw#U8;_St$!MyF`FeXC=B zp323=B{3;UAxD`psz;JyGCs1eY{Z-g#2DQ4Hmu`1xy7Xmj8+Ni!bI2ZjLOmEc40J} za@5|~?eX#Dlk4Ph@(Fy@+)sbqCt1DkYJirDKUYDfz$Uayh+|{%tNsbksLP|@vmf2} zIp5Gwz4UCk)rg5EFxfOPaLZrxy)sjUzZ=grM*5f5mL!Y?-V(|5DU-I=#?m2dUyBJ~ zUGfPR2z@ra>RX=ubp6xuYX(0wz=Ami4{m?%U3bA?=4kIr@F$LCx)vYaZR}Hw8bp<) z%!ED|L8k~`X7P3RLcIrK$p+o)yJ2%4sB-ie+izux-hptFy1c&|9M5hOx-jb>icQ>R zqbQqFcllBw$3k7Wv)n6Q{n^%iZut5KwVVEEK9u*`FdO9kN8RX)Ce-!Jx2<&&QdGam zn`KN&w;Fkh$Wi`v9W3HoRh%rHe}`H~^0lY#n^V9dW8u^VSo8 zQ!K7i-_QKvVVE2RMMDkY$}8XQN3|S3e_u_Ri%`VL(z_NUPyADhBF<6nLEI+rZ%Z2` zCZT8rx*NXNl3sp%p1wSvFiZY(GtTxJkILr{dVf_nq(596JZ2-t_uAEe zX``NSL*R8&sO;nP4_)!E{=T%$gYDG%;F{{#9jPgiGR#36aV3pZ{7PG^brd=~8N$eJfNG_<@@1N;GTKfP*Z#_MaLJjVa3=bgkEK zUssdM(C}aKEz!a9t<)ZG5+Idrk!E_|uyCMT@Ex0(n+p>s=LR=wlgceM?4_jO;xkkK zSXP=No~5E+YhG8Raq%UXAupMUs*5X5rcK*ax=VMgZ2!qM^flo$IuxYbhnJ~{<1gOP zj-@A#JLWj^Pk z%L}+xk^IW*5knvFd(zzb=KdIYxx|4DmuEP}#meP<8by*i-E?j&f1jwhk3j{4U#m+~ zjlbl-%z7Xb(Q9~etpST7NR;No3e@+?ECq?z-G<9JgKc+j*Qsq3Git>**?`*kjF5;3 zu{Pz~NY@;8QokFjb91yxP%xP5w>ptHeLM4gJl)sI3my$G+$%nL_)%CNXv~+fsK?*` zz7@us9876?qbb!gvo5c(bh!ZgiliN?H8Jn-9}iCD15- zh@IR7G8KxF&GE^p)Zo?8C)W#XG^g=hzV|GaP!>nDbWw0j<5BkN8&#My!JsLDKhcCEX+8}S{T zpSwam{Sx$b_3kHMA>yND3O>XbbiQaeZj9t8F}zHA(hkC88LCZaLXcbn08 zp9X4)i+>y8?ykaS_%-=BKF0fH7Gb@bOY`U`p_-anU0q$aW(Cv#y`_5Ww1p2M-y7g+ zZJ(V@_}>~Q^!>^G#w)1Cz2K?P($V=R3wdDG=Sqi0q@>6L_#GG=jDXzQ)pqaU?SWRU z3;x1Xg;k9HKBSFI7aLsHCy` zIxxLo|7|@`0XO7jltBsu7-p_W<$pO*OZ=LF>B-QZr6_8NpFqx0>&q9CD|_;_h$ zWuk`Ub``c3g6S6tur z0(0Kn{YWC!CrJZ5_M6oDiYKH8kph~nEbxHB1<1DNKWLdxhdXNi(iOS~K@XYG9JH){ z{)`VQ<7&52<>*>?)Gbz>Vbq%t*^mzY@p9|wFJS-s93A}}&JDAH#55>~TS_3niN*PD zuRYXs=ChRr2kJE8L&XMSg}U|bAHC=zl6NMrsyKYJvKYX7hGMo2vO$Kqv z(9X$jH@|!?ZH$_gQ}xxo%799YXu5>>zZ;~Y*Co%hU$h>{D$(pAIxGckQnsr>C>krXSLtIA7361 zt}ZV>b9H?T_)tp+WZBeHM1qC9OMIm0`g)Pm_4&Y{07JNcI#@N2_%&~Tni7qRV%2LME^coB&KNcw z1bcxR)!XoU&aGRw5V8wQ_a&e&L7aW-y)h+KRsYk2s`qlrgeq0No;T!UKp|z<{ic$q ziqhVY*VfkdJ=7>LEKI({G1_x6BH+TshD4PjI%QL;41zl+kjBva1F;To!)qTQ|G2AD1} zx_Nqf>gw)Z3Q;UK@a}#Rr%8iHA}%Qz5fk%Kvw%eOOgbH+g?6XQKEgfJr2Gc^9t#W3 zyLay(^ne;#0foHrO9;!ryX60xp58q$Ko9b_lbuPCK9lT7b&^sJ$>__1hMq@9WE=Ax zGaEvJP^{(4*vVn5?8@T4|08U?pL_H*Jfho!WCN#`Mm-rPg~6wGXeK%^lKTv6($Udz z6~u94g*pjvsx7$W|FK~a`oT!&ka_OBK07BeY*|5ua|7pwugc^m)?y{+;^$Zf8@M|a zn{iJ`=m3?;Ben?*=rYnrOX`?;9w5l*oGbY@56yM7=ngX>-jD?KC*2}aCPD~q$Wx;) zeS{>jK$ai$lbwr;)PNnrAtNJn3jAOiWWhkENXW;>r>&!dn7B}|Nm%p&ghIP{^FjEF zt&t3DSYAwTC`Cj>;A2Fg`Gl;f)t)G3PYCw#mU0C#fEgMZ+N%Q1M@X})sX5~%(phZK zdJg3jCI&_mlmrklV1ItFELq1?8D^p!=(Y-Y2SLW4kCR>kMr!XC-1q-S3lR7FBgfs)23=1+wRymS-21f=AB7~VhQirJ__jaxHNl@5d5!=}t+zA3K$_#p+*6B7O+ z7tdn==GJyWC4B-jfN#7l-XczRz);}~Bc4@v*CbOapmHpHy`lZ@k?7A$s9d^?xqwPj zIU2}cA>^A(=lh8MoV0CZv5a4ZcwmAW{EHj|ePsvO~ayh^4JA;5I?RflclW&@BNWVJrBUpx+wHRgtL{-jMb*h3@g9 z@#>EV0Rcf*e}4uD6hxcxD>VBd2Biy{(N~N_5W1kv7_RF;=?#HeNcI<$FVCSc@-y=C z@`Cs@0;rnKfBz==Q`pze)Ca5QBg77&Ab^0iHOuB;N)}vN(B=y7nUuQqU0`h#H_4!r zU)so;h@Es#@cwM(kGiSHAXd3B_TAF_(bk%QP2Qed7!&?3nhb%QZe>l3+}+9;>$YBc z#pSUgy%fGh&j`S*?Jm#l4TP_oFVyi*Pft5-j|I+ucUgR6^uwZA!*q}=<&>rIf+6LJ za@p_S+G#62iL_eK*1HzGwmd%98`k)D^sb6V4$4NBPw3NMi*t>MGLk*=&+@)u(JSwF z1$}zJV}C^Gd?vB4@&igXfXeQkpMQ~>)!$C3fM}P0e-^JG5*lv8hF3v`AM!~~f zIIM%MY}y}>XB|ckExw)IfmmZvsY2;z|7oieU;60f1sl5DwDtuPYjNp*Q@j_^ZD)UR#%Es@l97e3C*Y#{%ewE3U(!`; z9M@m;zH1RvJQkrEol83u8J2R_MDEBR8#Pr2afyk4+YO&l$7y_F-*fN>ssB^eWK>N$ zIXcs_in-|{y2Zm)Zo%u*^*T|}^-}5UWR;iZ#hv)!1dz}KG36%unLcWiO1{lXJ--vS zYNqJ3`;U7K^4WRv)+}z=Xs3l@HOTq|UleaK)h%I)F{SAf-Ojm}IllX{b2a54zLH8h zgTS}hTbnHl(gQ^6)rg%L^pnf`385V~k^HBTJH`~<9Lt+! zR$}Krlhqp+y||8lP%Qf--$!)C^YSc#kc34D$F_OO4U6se!vMap!j ze%PuGnXxxcrdf*nc8*VdxW^RThI{yjX6T~_qEMCgbFWu5MXkW(X3(yO%6*^$t6Tsx^L+M1?Gs;M zw!@W#DwMfx(PV1r*Aw2DiQB7$LXCX{v|7~cxl9aOy_MT}7G`gVykl3x^K=7`Vf2na z&gGSE8D*@5rg}a+tgh3|yPEQERq8*_>ekS+!4_gx)GwJL%=_9kIG6>Wz+*i%G?N+0 ztgEZ5-QYPrV=728KPUF;&IzT89`S^k`K@AMYI7VEVZ;%oF5>pxMa4=DAf{XLQibVdIGGo%19yt)asLf`=!Iiuya;Wa zl9>~!Y=PFIrKi`NB?|v@Pol*x#cVp(?F-4vYm8CS&D}?wN+j|Lw8>B(=@BlOUaiEw^?4%bgVJn;*tbrH^ojg76`W|k!B zrCHRDN_Z^a^}c$B{)-{43Ny?zj8+X(vGqjxjpk3-^adG`Eo6GG^ZP$;t42E=`_)vW z)e^kkTJr5J=DgmPlcin#_U{vT1!6^$WdMXM*FcURLWCvqJ9hOy!u$t}8lRqUpoMz!4xhIgmu>ztM>DU6@o$qcX#AFKY*VJzui!}Bp$ELjNkX~0)rw~f_LxU zg#~>*)3g=d9%9A^dUk1Rs|#}B0z*RD&3Q)PT_dV5NczZ8NWTGvJ2dCVz^QRTu7PTi zZa+xtA>)T1d6OI*pFtan5APZhIg!5&n9C{zI2l7V1)#Rpaj6Q%M)sGlUul}HfW1GL zbB|LPpV62VB=mo-o^*qn;c4ZyOO*oys;#BhHof`5jY6L4q{(OAG!Lw0`5xVENUN$e zk*9>CTTf3(+osxX85LOZR1p6`g5$xkB=s;b!)Yp#ouO37 z?nNRwp=pA812cU%K(inddK6=0<5%_WZ;&KoD1P8DtudnXMp$&9MTRP@)L@HBCh*z> z1P8Ceiu8n_C7*iDf@pc+3$%J&d3dxSEnJ3N%q;jUAhSmE)YzNGwDKT8j8e!w5_mtz zkCSnBu2dKudD|<%kUvOFPNBZX=@y;L3>Ois5iJA32bRKW3t1%aW*_p! zKl#0Eve}Yccl@i;mc>uF*O-fwXUGTg1 z%)X_i1xbDc8#@GEr@({I=b`ukv=_XLbb!A6{QVE11L1bwRcF(w_0udch1J>O0wnS? zU=8=kfOJ}!nveySSx1FOS^PgY?BXyF5ksnTQM1)#I3`+-ZZJAzI@f>wJ zF!}oM7cPPe>|S>`TqHq}4$QeydBC7_Yl8n#-J512rfJ39Ko!;M>S_omWIuqk)36x5 zw{Z|5v9i0{2MPt0{{}7)C=VKf(y}td-2x4P5wN*%^2dRKWX*y9>SFL+wr>5CkGH5+ z;J7=#xHyF06?LfGLTRSTmOXLOf%5pdGT?P5I|=`6IyyU#A^3n3qRTyI^dLQo~v#K_zCKD(dATFK}QgQ6xm5vj+LHUVKhH3R+o*}Y;$YOi0n;iNSK`Un~do2Pv9G_Yz?O) z4-J+IxAhdqt-FHix%eK69JZdCytkl$vS$8drhdZvqCkggrHNHN3M*dJu4evw?hism z!}@%;4&v>{v3^_rp69zvPDkDb#WvsZV5toR3j{RWgr@DTye5pa@B8kNMhLaOvkbn zTs=Gpp>Kn)hl0e0BK5(}L;(U3z+pj1O&tb^7~sYR@Q0$=asYv1;^CPct#x;;I~ZId zy95qmZpa^_dGH`GD2Rri|00k??P-aq5(pP6Uben24kn{4G{2r-FgZRH@#f7qL*;P zyF$y2iH-dL@Cxe5AX0768>~YXHq+@GG%yz9xx`OP5?r8g@$vP&SiJhjAQ+R95(Z!x zQgc>Le^bo^qOZlM*WPlnP!4Ja07Ehq-8%VMClkL&@a;_qPIh)?shczbDLZC);w9|0 z-mJ6tl|++1^}K zEG?GqZnSA=5vdn7{ANxHy5&Aofx z@ZLz6O)md`u;+dW33reHa9FTu@L@>G7xD%yMrh&JcxiCyzhnPJ|YGaBQJhlpwBTtRnBV zuB&Ow1LjP{Be=|%z`$x<2V{NXcf-TSXG+gkoS$!LVM?&}0YgPX@z`$Zsp&wXTjw!T zR1jQD999!7fZq;nM$Yp?hYZEYI-Gj2qprj0y<7o*ORJ82lHq zvP8~`q-@$MS=K0f}vPm8%K%!S%DEwDG?hD$f? ze`iE>6PKb51a<+UX~2_*!nMPuTleDCEBt{0dFOMD?vv?6y!r&R=NeYW$CI+>{J1#J zp`&^sl5KRyKtC!}Us6dv-=rHq;d-9pl5*k0@{t@T_7AKuNuvqMSpGNLM0~I3p3+A^ za$a3njA$DC*TVsU{R4a_!nRSWgF_nnEfMaFcG$f9G>{cJ?vLr3G3>7!eX3uC#+?VWdEd z50o4J%1kDJXh9XA@ZH%4bT@yWrrE-$AW_W#^(f@2vWkj|0;cya-^$3!=@`QR?IMx= z#e+WKr?JO90z9vdJ~O?!Gqn2b77s($#^z?aY90b({#M1YXXCfVPI9`s2Fgt`qkpVe zU$gFocPoVan_%LF8&F8$aClw9?VTFGKSkYDVjK0|>cN|nmf_*sQPOmCXj788M2#O5ngCTL+?$4j(ux*gq7#aj)6O-_WWbsX{cize$T4UE$%j=E||IL&CU&Rzy zfq&U?%c^=3tzr@qWPbO{*8WK6Uv#+J;7qI?xWBXZt`#N4-go%X(-Wd-b#}bB-wkpr zv)O%^oH-hAJ=S#5<)R2du22vf+8?cfcvr9+w0YGI>saulcr3>-G8DU^XbAGpnVy{; zEisY=rYxDWgpngDQLJPPr zu#XYZ(aw|h@Y)`O3J#8z7W#-dpqy5>wgP-xTTcXKUcT(m=OCJmWx~rp(ZLltqqQJ}5#b*sw%!ctOTQs>tyT%sLGG6oQ%d;kzebDwlhxqP zMMACMt0KIqF&8;ftAJMsq*JJw1g$BaN0>+yaC{OCaj?k?eF)8iYpFoB_qGmA#m`*j zx?HC~G`Ilgl(9@Kgr@JdUN^i&p&7Raiyn6G^VLcsFZ z^YIWW>I09f)@6SHzCah8mH}a5W+011g1YJ#lvNj+o3BBY@%HbD8q|;U7|d2m6_zSc z`R+02qk!fK+HDKYqUx7ftB|^H1eGLGj3aTka9MeTLc3}{SA9s;f+cHp(u&2*;ZGS| zRa{EX?)Jz?Z?|sDU?ibzpP(nPJ0vafBs6vsP zb_)v&Ue1Rd9UWX|1C;K+T<>+Bij_f-KWw6)^_?jKulj^Cx@C;NIC6g7T5K_Du2RnF z!(IP9v7qtt2pi}Fq(2lT)boZVV_;_Xu~~l!0*g0b>H%Z4IIH46b#CqQwcpnN1oZ2A zWK;@>N*CLvBdH_lPR*G}UoSKijz=yTsj+2&=qpjc<>i5B+*LwcoD}qa7`V6@5Y&mZ zb;#C+mFE$<38GqMCQ#QN{`<3!En%Ka>*?vKUS^6ba&^glhu1n4Iv2os*Ug(Tw z)Gkuzap^lnJkZpD8`BnmZREsi*Oif$HiWIz*4{o02P5Q}1~lv6V`2)5j3gp~^BxI7 z*DXSp=kEM>QfLtYn6X%xTdNr8>iW+E1(h!#N}qxF!=Q7PDYFMchf=`#)38+3zvQVHr*iPvkSrKDi+#asN}QX!Df4-_~&&=7&psBU9q$Q6?D zN5ZO^I&KMG>Xwuj7y1SGO(Tr`jwdlZQPa9b>Yb_4g_&3`osM#Hz9uXC#j=4lN#fHZ zCkvfI_oSqxTmrHoHCG85&}ZTyWSl<8YXbIEvLWy<3=$xRNPb~qRiEQ9#Mp&Viu(WLjmcqWud8p>TV!PSA?9=Pwdnjg*jxfep=_}li*Dp z0)P-68~XxO#^=a7?CYbwfB!w`h!0>zaOpRpKqXAQv+)LmBl6|N1UZj&k6w4^{#`dz z@md~8);E!e^C)D=!;kCzzWvMn-+xdOgt0`agC6 z9Wu~{5b=)<^_DSAzCdDQ;R_#w;2ZQrARDN9h1vfFkcv_exHc)PSintrMm6{avU(RP zjJ~VpWq_y_R1K(^9e#id&^>t)59dfSh&gYAn8mxSj2kc>s1{(gYLpsdf+*%2fUJ~Y zBY+YY-pn(&fdNDw^0L7JB?m3-w=u=4N>S<}(@XoGTuCRmJr$Nqz$>bQu21Ch;QGIR z|B#_K0AZ5mwVW}=qDRTn|2g~6r7r$2H!#fVNGa@sPv2%x-$Dfy_r~>`ScDj7udP*P zLFaT8V{uf<^R_hakt!p@acU~Q;LlXmALFFh*w{c6vu^}GaXX>~1qQUl!`TWCKx6P@ zpHnvU0 z`WBg$HyOD)@1>W(QEEoFRTR_`9|N91j{=~cPr&wM5j7&ev=~ag=Ip#4k*=~4Tfa+F z8BE-mvhDnOtn6$#^^}X9q!w$((arxxbWJtaeh}DUn*UZdGQKCRkDt5h2gxKR;97Lh zUxhq>uyzOL+1#e2{N(F<9qd-w*^D3?LQ%q)@|8u&pgQJQozFA+1G5>(V0sR)q|oE@ z!YUeqeWmyl0}n3?>@Js3s6tDO?azpXjg3INpp|2wr~d$@D#GWMfHp(&UtgstNXI@A zJWi8ZCja;@!jw9zxE+?LMu{OhGMOdLEZG2BWT@}6v|qb|Mg*Q?jdrMZp?-=HbE&Oq zILLfJ{RJ&NU{y{}lE#_uqh7kvUSp7CpzyEO=)ZC70)4;#TMectEHg7RfGQ3S4w^>u znC@1!M_mkR#@f<tJMW`!4G*fj?suY?jc4l0(S%9fK9Fbo zKJw4-pIRROqZ1r}d63Hm1n#h-nbw}Wxr69yrn5y|nbCb(Vy|09C8302T~O%PeV<9) z|4?$u8^;cDL3v;9zuzzv>%g@g*wKDXg$2-D7J0DxCK%>KX^*>sGx3)u!<+lMjuB<&PX9<5? zO>=^ky5;UuA%{@$ZznK8uUv+yNB@)HS0MjYx!}gC!)>(<_&Lo_St zIW{#P5(Uk+NC$&TriDLBI^(mX)`F1eU9>V3C&#kn%^nAZ=-zc;kl=lWhK9DJKqEd> zteEx+^02n<%#8%e{Wx(Cl^P{qIq~$LaZuB&j&B)QWVcF`2y^o@JI4}U8-4jfW6__0*?UNm$z?)_xASSmiVfg z2N|^Or)I{+_uzCe{Rox4=z11ZhX9v6gHZO8J7XTx*{XTygFUUU!gZFpaoxr)cjRu> ze##2U69By_czYtHO>*vsf^5PJ5-r3fBov^sgG!q35b>}O=EjP$x(Vik108CZ25(}3ar;U`mBdwD+VW+$5lq4ee3MakWLtb;@R0h32HPl zZd06e$*||?!-(|}#(;^!(T6xA0V|>B;)3|D78u%tpjrUX0pg}_Yby_E9kXiwy@wA2 z;T_#3BoqUEPjYfHpc#nuOfl;v6$r78%&7&17ise;O@>|(snXD(BnjRrrDOlFOmfFR z+R8~oAydmN+MlJ5GsOPrj_jK%hp+hvq>g@Ar{!hH6=80b&Fm-HyY7J0(@>bJH@+2O z)hySCVz%zrh>U22nU&$Kn>>qt31Zj!4P`+4z_&&`O?jak+aAU0=N#9{+`qtF_Hi5@ z=Q;OBbj>`&R5vfMshzkBT@hst4UBe(o@&LmtMKBu*WHzbuuDiEVj=;F^nu!+}$VrhxhmAoWb!2B@NO+CZ$1@ZqGbfue%S|hI(pLHIkkq>WR!B zTetgK`U*goH!?ap4U_p`N(#biLTtJNJT}Ev)`Gz4W5b1{m>0A@)unyZW%)O(YycXw zx3g<%ZEc;&#F!jR{I05Ktg)e`Q5NfeA;1%yr&PLwlReVV^0S7)U?4S8wDwk^Wz*F# zA2oE{fD;&-nqsMF^lR5R_#-=9y{HM)n9Z$sfvJGIT;^{BJe^j+j{TG0=`$rB0zP>y z6RQBG)saApAPVg|$R0rXhgehNI1GI56^e<9B78Fx6vk#|$RrwrI#5{?Srr@(%?(t2 z%{aks2nd+MYQ8nj`Kpahhdu;5$-lp)RClyTR$HTXqA&PR!HcV3`!Zk8{{=#<`Izrh zv=x`j)SbSj<&+Hl4{r#{zfmBsk*Zi_`-^mjXb~Q;xJgMmoc1PeO0St)u{-UW!NJ`E$sg>;gzc3;+!oDiaW`O0sM2wR-Y-)S!!P1CG7$f^yyTC)9a+!|*ZOTPIvBTq{GZ_;&k5qDvA zS=l}vk3-Nr+XG4iD#SxL)!{-*6nU2nz@fuNA35AcApU1LsR#TH{`f9b)zIF+(t3z^ zErEydgfUUjnT|kZv$DF1pt{hNO3;Kat*o?wXdDp&LOZ$!1J&r@jl{;1-LJ!!y&nk8 zSJ=Yf03`w01`gKjOy5K5PpPBovl!tw+xG_gLxa9Wn2A+x{_QWG)od@lN|6gnQ4Qk~GnC1NILZ&D)++6%*AMYn+*Hr@*>>lGY_hqP8p@wak? z;=3L@taa&V@j3mRfpQcdz!nT_Y_S5>qXgr*#6kod*K92Kj+9+szU=dCqY9gD5(H+Kdj@l%V7V4>PD{L$* zYpMp<)IzFUJ2FoHE`raQ<_K-Lx&B&;$!pTO3)65z8+!+lKA$FY9-1iW{o?7F)5&ZF zUs%q?aEln80woDIgy9>!vlp3ARIsB`Y{_4r4}G(|FL52~ArD(?vIcKZ z$vrJ1)u>Nj#~f9K-esq=Kaom%xS!iqqpqo>0dy3hy-9L4E5pINU)_HE7c2L?#H%Wi zyNfQP-b(17QaM(L?#I=i^CjSlGH~frtyeQ;5#p%quqyg1;;~7kW-{GzxKHkFjgM}E zhb}S4Jys53wmdF5xMma@D@{#?+7h{9ScT z9x&i1+w|W2bCHNQs4-YGxKxv}(@?l`7Q6%j9gtyYZUHyntf(-JbiPq2F4OWvT|Z zYVVzpKcfvd6tIrV`Dl=aR$hv8G$F@z!hwIyhHBf6W}hB6a-oIBTw`D`BRLR9<`xd# z!e)hw+X7AV>t-K+GkkU{Qp*>+fok)eH5iS|*z)pWcZIOz-hvEIe(_SzpEEH97d=Z+ zMPp5q*}_<_E+whkVh_#mHtj~U7u^_#PK{ZH=%Vd-PtIDWibNYpzgtX*$x<-N@o>i{ueRg__=ao2d4U0uwVfVn)}by{9J# zMv`J^FhO4kfQy_U({V=j#YO4HT?5{V{<1~suynL-A(rE@GkgukxIfa>$(xmquR+> zA!Z|OWVnUjyG%nZ@=WvFXgX zp40tf>74KGniil^Oe`l_(L>Q;08kCz@d0_S}3YfYIB=iSH2{L&xWa}#4??mVoC_j!p|j6Y|fGW1@(3>4h0 zvRwF1)vp)CQOgV?m%y0}N4eCC7x&;p;4lvV{J9CxEhwtFu&QyY>g($IO0PzI{IfD@ z$C4=sZWc1_E$2G$9yzdJD+DW2=iqo0j9EUC&iK;1?cq0_;MV;f9oyu+Dt3^dQdo}m-vz<~RUZAmJpJ^cs z9I_E49>ebgK;HA_N$6<&e?mnAhW{X(2IN0^EZDH&>tf>Mqmip(M}0B9)u7$gAjTcl zs4)FjTrfM#0lu_z(a1E=KpIIzxmtQ2KW2;~mJP)xx`JQ?A13LG&=qSR_6-!#i z{ZZLfS{BhbqtvgR#XMhAJELmQIRMBq1Mme&D)1LI*j`5Qug}N%TgWuC+GF>Y7pHhH z&!MY4Txqdzr9V0)D0-kabvgEjPaah9Bv#L@c_{}+SWnSseS0I4X24$oR6Bq`S-?N~ z@5#%^5TX`9y2C)SP%rcWAl?9*9XcLfnCLXy-_udksL_!Tj9a&mxpjt6xcL*2<)OmR za(eLM0XT6CbyGxKTsO?+Qdx0C#2Y`dQh*s^J+y~L6YQd>H*bC{rF4k;n&EiQtgMiN zF0jWknK>evxs_N0w#K78mgi5Z9tIymA|BVTJU8i#(tu!}Ic40Vn-3XPC~{|K544kV zT@H$*UwAU>%0bpIAF1R1i26@6i~>VJGbu7r zatf~_s{iyK?~ZpL||x*OiWbP zu$)v?NdPb0ojn$Cz7}m=pkahcKBda`Onab)-uUE%)UlPLHEr1ZRi5%eueEqw)oq36 zrKRgn&AeF!Ry?9oLd!VYz`<0-3#iN4P=xf zjKfmbN(lS74VVVf`GBqj>7)S@r=+5)@i=4Sq1Nj=i-4Y7GUi4&X=#E)6=^_FW3?cC*{jAu=)h{yl6I3<@o9K@#b3Url;m_9u z0Sx@|h6Ig*zX==m+bYoK?5Jws*uPfTE7Hj*S(luGR$6DcE?BQxFAMCVXOI z^rx^A%tyXqg6I}uTA{SXr=XBFHl{^%k3i>NcS5}fs|Pysn_600z~ny%s=fxqTJZG1 z`vIyySizvd!h?|Mf&x~AbwWX=c7hoZC}wi7CL)Uqb|(mZb{89|qomv6Lm<5yw4uYW zZ#B!!6;`H#*TqS)E0%1rAN6A8RYK)BoGhsuc+!2SPfr* zdk6IF0+&%!Z*|J6B;k) zl(r*3Su;uH`1^MvwK8~wzYqPatgPg*nTdo?RLkLl`&owjre2}>e!B8sX3J}laWo1V zuQ-YonWDOmkBYD1%M{;fm>Uzzk2E(SUn@vJ+j=?ntm>YULdJXaO|!rI?9aYPjrQC# zHF&{nHsyJ9VBK5$I&XW5-dy9m6hM$xHa9!rsJ*7+|15;!kzXe=$P>=oqj{IbL&(`e zdPfArf*QlG6Gq(=Ah|O`^&T#1E{tq!w?NDRI}-ALp0kjQg4P5cr^MU04MPcUfWrX! z9z9E4duQkB<|YKljmN?aGsvsD{q{P@2|y=(6^k_VvJFTTvX;K>PSCP6ST1ml@1x$X@D1@P521G;u_asok1 z5%4ZYAW@8AW*^MrFlm+t1;is8FndbW?hSCzh$tTJRJzDGiV=`rL0u02z;m`3M4k`9lZy|1{0%cnrdFn*BW{3EVFDu=>q@#{r`up z_YTLp@Bhb+?{|Ezf65+?b9Bp75JK?`0>zR06#{(2SJnl=+FWyVO zU!rCEt#V{+?4*-Zs9}yASEZOf%T04&)Ur{s)4iYD+XtTX+dS9#Uhe-+;VvckZD0H2 zpIe3j5My9B&S=s8XvjMg68=dRe|`@gDh@+Jz~c?ifm8;J6c8C%XmdEnsO0jZD`q!u zowpp{bKz?&hZ^2K3vx;TrLJaSIe`Iea%WQacBy*LXTO=7PhF4WcKC)i>$0TzYw|s565v9Jnn=el1I<_hr=Rv%;bZBKde_RliE_CBC zIRc;WQk6MZ)KnX6>O2y49=z zl}jU*Vv?7RaX4Qqt<`HS*<6V?0(-T0s;d6r9B6;vTs$+8xh+`pwu-)TvR1tdgO2`H zo9S^Q?~W*$$qmlW^2Ey@8H~f?E{i&xFixb*BI43>pESQtdD52RtRgTjy87tq=Gv1- zitaJ>$dA_^b~RUc9nEmPVTQaX7L9QGV!p)9gu0a474ciLZRUCQl6is2dF#mANyeVe z2dM4RT1nJB_l$?@?%%)P*xbAi+Cd0RQOOdqR9svf(#+mK*GH~Gyp>iwxWoGT4fXXw zfbk5q2l7ctkOYbk0`n7==E!I+elI97%OnqZN}r%f zpdr!#BpS5?Ugc^yCXrR5oO{mrZ1Sy!9n2@U+ly3=s9>k|4P|2yMaD-S0 zcJJ=(tV_o}#%JXJz$YLe?X#%=cbMRQ0M}_k)ij zc*a-Bw`8sK?R$fgsnq@zAI#NzK!g(|FOfMQOX2D?ef`Uqj6wTq05Qa(^s77VsD4~i z)}Bx6*ELBpuouA~zpEkC*%l9-Fu~SaJ7N)fEV3}E1FczT{Dy}X_Vhc&4@M`rIqd_nxX zz}ta&zJ$GqMOjlY9M-3UkZfVc3nEby%Bb*|m4r2yjt8)Nv{{$zqGG z(Oci6_jQY13Jlp>oSS;S1mvoAc=l}EUHwEw(I&)1uf3Nx^{l#;l#d6mrnhO15tAaqvgrA>22>?*^Ie-nB`b`SFLrkJ z6iUs94^Qs~Te!QI6#M@Cb{t-82%4Y>ybgf^LQw96hH9sbSwHvt=PLS>H&BZ*uGs+J z%_w%O)nkvtGK-R~Uo}!YYvY{1W?Ya+{40OH$FCmgL1e}??y;Q|<$PycTW#wJMG4e)yKSs7zhAXZ2_tQQ}1hB=?Ye=q9Q9Y2FT20h(@lEcq|&Bn1ZFi09vx=t-ZVXnh-F%*p)K0$X_K|VY@ZuL)cvXLi~>obXV2i8 z;VRFAPQ~F|;TD9y58+Ln0+JD?;_YfxI~Z5#ciA14d$fkg(t ziQjZ6--*fx7NI%fjq+RedKKcarxfxzmE~{y(N8i2saz#`-NeLWU*}c|0{p=iTJ_$( zb7z-#KYl&PrH;WLJA=Sv`R)tR(XyNm+ieQ-1O`uAKAnx_R$+eLA1vTH+05Bru(e~|1kce^Ms+MIYp>~E z92~n`yhv*mU9(%@+xDeurxc&v=!C^h3+(_i5$Oqb!kgz;{vO=8QzgA3a!dGun^VR1 z_Ir$2?Qf+x#&2I;{$ops&emetuYoyYZD|ea&n>bHFodsIJOF$_WJE+Q_S-AxB)A$$ zQX^E7QGF^&^uD9Hg1@KO^f~6^n%#aMMAN|*)0p&z6dNbtj>p(>O61insP}7t7wSt1F`(79&YCyQ~x;3J{ ztIN2zysS)~ai$O||Yv?1(edYt&vO+Ln{$!+GBj&?5R z8|GBlTQ^-!4V{Y0zB|K#{b)@v6--jINXbq5S1 zq}m0W76(ls`kyLDSpyD5GO)HO{`l&2UUxz5%aac2Yt-vmEKO|G8#Kpdp9!~SKHD!W zwMVRR+&R|Wc%7Lscb(W-uePx_{?x503o=XJ7bqT4ieUo8EkbKz#`NmNGjbb9DsAkJlQ+V`4^ucINOJhM~|8{<@y^ZvhKN?$GA>B+p@~&30@=#o<*?Eafr<^He?OtxP&ajz4|}&O?J%uDNs1MeY#y|(hpElkr)_@cFr8G$E-Ri^Xn8_wPDz+t%75Jc z=Es{hNfst%oo|7Ulxnot{@RTPl!ppyJ6L%6KtzEHCk1jdurR#9iEj(2wt^+_LiI0E zGR~)?^}%u4>Zr2Jdth2Ff6jf_bmP`7I|A1IhqQrCIEcmxy9hBZZ_$yU!zQu15arMi z2|a>&xi@WU!RuVYNN$}a1Z&jCFt^V)l?s?>E=<7&Y5GnQJ9 z9MT~)jwA3`0a!2u&Owr$fZtjAeR_y0hq=6hDpQUR{Ru(Of7fui%qm& zs`6IiY4&dRHucP-O=3`=lHI*$W|(njpobsNxK^yMZcmKyim{+^G@wCP8`nd{We$=Z z3#`BDQsjS${G;q`0Sq(~eV19>ix~dM>4XfiBvdl^UYzbfUVoDp-A*71h#ffOkYFRx zxOOjsP?1i7W2^znUS#@=VHK#s`Ha%V@8=K`>2#1OZP$6(mZI~0&qfNv0FOPlvnfNA z&;Ea^kJ0gK-MFmzr}tqs^M`#viMl>@B&)9i@mb;hg96R~uGGV;t6Ij_+t%#U;BcN) zpRIf5@Mn{?#qrRrRxAEfMf2izyEOW!Ps|tG7x>ewN|&w4 zKVA!m&#$kW8_28+M@? z_xD6XFhnvd@Y4^%5C|ZHZ!hB@7K7yZjQYVrTNsMWGJi|3!xsDs{;=Z{$BkC2mrwV< z{Ve#F5;h#)VURnhbp^Ki{+{Oc)cR7{?(<`3JUYf-MR=BEg4~9& z$?q|5MTOPRZdv}S+Y~cWlJSmhu;;4g zvZWrtVGJ-$Q}hxpz}hA~Xhcwel9Vi>pn_0ym10*RCUpx5+3i1@nyXi{WS%7a^d$A* z4QpPp_e>5;#d6<%?tFJKgB{}*qC-xkqVd5n^dXcYq>==fH3(Dy7+fWQt8ts^05$aV z!h0P~8F3%}9;JAG|59~!Zn~T?V+Ms)PEJFo%kGnqyzh8DcjVoyoxhbMQsxRb7_nwD ze$nxN6>p;Vc#G7F&32AIDa|I( zS!X)4IRVpNZB289(g8?LAOZqV6Y`@l!`mj1)~gs#l~aCxegynO1(MR`Sc$d}(ppf{lOy<$hS^$lSb_)a59DCbgX(nr5vL_DQ)8JH zc@1<^OTD*?-rIGy6*^lIIskzlw6mJ4s}hC5JvCrotHT~dlw<&?OWZz!pMs<)lNcuU zxZ_2M&=CD1LK)ycC(DYc<^UU|{W%8J7j%EHuI>j`@o$zr{CqH-kvL-nYZN0^k7!DW zVO~`4K2p$vfI7{hW+iwBA}Q+-jvfmLo(7crNx+b`lSDg;V{(<-w?Xv*uTN{h=N|wt zfpp#sTRUf0K{ej6m@x5+@_Xzv@BM!9YK6i67``}4g5Qnb9u#a_(dDO@MdU}yGB`}q z)oreOl3T?=`$ySh z;Nu^KR%>6XKrp)i$>#+6pLk{E68fkYSenFG3F42m&%_pr1X8?g5-X2Cce*`UJ-~k$ zN`#;AI~~^5g`6e;wE^8@_ZuXy1MZu*um^y6{|69T9GD*nGl7){kRb{dWw~l^eQECt zmmlJc%xtdLn6z@RZ?U>{?KSNmJN9;@L!dLLgs^8|V4ySm``J-@TqZD2@joz===b}* z-~z=M_U9PjwEl{7fe;#<7OAuTx1hY6N42|a;~5~{Ztt?v+R0$LigK=KF*221BwleM zpv4{O1;N;`-Czk2 zFG%1~RvQ!V#^<8}-j5)T>$cYh0E2F2Wgy4Pf00WK^ky^(m#}Ew2K_<6KB%qjN?EY9 z?_)OtL=R|-i|>e2XSO{_kL9>(wNX}>ZdjmO6;q7O| zp#WeeC^&d}TV@6C6W~O|jII5AFTZG0x;IZ3P5y7T18lQ&b>{VQRafXF_3R`d$P z4z}WX^=01zHURqN!<20cZ#$2ic5xRMa8=$v%-XPd!LrQTom$<27?wjwB4uV~CPpAg z&p6kwZ$xe1*pmPF&oDG+oTJJQ%n%{!LUM8y6cj+Zv?C)I;0?I8^l#5EejnDrAtm)S z4fq!>;e!<_FykRzXfTv_=P}cu zsw(&DJ!AeO9a`P^u;XB70FABM0#1{}vEcHQyQdq|u(vWPGt+E^Nd#6rSJ zvqF8=UGxn3fi^@c0J0X03)EO?pc)%TW>1)Y#R?CBOA6}%362Q(y?tog37jU#O>)Qd z2pCDoMFdO_12dZ)%q_8jB2>Nv~d+Pu|xZTIjjcfU71oHBU)I~>}#5F`fg4=oiV5Vw;! z5)hpfZ)Y{4VG@0=Zycrs2NWEnX@cF6%vwbLEdq!2u)=7d9^vs$oB#du_sny&RID`k z#bHnuBtaEt*vYeJ3098^pEL2O!6Bjue2+aU|5z2w7OKt~_gWb-gdtEasCu($T*XmG zYprVT;PB<7&pzW-`cCVnL+xsJyhY<%$9;{7t8$6abh~@P#je8NTe#F0g9nU9`ONrd z4LxJcnW_Z5Px$(+c~-QXXolmMuSAGkHRz^@d={lfFP9l_aym61KF+!KXP?8LCr*Y4 zU?o2~`%y%?==10_VzD~Vst7Lf!-sG80_6_mirV2!(GezJkvXFSQCv1gp^&&9z&TqH z;D8?{6!Qvv7>U^-HWftloj5Uk^oj~~&5UDLWnPoK)7kB;#(@DDl8YK8xyD@C2WP%} zbgBpP}7jVQCT?w zaD_Ws3gSIOb`CChO+gOesUrZ>VL+vQ9AvmZJO_**xjBgW0+dwpZwZu%#FhY|XOQ

je@z4D74lYmSBdc+S)No2L9j7j@^eF;;nT{YwWe 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 - - - - ApplicationVersion - - com.omnigroup.OmniGraffle - 139.18.0.187838 - - CreationDate - 2016-10-26 10:54:40 +0000 - Creator - Chris Mutel - GraphDocumentVersion - 8 - GuidesLocked - NO - GuidesVisible - YES - ImageCounter - 1 - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2016-10-26 11:11:36 +0000 - Modifier - Chris Mutel - NotesVisible - NO - OriginVisible - NO - PageBreaks - YES - PrintInfo - - NSBottomMargin - - float - 41 - - NSHorizonalPagination - - coded - BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG - - NSLeftMargin - - float - 18 - - NSPaperName - - string - A4 - - NSPaperSize - - size - {595, 842} - - NSPrintReverseOrientation - - int - 0 - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - ReadOnly - NO - Sheets - - - ActiveLayerIndex - 0 - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {559, 783}} - Class - SolidGraphic - ID - 2 - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - BaseZoom - 0 - CanvasOrigin - {0, 0} - ColumnAlign - 1 - ColumnSpacing - 36 - DisplayScale - 1 0/72 in = 1 0/72 in - GraphicsList - - - Bounds - {{402.08214209161542, 282.54121623875318}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 59 - Line - - ID - 21 - Position - 0.49713322520256042 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 4} - - Wrap - NO - - - Bounds - {{59.59796142578125, 537.7384033203125}, {110, 14}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 58 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 Biosphere exchange} - VerticalPad - 0 - - Wrap - NO - - - Bounds - {{59.59796142578125, 498.86917114257812}, {81, 28}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 57 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Align - 0 - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural - -\f0\fs24 \cf0 Activity in \ -other database} - VerticalPad - 0 - - Wrap - NO - - - Bounds - {{59.59796142578125, 464.84536743164062}, {111, 28}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 56 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Align - 0 - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural - -\f0\fs24 \cf0 Activity in\ -foreground database} - VerticalPad - 0 - - Wrap - NO - - - Bounds - {{21, 532.73837280273438}, {24, 24}} - Class - ShapedGraphic - ID - 55 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - - - Bounds - {{17, 501.71453857421875}, {32, 22.30926513671875}} - Class - ShapedGraphic - ID - 54 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.375952 - g - 0.778441 - r - 0.181287 - - Width - 2 - - - - - Bounds - {{17, 467.69073486328125}, {32, 22.30926513671875}} - Class - ShapedGraphic - ID - 53 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - - - Class - LineGraphic - ID - 52 - Points - - {419.99200833335647, 361.50399780273438} - {454.71783321525737, 383.6503634312707} - {489.73053866697478, 379.33552530209329} - {489.73053866697478, 379.33552530209329} - - Rotation - 38.100936889648438 - Style - - stroke - - HeadArrow - 0 - Legacy - - LineType - 1 - Pattern - 1 - TailArrow - 0 - - - - - Bounds - {{190.516845703125, 465.69073390960693}, {63, 42}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 51 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 Foreground\ -database \ -boundary} - VerticalPad - 0 - - Wrap - NO - - - Class - LineGraphic - ID - 50 - Points - - {258.99200439453125, 483} - {299.9840087890625, 479} - {324.87391379349589, 454} - {324.87391379349589, 454} - - Style - - stroke - - HeadArrow - 0 - Legacy - - LineType - 1 - Pattern - 1 - TailArrow - 0 - - - - - Bounds - {{436.80034515048141, 393.38729251283229}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 49 - Line - - ID - 36 - Position - 0.26078364253044128 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 12} - - Wrap - NO - - - Bounds - {{449.4919992554407, 286.12622976739942}, {23, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 48 - Line - - ID - 35 - Position - 0.42030641436576843 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 11} - - Wrap - NO - - - Bounds - {{408.16057565435523, 379.82791915036785}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 47 - Line - - ID - 33 - Position - 0.4768994152545929 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 1} - - Wrap - NO - - - Bounds - {{327.88190939896464, 474.6907496107508}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 46 - Line - - ID - 31 - Position - 0.54023385047912598 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 1} - - Wrap - NO - - - Bounds - {{294.14721778233456, 491.7145340290221}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 45 - Line - - ID - 29 - Position - 0.25558805465698242 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 10} - - Wrap - NO - - - Bounds - {{296.2952181114992, 374.25481299412655}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 44 - Line - - ID - 43 - Position - 0.57447385787963867 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 9} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 42 - - ID - 43 - Points - - {289.73053807470461, 420.5882698590728} - {315.95397684422102, 360.82322329686758} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 24 - - - - Bounds - {{304.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 42 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 A} - - - - Bounds - {{153.49201408097778, 388.06643779973342}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 40 - Line - - ID - 19 - Position - 0.35237178206443787 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 8} - - Wrap - NO - - - Bounds - {{123.59796090655041, 384.77838187115981}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 39 - Line - - ID - 18 - Position - 0.387602299451828 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 7} - - Wrap - NO - - - Bounds - {{213.51685002277893, 279.96262474046341}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 38 - Line - - ID - 11 - Position - 0.50116497278213501 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 6} - - Wrap - NO - - - Bounds - {{260.16554229653218, 383.76350574892541}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 37 - Line - - ID - 25 - Position - 0.42665106058120728 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 3} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 20 - - ID - 36 - Points - - {445.86601120408051, 420.52227827685414} - {457.11799740201985, 362.48571732861461} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 27 - - - - Class - LineGraphic - Head - - ID - 34 - - ID - 35 - Points - - {460.9919992554407, 322.50399783391111} - {460.9919992554407, 264.50400727155653} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 20 - - - - Bounds - {{441.99200439453125, 225.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 34 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 B} - - - - Class - LineGraphic - Head - - ID - 32 - - ID - 33 - Points - - {431.64261155152434, 420.61692924463688} - {400.22710669255747, 360.24988082018029} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 27 - - - - Bounds - {{371.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 32 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - - - Class - LineGraphic - Head - - ID - 30 - - ID - 31 - Points - - {326.30380541476723, 518.55056227571708} - {344.95888223984821, 459.5764483124579} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 26 - - - - Bounds - {{331.99200439453125, 421.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 30 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - - - Class - LineGraphic - Head - - ID - 24 - - ID - 29 - Points - - {312.06102444773387, 518.57486871770561} - {288.92298431774401, 460.43312688776319} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 26 - - - - Bounds - {{419.99200439453125, 421.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 27 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.375952 - g - 0.778441 - r - 0.181287 - - Width - 2 - - - - - Bounds - {{297.99200439453125, 519.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 26 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.375952 - g - 0.778441 - r - 0.181287 - - Width - 2 - - - - - Class - LineGraphic - Head - - ID - 4 - - ID - 25 - Points - - {275.49169677413806, 420.53991829417424} - {259.49231201154794, 362.46807731129451} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 24 - - - - Bounds - {{258.99200439453125, 421.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 24 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 B} - - - - Class - LineGraphic - Head - - ID - 3 - - ID - 21 - Points - - {442.11474705674823, 324.5431121970862} - {378.68586448249533, 264.19330164859889} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 20 - - - - Bounds - {{438.99200439453125, 323.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 20 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 C} - - - - Class - LineGraphic - Head - - ID - 17 - - ID - 19 - Points - - {161.99201408097778, 420.50399783391111} - {161.99201408097778, 362.50400727155585} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 13 - - - - Class - LineGraphic - Head - - ID - 16 - - ID - 18 - Points - - {148.43782658249165, 420.67848395025743} - {106.28156048435413, 359.01707968898575} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 13 - - - - Bounds - {{142.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 17 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - - - Bounds - {{75.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 16 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 D} - - - - Bounds - {{199.34239558608965, 379.66336371842732}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 15 - Line - - ID - 14 - Position - 0.4973413348197937 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 2} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 4 - - ID - 14 - Points - - {179.85624318084123, 421.47469996944807} - {236.12776562007497, 361.53329564556327} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 13 - - - - Bounds - {{139.99200439453125, 421.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 13 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 C} - - - - Class - LineGraphic - Head - - ID - 10 - - ID - 11 - Points - - {241.4369545203744, 322.65891896033929} - {202.6870307382155, 261.40903940576783} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 4 - - - - Bounds - {{172.99200439453125, 225.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 10 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - - - Bounds - {{247.80087522825426, 285.25012934013859}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 9 - Line - - ID - 8 - Position - 0.43523043394088745 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 5} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 7 - - ID - 8 - Points - - {255.01234627966639, 322.50529680033708} - {257.97291348293066, 264.4781794604026} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 4 - - - - Bounds - {{239.99200439453128, 225.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 7 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 C} - - - - Bounds - {{291.12643635250981, 287.50232150823564}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 6 - Line - - ID - 5 - Position - 0.41588246822357178 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 1} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 3 - - ID - 5 - Points - - {272.94517374686103, 324.64428051745756} - {337.10095006418675, 264.18979895055008} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 4 - - - - Bounds - {{231.99200439453125, 323.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 4 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 A} - - - - Bounds - {{311.99200439453125, 225.50399780273438}, {92, 38}} - Class - ShapedGraphic - ID - 3 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 Functional\ -Unit} - - - - GridInfo - - HPages - 1 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - HierarchicalOrientation - 3 - circoMinDist - 18 - circoSeparation - 0.0 - dotNodekSep - 0.3993055522441864 - dotRankSep - 0.60069441795349121 - layoutEngine - dot - neatoSeparation - 0.0 - twopiSeparation - 0.0 - - Orientation - 2 - PrintOnePage - - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 1 - UniqueID - 1 - VPages - 1 - - - ActiveLayerIndex - 0 - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {559, 783}} - Class - SolidGraphic - ID - 2 - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - BaseZoom - 0 - CanvasOrigin - {0, 0} - ColumnAlign - 1 - ColumnSpacing - 36 - DisplayScale - 1 0/72 in = 1.0000 in - GraphicsList - - - Bounds - {{401.99615468437497, 281.99639394988031}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 59 - Line - - ID - 21 - Position - 0.49713322520256042 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 4} - - Wrap - NO - - - Bounds - {{59.59796142578125, 537.7384033203125}, {110, 14}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 58 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 Biosphere exchange} - VerticalPad - 0 - - Wrap - NO - - - Bounds - {{59.59796142578125, 498.86917114257812}, {81, 28}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 57 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Align - 0 - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural - -\f0\fs24 \cf0 Activity in \ -other database} - VerticalPad - 0 - - Wrap - NO - - - Bounds - {{59.59796142578125, 464.84536743164062}, {111, 28}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 56 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Align - 0 - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural - -\f0\fs24 \cf0 Activity in\ -foreground database} - VerticalPad - 0 - - Wrap - NO - - - Bounds - {{21, 532.73837280273438}, {24, 24}} - Class - ShapedGraphic - ID - 55 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - - - Bounds - {{17, 501.71453857421875}, {32, 22.30926513671875}} - Class - ShapedGraphic - ID - 54 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.375952 - g - 0.778441 - r - 0.181287 - - Width - 2 - - - - - Bounds - {{17, 467.69073486328125}, {32, 22.30926513671875}} - Class - ShapedGraphic - ID - 53 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - - - Class - LineGraphic - ID - 52 - Points - - {419.99200833335647, 361.50399780273438} - {454.71783321525737, 383.6503634312707} - {489.73053866697478, 379.33552530209329} - {489.73053866697478, 379.33552530209329} - - Rotation - 38.100936889648438 - Style - - stroke - - HeadArrow - 0 - Legacy - - LineType - 1 - Pattern - 1 - TailArrow - 0 - - - - - Bounds - {{190.516845703125, 465.69073390960693}, {63, 42}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - ID - 51 - Shape - Rectangle - Style - - fill - - Draws - NO - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Pad - 0 - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 Foreground\ -database \ -boundary} - VerticalPad - 0 - - Wrap - NO - - - Class - LineGraphic - ID - 50 - Points - - {258.99200439453125, 483} - {299.9840087890625, 479} - {324.87391379349589, 454} - {324.87391379349589, 454} - - Style - - stroke - - HeadArrow - 0 - Legacy - - LineType - 1 - Pattern - 1 - TailArrow - 0 - - - - - Bounds - {{436.80034521174235, 393.38729251283229}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 49 - Line - - ID - 36 - Position - 0.26078364253044128 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 12} - - Wrap - NO - - - Bounds - {{449.4919992554407, 286.12622976739942}, {23, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 48 - Line - - ID - 35 - Position - 0.42030641436576843 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 11} - - Wrap - NO - - - Bounds - {{408.16057569697966, 379.82791913482129}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 47 - Line - - ID - 33 - Position - 0.4768994152545929 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 1} - - Wrap - NO - - - Bounds - {{327.88190943025353, 474.69074961828073}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 46 - Line - - ID - 31 - Position - 0.54023385047912598 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 1} - - Wrap - NO - - - Bounds - {{294.14721779016139, 491.7145340290221}, {24, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 45 - Line - - ID - 29 - Position - 0.25558805465698242 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 10} - - Wrap - NO - - - Bounds - {{296.29521813248209, 374.25481300119463}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 44 - Line - - ID - 43 - Position - 0.57447385787963867 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 9} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 42 - - ID - 43 - Points - - {289.73053808346572, 420.5882698592564} - {315.95397687425691, 360.8232233090352} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 24 - - - - Bounds - {{304.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 42 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 A} - - - - Bounds - {{153.49201408097778, 388.06643779973342}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 40 - Line - - ID - 19 - Position - 0.35237178206443787 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 8} - - Wrap - NO - - - Bounds - {{123.59796086434986, 384.77838188862643}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 39 - Line - - ID - 18 - Position - 0.387602299451828 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 7} - - Wrap - NO - - - Bounds - {{213.51685001198638, 279.96262474523371}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 38 - Line - - ID - 11 - Position - 0.50116497278213501 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 6} - - Wrap - NO - - - Bounds - {{260.16554229797276, 383.76350574892541}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 37 - Line - - ID - 25 - Position - 0.42665106058120728 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 3} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 20 - - ID - 36 - Points - - {445.86601123893854, 420.52227827685414} - {457.11799753812249, 362.48571732861461} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 27 - - - - Class - LineGraphic - Head - - ID - 34 - - ID - 35 - Points - - {460.9919992554407, 322.50399783391111} - {460.9919992554407, 264.50400727155653} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 20 - - - - Bounds - {{441.99200439453125, 225.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 34 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 B} - - - - Class - LineGraphic - Head - - ID - 32 - - ID - 33 - Points - - {431.64261157200264, 420.61692924417213} - {400.2271067594736, 360.24988078809088} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 27 - - - - Bounds - {{371.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 32 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\b\fs24 \cf0 C} - - - - Class - LineGraphic - Head - - ID - 30 - - ID - 31 - Points - - {326.30380542766471, 518.55056227593582} - {344.95888228678916, 459.57644832621008} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 26 - - - - Bounds - {{331.99200439453125, 421.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 30 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\b\fs24 \cf0 B} - - - - Class - LineGraphic - Head - - ID - 24 - - ID - 29 - Points - - {312.06102445221745, 518.57486871770561} - {288.92298433530817, 460.43312688776319} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 26 - - - - Bounds - {{419.99200439453125, 421.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 27 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.375952 - g - 0.778441 - r - 0.181287 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\b\fs24 \cf0 C} - - - - Bounds - {{297.99200439453125, 519.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 26 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.375952 - g - 0.778441 - r - 0.181287 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\b\fs24 \cf0 B} - - - - Class - LineGraphic - Head - - ID - 4 - - ID - 25 - Points - - {275.49169677478085, 420.53991829417424} - {259.49231201406064, 362.46807731129451} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 24 - - - - Bounds - {{258.99200439453125, 421.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 24 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 B} - - - - Class - LineGraphic - Head - - ID - 3 - - ID - 21 - Points - - {442.19159993609117, 324.44384876121779} - {378.43515871492423, 263.1977817759917} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 20 - - - - Bounds - {{438.99200439453125, 323.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 20 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 C} - - - - Class - LineGraphic - Head - - ID - 17 - - ID - 19 - Points - - {161.99201408097778, 420.50399783391111} - {161.99201408097778, 362.50400727155585} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 13 - - - - Class - LineGraphic - Head - - ID - 16 - - ID - 18 - Points - - {148.43782655834195, 420.67848395084059} - {106.28156041363387, 359.01707973312767} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 13 - - - - Bounds - {{142.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 17 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\b\fs24 \cf0 C} - - - - Bounds - {{75.99200439453125, 323.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 16 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 D} - - - - Bounds - {{199.34239558052508, 379.66336371394772}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 15 - Line - - ID - 14 - Position - 0.4973413348197937 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 2} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 4 - - ID - 14 - Points - - {179.85624317867337, 421.47469996770252} - {236.12776561107736, 361.5332956383204} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 13 - - - - Bounds - {{139.99200439453125, 421.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 13 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 C} - - - - Class - LineGraphic - Head - - ID - 10 - - ID - 11 - Points - - {241.43695451503257, 322.65891896046708} - {202.68703072199759, 261.40903941515899} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 4 - - - - Bounds - {{172.99200439453125, 225.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 10 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\i\b\fs24 \cf0 A} - - - - Bounds - {{247.8008752340416, 285.25012934034845}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 9 - Line - - ID - 8 - Position - 0.43523043394088745 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 5} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 7 - - ID - 8 - Points - - {255.01234628222826, 322.50529680034504} - {257.9729134929035, 264.47817946087446} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 4 - - - - Bounds - {{239.99200439453128, 225.50399780273438}, {38, 38}} - Class - ShapedGraphic - ID - 7 - Shape - Circle - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.843539 - g - 0.566467 - r - 0.23427 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 C} - - - - Bounds - {{291.18752390446633, 287.02962042413685}, {17, 24}} - Class - ShapedGraphic - FitText - YES - Flow - Resize - FontInfo - - Color - - w - 0 - - Font - Helvetica - Size - 12 - - ID - 6 - Line - - ID - 5 - Position - 0.41588246822357178 - RotationType - 0 - - Shape - Rectangle - Style - - shadow - - Draws - NO - - stroke - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 1} - - Wrap - NO - - - Class - LineGraphic - Head - - ID - 3 - - ID - 5 - Points - - {272.86979420118212, 324.54380899363798} - {337.35370913473355, 263.19429195704794} - - Style - - stroke - - HeadArrow - FilledArrow - Legacy - - LineType - 1 - TailArrow - 0 - - - Tail - - ID - 4 - - - - Bounds - {{231.99200439453125, 323.50399780273438}, {44, 38}} - Class - ShapedGraphic - ID - 4 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 A} - - - - Bounds - {{311.99200439453125, 224.50399780273438}, {92, 38}} - Class - ShapedGraphic - ID - 3 - Shape - RoundRect - Style - - shadow - - Draws - NO - - stroke - - Color - - b - 0.148646 - g - 0.450677 - r - 0.879233 - - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 Functional\ -Unit} - - - - GridInfo - - HPages - 1 - KeepToScale - - Layers - - - Lock - NO - Name - Layer 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - HierarchicalOrientation - 3 - circoMinDist - 18 - circoSeparation - 0.0 - dotRankSep - 0.60069441795349121 - layoutEngine - dot - neatoSeparation - 0.0 - twopiSeparation - 0.0 - - Orientation - 2 - PrintOnePage - - RowAlign - 1 - RowSpacing - 36 - SheetTitle - Canvas 2 - UniqueID - 2 - VPages - 1 - - - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UseEntirePage - - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - - name - Canvas 1 - - - Frame - {{1506, 221}, {929, 1139}} - ListView - - OutlineWidth - 142 - RightSidebar - - ShowRuler - - Sidebar - - SidebarWidth - 120 - VisibleRegion - {{-118, -107}, {794, 997}} - Zoom - 1 - ZoomValues - - - Canvas 1 - 1 - 1 - - - Canvas 2 - 1 - 1 - - - - - diff --git a/sphinx/source/reference/images/tagged-traversal-2.png b/sphinx/source/reference/images/tagged-traversal-2.png deleted file mode 100644 index d67672a6293c143f2d3f9d637ea67811bc703b55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41516 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 H7XkkVXuek3 diff --git a/sphinx/source/reference/images/tagged-traversal.png b/sphinx/source/reference/images/tagged-traversal.png deleted file mode 100644 index 602b124c86f7f2e9fa68c97594f90ddf6c22208c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40337 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 i`t4sFO*@da`__. + + +.. py:function:: find_differences_in_inputs(activity, rel_tol=0.0001, abs_tol=1e-09, locations=None, as_dataframe=False) + + Given an ``Activity``, try to see if other activities in the same database (with the same name and + reference product) have the same input levels. + + Tolerance values are inputs to `math.isclose `__. + + If differences are present, a difference dictionary is constructed, with the form: + + .. code-block:: python + + {Activity instance: [(name of input flow (str), amount)]} + + Note that this doesn't reference a specific exchange, but rather sums **all exchanges with the same input reference product**. + + Assumes that all similar activities produce the same amount of reference product. + + ``(x, y)``, where ``x`` is the number of similar activities, and ``y`` is a dictionary of the differences. This dictionary is empty if no differences are found. + + :param activity: ``Activity``. Activity to analyze. + :param rel_tol: float. Relative tolerance to decide if two inputs are the same. See above. + :param abs_tol: float. Absolute tolerance to decide if two inputs are the same. See above. + :param locations: list, optional. Locations to restrict comparison to, if present. + :param as_dataframe: bool. Return results as pandas DataFrame. + + :returns: dict or ``pandas.DataFrame``. + + +.. py:function:: compare_activities_by_lcia_score(activities, lcia_method, band=0.1) + + Compare selected activities to see if they are substantially different. + + Substantially different means that all LCIA scores lie within a band of ``band * max_lcia_score``. + + Inputs: + + ``activities``: List of ``Activity`` objects. + ``lcia_method``: Tuple identifying a ``Method`` + + :returns: Nothing, but prints to stdout. + + +.. py:function:: find_leaves(activity, lcia_method, results=None, lca_obj=None, amount=1, total_score=None, level=0, max_level=3, cutoff=0.025) + + Traverse the supply chain of an activity to find leaves - places where the impact of that + component falls below a threshold value. + + Returns a list of ``(impact of this activity, amount consumed, Activity instance)`` tuples. + + +.. py:function:: get_cpc(activity) + + +.. py:function:: get_value_for_cpc(lst, label) + + +.. py:function:: group_leaves(leaves) + + Group elements in ``leaves`` by their `CPC (Central Product Classification) `__ code. + + Returns a list of ``(fraction of total impact, specific impact, amount, Activity instance)`` tuples. + + +.. py:function:: compare_activities_by_grouped_leaves(activities, lcia_method, mode='relative', max_level=4, cutoff=0.0075, output_format='list', str_length=50) + + Compare activities by the impact of their different inputs, aggregated by the product classification of those inputs. + + :param activities: list of ``Activity`` instances. + :param lcia_method: tuple. LCIA method to use when traversing supply chain graph. + :param mode: str. If "relative" (default), results are returned as a fraction of total input. Otherwise, results are absolute impact per input exchange. + :param max_level: int. Maximum level in supply chain to examine. + :param cutoff: float. Fraction of total impact to cutoff supply chain graph traversal at. + :param output_format: str. See below. + :param str_length; int. If ``output_format`` is ``html``: + :param this controls how many characters each column label can have.: + + :raises ValueError: ``activities`` is malformed. + + :returns: + + * ``list``: Tuple of ``(column labels, data)`` + * ``html``: HTML string that will print nicely in Jupyter notebooks. + * ``pandas``: a pandas ``DataFrame``. + :rtype: Depends on ``output_format`` + + diff --git a/sphinx/technical/api/bw2analyzer/contribution/index.rst b/sphinx/technical/api/bw2analyzer/contribution/index.rst new file mode 100644 index 0000000..2bbb4cf --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/contribution/index.rst @@ -0,0 +1,153 @@ +:py:mod:`bw2analyzer.contribution` +================================== + +.. py:module:: bw2analyzer.contribution + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.contribution.ContributionAnalysis + + + + +.. py:class:: ContributionAnalysis + + .. py:method:: sort_array(data, limit=25, limit_type='number', total=None) + + Common sorting function for all ``top`` methods. Sorts by highest value first. + + Operates in either ``number`` or ``percent`` mode. In ``number`` mode, return ``limit`` values. In ``percent`` mode, return all values >= (total * limit); where ``0 < limit <= 1``. + + Returns 2-d numpy array of sorted values and row indices, e.g.: + + .. code-block:: python + + ContributionAnalysis().sort_array((1., 3., 2.)) + + returns + + .. code-block:: python + + ( + (3, 1), + (2, 2), + (1, 0) + ) + + :param \* *data*: A 1-d array of values to sort. + :type \* *data*: numpy array + :param \* *limit*: Number of values to return, or percentage cutoff. + :type \* *limit*: number, default=25 + :param \* *limit_type*: Either ``number`` or ``percent``. + :type \* *limit_type*: str, default=``number`` + :param \* *total*: Optional specification of summed data total. + :type \* *total*: number, default=None + + :returns: 2-d numpy array of values and row indices. + + + .. py:method:: top_matrix(matrix, rows=5, cols=5) + + Find most important (i.e. highest summed) rows and columns in a matrix, as well as the most corresponding non-zero individual elements in the top rows and columns. + + Only returns matrix values which are in the top rows and columns. Element values are returned as a tuple: ``(row, col, row index in top rows, col index in top cols, value)``. + + Example: + + .. code-block:: python + + matrix = [ + [0, 0, 1, 0], + [2, 0, 4, 0], + [3, 0, 1, 1], + [0, 7, 0, 1], + ] + + In this matrix, the row sums are ``(1, 6, 5, 8)``, and the columns sums are ``(5, 7, 6, 2)``. Therefore, the top rows are ``(3, 1)`` and the top columns are ``(1, 2)``. The result would therefore be: + + .. code-block:: python + + ( + ( + (3, 1, 0, 0, 7), + (3, 2, 0, 1, 1), + (1, 2, 1, 1, 4) + ), + (3, 1), + (1, 2) + ) + + :param \* *matrix*: Any Python object that supports the ``.sum(axis=)`` syntax. + :type \* *matrix*: array or matrix + :param \* *rows*: Number of rows to select. + :type \* *rows*: int + :param \* *cols*: Number of columns to select. + :type \* *cols*: int + + :returns: (elements, top rows, top columns) + + + .. py:method:: hinton_matrix(lca, rows=5, cols=5) + + + .. py:method:: annotate(sorted_data, rev_mapping) + + Reverse the mapping from database ids to array indices + + + .. py:method:: top_processes(matrix, **kwargs) + + Return an array of [value, index] technosphere processes. + + + .. py:method:: top_emissions(matrix, **kwargs) + + Return an array of [value, index] biosphere emissions. + + + .. py:method:: annotated_top_processes(lca, names=True, **kwargs) + + Get list of most damaging processes in an LCA, sorted by ``abs(direct impact)``. + + Returns a list of tuples: ``(lca score, supply, activity)``. If ``names`` is False, they returns the process key as the last element. + + + + .. py:method:: annotated_top_emissions(lca, names=True, **kwargs) + + Get list of most damaging biosphere flows in an LCA, sorted by ``abs(direct impact)``. + + Returns a list of tuples: ``(lca score, inventory amount, activity)``. If ``names`` is False, they returns the process key as the last element. + + + + .. py:method:: get_name(key) + + + .. py:method:: d3_treemap(matrix, rev_bio, rev_techno, limit=0.025, limit_type='percent') + + Construct treemap input data structure for LCA result. Output like: + + .. code-block:: python + + { + "name": "LCA result", + "children": [{ + "name": process 1, + "children": [ + {"name": emission 1, "size": score}, + {"name": emission 2, "size": score}, + ], + }] + } + + + + diff --git a/sphinx/technical/api/bw2analyzer/econ/index.rst b/sphinx/technical/api/bw2analyzer/econ/index.rst new file mode 100644 index 0000000..0dddf4a --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/econ/index.rst @@ -0,0 +1,85 @@ +:py:mod:`bw2analyzer.econ` +========================== + +.. py:module:: bw2analyzer.econ + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.econ.gini_coefficient + bw2analyzer.econ.herfindahl_index + bw2analyzer.econ.concentration_ratio + bw2analyzer.econ.theil_index + + + +.. py:function:: gini_coefficient(x) + + Return computed Gini coefficient. + + See https://en.wikipedia.org/wiki/Gini_coefficient + + Adapted from econpy library. + copyright: 2005-2009 Alan G. Isaac + license: MIT license + contact: aisaac AT american.edu + + :param \*x*: Data + :type \*x*: list or array + + :returns: Gini coefficient (float) + + +.. py:function:: herfindahl_index(x, normalize=True) + + Return computed Herfindahl index. + + See https://en.wikipedia.org/wiki/Herfindahl_index + + Normalized scores are bounded [0, 1]; non-normalized scores are [1/len(x), 1]. Normalization only counts non-zero values. + + :param \*x*: Data + :type \*x*: list or array + :param \*normalize*: Flag to normalize scores. + :type \*normalize*: bool, default=True + + :returns: Herfindahl index (float) + + +.. py:function:: concentration_ratio(x, number=4) + + Return computed concentration ratio. + + See https://en.wikipedia.org/wiki/Concentration_ratio + + The concentration ratio measures the share of the market controlled by the top *number* firms. Returned ratio values vary from 0 to 1. + + :param \*x*: Data + :type \*x*: list or array + :param \*number*: Number of values to consider. 4 and 8 are commonly used. + :type \*number*: int, default=4 + + :returns: Concentration ratio (float) + + +.. py:function:: theil_index(x) + + Return Theil entropy index. + + See https://en.wikipedia.org/wiki/Theil_Index + + The Theil index is a measure of economic inequality based on information theory. It is the difference between a dataset's maximum possible entropy and observed entropy. + + :param \*x*: Data + :type \*x*: list or array + + :returns: Theil index (float) + + diff --git a/sphinx/technical/api/bw2analyzer/health_check/index.rst b/sphinx/technical/api/bw2analyzer/health_check/index.rst new file mode 100644 index 0000000..f9da7dd --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/health_check/index.rst @@ -0,0 +1,46 @@ +:py:mod:`bw2analyzer.health_check` +================================== + +.. py:module:: bw2analyzer.health_check + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.health_check.DatabaseHealthCheck + + + + +.. py:class:: DatabaseHealthCheck(database) + + .. py:method:: check(graphs_dir=None) + + + .. py:method:: make_graphs(graphs_dir=None) + + + .. py:method:: page_rank() + + + .. py:method:: unique_exchanges() + + + .. py:method:: uncertainty_check() + + + .. py:method:: multioutput_processes() + + + .. py:method:: aggregated_processes(cutoff=500) + + + .. py:method:: no_self_production() + + + diff --git a/sphinx/technical/api/bw2analyzer/index.rst b/sphinx/technical/api/bw2analyzer/index.rst new file mode 100644 index 0000000..dadd764 --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/index.rst @@ -0,0 +1,463 @@ +:py:mod:`bw2analyzer` +===================== + +.. py:module:: bw2analyzer + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + comparisons/index.rst + contribution/index.rst + econ/index.rst + health_check/index.rst + lci/index.rst + matrix_grapher/index.rst + page_rank/index.rst + report/index.rst + sc_graph/index.rst + tagged/index.rst + utils/index.rst + version/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.ContributionAnalysis + bw2analyzer.DatabaseHealthCheck + bw2analyzer.PageRank + bw2analyzer.GTManipulator + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.compare_activities_by_grouped_leaves + bw2analyzer.compare_activities_by_lcia_score + bw2analyzer.find_differences_in_inputs + bw2analyzer.traverse_tagged_databases + bw2analyzer.print_recursive_calculation + bw2analyzer.print_recursive_supply_chain + + + +.. py:function:: compare_activities_by_grouped_leaves(activities, lcia_method, mode='relative', max_level=4, cutoff=0.0075, output_format='list', str_length=50) + + Compare activities by the impact of their different inputs, aggregated by the product classification of those inputs. + + :param activities: list of ``Activity`` instances. + :param lcia_method: tuple. LCIA method to use when traversing supply chain graph. + :param mode: str. If "relative" (default), results are returned as a fraction of total input. Otherwise, results are absolute impact per input exchange. + :param max_level: int. Maximum level in supply chain to examine. + :param cutoff: float. Fraction of total impact to cutoff supply chain graph traversal at. + :param output_format: str. See below. + :param str_length; int. If ``output_format`` is ``html``: + :param this controls how many characters each column label can have.: + + :raises ValueError: ``activities`` is malformed. + + :returns: + + * ``list``: Tuple of ``(column labels, data)`` + * ``html``: HTML string that will print nicely in Jupyter notebooks. + * ``pandas``: a pandas ``DataFrame``. + :rtype: Depends on ``output_format`` + + +.. py:function:: compare_activities_by_lcia_score(activities, lcia_method, band=0.1) + + Compare selected activities to see if they are substantially different. + + Substantially different means that all LCIA scores lie within a band of ``band * max_lcia_score``. + + Inputs: + + ``activities``: List of ``Activity`` objects. + ``lcia_method``: Tuple identifying a ``Method`` + + :returns: Nothing, but prints to stdout. + + +.. py:function:: find_differences_in_inputs(activity, rel_tol=0.0001, abs_tol=1e-09, locations=None, as_dataframe=False) + + Given an ``Activity``, try to see if other activities in the same database (with the same name and + reference product) have the same input levels. + + Tolerance values are inputs to `math.isclose `__. + + If differences are present, a difference dictionary is constructed, with the form: + + .. code-block:: python + + {Activity instance: [(name of input flow (str), amount)]} + + Note that this doesn't reference a specific exchange, but rather sums **all exchanges with the same input reference product**. + + Assumes that all similar activities produce the same amount of reference product. + + ``(x, y)``, where ``x`` is the number of similar activities, and ``y`` is a dictionary of the differences. This dictionary is empty if no differences are found. + + :param activity: ``Activity``. Activity to analyze. + :param rel_tol: float. Relative tolerance to decide if two inputs are the same. See above. + :param abs_tol: float. Absolute tolerance to decide if two inputs are the same. See above. + :param locations: list, optional. Locations to restrict comparison to, if present. + :param as_dataframe: bool. Return results as pandas DataFrame. + + :returns: dict or ``pandas.DataFrame``. + + +.. py:class:: ContributionAnalysis + + .. py:method:: sort_array(data, limit=25, limit_type='number', total=None) + + Common sorting function for all ``top`` methods. Sorts by highest value first. + + Operates in either ``number`` or ``percent`` mode. In ``number`` mode, return ``limit`` values. In ``percent`` mode, return all values >= (total * limit); where ``0 < limit <= 1``. + + Returns 2-d numpy array of sorted values and row indices, e.g.: + + .. code-block:: python + + ContributionAnalysis().sort_array((1., 3., 2.)) + + returns + + .. code-block:: python + + ( + (3, 1), + (2, 2), + (1, 0) + ) + + :param \* *data*: A 1-d array of values to sort. + :type \* *data*: numpy array + :param \* *limit*: Number of values to return, or percentage cutoff. + :type \* *limit*: number, default=25 + :param \* *limit_type*: Either ``number`` or ``percent``. + :type \* *limit_type*: str, default=``number`` + :param \* *total*: Optional specification of summed data total. + :type \* *total*: number, default=None + + :returns: 2-d numpy array of values and row indices. + + + .. py:method:: top_matrix(matrix, rows=5, cols=5) + + Find most important (i.e. highest summed) rows and columns in a matrix, as well as the most corresponding non-zero individual elements in the top rows and columns. + + Only returns matrix values which are in the top rows and columns. Element values are returned as a tuple: ``(row, col, row index in top rows, col index in top cols, value)``. + + Example: + + .. code-block:: python + + matrix = [ + [0, 0, 1, 0], + [2, 0, 4, 0], + [3, 0, 1, 1], + [0, 7, 0, 1], + ] + + In this matrix, the row sums are ``(1, 6, 5, 8)``, and the columns sums are ``(5, 7, 6, 2)``. Therefore, the top rows are ``(3, 1)`` and the top columns are ``(1, 2)``. The result would therefore be: + + .. code-block:: python + + ( + ( + (3, 1, 0, 0, 7), + (3, 2, 0, 1, 1), + (1, 2, 1, 1, 4) + ), + (3, 1), + (1, 2) + ) + + :param \* *matrix*: Any Python object that supports the ``.sum(axis=)`` syntax. + :type \* *matrix*: array or matrix + :param \* *rows*: Number of rows to select. + :type \* *rows*: int + :param \* *cols*: Number of columns to select. + :type \* *cols*: int + + :returns: (elements, top rows, top columns) + + + .. py:method:: hinton_matrix(lca, rows=5, cols=5) + + + .. py:method:: annotate(sorted_data, rev_mapping) + + Reverse the mapping from database ids to array indices + + + .. py:method:: top_processes(matrix, **kwargs) + + Return an array of [value, index] technosphere processes. + + + .. py:method:: top_emissions(matrix, **kwargs) + + Return an array of [value, index] biosphere emissions. + + + .. py:method:: annotated_top_processes(lca, names=True, **kwargs) + + Get list of most damaging processes in an LCA, sorted by ``abs(direct impact)``. + + Returns a list of tuples: ``(lca score, supply, activity)``. If ``names`` is False, they returns the process key as the last element. + + + + .. py:method:: annotated_top_emissions(lca, names=True, **kwargs) + + Get list of most damaging biosphere flows in an LCA, sorted by ``abs(direct impact)``. + + Returns a list of tuples: ``(lca score, inventory amount, activity)``. If ``names`` is False, they returns the process key as the last element. + + + + .. py:method:: get_name(key) + + + .. py:method:: d3_treemap(matrix, rev_bio, rev_techno, limit=0.025, limit_type='percent') + + Construct treemap input data structure for LCA result. Output like: + + .. code-block:: python + + { + "name": "LCA result", + "children": [{ + "name": process 1, + "children": [ + {"name": emission 1, "size": score}, + {"name": emission 2, "size": score}, + ], + }] + } + + + + +.. py:class:: DatabaseHealthCheck(database) + + .. py:method:: check(graphs_dir=None) + + + .. py:method:: make_graphs(graphs_dir=None) + + + .. py:method:: page_rank() + + + .. py:method:: unique_exchanges() + + + .. py:method:: uncertainty_check() + + + .. py:method:: multioutput_processes() + + + .. py:method:: aggregated_processes(cutoff=500) + + + .. py:method:: no_self_production() + + + +.. py:class:: PageRank(database) + + .. py:method:: calculate() + + + .. py:method:: page_rank(technosphere, alpha=0.85, max_iter=100, tol=1e-06) + + Return the PageRank of the nodes in the graph. + + Adapted from http://networkx.lanl.gov/svn/networkx/trunk/networkx/algorithms/link_analysis/pagerank_alg.py + + PageRank computes a ranking of the nodes in the graph G based on + the structure of the incoming links. It was originally designed as + an algorithm to rank web pages. + + The eigenvector calculation uses power iteration with a SciPy + sparse matrix representation. + + :param \* *technosphere*: The technosphere matrix. + :type \* *technosphere*: scipy sparse matrix + :param \* *alpha*: Damping parameter for PageRank, default=0.85 + :type \* *alpha*: float, optional + + :returns: + + * Dictionary of nodes (activity codes) with value as PageRank + + References + + .. [1] A. Langville and C. Meyer, + "A survey of eigenvector methods of web information retrieval." + http://citeseer.ist.psu.edu/713792.html + .. [2] Page, Lawrence; Brin, Sergey; Motwani, Rajeev and Winograd, Terry, + The PageRank citation ranking: Bringing order to the Web. 1999 + http://dbpubs.stanford.edu:8090/pub/showDoc.Fulltext?lang=en&doc=1999-66&format=pdf + + + +.. py:class:: GTManipulator + + Manipulate ``GraphTraversal`` results. + + .. py:method:: unroll_graph(nodes, edges, score, cutoff=0.005, max_links=2500) + :staticmethod: + + Unroll a ``GraphTraversal`` result, allowing the same activity to appear in the graph multiple times. + + + .. py:method:: add_metadata(nodes, lca) + :staticmethod: + + Add metadata to nodes, like name, unit, and category. + + + .. py:method:: d3_force_directed(nodes, edges, score) + :staticmethod: + + Reformat to D3 style, which is a list of nodes, and edge ids are node list indices. + + + .. py:method:: simplify(nodes, edges, score, limit=0.005) + :staticmethod: + + Simplify supply chain to include only nodes which individually contribute ``limit * score``. + + Only removes and combines edges; doesn't check to make sure amounts add up correctly. + + + .. py:method:: simplify_naive(nodes, edges, score, limit=0.0025) + :staticmethod: + + Naive simplification which simplifies removes links below an LCA score cutoff. Orphan nodes are also deleted. + + + .. py:method:: d3_treemap(nodes, edges, lca, add_biosphere=False) + :staticmethod: + + Add node data by traversing the graph; assign different metadata to leaf nodes. + + + +.. py:function:: traverse_tagged_databases(functional_unit, method, label='tag', default_tag='other', secondary_tags=[], fg_databases=None) + + Traverse a functional unit throughout its foreground database(s) or the + listed databses in fg_databses, and group impacts by tag label. + + Contribution analysis work by linking impacts to individual activities. + However, you also might want to group impacts in other ways. For example, + give individual biosphere exchanges their own grouping, or aggregate two + activities together. + + Consider this example system, where the letters are the tag labels, and the + numbers are exchange amounts. The functional unit is one unit of the tree + root. + + .. image:: images/tagged-traversal.png + :alt: Example tagged supply chain + + In this supply chain, tags are applied to activities and biosphere exchanges. + If a biosphere exchange is not tagged, it inherits the tag of its producing + activity. Similarly, links to other databases are assessed with the usual + LCA machinery, and the total LCA score is tagged according to its consuming + activity. If an activity does not have a tag, a default tag is applied. + + We can change our visualization to show the use of the default tags: + + .. image:: images/tagged-traversal-2.png + :alt: Example tagged supply chain + + And then we can manually calculate the tagged impacts. Normally we would + need to know the actual biosphere flows and their respective + characterization factors (CF), but in this example we assume that each + CF is one. Our result, group by tags, would therefore be: + + * **A**: :math:`6 + 27 = 33` + * **B**: :math:`30 + 44 = 74` + * **C**: :math:`5 + 16 + 48 = 69` + * **D**: :math:`14` + + This function will only traverse the foreground database, i.e. the + database of the functional unit activity. A functional unit can have + multiple starting nodes; in this case, all foreground databases are + traversed. + + Input arguments: + + * ``functional_unit``: A functional unit dictionary, e.g. ``{("foo", "bar"): 42}``. + * ``method``: A method name, e.g. ``("foo", "bar")`` + * ``label``: The label of the tag classifier. Default is ``"tag"`` + * ``default_tag``: The tag classifier to use if none was given. Default is ``"other"`` + * ``secondary_tags``: List of tuples in the format (secondary_label, secondary_default_tag). Default is empty list. + * ``fg_databases``: a list of foreground databases to be traversed, e.g. ['foreground', 'biomass', 'machinery'] + It's not recommended to include all databases of a project in the list to be traversed, especially not ecoinvent itself + + :returns: Aggregated tags dictionary from ``aggregate_tagged_graph``, and tagged supply chain graph from ``recurse_tagged_database``. + + +.. py:function:: print_recursive_calculation(activity, lcia_method, amount=1, max_level=3, cutoff=0.01, file_obj=None, tab_character=' ', use_matrix_values=False, _lca_obj=None, _total_score=None, __level=0, __first=True) + + Traverse a supply chain graph, and calculate the LCA scores of each component. Prints the result with the format: + + {tab_character * level }{fraction of total score} ({absolute LCA score for this input} | {amount of input}) {input activity} + + :param activity: ``Activity``. The starting point of the supply chain graph. + :param lcia_method: tuple. LCIA method to use when traversing supply chain graph. + :param amount: int. Amount of ``activity`` to assess. + :param max_level: int. Maximum depth to traverse. + :param cutoff: float. Fraction of total score to use as cutoff when deciding whether to traverse deeper. + :param file_obj: File-like object (supports ``.write``), optional. Output will be written to this object if provided. + :param tab_character: str. Character to use to indicate indentation. + :param use_matrix_values: bool. Take exchange values from the matrix instead of the exchange instance ``amount``. Useful for Monte Carlo, but can be incorrect if there is more than one exchange from the same pair of nodes. + + Normally internal args: + _lca_obj: ``LCA``. Can give an instance of the LCA class (e.g. when doing regionalized or Monte Carlo LCA) + _total_score: float. Needed if specifying ``_lca_obj``. + + Internal args (used during recursion, do not touch); + __level: int. + __first: bool. + + :returns: Nothing. Prints to ``sys.stdout`` or ``file_obj`` + + +.. py:function:: print_recursive_supply_chain(activity, amount=1, max_level=2, cutoff=0, file_obj=None, tab_character=' ', __level=0) + + Traverse a supply chain graph, and prints the inputs of each component. + + This function is only for exploration; use ``bw2calc.GraphTraversal`` for a better performing function. + + The results displayed here can also be incorrect if + + :param activity: ``Activity``. The starting point of the supply chain graph. + :param amount: int. Supply chain inputs will be scaled to this value. + :param max_level: int. Max depth to search for. + :param cutoff: float. Inputs with amounts less than ``amount * cutoff`` will not be printed or traversed further. + :param file_obj: File-like object (supports ``.write``), optional. Output will be written to this object if provided. + :param tab_character: str. Character to use to indicate indentation. + :param __level: int. Current level of the calculation. Only used internally, do not touch. + + :returns: Nothing. Prints to ``stdout`` or ``file_obj`` + + diff --git a/sphinx/technical/api/bw2analyzer/lci/index.rst b/sphinx/technical/api/bw2analyzer/lci/index.rst new file mode 100644 index 0000000..233b782 --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/lci/index.rst @@ -0,0 +1,29 @@ +:py:mod:`bw2analyzer.lci` +========================= + +.. py:module:: bw2analyzer.lci + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.lci.get_labeled_inventory + + + +.. py:function:: get_labeled_inventory(lca: bw2calc.LCA) -> pandas.DataFrame + + Take an LCA's inventory matrix and labels its rows (biosphere) and columns (technosphere) with activity metadata. + + :param \* *lca*: LCA object whose life cycle inventory has been calculated previously. + :type \* *lca*: bw2calc.LCA + + :returns: pd.DataFrame with activity information as row and column MultiIndices. + + diff --git a/sphinx/technical/api/bw2analyzer/matrix_grapher/index.rst b/sphinx/technical/api/bw2analyzer/matrix_grapher/index.rst new file mode 100644 index 0000000..3200495 --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/matrix_grapher/index.rst @@ -0,0 +1,31 @@ +:py:mod:`bw2analyzer.matrix_grapher` +==================================== + +.. py:module:: bw2analyzer.matrix_grapher + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.matrix_grapher.SparseMatrixGrapher + + + + +.. py:class:: SparseMatrixGrapher(matrix) + + .. py:method:: graph(filename=None, marker_string='c.', mew=0.5, ms=1, alpha=0.8, width=None, height=None, dpi=300) + + + .. py:method:: magnitude_graph(filename=None, dpi=600, width=None, height=None) + + + .. py:method:: ordered_graph(filename=None, dpi=600, width=None, height=None) + + + diff --git a/sphinx/technical/api/bw2analyzer/page_rank/index.rst b/sphinx/technical/api/bw2analyzer/page_rank/index.rst new file mode 100644 index 0000000..af6880f --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/page_rank/index.rst @@ -0,0 +1,64 @@ +:py:mod:`bw2analyzer.page_rank` +=============================== + +.. py:module:: bw2analyzer.page_rank + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.page_rank.PageRank + + + + +.. py:exception:: ConvergenceError + + Bases: :py:obj:`Exception` + + Common base class for all non-exit exceptions. + + +.. py:class:: PageRank(database) + + .. py:method:: calculate() + + + .. py:method:: page_rank(technosphere, alpha=0.85, max_iter=100, tol=1e-06) + + Return the PageRank of the nodes in the graph. + + Adapted from http://networkx.lanl.gov/svn/networkx/trunk/networkx/algorithms/link_analysis/pagerank_alg.py + + PageRank computes a ranking of the nodes in the graph G based on + the structure of the incoming links. It was originally designed as + an algorithm to rank web pages. + + The eigenvector calculation uses power iteration with a SciPy + sparse matrix representation. + + :param \* *technosphere*: The technosphere matrix. + :type \* *technosphere*: scipy sparse matrix + :param \* *alpha*: Damping parameter for PageRank, default=0.85 + :type \* *alpha*: float, optional + + :returns: + + * Dictionary of nodes (activity codes) with value as PageRank + + References + + .. [1] A. Langville and C. Meyer, + "A survey of eigenvector methods of web information retrieval." + http://citeseer.ist.psu.edu/713792.html + .. [2] Page, Lawrence; Brin, Sergey; Motwani, Rajeev and Winograd, Terry, + The PageRank citation ranking: Bringing order to the Web. 1999 + http://dbpubs.stanford.edu:8090/pub/showDoc.Fulltext?lang=en&doc=1999-66&format=pdf + + + diff --git a/sphinx/technical/api/bw2analyzer/report/index.rst b/sphinx/technical/api/bw2analyzer/report/index.rst new file mode 100644 index 0000000..91fa5d8 --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/report/index.rst @@ -0,0 +1,57 @@ +:py:mod:`bw2analyzer.report` +============================ + +.. py:module:: bw2analyzer.report + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.report.SerializedLCAReport + + + + +.. py:class:: SerializedLCAReport(activity, method, iterations=10000, cpus=None, outliers=0.025) + + A complete LCA report (i.e. LCA score, Monte Carlo uncertainty analysis, contribution analysis) that can be serialized to a defined standard. + + .. py:attribute:: version + :annotation: = 2 + + + + .. py:method:: calculate() + + Calculate LCA report data + + + .. py:method:: get_treemap(nodes, edges, lca, unroll_cutoff=0.01, simplify_limit=0.1) + + + .. py:method:: get_monte_carlo() + + Get Monte Carlo results + + + .. py:method:: get_force_directed(nodes, edges, lca) + + Get graph traversal results + + + .. py:method:: write() + + Write report data to file + + + .. py:method:: upload() + + Upload report data if allowed + + + diff --git a/sphinx/technical/api/bw2analyzer/sc_graph/index.rst b/sphinx/technical/api/bw2analyzer/sc_graph/index.rst new file mode 100644 index 0000000..fd8c315 --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/sc_graph/index.rst @@ -0,0 +1,75 @@ +:py:mod:`bw2analyzer.sc_graph` +============================== + +.. py:module:: bw2analyzer.sc_graph + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.sc_graph.GTManipulator + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.sc_graph.tupify + + + +.. py:function:: tupify(o) + + Transform edge from dict to tuples. Multiply impact by -1 because sort by min, not max + + +.. py:class:: GTManipulator + + Manipulate ``GraphTraversal`` results. + + .. py:method:: unroll_graph(nodes, edges, score, cutoff=0.005, max_links=2500) + :staticmethod: + + Unroll a ``GraphTraversal`` result, allowing the same activity to appear in the graph multiple times. + + + .. py:method:: add_metadata(nodes, lca) + :staticmethod: + + Add metadata to nodes, like name, unit, and category. + + + .. py:method:: d3_force_directed(nodes, edges, score) + :staticmethod: + + Reformat to D3 style, which is a list of nodes, and edge ids are node list indices. + + + .. py:method:: simplify(nodes, edges, score, limit=0.005) + :staticmethod: + + Simplify supply chain to include only nodes which individually contribute ``limit * score``. + + Only removes and combines edges; doesn't check to make sure amounts add up correctly. + + + .. py:method:: simplify_naive(nodes, edges, score, limit=0.0025) + :staticmethod: + + Naive simplification which simplifies removes links below an LCA score cutoff. Orphan nodes are also deleted. + + + .. py:method:: d3_treemap(nodes, edges, lca, add_biosphere=False) + :staticmethod: + + Add node data by traversing the graph; assign different metadata to leaf nodes. + + + diff --git a/sphinx/technical/api/bw2analyzer/tagged/index.rst b/sphinx/technical/api/bw2analyzer/tagged/index.rst new file mode 100644 index 0000000..2896708 --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/tagged/index.rst @@ -0,0 +1,224 @@ +:py:mod:`bw2analyzer.tagged` +============================ + +.. py:module:: bw2analyzer.tagged + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.tagged.traverse_tagged_databases + bw2analyzer.tagged.aggregate_tagged_graph + bw2analyzer.tagged.recurse_tagged_database + bw2analyzer.tagged.multi_traverse_tagged_databases + bw2analyzer.tagged.multi_aggregate_tagged_graph + bw2analyzer.tagged.multi_recurse_tagged_database + bw2analyzer.tagged.get_cum_impact + bw2analyzer.tagged.get_multi_cum_impact + + + +.. py:function:: traverse_tagged_databases(functional_unit, method, label='tag', default_tag='other', secondary_tags=[], fg_databases=None) + + Traverse a functional unit throughout its foreground database(s) or the + listed databses in fg_databses, and group impacts by tag label. + + Contribution analysis work by linking impacts to individual activities. + However, you also might want to group impacts in other ways. For example, + give individual biosphere exchanges their own grouping, or aggregate two + activities together. + + Consider this example system, where the letters are the tag labels, and the + numbers are exchange amounts. The functional unit is one unit of the tree + root. + + .. image:: images/tagged-traversal.png + :alt: Example tagged supply chain + + In this supply chain, tags are applied to activities and biosphere exchanges. + If a biosphere exchange is not tagged, it inherits the tag of its producing + activity. Similarly, links to other databases are assessed with the usual + LCA machinery, and the total LCA score is tagged according to its consuming + activity. If an activity does not have a tag, a default tag is applied. + + We can change our visualization to show the use of the default tags: + + .. image:: images/tagged-traversal-2.png + :alt: Example tagged supply chain + + And then we can manually calculate the tagged impacts. Normally we would + need to know the actual biosphere flows and their respective + characterization factors (CF), but in this example we assume that each + CF is one. Our result, group by tags, would therefore be: + + * **A**: :math:`6 + 27 = 33` + * **B**: :math:`30 + 44 = 74` + * **C**: :math:`5 + 16 + 48 = 69` + * **D**: :math:`14` + + This function will only traverse the foreground database, i.e. the + database of the functional unit activity. A functional unit can have + multiple starting nodes; in this case, all foreground databases are + traversed. + + Input arguments: + + * ``functional_unit``: A functional unit dictionary, e.g. ``{("foo", "bar"): 42}``. + * ``method``: A method name, e.g. ``("foo", "bar")`` + * ``label``: The label of the tag classifier. Default is ``"tag"`` + * ``default_tag``: The tag classifier to use if none was given. Default is ``"other"`` + * ``secondary_tags``: List of tuples in the format (secondary_label, secondary_default_tag). Default is empty list. + * ``fg_databases``: a list of foreground databases to be traversed, e.g. ['foreground', 'biomass', 'machinery'] + It's not recommended to include all databases of a project in the list to be traversed, especially not ecoinvent itself + + :returns: Aggregated tags dictionary from ``aggregate_tagged_graph``, and tagged supply chain graph from ``recurse_tagged_database``. + + +.. py:function:: aggregate_tagged_graph(graph) + + Aggregate a graph produced by ``recurse_tagged_database`` by the provided tags. + + Outputs a dictionary with keys of tags and numeric values. + + .. code-block:: python + + {'a tag': summed LCIA scores} + + + +.. py:function:: recurse_tagged_database(activity, amount, method_dict, lca, label, default_tag, secondary_tags=[], fg_databases=None, warned=False) + + Traverse a foreground database and assess activities and biosphere flows by tags. + + + Input arguments: + + * ``activity``: Activity tuple or object + * ``amount``: float + * ``method_dict``: Dictionary of biosphere flow tuples to CFs, e.g. ``{("biosphere", "foo"): 3}`` + * ``lca``: An ``LCA`` object that is already initialized, i.e. has already calculated LCI and LCIA with same method as in ``method_dict`` + * ``label``: string + * ``default_tag``: string + * ``secondary_tags``: List of tuples in the format (secondary_label, secondary_default_tag). Default is empty list. + + * ``fg_databases``: a list of foreground databases to be traversed, e.g. ['foreground', 'biomass', 'machinery'] + It's not recommended to include all databases of a project in the list to be traversed, especially not ecoinvent itself + + Returns: + + .. code-block:: python + + { + 'activity': activity object, + 'amount': float, + 'tag': string, + 'secondary_tags': [list of strings], + 'impact': float (impact of inputs from outside foreground database), + 'biosphere': [{ + 'amount': float, + 'impact': float, + 'tag': string, + 'secondary_tags': [list of strings] + }], + 'technosphere': [this data structure] + } + + + +.. py:function:: multi_traverse_tagged_databases(functional_unit, methods, label='tag', default_tag='other', secondary_tags=[]) + + Traverse a functional unit throughout its foreground database(s), and + group impacts (for multiple methods) by tag label. + + Input arguments: + * ``functional_unit``: A functional unit dictionary, e.g. ``{("foo", "bar"): 42}``. + * ``methods``: A list of method names, e.g. ``[("foo", "bar"), ("baz", "qux"), ...]`` + * ``label``: The label of the tag classifier. Default is ``"tag"`` + * ``default_tag``: The tag classifier to use if none was given. Default is ``"other"`` + * ``secondary_tags``: List of tuples in the format (secondary_label, secondary_default_tag). Default is empty list. + + :returns: Aggregated tags dictionary from ``aggregate_tagged_graph``, and tagged supply chain graph from ``recurse_tagged_database``. + + +.. py:function:: multi_aggregate_tagged_graph(graph) + + Aggregate a graph produced by ``multi_recurse_tagged_database`` by the provided tags. + + Outputs a dictionary with keys of tags and numeric values. + + Note: this only aggregates on the primary tag, secondary tags are not aggregated + + .. code-block:: python + + {'a tag': [list of summed LCIA scores with one sum per method]} + + + +.. py:function:: multi_recurse_tagged_database(activity, amount, methods, method_dicts, lca, label, default_tag, secondary_tags=[]) + + Traverse a foreground database and assess activities and biosphere flows by tags using multiple methods. + + Input arguments: + + * ``activity``: Activity tuple or object + * ``amount``: float + * ``methods``: list of LCA methods (tuples) + * ``method_dicts``: list of dictionaries of biosphere flow tuples to CFs, e.g. ``{("biosphere", "foo"): 3}`` corresponding to methods in ``methods`` + * ``lca``: An ``LCA`` object that is already initialized, i.e. has already calculated LCI + * ``label``: string + * ``default_tag``: string + * ``secondary_tags``: list of tuples in the format (secondary_label, secondary_default_tag). Default is empty list. + + Returns: + + .. code-block:: python + + { + 'activity': activity object, + 'amount': float, + 'tag': string, + 'secondary_tags': [list of strings], + 'impact': [list of floats (impact of inputs from outside foreground database) with one element per method], + 'biosphere': [{ + 'amount': float, + 'impact': [list of floats with one element per method], + 'tag': string, + 'secondary_tags': [list of strings] + }], + 'technosphere': [this data structure] + } + + + +.. py:function:: get_cum_impact(graph, max_levels=100) + + Add cumulative impact ``cum_impact`` to each ``technosphere`` level of a tagged graph. + + This function recurses until all levels in the graph have been checked, or the ``max_levels`` cutoff is reached + + Input arguments: + * ``graph``: A tagged supply chain graph from ``recurse_tagged_database``. + * ``max_levels``: maximum number of graph levels to check before giving up. Default is 100. + + :returns: Tagged supply chain graph with additional cumulative impact ``cum_impact`` key at each ``technosphere`` level. + + +.. py:function:: get_multi_cum_impact(graph, max_levels=100) + + Add cumulative impact ``cum_impact`` to each ``technosphere`` level of a multi method tagged graph. + + This function recurses until all levels in the graph have been checked, or the ``max_levels`` cutoff is reached + + Input arguments: + * ``graph``: A tagged supply chain graph from ``multi_recurse_tagged_database``. + * ``max_levels``: maximum number of graph levels to check before giving up. Default is 100. + + :returns: Tagged supply chain graph with additional cumulative impact ``cum_impact`` key at each ``technosphere`` level. + + diff --git a/sphinx/technical/api/bw2analyzer/utils/index.rst b/sphinx/technical/api/bw2analyzer/utils/index.rst new file mode 100644 index 0000000..3136580 --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/utils/index.rst @@ -0,0 +1,117 @@ +:py:mod:`bw2analyzer.utils` +=========================== + +.. py:module:: bw2analyzer.utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2analyzer.utils.contribution_for_all_datasets_one_method + bw2analyzer.utils.print_recursive_calculation + bw2analyzer.utils.print_recursive_supply_chain + bw2analyzer.utils.infinite_alphabet + bw2analyzer.utils.recursive_calculation_to_object + + + +.. py:function:: contribution_for_all_datasets_one_method(database, method, progress=True) + + Calculate contribution analysis (for technosphere processes) for all inventory datasets in one database for one LCIA method. + + :param \*database*: Name of database + :type \*database*: str + :param \*method*: Method tuple + :type \*method*: tuple + + :returns: NumPy array of relative contributions. Each column sums to one. + Lookup dictionary, dataset keys to row/column indices + + +.. py:function:: print_recursive_calculation(activity, lcia_method, amount=1, max_level=3, cutoff=0.01, file_obj=None, tab_character=' ', use_matrix_values=False, _lca_obj=None, _total_score=None, __level=0, __first=True) + + Traverse a supply chain graph, and calculate the LCA scores of each component. Prints the result with the format: + + {tab_character * level }{fraction of total score} ({absolute LCA score for this input} | {amount of input}) {input activity} + + :param activity: ``Activity``. The starting point of the supply chain graph. + :param lcia_method: tuple. LCIA method to use when traversing supply chain graph. + :param amount: int. Amount of ``activity`` to assess. + :param max_level: int. Maximum depth to traverse. + :param cutoff: float. Fraction of total score to use as cutoff when deciding whether to traverse deeper. + :param file_obj: File-like object (supports ``.write``), optional. Output will be written to this object if provided. + :param tab_character: str. Character to use to indicate indentation. + :param use_matrix_values: bool. Take exchange values from the matrix instead of the exchange instance ``amount``. Useful for Monte Carlo, but can be incorrect if there is more than one exchange from the same pair of nodes. + + Normally internal args: + _lca_obj: ``LCA``. Can give an instance of the LCA class (e.g. when doing regionalized or Monte Carlo LCA) + _total_score: float. Needed if specifying ``_lca_obj``. + + Internal args (used during recursion, do not touch); + __level: int. + __first: bool. + + :returns: Nothing. Prints to ``sys.stdout`` or ``file_obj`` + + +.. py:function:: print_recursive_supply_chain(activity, amount=1, max_level=2, cutoff=0, file_obj=None, tab_character=' ', __level=0) + + Traverse a supply chain graph, and prints the inputs of each component. + + This function is only for exploration; use ``bw2calc.GraphTraversal`` for a better performing function. + + The results displayed here can also be incorrect if + + :param activity: ``Activity``. The starting point of the supply chain graph. + :param amount: int. Supply chain inputs will be scaled to this value. + :param max_level: int. Max depth to search for. + :param cutoff: float. Inputs with amounts less than ``amount * cutoff`` will not be printed or traversed further. + :param file_obj: File-like object (supports ``.write``), optional. Output will be written to this object if provided. + :param tab_character: str. Character to use to indicate indentation. + :param __level: int. Current level of the calculation. Only used internally, do not touch. + + :returns: Nothing. Prints to ``stdout`` or ``file_obj`` + + +.. py:function:: infinite_alphabet() + + Return generator with values a-z, then aa-az, ba-bz, then aaa-aaz, aba-abz, etc. + + +.. py:function:: recursive_calculation_to_object(activity, lcia_method, amount=1, max_level=3, cutoff=0.01, as_dataframe=False, root_label='root', use_matrix_values=False, _lca_obj=None, _total_score=None, __result_list=None, __level=0, __label='', __parent=None) + + Traverse a supply chain graph, and calculate the LCA scores of each component. Adds a dictionary to ``result_list`` of the form: + + { + 'label': Label of this branch. Starts with nothing, then A, AA, AB, AAA, AAB, etc. + 'score': Absolute score of this activity + 'fraction': Fraction of total score of this activity + 'amount': Input amount of the reference product of this activity + 'name': Name of this activity + 'key': Activity key + 'root_label': Starting label of root element for recursion. + } + + :param activity: ``Activity``. The starting point of the supply chain graph. + :param lcia_method: tuple. LCIA method to use when traversing supply chain graph. + :param amount: int. Amount of ``activity`` to assess. + :param max_level: int. Maximum depth to traverse. + :param cutoff: float. Fraction of total score to use as cutoff when deciding whether to traverse deeper. + :param as_dataframe: Return results as a list (default) or a pandas ``DataFrame`` + :param use_matrix_values: bool. Take exchange values from the matrix instead of the exchange instance ``amount``. Useful for Monte Carlo, but can be incorrect if there is more than one exchange from the same pair of nodes. + + Internal args (used during recursion, do not touch): + __result_list: list. + __level: int. + __label: str. + __parent: str. + + :returns: List of dicts + + diff --git a/sphinx/technical/api/bw2analyzer/version/index.rst b/sphinx/technical/api/bw2analyzer/version/index.rst new file mode 100644 index 0000000..f8717be --- /dev/null +++ b/sphinx/technical/api/bw2analyzer/version/index.rst @@ -0,0 +1,14 @@ +:py:mod:`bw2analyzer.version` +============================= + +.. py:module:: bw2analyzer.version + + +Module Contents +--------------- + +.. py:data:: version + :annotation: = [0, 11, 4] + + + diff --git a/sphinx/technical/api/bw2calc/dense_lca/index.rst b/sphinx/technical/api/bw2calc/dense_lca/index.rst new file mode 100644 index 0000000..f56a2d5 --- /dev/null +++ b/sphinx/technical/api/bw2calc/dense_lca/index.rst @@ -0,0 +1,43 @@ +:py:mod:`bw2calc.dense_lca` +=========================== + +.. py:module:: bw2calc.dense_lca + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.dense_lca.DenseLCA + + + + +.. py:class:: DenseLCA(demand: dict, method: Optional[tuple] = None, weighting: Optional[str] = None, normalization: Optional[str] = None, data_objs: Optional[Iterable[Union[pathlib.Path, fs.base.FS, bw_processing.DatapackageBase]]] = None, remapping_dicts: Optional[Iterable[dict]] = None, log_config: Optional[dict] = None, seed_override: Optional[int] = None, use_arrays: bool = False, use_distributions: bool = False) + + Bases: :py:obj:`bw2calc.lca.LCA` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:method:: solve_linear_system() + + Master solution function for linear system :math:`Ax=B`. + + To most numerical analysts, matrix inversion is a sin. + + -- Nicolas Higham, Accuracy and Stability of Numerical Algorithms, Society for Industrial and Applied Mathematics, Philadelphia, PA, USA, 2002, p. 260. + + We use `UMFpack `_, which is a very fast solver for sparse matrices. + + If the technosphere matrix has already been factorized, then the decomposed technosphere (``self.solver``) is reused. Otherwise the calculation is redone completely. + + + + diff --git a/sphinx/technical/api/bw2calc/dictionary_manager/index.rst b/sphinx/technical/api/bw2calc/dictionary_manager/index.rst new file mode 100644 index 0000000..d22c4c7 --- /dev/null +++ b/sphinx/technical/api/bw2calc/dictionary_manager/index.rst @@ -0,0 +1,137 @@ +:py:mod:`bw2calc.dictionary_manager` +==================================== + +.. py:module:: bw2calc.dictionary_manager + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.dictionary_manager.ReversibleRemappableDictionary + bw2calc.dictionary_manager.DictionaryManager + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2calc.dictionary_manager.resolved + + + +.. py:function:: resolved(f) + + Decorator that resolves a ``partial`` function before it can be used + + +.. py:class:: ReversibleRemappableDictionary(obj) + + Bases: :py:obj:`collections.abc.Mapping` + + A dictionary that can be easily remapped or reversed. + + Perhaps overkill, but at the time it was easier than creating many dictionaries on the LCA object itself. + + Example usage:: + + In [1]: from bw2calc.dictionary_manager import ReversibleRemappableDictionary + + In [2]: d = ReversibleRemappableDictionary({1: 2}) + + In [3]: d.reverse + Out[3]: {2: 1} + + In [4]: d.remap({1: "foo"}) + + In [5]: d['foo'] + Out[5]: 2 + + In [6]: d.original + Out[6]: {1: 2} + + In [7]: d.reverse + Out[7]: {2: 'foo'} + + In [8]: d.unmap() + + In [9]: d[1] + Out[9]: 2 + + + .. py:property:: reversed + + + .. py:property:: original + + + .. py:method:: remap(mapping) + + Transform the keys based on the mapping dict ``mapping``. + + ``mapping`` doesn't need to cover every key in the original. + + Example usage: + + {1: 2}.remap({1: "foo"} >> {"foo": 2} + + + + .. py:method:: unmap() + + Restore dict to original state. + + + .. py:method:: __getitem__(key) + + + .. py:method:: __iter__() + + + .. py:method:: __len__() + + + .. py:method:: __str__() + + Return str(self). + + + +.. py:class:: DictionaryManager + + Class that handles dictionaries which can be remapped or reverse. + + Usage:: + + dm = DictionaryManager() + dm.foo = {1: 2} + dm.foo[1] + >> 2 + + + .. py:method:: __getattr__(attr) + + + .. py:method:: __setattr__(attr, value) + + Implement setattr(self, name, value). + + + .. py:method:: __len__() + + + .. py:method:: __iter__() + + + .. py:method:: __str__() + + Return str(self). + + + diff --git a/sphinx/technical/api/bw2calc/errors/index.rst b/sphinx/technical/api/bw2calc/errors/index.rst new file mode 100644 index 0000000..09923ed --- /dev/null +++ b/sphinx/technical/api/bw2calc/errors/index.rst @@ -0,0 +1,86 @@ +:py:mod:`bw2calc.errors` +======================== + +.. py:module:: bw2calc.errors + + +Module Contents +--------------- + +.. py:exception:: BW2CalcError + + Bases: :py:obj:`Exception` + + Base class for bw2calc errors + + +.. py:exception:: OutsideTechnosphere + + Bases: :py:obj:`BW2CalcError` + + The given demand array activity is not in the technosphere matrix + + +.. py:exception:: EfficiencyWarning + + Bases: :py:obj:`RuntimeWarning` + + Least squares is much less efficient than direct computation for square, full-rank matrices + + +.. py:exception:: NoSolutionFound + + Bases: :py:obj:`UserWarning` + + No solution to set of linear equations found within given constraints + + +.. py:exception:: NonsquareTechnosphere + + Bases: :py:obj:`BW2CalcError` + + The given data do not form a square technosphere matrix + + +.. py:exception:: MalformedFunctionalUnit + + Bases: :py:obj:`BW2CalcError` + + The given functional unit cannot be understood + + +.. py:exception:: EmptyBiosphere + + Bases: :py:obj:`BW2CalcError` + + Can't do impact assessment with no biosphere flows + + +.. py:exception:: AllArraysEmpty + + Bases: :py:obj:`BW2CalcError` + + Can't load the numpy arrays if all of them are empty + + +.. py:exception:: NoArrays + + Bases: :py:obj:`BW2CalcError` + + No arrays for given matrix + + +.. py:exception:: InconsistentGlobalIndex + + Bases: :py:obj:`BW2CalcError` + + LCIA matrices are diagonal, and use the ``col`` field for regionalization. If multiple LCIA datapackages are present, they must use the same value for ``GLO``, the global location, in order for filtering for site-generic LCIA to work correctly. + + +.. py:exception:: MultipleValues + + Bases: :py:obj:`BW2CalcError` + + Multiple values are present, but only one value is expected + + diff --git a/sphinx/technical/api/bw2calc/graph_traversal/index.rst b/sphinx/technical/api/bw2calc/graph_traversal/index.rst new file mode 100644 index 0000000..e673640 --- /dev/null +++ b/sphinx/technical/api/bw2calc/graph_traversal/index.rst @@ -0,0 +1,184 @@ +:py:mod:`bw2calc.graph_traversal` +================================= + +.. py:module:: bw2calc.graph_traversal + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.graph_traversal.CachingSolver + bw2calc.graph_traversal.AssumedDiagonalGraphTraversal + bw2calc.graph_traversal.GraphTraversal + bw2calc.graph_traversal.MultifunctionalGraphTraversal + + + + +.. py:class:: CachingSolver(lca) + + .. py:method:: __call__(product_index, amount=1) + + + +.. py:class:: AssumedDiagonalGraphTraversal + + Traverse a supply chain, following paths of greatest impact. + + This implementation uses a queue of datasets to assess. As the supply chain is traversed, datasets inputs are added to a list sorted by LCA score. Each activity in the sorted list is assessed, and added to the supply chain graph, as long as its impact is above a certain threshold, and the maximum number of calculations has not been exceeded. + + Because the next dataset assessed is chosen by its impact, not its position in the graph, this is neither a breadth-first nor a depth-first search, but rather "importance-first". + + This class is written in a functional style - no variables are stored in *self*, only methods. + + Should be used by calling the ``calculate`` method. + + .. warning:: Graph traversal with multioutput processes only works when other inputs are substituted (see `Multioutput processes in LCA `__ for a description of multiputput process math in LCA). + + + .. py:method:: calculate(lca, cutoff=0.005, max_calc=100000.0, skip_coproducts=False) + + Traverse the supply chain graph. + + :param \* *lca*: An instance of ``bw2calc.lca.LCA``. + :type \* *lca*: dict + :param \* *cutoff*: Cutoff criteria to stop LCA calculations. Relative score of total, i.e. 0.005 will cutoff if a dataset has a score less than 0.5 percent of the total. + :type \* *cutoff*: float, default=0.005 + :param \* *max_calc*: Maximum number of LCA calculations to perform. + :type \* *max_calc*: int, default=10000 + + :returns: Dictionary of nodes, edges, and number of LCA calculations. + + + .. py:method:: initialize_heap(lca, supply, characterized_biosphere) + + Create a `priority queue `_ or ``heap`` to store inventory datasets, sorted by LCA score. + + Populates the heap with each activity in ``demand``. Initial nodes are the *functional unit*, i.e. the complete demand, and each activity in the *functional unit*. Initial edges are inputs from each activity into the *functional unit*. + + The *functional unit* is an abstract dataset (as it doesn't exist in the matrix), and is assigned the index ``-1``. + + + + .. py:method:: cumulative_score(index, supply, characterized_biosphere, lca) + + Compute cumulative LCA score for a given activity + + + .. py:method:: unit_score(index, supply, characterized_biosphere) + + Compute the LCA impact caused by the direct emissions and resource consumption of a given activity + + + .. py:method:: traverse(heap, nodes, edges, counter, max_calc, cutoff, total_score, supply, characterized_biosphere, lca, skip_coproducts) + + Build a directed graph by traversing the supply chain. + + Node ids are actually technosphere row/col indices, which makes lookup easier. + + :returns: (nodes, edges, number of calculations) + + + +.. py:class:: GraphTraversal(*args, **kwargs) + + +.. py:class:: MultifunctionalGraphTraversal + + Traverse a supply chain, following paths of greatest impact. Can handle the differentiation between products and activities, and makes no assumptions about multifunctionality, substitution, or the special status of numbers on the diagonal. + + As soon as non-diagonal values are allowed, we lose any concept of a reference product. This means that we can trace the edges for an activity (both inputs and outputs, though in the matrix there is no functional difference), but we can't for a product, as we can't use the graph structure to determine *which activity* produced the product. There could be more than one, or even zero, depending on how your mental model of substitution works. Our algorithm is therefore: + + 1. Start with products (initially the products in the functional unit) + 2. For each product, determine which activities produced it by solving the linear system + 3a. For each of these activities, add on to our list of products to consider by looking at the edges for that activity, and excluding the edge which led to our original product + 3b. If we have already examined this activity, don't visit it again + 4. Keep iterating over the list of products until we run out of activities or hit our calculation limit + + The ``.calculate()`` function therefore returns the following: + + .. code-block:: python + + { + 'counter': int, # Number of LCA calculations done, + 'products': { + id: { # id is either the database integer id (if `translate_indices` is True) or the matrix row index + 'amount': float # Total amount of this product produced to satisfy the functional unit + 'supply_chain_score': float # The total impact of producing this product + } + }, + 'activities': { + id: { # id is either the database integer id (if `translate_indices` is True) or the matrix column index + 'amount': float # Total amount of this activity produced to satisfy the entire functional unit + 'direct_score': float # The impact of the direct emissions associated to this activity and its amount + }, + 'edges': [{ + 'target': int, # product id if type is activity else activity id + 'source': int, # activity id if type is product else product id + 'type': str, # 'product' or 'activity' + 'amount': float, # Total amount of the flow + 'exc_amount': float, # Value given in the technosphere matrix + 'supply_chain_score': float, # Total impact from the production of this product. Only for type 'product' + 'direct_score': float, # Impact from direct emissions of this activity. Only for type 'activity' + }] + } + + As in AssumedDiagonalGraphTraversal, we use a priority queue to examine products in order of their total impact. + + This class is written in a functional style, with only class methods. + + + .. py:method:: calculate(lca: bw2calc.LCA, cutoff: float = 0.005, max_calc: int = 100000.0, translate_indices: bool = True) + :classmethod: + + Traverse the supply chain graph. + + :param \* *lca*: An instance of ``bw2calc.lca.LCA``. + :type \* *lca*: dict + :param \* *cutoff*: Cutoff criteria to stop LCA calculations. Relative score of total, i.e. 0.005 will cutoff if a dataset has a score less than 0.5 percent of the total. + :type \* *cutoff*: float, default=0.005 + :param \* *max_calc*: Maximum number of LCA calculations to perform. + :type \* *max_calc*: int, default=10000 + + :returns: Dictionary of nodes, edges, and number of LCA calculations. + + + .. py:method:: clean_small_values(data, kind=dict, cutoff=5e-16) + :classmethod: + + + .. py:method:: consolidate_edges(edges) + :classmethod: + + + .. py:method:: initialize_heap(lca: bw2calc.LCA, solver: CachingSolver, translate_indices: bool, counter: int) + :classmethod: + + Create a `priority queue `_ or ``heap`` to store inventory datasets, sorted by LCA score. + + Populates the heap with each activity in ``demand``. Initial nodes are the *functional unit*, i.e. the complete demand, and each activity in the *functional unit*. Initial edges are inputs from each activity into the *functional unit*. + + The *functional unit* is an abstract dataset (as it doesn't exist in the matrix), and is assigned the index ``-1``. + + + + .. py:method:: traverse(heap: list, solver: CachingSolver, activities: dict, products: dict, edges: list, max_calc: int, cutoff: float, total_score: float, lca: bw2calc.LCA, translate_indices: bool, counter: int) + :classmethod: + + Build a directed graph by traversing the supply chain. + + Node ids are actually technosphere row/col indices, which makes lookup easier. + + :returns: (nodes, edges, number of calculations) + + + .. py:method:: visit_activity(heap: list, activity_index: int, counter: int, activities: dict, products: dict, edges: list, lca: bw2calc.LCA, characterized_biosphere: scipy.sparse.csr_matrix, solver: CachingSolver, cutoff_score: float, origin_product_index: int, translate_indices: bool) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2calc/index.rst b/sphinx/technical/api/bw2calc/index.rst new file mode 100644 index 0000000..8c73a61 --- /dev/null +++ b/sphinx/technical/api/bw2calc/index.rst @@ -0,0 +1,224 @@ +:py:mod:`bw2calc` +================= + +.. py:module:: bw2calc + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + dense_lca/index.rst + dictionary_manager/index.rst + errors/index.rst + graph_traversal/index.rst + lca/index.rst + least_squares/index.rst + log_utils/index.rst + monte_carlo/index.rst + multi_lca/index.rst + single_value_diagonal_matrix/index.rst + utils/index.rst + version/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.DenseLCA + bw2calc.GraphTraversal + bw2calc.MultifunctionalGraphTraversal + bw2calc.LeastSquaresLCA + bw2calc.MultiLCA + + + + +.. py:class:: DenseLCA(demand: dict, method: Optional[tuple] = None, weighting: Optional[str] = None, normalization: Optional[str] = None, data_objs: Optional[Iterable[Union[pathlib.Path, fs.base.FS, bw_processing.DatapackageBase]]] = None, remapping_dicts: Optional[Iterable[dict]] = None, log_config: Optional[dict] = None, seed_override: Optional[int] = None, use_arrays: bool = False, use_distributions: bool = False) + + Bases: :py:obj:`bw2calc.lca.LCA` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:method:: solve_linear_system() + + Master solution function for linear system :math:`Ax=B`. + + To most numerical analysts, matrix inversion is a sin. + + -- Nicolas Higham, Accuracy and Stability of Numerical Algorithms, Society for Industrial and Applied Mathematics, Philadelphia, PA, USA, 2002, p. 260. + + We use `UMFpack `_, which is a very fast solver for sparse matrices. + + If the technosphere matrix has already been factorized, then the decomposed technosphere (``self.solver``) is reused. Otherwise the calculation is redone completely. + + + + +.. py:class:: GraphTraversal(*args, **kwargs) + + +.. py:class:: MultifunctionalGraphTraversal + + Traverse a supply chain, following paths of greatest impact. Can handle the differentiation between products and activities, and makes no assumptions about multifunctionality, substitution, or the special status of numbers on the diagonal. + + As soon as non-diagonal values are allowed, we lose any concept of a reference product. This means that we can trace the edges for an activity (both inputs and outputs, though in the matrix there is no functional difference), but we can't for a product, as we can't use the graph structure to determine *which activity* produced the product. There could be more than one, or even zero, depending on how your mental model of substitution works. Our algorithm is therefore: + + 1. Start with products (initially the products in the functional unit) + 2. For each product, determine which activities produced it by solving the linear system + 3a. For each of these activities, add on to our list of products to consider by looking at the edges for that activity, and excluding the edge which led to our original product + 3b. If we have already examined this activity, don't visit it again + 4. Keep iterating over the list of products until we run out of activities or hit our calculation limit + + The ``.calculate()`` function therefore returns the following: + + .. code-block:: python + + { + 'counter': int, # Number of LCA calculations done, + 'products': { + id: { # id is either the database integer id (if `translate_indices` is True) or the matrix row index + 'amount': float # Total amount of this product produced to satisfy the functional unit + 'supply_chain_score': float # The total impact of producing this product + } + }, + 'activities': { + id: { # id is either the database integer id (if `translate_indices` is True) or the matrix column index + 'amount': float # Total amount of this activity produced to satisfy the entire functional unit + 'direct_score': float # The impact of the direct emissions associated to this activity and its amount + }, + 'edges': [{ + 'target': int, # product id if type is activity else activity id + 'source': int, # activity id if type is product else product id + 'type': str, # 'product' or 'activity' + 'amount': float, # Total amount of the flow + 'exc_amount': float, # Value given in the technosphere matrix + 'supply_chain_score': float, # Total impact from the production of this product. Only for type 'product' + 'direct_score': float, # Impact from direct emissions of this activity. Only for type 'activity' + }] + } + + As in AssumedDiagonalGraphTraversal, we use a priority queue to examine products in order of their total impact. + + This class is written in a functional style, with only class methods. + + + .. py:method:: calculate(lca: bw2calc.LCA, cutoff: float = 0.005, max_calc: int = 100000.0, translate_indices: bool = True) + :classmethod: + + Traverse the supply chain graph. + + :param \* *lca*: An instance of ``bw2calc.lca.LCA``. + :type \* *lca*: dict + :param \* *cutoff*: Cutoff criteria to stop LCA calculations. Relative score of total, i.e. 0.005 will cutoff if a dataset has a score less than 0.5 percent of the total. + :type \* *cutoff*: float, default=0.005 + :param \* *max_calc*: Maximum number of LCA calculations to perform. + :type \* *max_calc*: int, default=10000 + + :returns: Dictionary of nodes, edges, and number of LCA calculations. + + + .. py:method:: clean_small_values(data, kind=dict, cutoff=5e-16) + :classmethod: + + + .. py:method:: consolidate_edges(edges) + :classmethod: + + + .. py:method:: initialize_heap(lca: bw2calc.LCA, solver: CachingSolver, translate_indices: bool, counter: int) + :classmethod: + + Create a `priority queue `_ or ``heap`` to store inventory datasets, sorted by LCA score. + + Populates the heap with each activity in ``demand``. Initial nodes are the *functional unit*, i.e. the complete demand, and each activity in the *functional unit*. Initial edges are inputs from each activity into the *functional unit*. + + The *functional unit* is an abstract dataset (as it doesn't exist in the matrix), and is assigned the index ``-1``. + + + + .. py:method:: traverse(heap: list, solver: CachingSolver, activities: dict, products: dict, edges: list, max_calc: int, cutoff: float, total_score: float, lca: bw2calc.LCA, translate_indices: bool, counter: int) + :classmethod: + + Build a directed graph by traversing the supply chain. + + Node ids are actually technosphere row/col indices, which makes lookup easier. + + :returns: (nodes, edges, number of calculations) + + + .. py:method:: visit_activity(heap: list, activity_index: int, counter: int, activities: dict, products: dict, edges: list, lca: bw2calc.LCA, characterized_biosphere: scipy.sparse.csr_matrix, solver: CachingSolver, cutoff_score: float, origin_product_index: int, translate_indices: bool) + :classmethod: + + + +.. py:class:: LeastSquaresLCA(demand: dict, method: Optional[tuple] = None, weighting: Optional[str] = None, normalization: Optional[str] = None, data_objs: Optional[Iterable[Union[pathlib.Path, fs.base.FS, bw_processing.DatapackageBase]]] = None, remapping_dicts: Optional[Iterable[dict]] = None, log_config: Optional[dict] = None, seed_override: Optional[int] = None, use_arrays: bool = False, use_distributions: bool = False) + + Bases: :py:obj:`bw2calc.lca.LCA` + + Solve overdetermined technosphere matrix with more products than activities using least-squares approximation. + + See also: + + * `Multioutput processes in LCA `_ + * `LSMR in SciPy `_ + * `Another least-squares algorithm in SciPy `_ + + + .. py:method:: load_lci_data() -> None + + Load inventory data and create technosphere and biosphere matrices. + + + .. py:method:: solve_linear_system(solver=lsmr) -> numpy.ndarray + + Master solution function for linear system :math:`Ax=B`. + + To most numerical analysts, matrix inversion is a sin. + + -- Nicolas Higham, Accuracy and Stability of Numerical Algorithms, Society for Industrial and Applied Mathematics, Philadelphia, PA, USA, 2002, p. 260. + + We use `UMFpack `_, which is a very fast solver for sparse matrices. + + If the technosphere matrix has already been factorized, then the decomposed technosphere (``self.solver``) is reused. Otherwise the calculation is redone completely. + + + + .. py:method:: decompose_technosphere() -> None + :abstractmethod: + + Factorize the technosphere matrix into lower and upper triangular matrices, :math:`A=LU`. Does not solve the linear system :math:`Ax=B`. + + Doesn't return anything, but creates ``self.solver``. + + .. warning:: Incorrect results could occur if a technosphere matrix was factorized, and then a new technosphere matrix was constructed, as ``self.solver`` would still be the factorized older technosphere matrix. You are responsible for deleting ``self.solver`` when doing these types of advanced calculations. + + + + +.. py:class:: MultiLCA(cs_name, log_config=None) + + Wrapper class for performing LCA calculations with many functional units and LCIA methods. + + Needs to be passed a ``calculation_setup`` name. + + This class does not subclass the `LCA` class, and performs all calculations upon instantiation. + + Initialization creates `self.results`, which is a NumPy array of LCA scores, with rows of functional units and columns of LCIA methods. Ordering is the same as in the `calculation_setup`. + + + .. py:property:: all + + Get all possible databases by merging all functional units + + diff --git a/sphinx/technical/api/bw2calc/lca/index.rst b/sphinx/technical/api/bw2calc/lca/index.rst new file mode 100644 index 0000000..1c3c1bd --- /dev/null +++ b/sphinx/technical/api/bw2calc/lca/index.rst @@ -0,0 +1,352 @@ +:py:mod:`bw2calc.lca` +===================== + +.. py:module:: bw2calc.lca + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.lca.LCA + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2calc.lca.get_node + bw2calc.lca.logger + + +.. py:data:: get_node + + + + +.. py:data:: logger + + + + +.. py:class:: LCA(demand: dict, method: Optional[tuple] = None, weighting: Optional[str] = None, normalization: Optional[str] = None, data_objs: Optional[Iterable[Union[pathlib.Path, fs.base.FS, bw_processing.DatapackageBase]]] = None, remapping_dicts: Optional[Iterable[dict]] = None, log_config: Optional[dict] = None, seed_override: Optional[int] = None, use_arrays: bool = False, use_distributions: bool = False) + + Bases: :py:obj:`collections.abc.Iterator` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:property:: score + :type: float + + The LCIA score as a ``float``. + + Note that this is a `property `_, so it is ``foo.lca``, not ``foo.score()`` + + .. py:property:: activity_dict + + + .. py:property:: product_dict + + + .. py:property:: biosphere_dict + + + .. py:attribute:: matrix_labels + :annotation: = ['technosphere_mm', 'biosphere_mm', 'characterization_mm', 'normalization_mm', 'weighting_mm'] + + + + .. py:method:: keep_first_iteration() + + Set a flag to use the current values as first element when iterating. + + When creating the class instance, we already use the first index. This method allows us to use the values for the first index. + + Note that the methods ``.lci_calculation()`` and ``.lcia_calculation()`` will be called on the current values, even if these calculations have already been done. + + + .. py:method:: __next__() -> None + + Return the next item from the iterator. When exhausted, raise StopIteration + + + .. py:method:: ensure_bw2data_available() + + Raises ``ImportError`` is bw2data not available or version < 4. + + + .. py:method:: build_demand_array(demand: Optional[dict] = None) -> None + + Turn the demand dictionary into a *NumPy* array of correct size. + + :param \* *demand*: Demand dictionary. Optional, defaults to ``self.demand``. + :type \* *demand*: dict, optional + + :returns: A 1-dimensional NumPy array + + + .. py:method:: load_lci_data(nonsquare_ok=False) -> None + + Load inventory data and create technosphere and biosphere matrices. + + + .. py:method:: remap_inventory_dicts() -> None + + Remap ``self.dicts.activity|product|biosphere`` and ``self.demand`` from database integer IDs to keys (``(database name, code)``). + + Uses remapping dictionaries in ``self.remapping_dicts``. + + + .. py:method:: load_lcia_data(data_objs: Optional[Iterable[Union[fs.base.FS, bw_processing.DatapackageBase]]] = None) -> None + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: load_normalization_data(data_objs: Optional[Iterable[Union[fs.base.FS, bw_processing.DatapackageBase]]] = None) -> None + + Load normalization data. + + + .. py:method:: load_weighting_data(data_objs: Optional[Iterable[Union[fs.base.FS, bw_processing.DatapackageBase]]] = None) -> None + + Load normalization data. + + + .. py:method:: decompose_technosphere() -> None + + Factorize the technosphere matrix into lower and upper triangular matrices, :math:`A=LU`. Does not solve the linear system :math:`Ax=B`. + + Doesn't return anything, but creates ``self.solver``. + + .. warning:: Incorrect results could occur if a technosphere matrix was factorized, and then a new technosphere matrix was constructed, as ``self.solver`` would still be the factorized older technosphere matrix. You are responsible for deleting ``self.solver`` when doing these types of advanced calculations. + + + + .. py:method:: solve_linear_system() -> None + + Master solution function for linear system :math:`Ax=B`. + + To most numerical analysts, matrix inversion is a sin. + + -- Nicolas Higham, Accuracy and Stability of Numerical Algorithms, Society for Industrial and Applied Mathematics, Philadelphia, PA, USA, 2002, p. 260. + + We use `UMFpack `_, which is a very fast solver for sparse matrices. + + If the technosphere matrix has already been factorized, then the decomposed technosphere (``self.solver``) is reused. Otherwise the calculation is redone completely. + + + + .. py:method:: lci(demand: Optional[dict] = None, factorize: bool = False) -> None + + Calculate a life cycle inventory. + + #. Load LCI data, and construct the technosphere and biosphere matrices. + #. Build the demand array + #. Solve the linear system to get the supply array and life cycle inventory. + + :param \* *factorize*: Factorize the technosphere matrix. Makes additional calculations with the same technosphere matrix much faster. Default is ``False``; not useful is only doing one LCI calculation. + :type \* *factorize*: bool, optional + :param \* *builder*: Default is ``bw2calc.matrices.MatrixBuilder``, which is fine for most cases. Custom matrix builders can be used to manipulate data in creative ways before building the matrices. + :type \* *builder*: ``MatrixBuilder`` object, optional + + Doesn't return anything, but creates ``self.supply_array`` and ``self.inventory``. + + + + .. py:method:: lci_calculation() -> None + + The actual LCI calculation. + + Separated from ``lci`` to be reusable in cases where the matrices are already built, e.g. ``redo_lci`` and Monte Carlo classes. + + + + .. py:method:: lcia(demand: Optional[dict] = None) -> None + + Calculate the life cycle impact assessment. + + #. Load and construct the characterization matrix + #. Multiply the characterization matrix by the life cycle inventory + + Doesn't return anything, but creates ``self.characterized_inventory``. + + + + .. py:method:: lcia_calculation() -> None + + The actual LCIA calculation. + + Separated from ``lcia`` to be reusable in cases where the matrices are already built, e.g. ``redo_lcia`` and Monte Carlo classes. + + + + .. py:method:: normalize() -> None + + Multiply characterized inventory by flow-specific normalization factors. + + + .. py:method:: normalization_calculation() -> None + + The actual normalization calculation. + + Creates ``self.normalized_inventory``. + + + .. py:method:: weighting() -> None + + Backwards compatibility. Switching to verb form consistent with ``.normalize``. + + + .. py:method:: weight() -> None + + Multiply characterized inventory by weighting value. + + Can be done with or without normalization. + + + .. py:method:: weighting_calculation() -> None + + The actual weighting calculation. + + Multiples weighting value by normalized inventory, if available, otherwise by characterized inventory. + + Creates ``self.weighted_inventory``. + + + .. py:method:: _switch(obj: Union[tuple, Iterable[Union[fs.base.FS, bw_processing.DatapackageBase]]], label: str, matrix: str, func: Callable) -> None + + Switch a method, weighting, or normalization + + + .. py:method:: switch_method(method=Union[tuple, Iterable[Union[FS, bwp.DatapackageBase]]]) -> None + + Load a new method and replace ``.characterization_mm`` and ``.characterization_matrix``. + + Does not do any new calculations or change ``.characterized_inventory``. + + + .. py:method:: switch_normalization(normalization=Union[tuple, Iterable[Union[FS, bwp.DatapackageBase]]]) -> None + + Load a new normalization and replace ``.normalization_mm`` and ``.normalization_matrix``. + + Does not do any new calculations or change ``.normalized_inventory``. + + + .. py:method:: switch_weighting(weighting=Union[tuple, Iterable[Union[FS, bwp.DatapackageBase]]]) -> None + + Load a new weighting and replace ``.weighting_mm`` and ``.weighting_matrix``. + + Does not do any new calculations or change ``.weighted_inventory``. + + + .. py:method:: invert_technosphere_matrix() + + Use pardiso to efficiently calculate the inverse of the technosphere matrix. + + + .. py:method:: __check_demand(demand: Optional[dict] = None) + + + .. py:method:: redo_lci(demand: Optional[dict] = None) -> None + + Redo LCI with same databases but different demand. + + :param \* *demand*: A demand dictionary. + :type \* *demand*: dict + + Doesn't return anything, but overwrites ``self.demand_array``, ``self.supply_array``, and ``self.inventory``. + + .. warning:: If you want to redo the LCIA as well, use ``redo_lcia(demand)`` directly. + + + + .. py:method:: redo_lcia(demand: Optional[dict] = None) -> None + + Redo LCIA, optionally with new demand. + + :param \* *demand*: New demand dictionary. Optional, defaults to ``self.demand``. + :type \* *demand*: dict, optional + + Doesn't return anything, but overwrites ``self.characterized_inventory``. If ``demand`` is given, also overwrites ``self.demand_array``, ``self.supply_array``, and ``self.inventory``. + + + + .. py:method:: to_dataframe(matrix_label: str = 'characterized_inventory', row_dict: Optional[dict] = None, col_dict: Optional[dict] = None, annotate: bool = True, cutoff: numbers.Number = 200, cutoff_mode: str = 'number') -> pandas.DataFrame + + Return all nonzero elements of the given matrix as a Pandas dataframe. + + The LCA class instance must have the matrix ``matrix_label`` already; common labels are: + + * characterized_inventory + * inventory + * technosphere_matrix + * biosphere_matrix + * characterization_matrix + + For these common matrices, we already have ``row_dict`` and ``col_dict`` which link row and column indices to database ids. For other matrices, or if you have a custom mapping dictionary, override ``row_dict`` and/or ``col_dict``. They have the form ``{matrix index: identifier}``. + + If ``bw2data`` is installed, this function will try to look up metadata on the row and column objects. To turn this off, set ``annotate`` to ``False``. + + Instead of returning all possible values, you can apply a cutoff. This cutoff can be specified in two ways, controlled by ``cutoff_mode``, which should be either ``fraction`` or ``number``. + + If ``cutoff_mode`` is ``number`` (the default), then ``cutoff`` is the number of rows in the DataFrame. Data values are first sorted by their absolute value, and then the largest ``cutoff`` are taken. + + If ``cutoff_mode`` is ``fraction``, then only values whose absolute value is greater than ``cutoff * total_score`` are taken. ``cutoff`` must be between 0 and 1. + + The returned DataFrame will have the following columns: + + * amount + * col_index + * row_index + + If row or columns dictionaries are available, the following columns are added: + + * col_id + * row_id + + If ``bw2data`` is available, then the following columns are added: + + * col_code + * col_database + * col_location + * col_name + * col_reference_product + * col_type + * col_unit + * row_categories + * row_code + * row_database + * row_location + * row_name + * row_type + * row_unit + * source_product + + Returns a pandas ``DataFrame``. + + + + .. py:method:: has(label: str) -> bool + + Shortcut to find out if matrix data for type ``{label}_matrix`` is present in the given data objects. + + Returns a boolean. Will return ``True`` even if data for a zero-dimensional matrix is given. + + + .. py:method:: reverse_dict() + + + diff --git a/sphinx/technical/api/bw2calc/least_squares/index.rst b/sphinx/technical/api/bw2calc/least_squares/index.rst new file mode 100644 index 0000000..dbcaadf --- /dev/null +++ b/sphinx/technical/api/bw2calc/least_squares/index.rst @@ -0,0 +1,63 @@ +:py:mod:`bw2calc.least_squares` +=============================== + +.. py:module:: bw2calc.least_squares + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.least_squares.LeastSquaresLCA + + + + +.. py:class:: LeastSquaresLCA(demand: dict, method: Optional[tuple] = None, weighting: Optional[str] = None, normalization: Optional[str] = None, data_objs: Optional[Iterable[Union[pathlib.Path, fs.base.FS, bw_processing.DatapackageBase]]] = None, remapping_dicts: Optional[Iterable[dict]] = None, log_config: Optional[dict] = None, seed_override: Optional[int] = None, use_arrays: bool = False, use_distributions: bool = False) + + Bases: :py:obj:`bw2calc.lca.LCA` + + Solve overdetermined technosphere matrix with more products than activities using least-squares approximation. + + See also: + + * `Multioutput processes in LCA `_ + * `LSMR in SciPy `_ + * `Another least-squares algorithm in SciPy `_ + + + .. py:method:: load_lci_data() -> None + + Load inventory data and create technosphere and biosphere matrices. + + + .. py:method:: solve_linear_system(solver=lsmr) -> numpy.ndarray + + Master solution function for linear system :math:`Ax=B`. + + To most numerical analysts, matrix inversion is a sin. + + -- Nicolas Higham, Accuracy and Stability of Numerical Algorithms, Society for Industrial and Applied Mathematics, Philadelphia, PA, USA, 2002, p. 260. + + We use `UMFpack `_, which is a very fast solver for sparse matrices. + + If the technosphere matrix has already been factorized, then the decomposed technosphere (``self.solver``) is reused. Otherwise the calculation is redone completely. + + + + .. py:method:: decompose_technosphere() -> None + :abstractmethod: + + Factorize the technosphere matrix into lower and upper triangular matrices, :math:`A=LU`. Does not solve the linear system :math:`Ax=B`. + + Doesn't return anything, but creates ``self.solver``. + + .. warning:: Incorrect results could occur if a technosphere matrix was factorized, and then a new technosphere matrix was constructed, as ``self.solver`` would still be the factorized older technosphere matrix. You are responsible for deleting ``self.solver`` when doing these types of advanced calculations. + + + + diff --git a/sphinx/technical/api/bw2calc/log_utils/index.rst b/sphinx/technical/api/bw2calc/log_utils/index.rst new file mode 100644 index 0000000..c42da5a --- /dev/null +++ b/sphinx/technical/api/bw2calc/log_utils/index.rst @@ -0,0 +1,128 @@ +:py:mod:`bw2calc.log_utils` +=========================== + +.. py:module:: bw2calc.log_utils + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.log_utils.JSONFormatter + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2calc.log_utils.create_logger + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2calc.log_utils.BUILTIN_ATTRS + + +.. py:data:: BUILTIN_ATTRS + + + + +.. py:class:: JSONFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None) + + Bases: :py:obj:`logging.Formatter` + + Formatter instances are used to convert a LogRecord to text. + + Formatters need to know how a LogRecord is constructed. They are + responsible for converting a LogRecord to (usually) a string which can + be interpreted by either a human or an external system. The base Formatter + allows a formatting string to be specified. If none is supplied, the + style-dependent default value, "%(message)s", "{message}", or + "${message}", is used. + + The Formatter can be initialized with a format string which makes use of + knowledge of the LogRecord attributes - e.g. the default value mentioned + above makes use of the fact that the user's message and arguments are pre- + formatted into a LogRecord's message attribute. Currently, the useful + attributes in a LogRecord are described by: + + %(name)s Name of the logger (logging channel) + %(levelno)s Numeric logging level for the message (DEBUG, INFO, + WARNING, ERROR, CRITICAL) + %(levelname)s Text logging level for the message ("DEBUG", "INFO", + "WARNING", "ERROR", "CRITICAL") + %(pathname)s Full pathname of the source file where the logging + call was issued (if available) + %(filename)s Filename portion of pathname + %(module)s Module (name portion of filename) + %(lineno)d Source line number where the logging call was issued + (if available) + %(funcName)s Function name + %(created)f Time when the LogRecord was created (time.time() + return value) + %(asctime)s Textual time when the LogRecord was created + %(msecs)d Millisecond portion of the creation time + %(relativeCreated)d Time in milliseconds when the LogRecord was created, + relative to the time the logging module was loaded + (typically at application startup time) + %(thread)d Thread ID (if available) + %(threadName)s Thread name (if available) + %(process)d Process ID (if available) + %(message)s The result of record.getMessage(), computed just as + the record is emitted + + .. py:attribute:: json_lib + + + + + .. py:method:: format(record) + + Format the specified record as text. + + The record's attribute dictionary is used as the operand to a + string formatting operation which yields the returned string. + Before formatting the dictionary, a couple of preparatory steps + are carried out. The message attribute of the record is computed + using LogRecord.getMessage(). If the formatting string uses the + time (as determined by a call to usesTime(), formatTime() is + called to format the event time. If there is exception information, + it is formatted using formatException() and appended to the message. + + + .. py:method:: to_json(record) + + + .. py:method:: extra_from_record(record) + + + .. py:method:: json_record(message, extra, record) + + + +.. py:function:: create_logger(dirpath=None, name=None, **kwargs) + + Create a ``logger`` instance named ``bw2calc`` that can be used to log calculations. + + ``dirpath`` is the directory where the log file is saved. If ``dirpath`` is ``None``, no logger is created. + + ``name`` is the name of the calculation run, used to construct the log filepath. + + You can add other types of loggers, just add another handler to the ``bw2calc`` named logger before starting your calculations. + + Returns the filepath of the created log file. + + TODO: Decide on whether we copy safe_filepath to this package or create a common core package. + + diff --git a/sphinx/technical/api/bw2calc/monte_carlo/index.rst b/sphinx/technical/api/bw2calc/monte_carlo/index.rst new file mode 100644 index 0000000..8a19acb --- /dev/null +++ b/sphinx/technical/api/bw2calc/monte_carlo/index.rst @@ -0,0 +1,140 @@ +:py:mod:`bw2calc.monte_carlo` +============================= + +.. py:module:: bw2calc.monte_carlo + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.monte_carlo.MonteCarloLCA + bw2calc.monte_carlo.IterativeMonteCarlo + bw2calc.monte_carlo.ComparativeMonteCarlo + bw2calc.monte_carlo.ParallelMonteCarlo + bw2calc.monte_carlo.MultiMonteCarlo + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2calc.monte_carlo.single_worker + bw2calc.monte_carlo.direct_solving_worker + bw2calc.monte_carlo.multi_worker + + + +.. py:class:: MonteCarloLCA(*args, **kwargs) + + Bases: :py:obj:`bw2calc.lca.LCA` + + Normal ``LCA`` class now supports Monte Carlo and iterative use. You normally want to use it instead. + + +.. py:class:: IterativeMonteCarlo(*args, iter_solver=cgs, **kwargs) + + Bases: :py:obj:`MonteCarloLCA` + + Base class to use iterative techniques instead of `LU factorization `_ in Monte Carlo. + + .. py:method:: solve_linear_system() + + Master solution function for linear system :math:`Ax=B`. + + To most numerical analysts, matrix inversion is a sin. + + -- Nicolas Higham, Accuracy and Stability of Numerical Algorithms, Society for Industrial and Applied Mathematics, Philadelphia, PA, USA, 2002, p. 260. + + We use `UMFpack `_, which is a very fast solver for sparse matrices. + + If the technosphere matrix has already been factorized, then the decomposed technosphere (``self.solver``) is reused. Otherwise the calculation is redone completely. + + + + +.. py:class:: ComparativeMonteCarlo(demands, *args, **kwargs) + + Bases: :py:obj:`IterativeMonteCarlo` + + First draft approach at comparative LCA + + .. py:method:: load_data() + + + .. py:method:: __next__() + + Return the next item from the iterator. When exhausted, raise StopIteration + + + +.. py:function:: single_worker(args) + + +.. py:function:: direct_solving_worker(args) + + +.. py:class:: ParallelMonteCarlo(demand, method=None, data_objs=None, iterations=1000, chunk_size=None, cpus=None, log_config=None) + + Split a Monte Carlo calculation into parallel jobs + + .. py:method:: calculate(worker=single_worker) + + + +.. py:function:: multi_worker(args) + + Calculate a single Monte Carlo iteration for many demands. + + ``args`` are in order: + * ``project``: Name of project + * ``demands``: List of demand dictionaries + * ``method``: LCIA method + + Returns a list of results: ``[(demand dictionary, result)]`` + + + +.. py:class:: MultiMonteCarlo(demands, method=None, data_objs=None, iterations=100, cpus=None) + + This is a class for the efficient calculation of *many* demand vectors from + each Monte Carlo iteration. + + :param \* ``args`` is a list of demand dictionaries: + :param \* ``method`` is a LCIA method: + :param \* ``iterations`` is the number of Monte Carlo iterations desired: + :param \* ``cpus`` is the: + :type \* ``cpus`` is the: optional + + The input list can have complex demands, so ``[{('foo', 'bar'): 1, ('foo', 'baz'): 1}, {('foo', 'another'): 1}]`` is OK. + + Call ``.calculate()`` to generate results. + + + .. py:method:: merge_results(objs) + + Merge the results from each ``multi_worker`` worker. + + ``[('a', [0,1]), ('a', [2,3])]`` becomes ``[('a', [0,1,2,3)]``. + + + + .. py:method:: calculate(worker=multi_worker) + + Calculate Monte Carlo results for many demand vectors. + + Returns a list of results with the format:: + + [(demand dictionary, [lca scores])] + + There is no guarantee that the results are returned in the same order as the ``demand`` input variable. + + + + diff --git a/sphinx/technical/api/bw2calc/multi_lca/index.rst b/sphinx/technical/api/bw2calc/multi_lca/index.rst new file mode 100644 index 0000000..da566e1 --- /dev/null +++ b/sphinx/technical/api/bw2calc/multi_lca/index.rst @@ -0,0 +1,61 @@ +:py:mod:`bw2calc.multi_lca` +=========================== + +.. py:module:: bw2calc.multi_lca + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.multi_lca.InventoryMatrices + bw2calc.multi_lca.MultiLCA + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2calc.multi_lca.calculation_setups + bw2calc.multi_lca.logger + + +.. py:data:: calculation_setups + + + + +.. py:data:: logger + + + + +.. py:class:: InventoryMatrices(biosphere_matrix, supply_arrays) + + .. py:method:: __getitem__(fu_index) + + + +.. py:class:: MultiLCA(cs_name, log_config=None) + + Wrapper class for performing LCA calculations with many functional units and LCIA methods. + + Needs to be passed a ``calculation_setup`` name. + + This class does not subclass the `LCA` class, and performs all calculations upon instantiation. + + Initialization creates `self.results`, which is a NumPy array of LCA scores, with rows of functional units and columns of LCIA methods. Ordering is the same as in the `calculation_setup`. + + + .. py:property:: all + + Get all possible databases by merging all functional units + + diff --git a/sphinx/technical/api/bw2calc/single_value_diagonal_matrix/index.rst b/sphinx/technical/api/bw2calc/single_value_diagonal_matrix/index.rst new file mode 100644 index 0000000..02b8d75 --- /dev/null +++ b/sphinx/technical/api/bw2calc/single_value_diagonal_matrix/index.rst @@ -0,0 +1,46 @@ +:py:mod:`bw2calc.single_value_diagonal_matrix` +============================================== + +.. py:module:: bw2calc.single_value_diagonal_matrix + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2calc.single_value_diagonal_matrix.SingleValueDiagonalMatrix + + + + +.. py:class:: SingleValueDiagonalMatrix(*, packages: Sequence[bw_processing.Datapackage], matrix: str, dimension: int, use_vectors: bool = True, use_arrays: bool = True, use_distributions: bool = False, seed_override: Union[int, None] = None, indexer_override: Any = None, custom_filter: Union[Callable, None] = None) + + Bases: :py:obj:`matrix_utils.MappedMatrix` + + A scipy sparse matrix handler which takes in ``bw_processing`` data packages. Row and column ids are mapped to matrix indices, and a matrix is constructed. + + `indexer_override` allows for custom indexer behaviour. Indexers should follow a simple API: they must support `.__next__()`, and have the attribute `.index`, which returns an integer. + + `custom_filter` allows you to remove some data based on their indices. It is applied to all resource groups. If you need more fine-grained control, process the matrix after construction/iteration. `custom_filter` should take the indices array as an input, and return a Numpy boolean array with the same length as the indices array. + + :param \* packages: A list of Ddatapackage objects. + :param \* matrix: The string identifying the matrix to be built. + :param \* use_vectors: Flag to use vector data from datapackages + :param \* use_arrays: Flag to use array data from datapackages + :param \* use_distributions: Flag to use `stats_arrays` distribution data from datapackages + :param \* row_mapper: Optional instance of `ArrayMapper`. Used when matrices must align. + :param \* col_mapper: Optional instance of `ArrayMapper`. Used when matrices must align. + :param \* seed_override: Optional integer. Overrides the RNG seed given in the datapackage, if any. + :param \* indexer_override: Parameter for custom indexers. See above. + :param \* diagonal: If True, only use the `row` indices to build a diagonal matrix. + :param \* custom_filter: Callable for function to filter data based on `indices` values. See above. + :param \* empty_ok: If False, raise `AllArraysEmpty` if the matrix would be empty + + .. py:method:: rebuild_matrix() + + + diff --git a/sphinx/technical/api/bw2calc/utils/index.rst b/sphinx/technical/api/bw2calc/utils/index.rst new file mode 100644 index 0000000..e0abfdb --- /dev/null +++ b/sphinx/technical/api/bw2calc/utils/index.rst @@ -0,0 +1,39 @@ +:py:mod:`bw2calc.utils` +======================= + +.. py:module:: bw2calc.utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2calc.utils.get_seed + bw2calc.utils.consistent_global_index + bw2calc.utils.wrap_functional_unit + bw2calc.utils.get_datapackage + + + +.. py:function:: get_seed(seed=None) + + Get valid Numpy random seed value + + +.. py:function:: consistent_global_index(packages, matrix='characterization_matrix') + + +.. py:function:: wrap_functional_unit(dct) + + Transform functional units for effective logging. + Turns ``Activity`` objects into their keys. + + +.. py:function:: get_datapackage(obj) + + diff --git a/sphinx/technical/api/bw2calc/version/index.rst b/sphinx/technical/api/bw2calc/version/index.rst new file mode 100644 index 0000000..27061c5 --- /dev/null +++ b/sphinx/technical/api/bw2calc/version/index.rst @@ -0,0 +1,14 @@ +:py:mod:`bw2calc.version` +========================= + +.. py:module:: bw2calc.version + + +Module Contents +--------------- + +.. py:data:: version + :annotation: = [2, 0, 'DEV12'] + + + diff --git a/sphinx/technical/api/bw2data/backends/base/index.rst b/sphinx/technical/api/bw2data/backends/base/index.rst new file mode 100644 index 0000000..67d2338 --- /dev/null +++ b/sphinx/technical/api/bw2data/backends/base/index.rst @@ -0,0 +1,572 @@ +:py:mod:`bw2data.backends.base` +=============================== + +.. py:module:: bw2data.backends.base + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.base.Database + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.base._VALID_KEYS + bw2data.backends.base.SQLiteBackend + + +.. py:data:: _VALID_KEYS + + + + +.. py:class:: Database(name=None, *args, **kwargs) + + Bases: :py:obj:`peewee.Model` + + A base class for SQLite backends. + + Subclasses must support at least the following calls: + + * ``load()`` + * ``write(data)`` + + In addition, they should specify their backend with the ``backend`` attribute (a unicode string). + + * ``rename`` + * ``copy`` + * ``find_dependents`` + * ``random`` + * ``process`` + + For new classes to be recognized by the ``DatabaseChooser``, they need to be registered with the ``config`` object, e.g.: + + .. code-block:: python + + config.backends['backend type string'] = BackendClass + + Instantiation does not load any data. If this database is not yet registered in the metadata store, a warning is written to ``stdout``. + + The data schema for databases in voluptuous is: + + .. code-block:: python + + exchange = { + Required("input"): valid_tuple, + Required("type"): basestring, + } + exchange.update(uncertainty_dict) + lci_dataset = { + Optional("categories"): Any(list, tuple), + Optional("location"): object, + Optional("unit"): basestring, + Optional("name"): basestring, + Optional("type"): basestring, + Optional("exchanges"): [exchange] + } + db_validator = Schema({valid_tuple: lci_dataset}, extra=True) + + where: + * ``valid_tuple`` is a :ref:`dataset identifier `, like ``("ecoinvent", "super strong steel")`` + * ``uncertainty_fields`` are fields from an :ref:`uncertainty dictionary `. + + Processing a Database actually produces two parameter arrays: one for the exchanges, which make up the technosphere and biosphere matrices, and a geomapping array which links activities to locations. + + :param \*name*: Name of the database to manage. + :type \*name*: unicode string + + .. py:property:: node_class + + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:property:: metadata + + + .. py:property:: registered + + + .. py:property:: _metadata + + + .. py:attribute:: name + + + + + .. py:attribute:: backend + + + + + .. py:attribute:: depends + + + + + .. py:attribute:: geocollections + + + + + .. py:attribute:: dirty + + + + + .. py:attribute:: searchable + + + + + .. py:attribute:: extra + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: __repr__ + + + + + .. py:attribute:: filters + + + + + .. py:attribute:: order_by + + + + + .. py:method:: __str__() + + + .. py:method:: __lt__(other) + + + .. py:method:: exists(name) + :classmethod: + + + .. py:method:: set_dirty(name) + :classmethod: + + + .. py:method:: copy(name) + + Make a copy of the database. + + Internal links within the database will be updated to match the new database name, i.e. ``("old name", "some id")`` will be converted to ``("new name", "some id")`` for all exchanges. + + :param \* *name*: Name of the new database. Must not already exist. + :type \* *name*: str + + + .. py:method:: dirpath_processed() + + + .. py:method:: filepath_intermediate() + + + .. py:method:: filename_processed() + + + .. py:method:: filepath_processed(clean=True) + + + .. py:method:: datapackage() + + + .. py:method:: find_dependents(data=None, ignore=None) + + Get sorted list of direct dependent databases (databases linked from exchanges). + + :param \* *data*: Inventory data + :type \* *data*: dict, optional + :param \* *ignore*: List of database names to ignore + :type \* *ignore*: list + + :returns: List of database names + + + .. py:method:: find_graph_dependents() + + Recursively get list of all dependent databases. + + :returns: A set of database names + + + .. py:method:: query(*queries) + + Search through the database. + + + .. py:method:: relabel_data(data, new_name) + + Relabel database keys and exchanges. + + In a database which internally refer to the same database, update to new database name ``new_name``. + + Needed to copy a database completely or cut out a section of a database. + + For example: + + .. code-block:: python + + data = { + ("old and boring", 1): + {"exchanges": [ + {"input": ("old and boring", 42), + "amount": 1.0}, + ] + }, + ("old and boring", 2): + {"exchanges": [ + {"input": ("old and boring", 1), + "amount": 4.0} + ] + } + } + print(relabel_database(data, "shiny new")) + >> { + ("shiny new", 1): + {"exchanges": [ + {"input": ("old and boring", 42), + "amount": 1.0}, + ] + }, + ("shiny new", 2): + {"exchanges": [ + {"input": ("shiny new", 1), + "amount": 4.0} + ] + } + } + + In the example, the exchange to ``("old and boring", 42)`` does not change, as this is not part of the updated data. + + :param \* *data*: The data to modify + :type \* *data*: dict + :param \* *new_name*: The name of the modified database + :type \* *new_name*: str + + :returns: The modified data + + + .. py:method:: rename(name) + + Rename a database. Modifies exchanges to link to new name. + + :param \* *name*: New name. + :type \* *name*: str + + :returns: self # Backwards compatibility + + + .. py:method:: __iter__() + + + .. py:method:: __len__() + + + .. py:method:: __contains__(obj) + + + .. py:method:: _get_queryset(random=False, filters=True) + + + .. py:method:: _get_filters() + + + .. py:method:: _set_filters(filters) + + + .. py:method:: _get_order_by() + + + .. py:method:: _set_order_by(field) + + + .. py:method:: random(filters=True, true_random=False) + + True random requires loading and sorting data in SQLite, and can be resource-intensive. + + + .. py:method:: get_node(code=None, **kwargs) + + + .. py:method:: _drop_indices() + + + .. py:method:: _add_indices() + + + .. py:method:: _efficient_write_dataset(index, key, ds, exchanges, activities) + + + .. py:method:: _efficient_write_many_data(data, indices=True) + + + .. py:method:: write(data, process=True) + + Write ``data`` to database. + + ``data`` must be a dictionary of the form:: + + { + ('database name', 'dataset code'): {dataset} + } + + Writing a database will first deletes all existing data. + + + .. py:method:: write_exchanges(technosphere, biosphere, dependents) + + Write IO data directly to processed arrays. + + Product data is stored in SQLite as normal activities. + Exchange data is written directly to NumPy structured arrays. + + Technosphere and biosphere data has format ``(row id, col id, value, flip)``. + + + + .. py:method:: load(*args, **kwargs) + + + .. py:method:: new_activity(code, **kwargs) + + + .. py:method:: new_node(code=None, **kwargs) + + + .. py:method:: make_searchable(reset=False) + + + .. py:method:: make_unsearchable() + + + .. py:method:: delete_instance() + + + .. py:method:: delete_data(keep_params=False, warn=True) + + Delete all data from SQLite database and Whoosh index + + + .. py:method:: exchange_data_iterator(sql, dependents, flip=False) + + Iterate over exchanges and format for ``bw_processing`` arrays. + + ``dependents`` is a set of dependent database names. + + ``flip`` means flip the numeric sign; see ``bw_processing`` docs. + + Uses raw sqlite3 to retrieve data for ~2x speed boost. + + + .. py:method:: clean_all() + :classmethod: + + + .. py:method:: process(csv=False) + + Create structured arrays for the technosphere and biosphere matrices. + + Uses ``bw_processing`` for array creation and metadata serialization. + + Also creates a ``geomapping`` array, linking activities to locations. Used for regionalized calculations. + + Use a raw SQLite3 cursor instead of Peewee for a ~2 times speed advantage. + + + + .. py:method:: search(string, **kwargs) + + Search this database for ``string``. + + The searcher include the following fields: + + * name + * comment + * categories + * location + * reference product + + ``string`` can include wild cards, e.g. ``"trans*"``. + + By default, the ``name`` field is given the most weight. The full weighting set is called the ``boost`` dictionary, and the default weights are:: + + { + "name": 5, + "comment": 1, + "product": 3, + "categories": 2, + "location": 3 + } + + Optional keyword arguments: + + * ``limit``: Number of results to return. + * ``boosts``: Dictionary of field names and numeric boosts - see default boost values above. New values must be in the same format, but with different weights. + * ``filter``: Dictionary of criteria that search results must meet, e.g. ``{'categories': 'air'}``. Keys must be one of the above fields. + * ``mask``: Dictionary of criteria that exclude search results. Same format as ``filter``. + * ``facet``: Field to facet results. Must be one of ``name``, ``product``, ``categories``, ``location``, or ``database``. + * ``proxy``: Return ``Activity`` proxies instead of raw Whoosh documents. Default is ``True``. + + Returns a list of ``Activity`` datasets. + + + .. py:method:: set_geocollections() + + Set ``geocollections`` attribute for databases which don't currently have it. + + + .. py:method:: graph_technosphere(filename=None, **kwargs) + + + .. py:method:: delete_duplicate_exchanges(fields=['amount', 'type']) + + Delete exchanges which are exact duplicates. Useful if you accidentally ran your input data notebook twice. + + To determine uniqueness, we look at the exchange input and output nodes, and at the exchanges values for fields ``fields``. + + + .. py:method:: backup() + + Save a backup to ``backups`` folder. + + :returns: File path of backup. + + + .. py:method:: nodes_to_dataframe(columns: Optional[List[str]] = None, return_sorted: bool = True) -> pandas.DataFrame + + Return a pandas DataFrame with all database nodes. Uses the provided node attributes by default, such as name, unit, location. + + By default, returns a DataFrame sorted by name, reference product, location, and unit. Set ``return_sorted`` to ``False`` to skip sorting. + + Specify ``columns`` to get custom columns. You will need to write your own function to get more customization, there are endless possibilities here. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: edges_to_dataframe(categorical: bool = True, formatters: Optional[List[Callable]] = None) -> pandas.DataFrame + + Return a pandas DataFrame with all database exchanges. Standard DataFrame columns are: + + target_id: int, + target_database: str, + target_code: str, + target_name: Optional[str], + target_reference_product: Optional[str], + target_location: Optional[str], + target_unit: Optional[str], + target_type: Optional[str] + source_id: int, + source_database: str, + source_code: str, + source_name: Optional[str], + source_product: Optional[str], # Note different label + source_location: Optional[str], + source_unit: Optional[str], + source_categories: Optional[str] # Tuple concatenated with "::" as in `bw2io` + edge_amount: float, + edge_type: str, + + Target is the node consuming the edge, source is the node or flow being consumed. The terms target and source were chosen because they also work well for biosphere edges. + + Args: + + ``categorical`` will turn each string column in a `pandas Categorical Series `__. This takes 1-2 extra seconds, but saves around 50% of the memory consumption. + + ``formatters`` is a list of callables that modify each row. These functions must take the following keyword arguments, and use the `Wurst internal data format `__: + + * ``node``: The target node, as a dict + * ``edge``: The edge, including attributes of the source node + * ``row``: The current row dict being modified. + + The functions in ``formatters`` don't need to return anything, they modify ``row`` in place. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: _iotable_edges_to_dataframe() -> pandas.DataFrame + + Return a pandas DataFrame with all database exchanges. DataFrame columns are: + + target_id: int, + target_database: str, + target_code: str, + target_name: Optional[str], + target_reference_product: Optional[str], + target_location: Optional[str], + target_unit: Optional[str], + target_type: Optional[str] + source_id: int, + source_database: str, + source_code: str, + source_name: Optional[str], + source_product: Optional[str], # Note different label + source_location: Optional[str], + source_unit: Optional[str], + source_categories: Optional[str] # Tuple concatenated with "::" as in `bw2io` + edge_amount: float, + edge_type: str, + + Target is the node consuming the edge, source is the node or flow being consumed. The terms target and source were chosen because they also work well for biosphere edges. + + As IO Tables are normally quite large, the DataFrame building will operate directly on Numpy arrays, and therefore special formatters are not supported in this function. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: _sqlite_edges_to_dataframe(categorical: bool = True, formatters: Optional[List[Callable]] = None) -> pandas.DataFrame + + + .. py:method:: validate(data) + + + .. py:method:: add_geomappings(data) + + + .. py:method:: register(write_empty=True, **kwargs) + + Legacy method to register a database with the metadata store. + Writing data automatically sets the following metadata: + * *depends*: Names of the databases that this database references, e.g. "biosphere" + * *number*: Number of processes in this database. + + + .. py:method:: deregister() + + Legacy method to remove an object from the metadata store. Does not delete any data. + + + +.. py:data:: SQLiteBackend + + + + diff --git a/sphinx/technical/api/bw2data/backends/index.rst b/sphinx/technical/api/bw2data/backends/index.rst new file mode 100644 index 0000000..25fde69 --- /dev/null +++ b/sphinx/technical/api/bw2data/backends/index.rst @@ -0,0 +1,831 @@ +:py:mod:`bw2data.backends` +========================== + +.. py:module:: bw2data.backends + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + base/index.rst + iotable/index.rst + proxies/index.rst + schema/index.rst + utils/index.rst + wurst_extraction/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.SubstitutableDatabase + bw2data.backends.Database + bw2data.backends.ActivityDataset + bw2data.backends.ExchangeDataset + bw2data.backends.Activity + bw2data.backends.Exchange + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.get_id + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.projects + bw2data.backends.SQLiteBackend + bw2data.backends.sqlite3_lci_db + bw2data.backends.Node + bw2data.backends.Edge + + +.. py:data:: projects + + + + +.. py:class:: SubstitutableDatabase(filepath, tables) + + .. py:property:: db + + + .. py:method:: _create_database() + + + .. py:method:: change_path(filepath) + + + .. py:method:: atomic() + + + .. py:method:: execute_sql(*args, **kwargs) + + + .. py:method:: transaction() + + + .. py:method:: vacuum() + + + +.. py:class:: Database(name=None, *args, **kwargs) + + Bases: :py:obj:`peewee.Model` + + A base class for SQLite backends. + + Subclasses must support at least the following calls: + + * ``load()`` + * ``write(data)`` + + In addition, they should specify their backend with the ``backend`` attribute (a unicode string). + + * ``rename`` + * ``copy`` + * ``find_dependents`` + * ``random`` + * ``process`` + + For new classes to be recognized by the ``DatabaseChooser``, they need to be registered with the ``config`` object, e.g.: + + .. code-block:: python + + config.backends['backend type string'] = BackendClass + + Instantiation does not load any data. If this database is not yet registered in the metadata store, a warning is written to ``stdout``. + + The data schema for databases in voluptuous is: + + .. code-block:: python + + exchange = { + Required("input"): valid_tuple, + Required("type"): basestring, + } + exchange.update(uncertainty_dict) + lci_dataset = { + Optional("categories"): Any(list, tuple), + Optional("location"): object, + Optional("unit"): basestring, + Optional("name"): basestring, + Optional("type"): basestring, + Optional("exchanges"): [exchange] + } + db_validator = Schema({valid_tuple: lci_dataset}, extra=True) + + where: + * ``valid_tuple`` is a :ref:`dataset identifier `, like ``("ecoinvent", "super strong steel")`` + * ``uncertainty_fields`` are fields from an :ref:`uncertainty dictionary `. + + Processing a Database actually produces two parameter arrays: one for the exchanges, which make up the technosphere and biosphere matrices, and a geomapping array which links activities to locations. + + :param \*name*: Name of the database to manage. + :type \*name*: unicode string + + .. py:property:: node_class + + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:property:: metadata + + + .. py:property:: registered + + + .. py:property:: _metadata + + + .. py:attribute:: name + + + + + .. py:attribute:: backend + + + + + .. py:attribute:: depends + + + + + .. py:attribute:: geocollections + + + + + .. py:attribute:: dirty + + + + + .. py:attribute:: searchable + + + + + .. py:attribute:: extra + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: __repr__ + + + + + .. py:attribute:: filters + + + + + .. py:attribute:: order_by + + + + + .. py:method:: __str__() + + + .. py:method:: __lt__(other) + + + .. py:method:: exists(name) + :classmethod: + + + .. py:method:: set_dirty(name) + :classmethod: + + + .. py:method:: copy(name) + + Make a copy of the database. + + Internal links within the database will be updated to match the new database name, i.e. ``("old name", "some id")`` will be converted to ``("new name", "some id")`` for all exchanges. + + :param \* *name*: Name of the new database. Must not already exist. + :type \* *name*: str + + + .. py:method:: dirpath_processed() + + + .. py:method:: filepath_intermediate() + + + .. py:method:: filename_processed() + + + .. py:method:: filepath_processed(clean=True) + + + .. py:method:: datapackage() + + + .. py:method:: find_dependents(data=None, ignore=None) + + Get sorted list of direct dependent databases (databases linked from exchanges). + + :param \* *data*: Inventory data + :type \* *data*: dict, optional + :param \* *ignore*: List of database names to ignore + :type \* *ignore*: list + + :returns: List of database names + + + .. py:method:: find_graph_dependents() + + Recursively get list of all dependent databases. + + :returns: A set of database names + + + .. py:method:: query(*queries) + + Search through the database. + + + .. py:method:: relabel_data(data, new_name) + + Relabel database keys and exchanges. + + In a database which internally refer to the same database, update to new database name ``new_name``. + + Needed to copy a database completely or cut out a section of a database. + + For example: + + .. code-block:: python + + data = { + ("old and boring", 1): + {"exchanges": [ + {"input": ("old and boring", 42), + "amount": 1.0}, + ] + }, + ("old and boring", 2): + {"exchanges": [ + {"input": ("old and boring", 1), + "amount": 4.0} + ] + } + } + print(relabel_database(data, "shiny new")) + >> { + ("shiny new", 1): + {"exchanges": [ + {"input": ("old and boring", 42), + "amount": 1.0}, + ] + }, + ("shiny new", 2): + {"exchanges": [ + {"input": ("shiny new", 1), + "amount": 4.0} + ] + } + } + + In the example, the exchange to ``("old and boring", 42)`` does not change, as this is not part of the updated data. + + :param \* *data*: The data to modify + :type \* *data*: dict + :param \* *new_name*: The name of the modified database + :type \* *new_name*: str + + :returns: The modified data + + + .. py:method:: rename(name) + + Rename a database. Modifies exchanges to link to new name. + + :param \* *name*: New name. + :type \* *name*: str + + :returns: self # Backwards compatibility + + + .. py:method:: __iter__() + + + .. py:method:: __len__() + + + .. py:method:: __contains__(obj) + + + .. py:method:: _get_queryset(random=False, filters=True) + + + .. py:method:: _get_filters() + + + .. py:method:: _set_filters(filters) + + + .. py:method:: _get_order_by() + + + .. py:method:: _set_order_by(field) + + + .. py:method:: random(filters=True, true_random=False) + + True random requires loading and sorting data in SQLite, and can be resource-intensive. + + + .. py:method:: get_node(code=None, **kwargs) + + + .. py:method:: _drop_indices() + + + .. py:method:: _add_indices() + + + .. py:method:: _efficient_write_dataset(index, key, ds, exchanges, activities) + + + .. py:method:: _efficient_write_many_data(data, indices=True) + + + .. py:method:: write(data, process=True) + + Write ``data`` to database. + + ``data`` must be a dictionary of the form:: + + { + ('database name', 'dataset code'): {dataset} + } + + Writing a database will first deletes all existing data. + + + .. py:method:: write_exchanges(technosphere, biosphere, dependents) + + Write IO data directly to processed arrays. + + Product data is stored in SQLite as normal activities. + Exchange data is written directly to NumPy structured arrays. + + Technosphere and biosphere data has format ``(row id, col id, value, flip)``. + + + + .. py:method:: load(*args, **kwargs) + + + .. py:method:: new_activity(code, **kwargs) + + + .. py:method:: new_node(code=None, **kwargs) + + + .. py:method:: make_searchable(reset=False) + + + .. py:method:: make_unsearchable() + + + .. py:method:: delete_instance() + + + .. py:method:: delete_data(keep_params=False, warn=True) + + Delete all data from SQLite database and Whoosh index + + + .. py:method:: exchange_data_iterator(sql, dependents, flip=False) + + Iterate over exchanges and format for ``bw_processing`` arrays. + + ``dependents`` is a set of dependent database names. + + ``flip`` means flip the numeric sign; see ``bw_processing`` docs. + + Uses raw sqlite3 to retrieve data for ~2x speed boost. + + + .. py:method:: clean_all() + :classmethod: + + + .. py:method:: process(csv=False) + + Create structured arrays for the technosphere and biosphere matrices. + + Uses ``bw_processing`` for array creation and metadata serialization. + + Also creates a ``geomapping`` array, linking activities to locations. Used for regionalized calculations. + + Use a raw SQLite3 cursor instead of Peewee for a ~2 times speed advantage. + + + + .. py:method:: search(string, **kwargs) + + Search this database for ``string``. + + The searcher include the following fields: + + * name + * comment + * categories + * location + * reference product + + ``string`` can include wild cards, e.g. ``"trans*"``. + + By default, the ``name`` field is given the most weight. The full weighting set is called the ``boost`` dictionary, and the default weights are:: + + { + "name": 5, + "comment": 1, + "product": 3, + "categories": 2, + "location": 3 + } + + Optional keyword arguments: + + * ``limit``: Number of results to return. + * ``boosts``: Dictionary of field names and numeric boosts - see default boost values above. New values must be in the same format, but with different weights. + * ``filter``: Dictionary of criteria that search results must meet, e.g. ``{'categories': 'air'}``. Keys must be one of the above fields. + * ``mask``: Dictionary of criteria that exclude search results. Same format as ``filter``. + * ``facet``: Field to facet results. Must be one of ``name``, ``product``, ``categories``, ``location``, or ``database``. + * ``proxy``: Return ``Activity`` proxies instead of raw Whoosh documents. Default is ``True``. + + Returns a list of ``Activity`` datasets. + + + .. py:method:: set_geocollections() + + Set ``geocollections`` attribute for databases which don't currently have it. + + + .. py:method:: graph_technosphere(filename=None, **kwargs) + + + .. py:method:: delete_duplicate_exchanges(fields=['amount', 'type']) + + Delete exchanges which are exact duplicates. Useful if you accidentally ran your input data notebook twice. + + To determine uniqueness, we look at the exchange input and output nodes, and at the exchanges values for fields ``fields``. + + + .. py:method:: backup() + + Save a backup to ``backups`` folder. + + :returns: File path of backup. + + + .. py:method:: nodes_to_dataframe(columns: Optional[List[str]] = None, return_sorted: bool = True) -> pandas.DataFrame + + Return a pandas DataFrame with all database nodes. Uses the provided node attributes by default, such as name, unit, location. + + By default, returns a DataFrame sorted by name, reference product, location, and unit. Set ``return_sorted`` to ``False`` to skip sorting. + + Specify ``columns`` to get custom columns. You will need to write your own function to get more customization, there are endless possibilities here. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: edges_to_dataframe(categorical: bool = True, formatters: Optional[List[Callable]] = None) -> pandas.DataFrame + + Return a pandas DataFrame with all database exchanges. Standard DataFrame columns are: + + target_id: int, + target_database: str, + target_code: str, + target_name: Optional[str], + target_reference_product: Optional[str], + target_location: Optional[str], + target_unit: Optional[str], + target_type: Optional[str] + source_id: int, + source_database: str, + source_code: str, + source_name: Optional[str], + source_product: Optional[str], # Note different label + source_location: Optional[str], + source_unit: Optional[str], + source_categories: Optional[str] # Tuple concatenated with "::" as in `bw2io` + edge_amount: float, + edge_type: str, + + Target is the node consuming the edge, source is the node or flow being consumed. The terms target and source were chosen because they also work well for biosphere edges. + + Args: + + ``categorical`` will turn each string column in a `pandas Categorical Series `__. This takes 1-2 extra seconds, but saves around 50% of the memory consumption. + + ``formatters`` is a list of callables that modify each row. These functions must take the following keyword arguments, and use the `Wurst internal data format `__: + + * ``node``: The target node, as a dict + * ``edge``: The edge, including attributes of the source node + * ``row``: The current row dict being modified. + + The functions in ``formatters`` don't need to return anything, they modify ``row`` in place. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: _iotable_edges_to_dataframe() -> pandas.DataFrame + + Return a pandas DataFrame with all database exchanges. DataFrame columns are: + + target_id: int, + target_database: str, + target_code: str, + target_name: Optional[str], + target_reference_product: Optional[str], + target_location: Optional[str], + target_unit: Optional[str], + target_type: Optional[str] + source_id: int, + source_database: str, + source_code: str, + source_name: Optional[str], + source_product: Optional[str], # Note different label + source_location: Optional[str], + source_unit: Optional[str], + source_categories: Optional[str] # Tuple concatenated with "::" as in `bw2io` + edge_amount: float, + edge_type: str, + + Target is the node consuming the edge, source is the node or flow being consumed. The terms target and source were chosen because they also work well for biosphere edges. + + As IO Tables are normally quite large, the DataFrame building will operate directly on Numpy arrays, and therefore special formatters are not supported in this function. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: _sqlite_edges_to_dataframe(categorical: bool = True, formatters: Optional[List[Callable]] = None) -> pandas.DataFrame + + + .. py:method:: validate(data) + + + .. py:method:: add_geomappings(data) + + + .. py:method:: register(write_empty=True, **kwargs) + + Legacy method to register a database with the metadata store. + Writing data automatically sets the following metadata: + * *depends*: Names of the databases that this database references, e.g. "biosphere" + * *number*: Number of processes in this database. + + + .. py:method:: deregister() + + Legacy method to remove an object from the metadata store. Does not delete any data. + + + +.. py:data:: SQLiteBackend + + + + +.. py:class:: ActivityDataset + + Bases: :py:obj:`peewee.Model` + + .. py:property:: key + + + .. py:attribute:: data + + + + + .. py:attribute:: code + + + + + .. py:attribute:: database + + + + + .. py:attribute:: location + + + + + .. py:attribute:: name + + + + + .. py:attribute:: product + + + + + .. py:attribute:: type + + + + + +.. py:class:: ExchangeDataset + + Bases: :py:obj:`peewee.Model` + + .. py:attribute:: data + + + + + .. py:attribute:: input_code + + + + + .. py:attribute:: input_database + + + + + .. py:attribute:: output_code + + + + + .. py:attribute:: output_database + + + + + .. py:attribute:: type + + + + + +.. py:function:: get_id(key) + + +.. py:data:: sqlite3_lci_db + + + + +.. py:class:: Activity(document=None, **kwargs) + + Bases: :py:obj:`bw2data.proxies.ActivityProxyBase` + + A MutableMapping is a generic container for associating + key/value pairs. + + This class provides concrete generic implementations of all + methods except for __getitem__, __setitem__, __delitem__, + __iter__, and __len__. + + .. py:property:: id + + + .. py:property:: key + + + .. py:method:: __getitem__(key) + + + .. py:method:: __setitem__(key, value) + + + .. py:method:: delete() + + + .. py:method:: save() + + + .. py:method:: _change_code(new_code) + + + .. py:method:: _change_database(new_database) + + + .. py:method:: exchanges() + + + .. py:method:: edges() + + + .. py:method:: technosphere(include_substitution=False) + + + .. py:method:: biosphere() + + + .. py:method:: production(include_substitution=False) + + + .. py:method:: rp_exchange() + + Return an ``Exchange`` object corresponding to the reference production. Uses the following in order: + + * The ``production`` exchange, if only one is present + * The ``production`` exchange with the same name as the activity ``reference product``. + + Raises ``ValueError`` if no suitable exchange is found. + + + .. py:method:: producers() + + + .. py:method:: substitution() + + + .. py:method:: upstream(kinds=('technosphere', 'generic consumption')) + + + .. py:method:: consumers(kinds=('technosphere', 'generic consumption')) + + + .. py:method:: new_exchange(**kwargs) + + + .. py:method:: new_edge(**kwargs) + + Create a new exchange linked to this activity + + + .. py:method:: copy(code=None, **kwargs) + + Copy the activity. Returns a new `Activity`. + + `code` is the new activity code; if not given, a UUID is used. + + `kwargs` are additional new fields and field values, e.g. name='foo' + + + + +.. py:class:: Exchange(document=None, **kwargs) + + Bases: :py:obj:`bw2data.proxies.ExchangeProxyBase` + + A MutableMapping is a generic container for associating + key/value pairs. + + This class provides concrete generic implementations of all + methods except for __getitem__, __setitem__, __delitem__, + __iter__, and __len__. + + .. py:method:: save() + + + .. py:method:: delete() + + + +.. py:data:: Node + + + + +.. py:data:: Edge + + + + diff --git a/sphinx/technical/api/bw2data/backends/iotable/index.rst b/sphinx/technical/api/bw2data/backends/iotable/index.rst new file mode 100644 index 0000000..52d0f7f --- /dev/null +++ b/sphinx/technical/api/bw2data/backends/iotable/index.rst @@ -0,0 +1,154 @@ +:py:mod:`bw2data.backends.iotable` +================================== + +.. py:module:: bw2data.backends.iotable + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.iotable.ReadOnlyExchange + bw2data.backends.iotable.IOTableExchanges + bw2data.backends.iotable.IOTableActivity + + + + +.. py:class:: ReadOnlyExchange(**kwargs) + + Bases: :py:obj:`collections.abc.Mapping` + + Non-mutable dictionary which mimics ``bw2data.proxies.Exchange``, but is read-only and doesn't link to a SQLite database row. + + .. py:attribute:: __contains__ + + + + + .. py:attribute:: __iter__ + + + + + .. py:attribute:: __len__ + + + + + .. py:attribute:: __getitem__ + + + + + .. py:attribute:: __eq__ + + + + + .. py:attribute:: __hash__ + + + + + .. py:attribute:: unit + + + + + .. py:attribute:: lca + + + + + .. py:attribute:: as_dict + + + + + .. py:method:: __lt__(other) + + Return self None + + + +.. py:class:: IOTableExchanges(datapackage: bw_processing.Datapackage, target: Optional[bw2data.backends.proxies.Activity] = None, biosphere: bool = True, technosphere: bool = True, production: bool = True) + + Bases: :py:obj:`collections.abc.Iterable` + + .. py:attribute:: to_dataframe + + + + + .. py:method:: _group_and_filter_resources(datapackage) + + + .. py:method:: _add_arrays_to_resources(resources, datapackage) + + + .. py:method:: _reduce_arrays_to_selected_types(resources, technosphere, production, biosphere) + + + .. py:method:: _mask_resource_arrays(resource, mask) + + + .. py:method:: __iter__() + + + .. py:method:: _raw_technosphere_iterator(negative=True) + + + .. py:method:: _raw_biosphere_iterator() + + + .. py:method:: __next__() + :abstractmethod: + + + .. py:method:: __len__() + + + +.. py:class:: IOTableActivity + + Bases: :py:obj:`bw2data.backends.proxies.Activity` + + .. py:method:: delete() -> None + :abstractmethod: + + + .. py:method:: rp_exchange() + + + .. py:method:: _get_db() + + + .. py:method:: technosphere() -> IOTableExchanges + + + .. py:method:: biosphere() + + + .. py:method:: production() + + + .. py:method:: exchanges() + + + .. py:method:: substitution() + + + diff --git a/sphinx/technical/api/bw2data/backends/proxies/index.rst b/sphinx/technical/api/bw2data/backends/proxies/index.rst new file mode 100644 index 0000000..a723664 --- /dev/null +++ b/sphinx/technical/api/bw2data/backends/proxies/index.rst @@ -0,0 +1,214 @@ +:py:mod:`bw2data.backends.proxies` +================================== + +.. py:module:: bw2data.backends.proxies + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.proxies.Exchanges + bw2data.backends.proxies.Activity + bw2data.backends.proxies.Exchange + + + + +.. py:class:: Exchanges(key, kinds=None, reverse=False) + + Bases: :py:obj:`collections.abc.Iterable` + + Iterator for exchanges with some additional methods. + + This is not a generator; ``next()`` is not supported. Everything time you start to iterate over the object you get a new list starting from the beginning. However, to get a single item you can do ``next(iter(foo))``. + + Ordering is by database row id. + + Supports the following: + + .. code-block:: python + + exchanges = activity.exchanges() + + # Iterate + for exc in exchanges: + pass + + # Length + len(exchanges) + + # Delete all + exchanges.delete() + + + .. py:method:: filter(expr) + + + .. py:method:: delete() + + + .. py:method:: _get_queryset() + + + .. py:method:: __iter__() + + + .. py:method:: __len__() + + + .. py:method:: to_dataframe(categorical: bool = True, formatters: Optional[List[Callable]] = None) -> pandas.DataFrame + + Return a pandas DataFrame with all node exchanges. Standard DataFrame columns are: + + target_id: int, + target_database: str, + target_code: str, + target_name: Optional[str], + target_reference_product: Optional[str], + target_location: Optional[str], + target_unit: Optional[str], + target_type: Optional[str] + source_id: int, + source_database: str, + source_code: str, + source_name: Optional[str], + source_product: Optional[str], # Note different label + source_location: Optional[str], + source_unit: Optional[str], + source_categories: Optional[str] # Tuple concatenated with "::" as in `bw2io` + edge_amount: float, + edge_type: str, + + Target is the node consuming the edge, source is the node or flow being consumed. The terms target and source were chosen because they also work well for biosphere edges. + + Args: + + ``categorical`` will turn each string column in a `pandas Categorical Series `__. This takes 1-2 extra seconds, but saves around 50% of the memory consumption. + + ``formatters`` is a list of callables that modify each row. These functions must take the following keyword arguments, and use the `Wurst internal data format `__: + + * ``node``: The target node, as a dict + * ``edge``: The edge, including attributes of the source node + * ``row``: The current row dict being modified. + + The functions in ``formatters`` don't need to return anything, they modify ``row`` in place. + + Returns a pandas ``DataFrame``. + + + + +.. py:class:: Activity(document=None, **kwargs) + + Bases: :py:obj:`bw2data.proxies.ActivityProxyBase` + + A MutableMapping is a generic container for associating + key/value pairs. + + This class provides concrete generic implementations of all + methods except for __getitem__, __setitem__, __delitem__, + __iter__, and __len__. + + .. py:property:: id + + + .. py:property:: key + + + .. py:method:: __getitem__(key) + + + .. py:method:: __setitem__(key, value) + + + .. py:method:: delete() + + + .. py:method:: save() + + + .. py:method:: _change_code(new_code) + + + .. py:method:: _change_database(new_database) + + + .. py:method:: exchanges() + + + .. py:method:: edges() + + + .. py:method:: technosphere(include_substitution=False) + + + .. py:method:: biosphere() + + + .. py:method:: production(include_substitution=False) + + + .. py:method:: rp_exchange() + + Return an ``Exchange`` object corresponding to the reference production. Uses the following in order: + + * The ``production`` exchange, if only one is present + * The ``production`` exchange with the same name as the activity ``reference product``. + + Raises ``ValueError`` if no suitable exchange is found. + + + .. py:method:: producers() + + + .. py:method:: substitution() + + + .. py:method:: upstream(kinds=('technosphere', 'generic consumption')) + + + .. py:method:: consumers(kinds=('technosphere', 'generic consumption')) + + + .. py:method:: new_exchange(**kwargs) + + + .. py:method:: new_edge(**kwargs) + + Create a new exchange linked to this activity + + + .. py:method:: copy(code=None, **kwargs) + + Copy the activity. Returns a new `Activity`. + + `code` is the new activity code; if not given, a UUID is used. + + `kwargs` are additional new fields and field values, e.g. name='foo' + + + + +.. py:class:: Exchange(document=None, **kwargs) + + Bases: :py:obj:`bw2data.proxies.ExchangeProxyBase` + + A MutableMapping is a generic container for associating + key/value pairs. + + This class provides concrete generic implementations of all + methods except for __getitem__, __setitem__, __delitem__, + __iter__, and __len__. + + .. py:method:: save() + + + .. py:method:: delete() + + + diff --git a/sphinx/technical/api/bw2data/backends/schema/index.rst b/sphinx/technical/api/bw2data/backends/schema/index.rst new file mode 100644 index 0000000..7057ab4 --- /dev/null +++ b/sphinx/technical/api/bw2data/backends/schema/index.rst @@ -0,0 +1,109 @@ +:py:mod:`bw2data.backends.schema` +================================= + +.. py:module:: bw2data.backends.schema + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.schema.ActivityDataset + bw2data.backends.schema.ExchangeDataset + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.schema.get_id + + + +.. py:class:: ActivityDataset + + Bases: :py:obj:`peewee.Model` + + .. py:property:: key + + + .. py:attribute:: data + + + + + .. py:attribute:: code + + + + + .. py:attribute:: database + + + + + .. py:attribute:: location + + + + + .. py:attribute:: name + + + + + .. py:attribute:: product + + + + + .. py:attribute:: type + + + + + +.. py:class:: ExchangeDataset + + Bases: :py:obj:`peewee.Model` + + .. py:attribute:: data + + + + + .. py:attribute:: input_code + + + + + .. py:attribute:: input_database + + + + + .. py:attribute:: output_code + + + + + .. py:attribute:: output_database + + + + + .. py:attribute:: type + + + + + +.. py:function:: get_id(key) + + diff --git a/sphinx/technical/api/bw2data/backends/utils/index.rst b/sphinx/technical/api/bw2data/backends/utils/index.rst new file mode 100644 index 0000000..3108020 --- /dev/null +++ b/sphinx/technical/api/bw2data/backends/utils/index.rst @@ -0,0 +1,54 @@ +:py:mod:`bw2data.backends.utils` +================================ + +.. py:module:: bw2data.backends.utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.utils.get_csv_data_dict + bw2data.backends.utils.check_exchange_amount + bw2data.backends.utils.dict_as_activitydataset + bw2data.backends.utils.dict_as_exchangedataset + bw2data.backends.utils.replace_cfs + bw2data.backends.utils.retupleize_geo_strings + + + +.. py:function:: get_csv_data_dict(ds) + + +.. py:function:: check_exchange_amount(exc) + + Check exchange data validity when processing + + +.. py:function:: dict_as_activitydataset(ds) + + +.. py:function:: dict_as_exchangedataset(ds) + + +.. py:function:: replace_cfs(old_key, new_key) + + Replace ``old_key`` with ``new_key`` in characterization factors. + + Returns list of modified methods. + + +.. py:function:: retupleize_geo_strings(value) + + Transform data from SQLite representation to Python objects. + + We are using a SQLite3 cursor, which means that the Peewee data conversion code is not called. So ``('foo', 'bar')`` is stored as a string, not a tuple. This code tries to do this conversion correctly. + + TODO: Adapt what Peewee does in this case? + + diff --git a/sphinx/technical/api/bw2data/backends/wurst_extraction/index.rst b/sphinx/technical/api/bw2data/backends/wurst_extraction/index.rst new file mode 100644 index 0000000..945113f --- /dev/null +++ b/sphinx/technical/api/bw2data/backends/wurst_extraction/index.rst @@ -0,0 +1,64 @@ +:py:mod:`bw2data.backends.wurst_extraction` +=========================================== + +.. py:module:: bw2data.backends.wurst_extraction + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.backends.wurst_extraction._list_or_dict + bw2data.backends.wurst_extraction.extract_activity + bw2data.backends.wurst_extraction.extract_exchange + bw2data.backends.wurst_extraction.add_exchanges_to_consumers + bw2data.backends.wurst_extraction.add_input_info_for_indigenous_exchanges + bw2data.backends.wurst_extraction.add_input_info_for_external_exchanges + bw2data.backends.wurst_extraction.extract_brightway_databases + + + +.. py:function:: _list_or_dict(obj) + + +.. py:function:: extract_activity(proxy, add_identifiers=False) + + Get data in Wurst internal format for an ``ActivityDataset`` + + +.. py:function:: extract_exchange(proxy, add_properties=False) + + Get data in Wurst internal format for an ``ExchangeDataset`` + + +.. py:function:: add_exchanges_to_consumers(activities, exchange_qs, add_properties=False, add_identifiers=False) + + Retrieve exchanges from database, and add to activities. + + Assumes that activities are single output, and that the exchange code is the same as the activity code. This assumption is valid for ecoinvent 3.3 cutoff imported into Brightway2. + + +.. py:function:: add_input_info_for_indigenous_exchanges(activities, names, add_identifiers=False) + + Add details on exchange inputs if these activities are already available + + +.. py:function:: add_input_info_for_external_exchanges(activities, names, add_identifiers=False) + + Add details on exchange inputs from other databases + + +.. py:function:: extract_brightway_databases(database_names, add_properties=False, add_identifiers=False) + + Extract a Brightway2 SQLiteBackend database to the Wurst internal format. + + ``database_names`` is a list of database names. You should already be in the correct project. + + Returns a list of dataset documents. + + diff --git a/sphinx/technical/api/bw2data/bin/bw2_uptodate/index.rst b/sphinx/technical/api/bw2data/bin/bw2_uptodate/index.rst new file mode 100644 index 0000000..2a52440 --- /dev/null +++ b/sphinx/technical/api/bw2data/bin/bw2_uptodate/index.rst @@ -0,0 +1,58 @@ +:py:mod:`bw2data.bin.bw2_uptodate` +================================== + +.. py:module:: bw2data.bin.bw2_uptodate + +.. autoapi-nested-parse:: + + Brightway2 updating made simple. + + Usage: + bw2-uptodate + bw2-uptodate -l | --list + bw2-uptodate -h | --help + bw2-uptodate --version + + Options: + --list List the updates needed, but don't do anything + -h --help Show this screen. + --version Show version. + + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.bin.bw2_uptodate.UpdaterInterface + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.bin.bw2_uptodate.main + + + +.. py:class:: UpdaterInterface + + .. py:method:: needed() + + + .. py:method:: list() + + + .. py:method:: update(confirm=True) + + + +.. py:function:: main() + + diff --git a/sphinx/technical/api/bw2data/bin/index.rst b/sphinx/technical/api/bw2data/bin/index.rst new file mode 100644 index 0000000..a292618 --- /dev/null +++ b/sphinx/technical/api/bw2data/bin/index.rst @@ -0,0 +1,15 @@ +:py:mod:`bw2data.bin` +===================== + +.. py:module:: bw2data.bin + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + bw2_uptodate/index.rst + + diff --git a/sphinx/technical/api/bw2data/compat/index.rst b/sphinx/technical/api/bw2data/compat/index.rst new file mode 100644 index 0000000..9d257dd --- /dev/null +++ b/sphinx/technical/api/bw2data/compat/index.rst @@ -0,0 +1,114 @@ +:py:mod:`bw2data.compat` +======================== + +.. py:module:: bw2data.compat + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.compat.Mapping + bw2data.compat._Databases + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.compat.unpack + bw2data.compat.translate_key + bw2data.compat.prepare_lca_inputs + bw2data.compat.get_database_filepath + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.compat.databases + + +.. py:class:: Mapping + + A dictionary that maps object ids, like ``("Ecoinvent 2.2", 42)``, to integers. + + Used only for backwards compatibility; preferred method is now to look up the ids of activities directly in the SQlite database. + + .. py:method:: add(keys) + + + .. py:method:: __getitem__(key) + + + .. py:method:: delete(keys) + + + .. py:method:: __str__() + + Return str(self). + + + .. py:method:: __len__() + + + +.. py:class:: _Databases + + .. py:method:: clean() + + + .. py:method:: set_dirty(name) + + + .. py:method:: __getitem__(name) + + + .. py:method:: __contains__(name) + + + .. py:method:: __len__() + + + .. py:method:: __delitem__(name) + + + .. py:method:: __iter__() + + + .. py:method:: flush() + + + .. py:method:: __setitem__(*args, **kwargs) + + + +.. py:data:: databases + + + + +.. py:function:: unpack(dct) + + +.. py:function:: translate_key(key) + + +.. py:function:: prepare_lca_inputs(demand=None, method=None, weighting=None, normalization=None, demands=None, remapping=True, demand_database_last=True) + + Prepare LCA input arguments in Brightway 2.5 style. + + +.. py:function:: get_database_filepath(functional_unit) + + Get filepaths for all databases in supply chain of `functional_unit` + + diff --git a/sphinx/technical/api/bw2data/configuration/index.rst b/sphinx/technical/api/bw2data/configuration/index.rst new file mode 100644 index 0000000..ebcc003 --- /dev/null +++ b/sphinx/technical/api/bw2data/configuration/index.rst @@ -0,0 +1,79 @@ +:py:mod:`bw2data.configuration` +=============================== + +.. py:module:: bw2data.configuration + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.configuration.Config + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.configuration.config + + +.. py:class:: Config + + A singleton that stores configuration settings + + .. py:property:: biosphere + + Get name for ``biosphere`` database from user preferences. + + Default name is ``biosphere3``; change this by changing ``config.p["biosphere_database"]``. + + .. py:property:: global_location + + Get name for global location from user preferences. + + Default name is ``GLO``; change this by changing ``config.p["global_location"]``. + + .. py:attribute:: version + :annotation: = 3 + + + + .. py:attribute:: backends + + + + + .. py:attribute:: cache + + + + + .. py:attribute:: metadata + :annotation: = [] + + + + .. py:attribute:: sqlite3_databases + :annotation: = [] + + + + .. py:attribute:: _windows + + + + + +.. py:data:: config + + + + diff --git a/sphinx/technical/api/bw2data/data_store/index.rst b/sphinx/technical/api/bw2data/data_store/index.rst new file mode 100644 index 0000000..f5f9329 --- /dev/null +++ b/sphinx/technical/api/bw2data/data_store/index.rst @@ -0,0 +1,189 @@ +:py:mod:`bw2data.data_store` +============================ + +.. py:module:: bw2data.data_store + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.data_store.DataStore + bw2data.data_store.ProcessedDataStore + + + + +.. py:class:: DataStore(name) + + Base class for all Brightway2 data stores. Subclasses should define: + + * **metadata**: A :ref:`serialized-dict` instance, e.g. ``databases`` or ``methods``. The custom is that each type of data store has a new metadata store, so the data store ``Foo`` would have a metadata store ``foos``. + * **validator**: A data validator. Optional. See bw2data.validate. + + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:property:: registered + + + .. py:attribute:: validator + + + + + .. py:attribute:: _metadata + + + + + .. py:attribute:: _intermediate_dir + :annotation: = intermediate + + + + .. py:attribute:: __repr__ + + + + + .. py:attribute:: metadata + + + + + .. py:method:: __str__() + + Return str(self). + + + .. py:method:: _get_metadata() + + + .. py:method:: _set_metadata(value) + + + .. py:method:: register(**kwargs) + + Register an object with the metadata store. Takes any number of keyword arguments. + + + .. py:method:: deregister() + + Remove an object from the metadata store. Does not delete any files. + + + .. py:method:: load() + + Load the intermediate data for this object. + + :returns: The intermediate data. + + + .. py:method:: copy(name) + + Make a copy of this object with a new ``name``. + + This method only changes the name, but not any of the data or metadata. + + :param \* *name*: Name of the new object. + :type \* *name*: object + + :returns: The new object. + + + .. py:method:: backup() + + Save a backup to ``backups`` folder. + + :returns: File path of backup. + + + .. py:method:: write(data) + + Serialize intermediate data to disk. + + :param \* *data*: The data + :type \* *data*: object + + + .. py:method:: validate(data) + + Validate data. Must be called manually. + + + +.. py:class:: ProcessedDataStore(name) + + Bases: :py:obj:`DataStore` + + Brightway2 data stores that can be processed to NumPy arrays. + + In addition to ``metadata`` and (optionally) ``validator``, subclasses should override ``add_geomappings``. This method takes the entire dataset, and loads objects to :ref:`geomapping` as needed. + + + .. py:attribute:: matrix + :annotation: = unknown + + + + .. py:method:: dirpath_processed() + + + .. py:method:: filename_processed() + + + .. py:method:: filepath_processed() + + + .. py:method:: datapackage() + + + .. py:method:: write(data, process=True) + + Serialize intermediate data to disk. + + :param \* *data*: The data + :type \* *data*: object + + + .. py:method:: process_row(row) + :abstractmethod: + + Translate data into a dictionary suitable for array inputs. + + See `bw_processing documentation `__. + + + .. py:method:: process(**extra_metadata) + + Process intermediate data from a Python dictionary to a `stats_arrays `_ array, which is a `NumPy `_ `Structured `_ `Array `_. A structured array (also called record array) is a heterogeneous array, where each column has a different label and data type. + + Processed arrays are saved in the ``processed`` directory. + + If the uncertainty type is no uncertainty, undefined, or not specified, then the 'amount' value is used for 'loc' as well. This is needed for the random number generator. + + Doesn't return anything, but writes a file to disk. + + + + .. py:method:: add_geomappings(data) + + Add objects to ``geomapping``, if necessary. + + :param \* *data*: The data + :type \* *data*: object + + + .. py:method:: validate(data) + + Validate data. Must be called manually. + + + diff --git a/sphinx/technical/api/bw2data/database/index.rst b/sphinx/technical/api/bw2data/database/index.rst new file mode 100644 index 0000000..97b62c1 --- /dev/null +++ b/sphinx/technical/api/bw2data/database/index.rst @@ -0,0 +1,22 @@ +:py:mod:`bw2data.database` +========================== + +.. py:module:: bw2data.database + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.database.DatabaseChooser + + + +.. py:function:: DatabaseChooser(name, backend=None) + + diff --git a/sphinx/technical/api/bw2data/errors/index.rst b/sphinx/technical/api/bw2data/errors/index.rst new file mode 100644 index 0000000..a3704f6 --- /dev/null +++ b/sphinx/technical/api/bw2data/errors/index.rst @@ -0,0 +1,114 @@ +:py:mod:`bw2data.errors` +======================== + +.. py:module:: bw2data.errors + + +Module Contents +--------------- + +.. py:exception:: BW2Exception + + Bases: :py:obj:`BaseException` + + Base class for exceptions in Brightway2 + + +.. py:exception:: InvalidExchange + + Bases: :py:obj:`BW2Exception` + + Exchange is missing 'amount' or 'input' + + +.. py:exception:: DuplicateNode + + Bases: :py:obj:`BW2Exception` + + Can't have nodes with same unique identifiers + + +.. py:exception:: MissingIntermediateData + + Bases: :py:obj:`BW2Exception` + + Base class for exceptions in Brightway2 + + +.. py:exception:: UnknownObject + + Bases: :py:obj:`BW2Exception` + + Base class for exceptions in Brightway2 + + +.. py:exception:: MultipleResults + + Bases: :py:obj:`BW2Exception` + + Base class for exceptions in Brightway2 + + +.. py:exception:: UntypedExchange + + Bases: :py:obj:`BW2Exception` + + Exchange doesn't have 'type' attribute + + +.. py:exception:: WebUIError + + Bases: :py:obj:`BW2Exception` + + Can't find running instance of bw2-web + + +.. py:exception:: ValidityError + + Bases: :py:obj:`BW2Exception` + + The activity or exchange dataset does not have all the required fields + + +.. py:exception:: NotAllowed + + Bases: :py:obj:`BW2Exception` + + This operation is not allowed + + +.. py:exception:: WrongDatabase + + Bases: :py:obj:`BW2Exception` + + Can't save activities from database `x` to database `y`. + + +.. py:exception:: NotFound + + Bases: :py:obj:`BW2Exception` + + Requested web resource not found + + +.. py:exception:: PickleError + + Bases: :py:obj:`BW2Exception` + + Pickle file can't be loaded due to updated library file structure + + +.. py:exception:: Brightway2Project + + Bases: :py:obj:`BW2Exception` + + This project is not yet migrated to Brightway 2.5 + + +.. py:exception:: InvalidDatapackage + + Bases: :py:obj:`BW2Exception` + + The given datapackage can't be used for the requested task. + + diff --git a/sphinx/technical/api/bw2data/fatomic/index.rst b/sphinx/technical/api/bw2data/fatomic/index.rst new file mode 100644 index 0000000..5dbf909 --- /dev/null +++ b/sphinx/technical/api/bw2data/fatomic/index.rst @@ -0,0 +1,57 @@ +:py:mod:`bw2data.fatomic` +========================= + +.. py:module:: bw2data.fatomic + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.fatomic.open + bw2data.fatomic.writeall + bw2data.fatomic.transform + bw2data.fatomic.transformall + bw2data.fatomic.transformchunks + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.fatomic.replace + bw2data.fatomic.writechunks + + +.. py:data:: replace + + + + +.. py:function:: open(filename, mode, *args, **kwargs) + + +.. py:function:: writeall(filename, contents, binary=None) + + +.. py:data:: writechunks + + + + +.. py:function:: transform(filename, func, binary=False) + + +.. py:function:: transformall(filename, func, binary=False) + + +.. py:function:: transformchunks(filename, func, chunksize=None, binary=False) + + diff --git a/sphinx/technical/api/bw2data/filesystem/index.rst b/sphinx/technical/api/bw2data/filesystem/index.rst new file mode 100644 index 0000000..6204341 --- /dev/null +++ b/sphinx/technical/api/bw2data/filesystem/index.rst @@ -0,0 +1,49 @@ +:py:mod:`bw2data.filesystem` +============================ + +.. py:module:: bw2data.filesystem + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.filesystem.create_dir + bw2data.filesystem.check_dir + bw2data.filesystem.md5 + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.filesystem.re_slugify + + +.. py:data:: re_slugify + + + + +.. py:function:: create_dir(dirpath) + + Create directory tree to `dirpath`; ignore if already exists + + +.. py:function:: check_dir(directory) + + Returns ``True`` if given path is a directory and writeable, ``False`` otherwise. + + +.. py:function:: md5(filepath, blocksize=65536) + + Generate MD5 hash for file at `filepath` + + diff --git a/sphinx/technical/api/bw2data/ia_data_store/index.rst b/sphinx/technical/api/bw2data/ia_data_store/index.rst new file mode 100644 index 0000000..43ed5ff --- /dev/null +++ b/sphinx/technical/api/bw2data/ia_data_store/index.rst @@ -0,0 +1,97 @@ +:py:mod:`bw2data.ia_data_store` +=============================== + +.. py:module:: bw2data.ia_data_store + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.ia_data_store.ImpactAssessmentDataStore + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.ia_data_store.abbreviate + + + +.. py:function:: abbreviate(names, length=8) + + Take a tuple or list, and construct a string, doing the following: + + First, apply :func:`.filesystem.safe_filename` to each element in ``names``. + + Next, take the following, in order: + * The first word of the first element in names, lower-cased, where word is defined as everything up to the first empty space character. + * Join the rest of the first element (i.e. after the first word) with all other elements. Use the empty space character to join. + * In this long string separated by spaces, take the lowercase first character of each word. Add the first word to this new string. + * Finally, add a dash, and then the MD5 hash of the entire identifier, where each element is joined by a dash character. + + ``('ReCiPe Endpoint (E,A)', 'human health', 'ionising radiation')`` becomes ``'recipee(hhir-70eeef20a20deb6347ad428e3f6c5f3c'``. + + The MD5 hash is needed because taking the first characters doesn't guarantee unique strings. + + + +.. py:class:: ImpactAssessmentDataStore(name) + + Bases: :py:obj:`bw2data.data_store.ProcessedDataStore` + + A subclass of ``DataStore`` for impact assessment methods. + + IA objects are hierarchically structured, and their identifier uses this structure, like ``('ecological scarcity 2006', 'total', 'natural resources')``. The identifier must be a ``tuple``, i.e. ``()``, not a ``list``, i.e. ``[]``. The identifier should only contain unicode strings, and can be of any length >= 1. + + Because impact assessment methods are identified by a tuple of strings, e.g. ``('ReCiPe Endpoint (E,A)', 'human health', 'ionising radiation')``, we need to transform this identifier before it can be used e.g. as a filename. We do this using the :func:`.abbreviate` function, which returns a single unicode string. + + :param \* *name*: Name of the IA object to manage. Must be a tuple of unicode strings. + :type \* *name*: tuple + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:method:: __str__() + + Return str(self). + + + .. py:method:: get_abbreviation() + + Retrieve the abbreviation of the method identifier from the metadata store. See class documentation. + + + .. py:method:: copy(name=None) + + Make a copy of the method, including its CFs and metadata. + + If ``name`` is not provided, add "Copy of" to the last element of the original name, e.g. ``("foo", "bar")`` becomes ``("foo", "Copy of bar")`` + + :param \* *name*: Name of the new method. + :type \* *name*: tuple, optional + + :returns: The new object. + + + .. py:method:: register(**kwargs) + + Register an object with the metadata store. + + The metadata key ``abbreviation`` is set automatically. + + Objects must be registered before data can be written. If this object is not yet registered in the metadata store, a warning is written to **stdout**. + + Takes any number of keyword arguments. + + + + diff --git a/sphinx/technical/api/bw2data/index.rst b/sphinx/technical/api/bw2data/index.rst new file mode 100644 index 0000000..b6331ca --- /dev/null +++ b/sphinx/technical/api/bw2data/index.rst @@ -0,0 +1,967 @@ +:py:mod:`bw2data` +================= + +.. py:module:: bw2data + + +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + + backends/index.rst + bin/index.rst + search/index.rst + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + compat/index.rst + configuration/index.rst + data_store/index.rst + database/index.rst + errors/index.rst + fatomic/index.rst + filesystem/index.rst + ia_data_store/index.rst + logs/index.rst + meta/index.rst + method/index.rst + parameters/index.rst + project/index.rst + proxies/index.rst + query/index.rst + serialization/index.rst + sqlite/index.rst + tests/index.rst + updates/index.rst + utils/index.rst + validate/index.rst + version/index.rst + weighting_normalization/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.JsonWrapper + bw2data.Database + bw2data.Method + bw2data.Searcher + bw2data.IndexManager + bw2data.Weighting + bw2data.Normalization + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.set_data_dir + bw2data.get_activity + bw2data.get_node + bw2data.get_id + bw2data.prepare_lca_inputs + bw2data.extract_brightway_databases + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.config + bw2data.projects + bw2data.dynamic_calculation_setups + bw2data.calculation_setups + bw2data.geomapping + bw2data.methods + bw2data.normalizations + bw2data.preferences + bw2data.weightings + bw2data.Node + bw2data.Edge + bw2data.databases + bw2data.mapping + bw2data.parameters + + +.. py:data:: config + + + + +.. py:data:: projects + + + + +.. py:function:: set_data_dir(dirpath, permanent=True) + + Set the Brightway2 data directory to ``dirpath``. + + If ``permanent`` is ``True``, then set ``dirpath`` as the default data directory. + + Creates ``dirpath`` if needed. Also creates basic directories, and resets metadata. + + + +.. py:data:: dynamic_calculation_setups + + + + +.. py:data:: calculation_setups + + + + +.. py:data:: geomapping + + + + +.. py:data:: methods + + + + +.. py:data:: normalizations + + + + +.. py:data:: preferences + + + + +.. py:data:: weightings + + + + +.. py:class:: JsonWrapper + + .. py:method:: dump(data, filepath) + :classmethod: + + + .. py:method:: dump_bz2(data, filepath) + :classmethod: + + + .. py:method:: load(file) + :classmethod: + + + .. py:method:: load_bz2(filepath) + :classmethod: + + + .. py:method:: dumps(data) + :classmethod: + + + .. py:method:: loads(data) + :classmethod: + + + +.. py:class:: Database(name=None, *args, **kwargs) + + Bases: :py:obj:`peewee.Model` + + A base class for SQLite backends. + + Subclasses must support at least the following calls: + + * ``load()`` + * ``write(data)`` + + In addition, they should specify their backend with the ``backend`` attribute (a unicode string). + + * ``rename`` + * ``copy`` + * ``find_dependents`` + * ``random`` + * ``process`` + + For new classes to be recognized by the ``DatabaseChooser``, they need to be registered with the ``config`` object, e.g.: + + .. code-block:: python + + config.backends['backend type string'] = BackendClass + + Instantiation does not load any data. If this database is not yet registered in the metadata store, a warning is written to ``stdout``. + + The data schema for databases in voluptuous is: + + .. code-block:: python + + exchange = { + Required("input"): valid_tuple, + Required("type"): basestring, + } + exchange.update(uncertainty_dict) + lci_dataset = { + Optional("categories"): Any(list, tuple), + Optional("location"): object, + Optional("unit"): basestring, + Optional("name"): basestring, + Optional("type"): basestring, + Optional("exchanges"): [exchange] + } + db_validator = Schema({valid_tuple: lci_dataset}, extra=True) + + where: + * ``valid_tuple`` is a :ref:`dataset identifier `, like ``("ecoinvent", "super strong steel")`` + * ``uncertainty_fields`` are fields from an :ref:`uncertainty dictionary `. + + Processing a Database actually produces two parameter arrays: one for the exchanges, which make up the technosphere and biosphere matrices, and a geomapping array which links activities to locations. + + :param \*name*: Name of the database to manage. + :type \*name*: unicode string + + .. py:property:: node_class + + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:property:: metadata + + + .. py:property:: registered + + + .. py:property:: _metadata + + + .. py:attribute:: name + + + + + .. py:attribute:: backend + + + + + .. py:attribute:: depends + + + + + .. py:attribute:: geocollections + + + + + .. py:attribute:: dirty + + + + + .. py:attribute:: searchable + + + + + .. py:attribute:: extra + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: __repr__ + + + + + .. py:attribute:: filters + + + + + .. py:attribute:: order_by + + + + + .. py:method:: __str__() + + + .. py:method:: __lt__(other) + + + .. py:method:: exists(name) + :classmethod: + + + .. py:method:: set_dirty(name) + :classmethod: + + + .. py:method:: copy(name) + + Make a copy of the database. + + Internal links within the database will be updated to match the new database name, i.e. ``("old name", "some id")`` will be converted to ``("new name", "some id")`` for all exchanges. + + :param \* *name*: Name of the new database. Must not already exist. + :type \* *name*: str + + + .. py:method:: dirpath_processed() + + + .. py:method:: filepath_intermediate() + + + .. py:method:: filename_processed() + + + .. py:method:: filepath_processed(clean=True) + + + .. py:method:: datapackage() + + + .. py:method:: find_dependents(data=None, ignore=None) + + Get sorted list of direct dependent databases (databases linked from exchanges). + + :param \* *data*: Inventory data + :type \* *data*: dict, optional + :param \* *ignore*: List of database names to ignore + :type \* *ignore*: list + + :returns: List of database names + + + .. py:method:: find_graph_dependents() + + Recursively get list of all dependent databases. + + :returns: A set of database names + + + .. py:method:: query(*queries) + + Search through the database. + + + .. py:method:: relabel_data(data, new_name) + + Relabel database keys and exchanges. + + In a database which internally refer to the same database, update to new database name ``new_name``. + + Needed to copy a database completely or cut out a section of a database. + + For example: + + .. code-block:: python + + data = { + ("old and boring", 1): + {"exchanges": [ + {"input": ("old and boring", 42), + "amount": 1.0}, + ] + }, + ("old and boring", 2): + {"exchanges": [ + {"input": ("old and boring", 1), + "amount": 4.0} + ] + } + } + print(relabel_database(data, "shiny new")) + >> { + ("shiny new", 1): + {"exchanges": [ + {"input": ("old and boring", 42), + "amount": 1.0}, + ] + }, + ("shiny new", 2): + {"exchanges": [ + {"input": ("shiny new", 1), + "amount": 4.0} + ] + } + } + + In the example, the exchange to ``("old and boring", 42)`` does not change, as this is not part of the updated data. + + :param \* *data*: The data to modify + :type \* *data*: dict + :param \* *new_name*: The name of the modified database + :type \* *new_name*: str + + :returns: The modified data + + + .. py:method:: rename(name) + + Rename a database. Modifies exchanges to link to new name. + + :param \* *name*: New name. + :type \* *name*: str + + :returns: self # Backwards compatibility + + + .. py:method:: __iter__() + + + .. py:method:: __len__() + + + .. py:method:: __contains__(obj) + + + .. py:method:: _get_queryset(random=False, filters=True) + + + .. py:method:: _get_filters() + + + .. py:method:: _set_filters(filters) + + + .. py:method:: _get_order_by() + + + .. py:method:: _set_order_by(field) + + + .. py:method:: random(filters=True, true_random=False) + + True random requires loading and sorting data in SQLite, and can be resource-intensive. + + + .. py:method:: get_node(code=None, **kwargs) + + + .. py:method:: _drop_indices() + + + .. py:method:: _add_indices() + + + .. py:method:: _efficient_write_dataset(index, key, ds, exchanges, activities) + + + .. py:method:: _efficient_write_many_data(data, indices=True) + + + .. py:method:: write(data, process=True) + + Write ``data`` to database. + + ``data`` must be a dictionary of the form:: + + { + ('database name', 'dataset code'): {dataset} + } + + Writing a database will first deletes all existing data. + + + .. py:method:: write_exchanges(technosphere, biosphere, dependents) + + Write IO data directly to processed arrays. + + Product data is stored in SQLite as normal activities. + Exchange data is written directly to NumPy structured arrays. + + Technosphere and biosphere data has format ``(row id, col id, value, flip)``. + + + + .. py:method:: load(*args, **kwargs) + + + .. py:method:: new_activity(code, **kwargs) + + + .. py:method:: new_node(code=None, **kwargs) + + + .. py:method:: make_searchable(reset=False) + + + .. py:method:: make_unsearchable() + + + .. py:method:: delete_instance() + + + .. py:method:: delete_data(keep_params=False, warn=True) + + Delete all data from SQLite database and Whoosh index + + + .. py:method:: exchange_data_iterator(sql, dependents, flip=False) + + Iterate over exchanges and format for ``bw_processing`` arrays. + + ``dependents`` is a set of dependent database names. + + ``flip`` means flip the numeric sign; see ``bw_processing`` docs. + + Uses raw sqlite3 to retrieve data for ~2x speed boost. + + + .. py:method:: clean_all() + :classmethod: + + + .. py:method:: process(csv=False) + + Create structured arrays for the technosphere and biosphere matrices. + + Uses ``bw_processing`` for array creation and metadata serialization. + + Also creates a ``geomapping`` array, linking activities to locations. Used for regionalized calculations. + + Use a raw SQLite3 cursor instead of Peewee for a ~2 times speed advantage. + + + + .. py:method:: search(string, **kwargs) + + Search this database for ``string``. + + The searcher include the following fields: + + * name + * comment + * categories + * location + * reference product + + ``string`` can include wild cards, e.g. ``"trans*"``. + + By default, the ``name`` field is given the most weight. The full weighting set is called the ``boost`` dictionary, and the default weights are:: + + { + "name": 5, + "comment": 1, + "product": 3, + "categories": 2, + "location": 3 + } + + Optional keyword arguments: + + * ``limit``: Number of results to return. + * ``boosts``: Dictionary of field names and numeric boosts - see default boost values above. New values must be in the same format, but with different weights. + * ``filter``: Dictionary of criteria that search results must meet, e.g. ``{'categories': 'air'}``. Keys must be one of the above fields. + * ``mask``: Dictionary of criteria that exclude search results. Same format as ``filter``. + * ``facet``: Field to facet results. Must be one of ``name``, ``product``, ``categories``, ``location``, or ``database``. + * ``proxy``: Return ``Activity`` proxies instead of raw Whoosh documents. Default is ``True``. + + Returns a list of ``Activity`` datasets. + + + .. py:method:: set_geocollections() + + Set ``geocollections`` attribute for databases which don't currently have it. + + + .. py:method:: graph_technosphere(filename=None, **kwargs) + + + .. py:method:: delete_duplicate_exchanges(fields=['amount', 'type']) + + Delete exchanges which are exact duplicates. Useful if you accidentally ran your input data notebook twice. + + To determine uniqueness, we look at the exchange input and output nodes, and at the exchanges values for fields ``fields``. + + + .. py:method:: backup() + + Save a backup to ``backups`` folder. + + :returns: File path of backup. + + + .. py:method:: nodes_to_dataframe(columns: Optional[List[str]] = None, return_sorted: bool = True) -> pandas.DataFrame + + Return a pandas DataFrame with all database nodes. Uses the provided node attributes by default, such as name, unit, location. + + By default, returns a DataFrame sorted by name, reference product, location, and unit. Set ``return_sorted`` to ``False`` to skip sorting. + + Specify ``columns`` to get custom columns. You will need to write your own function to get more customization, there are endless possibilities here. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: edges_to_dataframe(categorical: bool = True, formatters: Optional[List[Callable]] = None) -> pandas.DataFrame + + Return a pandas DataFrame with all database exchanges. Standard DataFrame columns are: + + target_id: int, + target_database: str, + target_code: str, + target_name: Optional[str], + target_reference_product: Optional[str], + target_location: Optional[str], + target_unit: Optional[str], + target_type: Optional[str] + source_id: int, + source_database: str, + source_code: str, + source_name: Optional[str], + source_product: Optional[str], # Note different label + source_location: Optional[str], + source_unit: Optional[str], + source_categories: Optional[str] # Tuple concatenated with "::" as in `bw2io` + edge_amount: float, + edge_type: str, + + Target is the node consuming the edge, source is the node or flow being consumed. The terms target and source were chosen because they also work well for biosphere edges. + + Args: + + ``categorical`` will turn each string column in a `pandas Categorical Series `__. This takes 1-2 extra seconds, but saves around 50% of the memory consumption. + + ``formatters`` is a list of callables that modify each row. These functions must take the following keyword arguments, and use the `Wurst internal data format `__: + + * ``node``: The target node, as a dict + * ``edge``: The edge, including attributes of the source node + * ``row``: The current row dict being modified. + + The functions in ``formatters`` don't need to return anything, they modify ``row`` in place. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: _iotable_edges_to_dataframe() -> pandas.DataFrame + + Return a pandas DataFrame with all database exchanges. DataFrame columns are: + + target_id: int, + target_database: str, + target_code: str, + target_name: Optional[str], + target_reference_product: Optional[str], + target_location: Optional[str], + target_unit: Optional[str], + target_type: Optional[str] + source_id: int, + source_database: str, + source_code: str, + source_name: Optional[str], + source_product: Optional[str], # Note different label + source_location: Optional[str], + source_unit: Optional[str], + source_categories: Optional[str] # Tuple concatenated with "::" as in `bw2io` + edge_amount: float, + edge_type: str, + + Target is the node consuming the edge, source is the node or flow being consumed. The terms target and source were chosen because they also work well for biosphere edges. + + As IO Tables are normally quite large, the DataFrame building will operate directly on Numpy arrays, and therefore special formatters are not supported in this function. + + Returns a pandas ``DataFrame``. + + + + .. py:method:: _sqlite_edges_to_dataframe(categorical: bool = True, formatters: Optional[List[Callable]] = None) -> pandas.DataFrame + + + .. py:method:: validate(data) + + + .. py:method:: add_geomappings(data) + + + .. py:method:: register(write_empty=True, **kwargs) + + Legacy method to register a database with the metadata store. + Writing data automatically sets the following metadata: + * *depends*: Names of the databases that this database references, e.g. "biosphere" + * *number*: Number of processes in this database. + + + .. py:method:: deregister() + + Legacy method to remove an object from the metadata store. Does not delete any data. + + + +.. py:function:: get_activity(key=None, **kwargs) + + Support multiple ways to get exactly one activity node. + + ``key`` can be an integer or a key tuple. + + +.. py:function:: get_node(**kwargs) + + +.. py:class:: Method(name) + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + A manager for an impact assessment method. This class can register or deregister methods, write intermediate data, process data to parameter arrays, validate, and copy methods. + + The Method class never holds intermediate data, but it can load or write intermediate data. The only attribute is *name*, which is the name of the method being managed. + + Instantiation does not load any data. If this method is not yet registered in the metadata store, a warning is written to ``stdout``. + + Methods are hierarchally structured, and this structure is preserved in the method name. It is a tuple of strings, like ``('ecological scarcity 2006', 'total', 'natural resources')``. + + The data schema for IA methods is: + + .. code-block:: python + + Schema([Any( + [valid_tuple, maybe_uncertainty], # site-generic + [valid_tuple, maybe_uncertainty, object] # regionalized + )]) + + where: + * *valid_tuple* (tuple): A dataset identifier, like ``("biosphere", "CO2")``. + * *maybe_uncertainty* (uncertainty dict or number): Either a number or an uncertainty dictionary. + * *object* (object, optional) is a location identifier, used only for regionalized LCIA. + + :param \* *name*: Name of impact assessment method to manage. + :type \* *name*: tuple + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = characterization_matrix + + + + .. py:method:: add_geomappings(data) + + Add objects to ``geomapping``, if necessary. + + :param \* *data*: The data + :type \* *data*: object + + + .. py:method:: process_row(row) + + Given ``(flow, amount, maybe location)``, return a dictionary for array insertion. + + + .. py:method:: write(data, process=True) + + Serialize intermediate data to disk. + + Sets the metadata key ``num_cfs`` automatically. + + + .. py:method:: process(**extra_metadata) + + Process intermediate data from a Python dictionary to a `stats_arrays `_ array, which is a `NumPy `_ `Structured `_ `Array `_. A structured array (also called record array) is a heterogeneous array, where each column has a different label and data type. + + Processed arrays are saved in the ``processed`` directory. + + If the uncertainty type is no uncertainty, undefined, or not specified, then the 'amount' value is used for 'loc' as well. This is needed for the random number generator. + + Doesn't return anything, but writes a file to disk. + + + + +.. py:class:: Searcher(database) + + .. py:method:: __enter__() + + + .. py:method:: __exit__(type, value, traceback) + + + .. py:method:: search(string, limit=25, facet=None, proxy=True, boosts=None, filter=None, mask=None, node_class=None) + + + +.. py:class:: IndexManager(database_path, dir_name='whoosh') + + .. py:method:: get() + + + .. py:method:: create() + + + .. py:method:: _format_dataset(ds) + + + .. py:method:: add_dataset(ds) + + + .. py:method:: add_datasets(datasets) + + + .. py:method:: update_dataset(ds) + + + .. py:method:: delete_dataset(ds) + + + .. py:method:: delete_database() + + + +.. py:class:: Weighting + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + LCIA weighting data - used to combine or compare different impact categories. + + The data schema for weighting is a one-element list: + + .. code-block:: python + + Schema(All( + [uncertainty_dict], + Length(min=1, max=1) + )) + + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = weighting_matrix + + + + .. py:method:: write(data) + + Because of DataStore assumptions, need a one-element list + + + .. py:method:: process_row(row) + + Return an empty tuple (as ``dtype_fields`` is empty), and the weighting uncertainty dictionary. + + + +.. py:class:: Normalization + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + LCIA normalization data - used to transform meaningful units, like mass or damage, into "person-equivalents" or some such thing. + + The data schema for IA normalization is: + + .. code-block:: python + + Schema([ + [valid_tuple, maybe_uncertainty] + ]) + + where: + * ``valid_tuple`` is a dataset identifier, like ``("biosphere", "CO2")`` + * ``maybe_uncertainty`` is either a number or an uncertainty dictionary + + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = normalization_matrix + + + + .. py:method:: process_row(row) + + Given ``(flow key, amount)``, return a dictionary for array insertion. + + + +.. py:function:: get_id(key) + + +.. py:data:: Node + + + + +.. py:data:: Edge + + + + +.. py:function:: prepare_lca_inputs(demand=None, method=None, weighting=None, normalization=None, demands=None, remapping=True, demand_database_last=True) + + Prepare LCA input arguments in Brightway 2.5 style. + + +.. py:data:: databases + + + + +.. py:function:: extract_brightway_databases(database_names, add_properties=False, add_identifiers=False) + + Extract a Brightway2 SQLiteBackend database to the Wurst internal format. + + ``database_names`` is a list of database names. You should already be in the correct project. + + Returns a list of dataset documents. + + +.. py:data:: mapping + + + + +.. py:data:: parameters + + + + diff --git a/sphinx/technical/api/bw2data/logs/index.rst b/sphinx/technical/api/bw2data/logs/index.rst new file mode 100644 index 0000000..f6478d1 --- /dev/null +++ b/sphinx/technical/api/bw2data/logs/index.rst @@ -0,0 +1,74 @@ +:py:mod:`bw2data.logs` +====================== + +.. py:module:: bw2data.logs + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.logs.FakeLog + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.logs.get_logger + bw2data.logs.get_io_logger + bw2data.logs.get_verbose_logger + bw2data.logs.upload_logs_to_server + bw2data.logs.close_log + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.logs.anyjson + + +.. py:data:: anyjson + + + + +.. py:class:: FakeLog + + Like a log object, but does nothing + + .. py:method:: fake_function(*args, **kwargs) + + + .. py:method:: __getattr__(attr) + + + +.. py:function:: get_logger(name, level=logging.INFO) + + +.. py:function:: get_io_logger(name) + + Build a logger that records only relevent data for display later as HTML. + + +.. py:function:: get_verbose_logger(name, level=logging.WARNING) + + +.. py:function:: upload_logs_to_server(metadata={}) + + +.. py:function:: close_log(log) + + Detach log handlers; flush to disk + + diff --git a/sphinx/technical/api/bw2data/meta/index.rst b/sphinx/technical/api/bw2data/meta/index.rst new file mode 100644 index 0000000..454b79b --- /dev/null +++ b/sphinx/technical/api/bw2data/meta/index.rst @@ -0,0 +1,235 @@ +:py:mod:`bw2data.meta` +====================== + +.. py:module:: bw2data.meta + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.meta.GeoMapping + bw2data.meta.Databases + bw2data.meta.CalculationSetups + bw2data.meta.DynamicCalculationSetups + bw2data.meta.Methods + bw2data.meta.WeightingMeta + bw2data.meta.NormalizationMeta + bw2data.meta.Preferences + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.meta.geomapping + bw2data.meta.methods + bw2data.meta.normalizations + bw2data.meta.preferences + bw2data.meta.weightings + bw2data.meta.calculation_setups + bw2data.meta.dynamic_calculation_setups + + +.. py:class:: GeoMapping(*args, **kwargs) + + Bases: :py:obj:`bw2data.serialization.PickledDict` + + A dictionary that maps location codes to integers. Needed because parameter arrays have integer ``geo`` fields. + + File data is stored in ``geomapping.pickle``. + + This dictionary does not support setting items directly; instead, use the ``add`` method to add multiple keys. + + .. py:attribute:: filename + :annotation: = geomapping.pickle + + + + .. py:method:: add(keys) + + Add a set of keys. These keys can already be in the mapping; only new keys will be added. + + :param \* *keys*: The keys to add. + :type \* *keys*: list + + + .. py:method:: delete(keys) + + Delete a set of keys. + + :param \*keys*: The keys to delete. + :type \*keys*: list + + + .. py:method:: __setitem__(key, value) + :abstractmethod: + + + .. py:method:: __str__() + + Return str(self). + + + .. py:method:: __len__() + + + +.. py:class:: Databases(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.SerializedDict` + + A dictionary for database metadata. This class includes methods to manage database versions. File data is saved in ``databases.json``. + + .. py:attribute:: filename + :annotation: = databases.json + + + + .. py:method:: increment_version(database, number=None) + + Increment the ``database`` version. Returns the new version. + + + .. py:method:: version(database) + + Return the ``database`` version + + + .. py:method:: set_modified(database) + + + .. py:method:: set_dirty(database) + + + .. py:method:: clean() + + + .. py:method:: __delitem__(name) + + + +.. py:class:: CalculationSetups(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.PickledDict` + + A dictionary for calculation setups. + + Keys: + * `inv`: List of functional units, e.g. ``[{(key): amount}, {(key): amount}]`` + * `ia`: List of LCIA methods, e.g. ``[(method), (method)]``. + + + .. py:attribute:: filename + :annotation: = setups.pickle + + + + +.. py:class:: DynamicCalculationSetups(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.PickledDict` + + A dictionary for Dynamic calculation setups. + + Keys: + * `inv`: List of functional units, e.g. ``[{(key): amount}, {(key): amount}]`` + * `ia`: Dictionary of orst case LCIA method and the relative dynamic LCIA method, e.g. `` [{dLCIA_method_1_worstcase:dLCIA_method_1 , dLCIA_method_2_worstcase:dLCIA_method_2}]``. + + + .. py:attribute:: filename + :annotation: = dynamicsetups.pickle + + + + +.. py:class:: Methods(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.CompoundJSONDict` + + A dictionary for method metadata. File data is saved in ``methods.json``. + + .. py:attribute:: filename + :annotation: = methods.json + + + + +.. py:class:: WeightingMeta(dirpath=None) + + Bases: :py:obj:`Methods` + + A dictionary for weighting metadata. File data is saved in ``methods.json``. + + .. py:attribute:: filename + :annotation: = weightings.json + + + + +.. py:class:: NormalizationMeta(dirpath=None) + + Bases: :py:obj:`Methods` + + A dictionary for normalization metadata. File data is saved in ``methods.json``. + + .. py:attribute:: filename + :annotation: = normalizations.json + + + + +.. py:class:: Preferences(*args, **kwargs) + + Bases: :py:obj:`bw2data.serialization.PickledDict` + + A dictionary of project-specific preferences. + + .. py:attribute:: filename + :annotation: = preferences.pickle + + + + +.. py:data:: geomapping + + + + +.. py:data:: methods + + + + +.. py:data:: normalizations + + + + +.. py:data:: preferences + + + + +.. py:data:: weightings + + + + +.. py:data:: calculation_setups + + + + +.. py:data:: dynamic_calculation_setups + + + + diff --git a/sphinx/technical/api/bw2data/method/index.rst b/sphinx/technical/api/bw2data/method/index.rst new file mode 100644 index 0000000..d37ba8b --- /dev/null +++ b/sphinx/technical/api/bw2data/method/index.rst @@ -0,0 +1,96 @@ +:py:mod:`bw2data.method` +======================== + +.. py:module:: bw2data.method + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.method.Method + + + + +.. py:class:: Method(name) + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + A manager for an impact assessment method. This class can register or deregister methods, write intermediate data, process data to parameter arrays, validate, and copy methods. + + The Method class never holds intermediate data, but it can load or write intermediate data. The only attribute is *name*, which is the name of the method being managed. + + Instantiation does not load any data. If this method is not yet registered in the metadata store, a warning is written to ``stdout``. + + Methods are hierarchally structured, and this structure is preserved in the method name. It is a tuple of strings, like ``('ecological scarcity 2006', 'total', 'natural resources')``. + + The data schema for IA methods is: + + .. code-block:: python + + Schema([Any( + [valid_tuple, maybe_uncertainty], # site-generic + [valid_tuple, maybe_uncertainty, object] # regionalized + )]) + + where: + * *valid_tuple* (tuple): A dataset identifier, like ``("biosphere", "CO2")``. + * *maybe_uncertainty* (uncertainty dict or number): Either a number or an uncertainty dictionary. + * *object* (object, optional) is a location identifier, used only for regionalized LCIA. + + :param \* *name*: Name of impact assessment method to manage. + :type \* *name*: tuple + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = characterization_matrix + + + + .. py:method:: add_geomappings(data) + + Add objects to ``geomapping``, if necessary. + + :param \* *data*: The data + :type \* *data*: object + + + .. py:method:: process_row(row) + + Given ``(flow, amount, maybe location)``, return a dictionary for array insertion. + + + .. py:method:: write(data, process=True) + + Serialize intermediate data to disk. + + Sets the metadata key ``num_cfs`` automatically. + + + .. py:method:: process(**extra_metadata) + + Process intermediate data from a Python dictionary to a `stats_arrays `_ array, which is a `NumPy `_ `Structured `_ `Array `_. A structured array (also called record array) is a heterogeneous array, where each column has a different label and data type. + + Processed arrays are saved in the ``processed`` directory. + + If the uncertainty type is no uncertainty, undefined, or not specified, then the 'amount' value is used for 'loc' as well. This is needed for the random number generator. + + Doesn't return anything, but writes a file to disk. + + + + diff --git a/sphinx/technical/api/bw2data/parameters/index.rst b/sphinx/technical/api/bw2data/parameters/index.rst new file mode 100644 index 0000000..2724205 --- /dev/null +++ b/sphinx/technical/api/bw2data/parameters/index.rst @@ -0,0 +1,1055 @@ +:py:mod:`bw2data.parameters` +============================ + +.. py:module:: bw2data.parameters + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.parameters.ParameterBase + bw2data.parameters.ProjectParameter + bw2data.parameters.DatabaseParameter + bw2data.parameters.ActivityParameter + bw2data.parameters.ParameterizedExchange + bw2data.parameters.Group + bw2data.parameters.GroupDependency + bw2data.parameters.ParameterManager + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.parameters.get_new_symbols + bw2data.parameters.alter_parameter_formula + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.parameters.clean + bw2data.parameters.nonempty + bw2data.parameters.AUTOUPDATE_TRIGGER + bw2data.parameters._CROSSDATABASE_TEMPLATE + bw2data.parameters.CROSSDATASE_INSERT_TRIGGER + bw2data.parameters.CROSSDATASE_UPDATE_TRIGGER + bw2data.parameters._CROSSGROUP_TEMPLATE + bw2data.parameters.CROSSGROUP_INSERT_TRIGGER + bw2data.parameters.CROSSGROUP_UPDATE_TRIGGER + bw2data.parameters._CLOSURE_TEMPLATE + bw2data.parameters.GD_INSERT_TRIGGER + bw2data.parameters.GD_UPDATE_TRIGGER + bw2data.parameters._PE_GROUP_TEMPLATE + bw2data.parameters.PE_INSERT_TRIGGER + bw2data.parameters.PE_UPDATE_TRIGGER + bw2data.parameters.parameters + + +.. py:data:: clean + + + + +.. py:data:: nonempty + + + Autoupdate `updated` field in Group when parameters change + +.. py:data:: AUTOUPDATE_TRIGGER + :annotation: = Multiline-String + + .. raw:: html + +

Show Value + + .. code-block:: text + :linenos: + + CREATE TRIGGER IF NOT EXISTS {table}_{action}_trigger AFTER {action} ON {table} BEGIN + UPDATE group_table SET updated = datetime('now') WHERE name = {name}; + END; + + .. raw:: html + +
+ + Activity parameter groups can't cross databases + +.. py:data:: _CROSSDATABASE_TEMPLATE + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + CREATE TRIGGER IF NOT EXISTS ap_crossdatabase_{action} BEFORE {action} ON activityparameter BEGIN + SELECT CASE WHEN + ((SELECT COUNT(*) FROM activityparameter WHERE "group" = NEW."group") > 0) + AND (NEW.database NOT IN (SELECT DISTINCT "database" FROM activityparameter where "group" = NEW."group")) + THEN RAISE(ABORT,'Cross database group') + END; + END; + + .. raw:: html + +
+ + + +.. py:data:: CROSSDATASE_INSERT_TRIGGER + + + + +.. py:data:: CROSSDATASE_UPDATE_TRIGGER + + + Activities can't be in multiple activity parameter groups + +.. py:data:: _CROSSGROUP_TEMPLATE + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + CREATE TRIGGER IF NOT EXISTS ap_crossgroup_{action} BEFORE {action} ON activityparameter BEGIN + SELECT CASE WHEN EXISTS (SELECT * FROM activityparameter AS a WHERE + a.database = NEW.database AND + a.code = NEW.code AND + a."group" != NEW."group") + THEN RAISE(ABORT,'Cross group activity') + END; + END; + + .. raw:: html + +
+ + + +.. py:data:: CROSSGROUP_INSERT_TRIGGER + + + + +.. py:data:: CROSSGROUP_UPDATE_TRIGGER + + + No circular dependences in activity parameter group dependencies + +.. py:data:: _CLOSURE_TEMPLATE + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + CREATE TRIGGER IF NOT EXISTS gd_circular_{action} BEFORE {action} ON groupdependency BEGIN + SELECT CASE WHEN EXISTS (SELECT * FROM groupdependency AS g WHERE g."group" = NEW.depends AND g.depends = NEW."group") + THEN RAISE(ABORT,'Circular dependency') + END; + END; + + + .. raw:: html + +
+ + + +.. py:data:: GD_INSERT_TRIGGER + + + + +.. py:data:: GD_UPDATE_TRIGGER + + + Parameterized exchange groups must be in activityparameters table + +.. py:data:: _PE_GROUP_TEMPLATE + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + CREATE TRIGGER IF NOT EXISTS pe_group_{action} BEFORE {action} ON parameterizedexchange BEGIN + SELECT CASE WHEN + ((SELECT COUNT(*) FROM activityparameter WHERE "group" = NEW."group") < 1) + THEN RAISE(ABORT,'Missing activity parameter group') + END; + END; + + + .. raw:: html + +
+ + + +.. py:data:: PE_INSERT_TRIGGER + + + + +.. py:data:: PE_UPDATE_TRIGGER + + + + +.. py:class:: ParameterBase + + Bases: :py:obj:`peewee.Model` + + .. py:attribute:: __repr__ + + + + + .. py:method:: __lt__(other) + + + .. py:method:: create_table() + :classmethod: + + + .. py:method:: expire_downstream(group) + :staticmethod: + + Expire any activity parameters that depend on this group + + + +.. py:class:: ProjectParameter + + Bases: :py:obj:`ParameterBase` + + Parameter set for a project. Group name is 'project'. + + Columns: + + * name: str, unique + * formula: str, optional + * amount: float, optional + * data: object, optional. Used for any other metadata. + + Note that there is no magic for reading and writing to ``data`` (unlike ``Activity`` objects) - it must be used directly. + + + .. py:property:: dict + + Parameter data as a standardized dictionary + + .. py:attribute:: name + + + + + .. py:attribute:: formula + + + + + .. py:attribute:: amount + + + + + .. py:attribute:: data + + + + + .. py:attribute:: _old_name + :annotation: = 'project' + + + + .. py:attribute:: _new_name + :annotation: = 'project' + + + + .. py:attribute:: _db_table + :annotation: = projectparameter + + + + .. py:method:: __str__() + + + .. py:method:: save(*args, **kwargs) + + + .. py:method:: load(group=None) + :staticmethod: + + Return dictionary of parameter data with names as keys and ``.dict()`` as values. + + + .. py:method:: static(ignored='project', only=None) + :staticmethod: + + Get dictionary of ``{name: amount}`` for all project parameters. + + ``only`` restricts returned names to ones found in ``only``. ``ignored`` included for API compatibility with other ``recalculate`` methods. + + + .. py:method:: expired() + :staticmethod: + + Return boolean - is this group expired? + + + .. py:method:: recalculate(ignored=None) + :staticmethod: + + Recalculate all parameters. + + ``ignored`` included for API compatibility with other ``recalculate`` methods - it will really be ignored. + + + .. py:method:: dependency_chain() + :staticmethod: + + Determine if ```ProjectParameter`` parameters have dependencies + within the group. + + Returns: + + .. code-block:: python + + [ + { + 'kind': 'project', + 'group': 'project', + 'names': set of variables names + } + ] + + + + .. py:method:: is_dependency_within_group(name) + :staticmethod: + + + .. py:method:: is_deletable() + + Perform a test to see if the current parameter can be deleted. + + + .. py:method:: update_formula_parameter_name(old, new) + :classmethod: + + Performs an update of the formula of relevant parameters. + + NOTE: Make sure to wrap this in an .atomic() statement! + + + +.. py:class:: DatabaseParameter + + Bases: :py:obj:`ParameterBase` + + Parameter set for a database. Group name is the name of the database. + + Columns: + + * database: str + * name: str, unique within a database + * formula: str, optional + * amount: float, optional + * data: object, optional. Used for any other metadata. + + Note that there is no magic for reading and writing to ``data`` (unlike ``Activity`` objects) - it must be used directly. + + + .. py:class:: Meta + + .. py:attribute:: indexes + :annotation: = [[['database', 'name'], True]] + + + + .. py:attribute:: constraints + + + + + + .. py:property:: dict + + Parameter data as a standardized dictionary + + .. py:attribute:: database + + + + + .. py:attribute:: name + + + + + .. py:attribute:: formula + + + + + .. py:attribute:: amount + + + + + .. py:attribute:: data + + + + + .. py:attribute:: _old_name + :annotation: = OLD.database + + + + .. py:attribute:: _new_name + :annotation: = NEW.database + + + + .. py:attribute:: _db_table + :annotation: = databaseparameter + + + + .. py:method:: __str__() + + + .. py:method:: load(database) + :staticmethod: + + Return dictionary of parameter data with names as keys and ``.dict()`` as values. + + + .. py:method:: expired(database) + :staticmethod: + + Return boolean - is this group expired? + + + .. py:method:: static(database, only=None) + :staticmethod: + + Return dictionary of {name: amount} for database group. + + + .. py:method:: recalculate(database) + :staticmethod: + + Recalculate all database parameters for ``database``, if expired. + + + .. py:method:: dependency_chain(group, include_self=False) + :staticmethod: + + Find where each missing variable is defined in dependency chain. + + If ``include_self`` is True will include parameters within the group as possible dependencies + + Returns: + + .. code-block:: python + + [ + { + 'kind': one of 'project', 'database', 'activity', + 'group': group name, + 'names': set of variables names + } + ] + + + + .. py:method:: is_dependency_within_group(name, database) + :staticmethod: + + + .. py:method:: save(*args, **kwargs) + + Save this model instance + + + .. py:method:: is_deletable() + + Perform a test to see if the current parameter can be deleted. + + + .. py:method:: is_dependent_on(name) + :staticmethod: + + Test if any database parameters are dependent on the given + project parameter name. + + + .. py:method:: update_formula_project_parameter_name(old, new) + :classmethod: + + Performs an update of the formula of relevant parameters. + + This method specifically targets project parameters used in database + formulas + + + .. py:method:: update_formula_database_parameter_name(old, new) + :classmethod: + + Performs an update of the formula of relevant parameters. + + This method specifically targets database parameters used in database + formulas + + + +.. py:class:: ActivityParameter + + Bases: :py:obj:`ParameterBase` + + Parameter set for a group of activities. + + Columns: + + * group: str + * database: str + * code: str. Code and database define the linked activity for this parameter. + * name: str, unique within a group + * formula: str, optional + * amount: float, optional + * data: object, optional. Used for any other metadata. + + Activities can only have parameters in one group. Group names cannot be 'project' or the name of any existing database. + + Activity parameter groups can depend on other activity parameter groups, so that a formula in group "a" can depend on a variable in group "b". This dependency information is stored in ``Group.order`` - in our small example, we could define the following: + + .. code-block:: python + + a = Group.get(name="a") + a.order = ["b", "c"] + a.save() + + In this case, a variable not found in "a" would be searched for in "b" and then "c", in that order. Database and then project parameters are also implicitly included at the end of ``Group.order``. + + Note that there is no magic for reading and writing to ``data`` (unlike ``Activity`` objects) - it must be used directly. + + + .. py:class:: Meta + + .. py:attribute:: indexes + :annotation: = [[['group', 'name'], True]] + + + + .. py:attribute:: constraints + + + + + + .. py:property:: dict + + Parameter data as a standardized dictionary + + .. py:attribute:: group + + + + + .. py:attribute:: database + + + + + .. py:attribute:: code + + + + + .. py:attribute:: name + + + + + .. py:attribute:: formula + + + + + .. py:attribute:: amount + + + + + .. py:attribute:: data + + + + + .. py:attribute:: _old_name + :annotation: = OLD."group" + + + + .. py:attribute:: _new_name + :annotation: = NEW."group" + + + + .. py:attribute:: _db_table + :annotation: = activityparameter + + + + .. py:method:: __str__() + + + .. py:method:: load(group) + :staticmethod: + + Return dictionary of parameter data with names as keys and ``.dict()`` as values. + + + .. py:method:: static(group, only=None, full=False) + :staticmethod: + + Get dictionary of ``{name: amount}`` for parameters defined in ``group``. + + ``only`` restricts returned names to ones found in ``only``. ``full`` returns all names, including those found in the dependency chain. + + + .. py:method:: _static_dependencies(group) + :staticmethod: + + Get dictionary of ``{name: amount}`` for all variables defined in dependency chain. + + Be careful! This could have variables which overlap with local variable names. Designed for internal use. + + + .. py:method:: insert_dummy(group, activity) + :staticmethod: + + + .. py:method:: expired(group) + :staticmethod: + + Return boolean - is this group expired? + + + .. py:method:: dependency_chain(group, include_self=False) + :staticmethod: + + Find where each missing variable is defined in dependency chain. + + Will also load in all parameters needed to resolve the ``ParameterizedExchanges`` for this group. + + If ``include_self`` is True will include parameters within the group as possible dependencies + + Returns: + + .. code-block:: python + + [ + { + 'kind': one of 'project', 'database', 'activity', + 'group': group name, + 'names': set of variables names + } + ] + + + + .. py:method:: is_dependency_within_group(name, group, include_order=False) + :staticmethod: + + Determine if the given parameter `name` is a dependency within + the given activity `group`. + + The optional ``include_order`` parameter will include dependencies + from groups found in the the ``Group``.`order` field. + + + .. py:method:: recalculate(group) + :staticmethod: + + Recalculate all values for activity parameters in this group, and update their underlying `Activity` and `Exchange` values. + + + .. py:method:: recalculate_exchanges(group) + :staticmethod: + + Recalculate formulas for all parameterized exchanges in group ``group``. + + + .. py:method:: save(*args, **kwargs) + + Save this model instance + + + .. py:method:: is_deletable() + + Perform a test to see if the current parameter can be deleted. + + + .. py:method:: is_dependent_on(name, group) + :staticmethod: + + Test if any activity parameters are dependent on the given + parameter name from the given group. + + + .. py:method:: update_formula_project_parameter_name(old, new) + :classmethod: + + Performs an update of the formula of relevant parameters. + + This method specifically targets project parameters used in activity + formulas + + + .. py:method:: update_formula_database_parameter_name(old, new) + :classmethod: + + Performs an update of the formula of relevant parameters. + + This method specifically targets database parameters used in activity + formulas + + + .. py:method:: update_formula_activity_parameter_name(old, new, include_order=False) + :classmethod: + + Performs an update of the formula of relevant parameters. + + This method specifically targets activity parameters used in activity + formulas + + + .. py:method:: create_table() + :classmethod: + + + +.. py:class:: ParameterizedExchange + + Bases: :py:obj:`peewee.Model` + + .. py:attribute:: group + + + + + .. py:attribute:: exchange + + + + + .. py:attribute:: formula + + + + + .. py:method:: create_table() + :classmethod: + + + .. py:method:: save(*args, **kwargs) + + + .. py:method:: load(group) + :staticmethod: + + Return dictionary of parameter data with names as keys and ``.dict()`` as values. + + + .. py:method:: recalculate(group) + :staticmethod: + + Shortcut for ``ActivityParameter.recalculate_exchanges``. + + + +.. py:class:: Group + + Bases: :py:obj:`peewee.Model` + + .. py:class:: Meta + + .. py:attribute:: table_name + :annotation: = group_table + + + + + .. py:attribute:: name + + + + + .. py:attribute:: fresh + + + + + .. py:attribute:: updated + + + + + .. py:attribute:: order + + + + + .. py:method:: expire() + + Set ``fresh`` to ``False`` + + + .. py:method:: freshen() + + Set ``fresh`` to ``True`` + + + .. py:method:: save(*args, **kwargs) + + Save this model instance. Will remove 'project' and database names from ``order``. + + + .. py:method:: purge_order() + + + +.. py:class:: GroupDependency + + Bases: :py:obj:`peewee.Model` + + .. py:class:: Meta + + .. py:attribute:: indexes + :annotation: = [[['group', 'depends'], True]] + + + + .. py:attribute:: constraints + + + + + + .. py:attribute:: group + + + + + .. py:attribute:: depends + + + + + .. py:method:: save(*args, **kwargs) + + + .. py:method:: create_table() + :classmethod: + + + +.. py:class:: ParameterManager + + .. py:method:: add_to_group(group, activity) + + Add `activity` to group. + + Creates ``group`` if needed. + + Will delete any existing ``ActivityParameter`` for this activity. + + Deletes `parameters` key from `Activity`. + + + .. py:method:: remove_from_group(group, activity, restore_amounts=True) + + Remove `activity` from `group`. + + Will delete any existing ``ActivityParameter`` and ``ParameterizedExchange`` for this activity. + + Restores `parameters` key to this `Activity`. + By default, restores `amount` value of each parameterized exchange + of the `Activity` to the original value. This can be avoided by using + the ``restore_amounts`` parameter. + + + + .. py:method:: add_exchanges_to_group(group, activity) + + Add exchanges with formulas from ``activity`` to ``group``. + + Every exchange with a formula field will have its original `amount` + value stored as `original_amount`. This original value can be + restored when parameterization is removed from the activity with + `remove_from_group`. + + + + .. py:method:: remove_exchanges_from_group(group, activity, restore_original=True) + + Takes a group and activity and removes all ``ParameterizedExchange`` + objects from the group. + + The ``restore_original`` parameter determines if the original amount + values will be restored to those exchanges where a formula was used + to alter the amount. + + + + .. py:method:: new_project_parameters(data, overwrite=True) + + Efficiently and correctly enter multiple parameters. + + Will overwrite existing project parameters with the same name, unless ``overwrite`` is false, in which case a ``ValueError`` is raised. + + ``data`` should be a list of dictionaries: + + .. code-block:: python + + [{ + 'name': name of variable (unique), + 'amount': numeric value of variable (optional), + 'formula': formula in Python as string (optional), + optional keys like uncertainty, etc. (no limitations) + }] + + + + .. py:method:: new_database_parameters(data, database, overwrite=True) + + Efficiently and correctly enter multiple parameters. Deletes **all** existing database parameters for this database. + + Will overwrite existing database parameters with the same name, unless ``overwrite`` is false, in which case a ``ValueError`` is raised. + + ``database`` should be an existing database. ``data`` should be a list of dictionaries: + + .. code-block:: python + + [{ + 'name': name of variable (unique), + 'amount': numeric value of variable (optional), + 'formula': formula in Python as string (optional), + optional keys like uncertainty, etc. (no limitations) + }] + + + + .. py:method:: new_activity_parameters(data, group, overwrite=True) + + Efficiently and correctly enter multiple parameters. Deletes **all** existing activity parameters for this group. + + Will overwrite existing parameters in the same group with the same name, unless ``overwrite`` is false, in which case a ``ValueError`` is raised. + + Input parameters must refer to a single, existing database. + + ``group`` is the group name; will be autocreated if necessary. ``data`` should be a list of dictionaries: + + .. code-block:: python + + [{ + 'name': name of variable (unique), + 'database': activity database, + 'code': activity code, + 'amount': numeric value of variable (optional), + 'formula': formula in Python as string (optional), + optional keys like uncertainty, etc. (no limitations) + }] + + + + .. py:method:: rename_project_parameter(parameter, new_name, update_dependencies=False) + + Given a parameter and a new name, safely update the parameter. + + Will raise a TypeError if the given parameter is of the incorrect type. + Will raise a ValueError if other parameters depend on the given one + and ``update_dependencies`` is False. + + + + .. py:method:: rename_database_parameter(parameter, new_name, update_dependencies=False) + + Given a parameter and a new name, safely update the parameter. + + Will raise a TypeError if the given parameter is of the incorrect type. + Will raise a ValueError if other parameters depend on the given one + and ``update_dependencies`` is False. + + + + .. py:method:: rename_activity_parameter(parameter, new_name, update_dependencies=False) + + Given a parameter and a new name, safely update the parameter. + + Will raise a TypeError if the given parameter is of the incorrect type. + Will raise a ValueError if other parameters depend on the given one + and ``update_dependencies`` is False. + + + + .. py:method:: recalculate() + + Recalculate all expired project, database, and activity parameters, as well as exchanges. + + + .. py:method:: __len__() + + + .. py:method:: __repr__() + + Return repr(self). + + + +.. py:data:: parameters + + + + +.. py:function:: get_new_symbols(data, context=None) + + +.. py:function:: alter_parameter_formula(parameter, old, new) + + Replace the `old` part with `new` in the formula field and return + the parameter itself. + + diff --git a/sphinx/technical/api/bw2data/project/index.rst b/sphinx/technical/api/bw2data/project/index.rst new file mode 100644 index 0000000..61058d0 --- /dev/null +++ b/sphinx/technical/api/bw2data/project/index.rst @@ -0,0 +1,250 @@ +:py:mod:`bw2data.project` +========================= + +.. py:module:: bw2data.project + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.project.ProjectDataset + bw2data.project.ProjectManager + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.project.lockable + bw2data.project.writable_project + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.project.READ_ONLY_PROJECT + bw2data.project.projects + + +.. py:data:: READ_ONLY_PROJECT + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + + ***Read only project*** + + This project is being used by another process and no writes can be made until: + 1. You close the other program, or switch to a different project, *and* + 2. You call `projects.enable_writes` *and* get the response `True`. + + If you are **sure** that this warning is incorrect, call + `projects.enable_writes(force=True)` to enable writes. + + + .. raw:: html + +
+ + + +.. py:function:: lockable() + + +.. py:class:: ProjectDataset + + Bases: :py:obj:`peewee.Model` + + .. py:attribute:: data + + + + + .. py:attribute:: name + + + + + .. py:attribute:: full_hash + + + + + .. py:attribute:: __repr__ + + + + + .. py:method:: __str__() + + + .. py:method:: __lt__(other) + + + +.. py:class:: ProjectManager + + Bases: :py:obj:`collections.abc.Iterable` + + .. py:property:: current + + + .. py:property:: twofive + + + .. py:property:: dir + + + .. py:property:: logs_dir + + + .. py:property:: output_dir + + Get directory for output files. + + Uses environment variable ``BRIGHTWAY2_OUTPUT_DIR``; ``preferences['output_dir']``; or directory ``output`` in current project. + + Returns output directory path. + + .. py:attribute:: _basic_directories + :annotation: = ['backups', 'intermediate', 'lci', 'processed'] + + + + .. py:attribute:: _is_temp_dir + :annotation: = False + + + + .. py:attribute:: read_only + :annotation: = False + + + + .. py:method:: __iter__() + + + .. py:method:: __contains__(name) + + + .. py:method:: __len__() + + + .. py:method:: __repr__() + + Return repr(self). + + + .. py:method:: _get_base_directories() + + + .. py:method:: _create_base_directories() + + + .. py:method:: set_current(name, writable=True, update=True) + + + .. py:method:: _do_automatic_updates() + + Run any available automatic updates + + + .. py:method:: _reset_meta() + + + .. py:method:: _reset_sqlite3_databases() + + + .. py:method:: create_project(name=None, **kwargs) + + + .. py:method:: copy_project(new_name, switch=True) + + Copy current project to a new project named ``new_name``. If ``switch``, switch to new project. + + + .. py:method:: request_directory(name) + + Return the absolute path to the subdirectory ``dirname``, creating it if necessary. + + Returns ``False`` if directory can't be created. + + + .. py:method:: _use_temp_directory() + + Point the ProjectManager towards a temporary directory instead of `user_data_dir`. + + Used exclusively for tests. + + + .. py:method:: _restore_orig_directory() + + Point the ProjectManager back to original directories. + + Used exclusively in tests. + + + .. py:method:: migrate_project_25() + + Migrate project to Brightway 2.5. + + Reprocesses all databases and LCIA objects. + + + .. py:method:: delete_project(name=None, delete_dir=False) + + Delete project ``name``, or the current project. + + ``name`` is the project to delete. If ``name`` is not provided, delete the current project. + + By default, the underlying project directory is not deleted; only the project name is removed from the list of active projects. If ``delete_dir`` is ``True``, then also delete the project directory. + + If deleting the current project, this function sets the current directory to ``default`` if it exists, or to a random project. + + Returns the current project. + + + .. py:method:: purge_deleted_directories() + + Delete project directories for projects which are no longer registered. + + Returns number of directories deleted. + + + .. py:method:: report() + + Give a report on current projects, including installed databases and file sizes. + + Returns tuples of ``(project name, number of databases, size of all databases (GB))``. + + + .. py:method:: use_short_hash() + + + .. py:method:: use_full_hash() + + + +.. py:data:: projects + + + + +.. py:function:: writable_project(wrapped, instance, args, kwargs) + + diff --git a/sphinx/technical/api/bw2data/proxies/index.rst b/sphinx/technical/api/bw2data/proxies/index.rst new file mode 100644 index 0000000..d963cef --- /dev/null +++ b/sphinx/technical/api/bw2data/proxies/index.rst @@ -0,0 +1,224 @@ +:py:mod:`bw2data.proxies` +========================= + +.. py:module:: bw2data.proxies + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.proxies.ProxyBase + bw2data.proxies.ActivityProxyBase + bw2data.proxies.ExchangeProxyBase + + + + +.. py:class:: ProxyBase(data, *args, **kwargs) + + Bases: :py:obj:`collections.abc.MutableMapping` + + A MutableMapping is a generic container for associating + key/value pairs. + + This class provides concrete generic implementations of all + methods except for __getitem__, __setitem__, __delitem__, + __iter__, and __len__. + + .. py:attribute:: __repr__ + + + + + .. py:method:: as_dict() + + + .. py:method:: __str__() + + Return str(self). + + + .. py:method:: __contains__(key) + + + .. py:method:: __iter__() + + + .. py:method:: __len__() + + + .. py:method:: __getitem__(key) + + + .. py:method:: __setitem__(key, value) + + + .. py:method:: __delitem__(key) + + + .. py:method:: __eq__(other) + + Return self==value. + + + .. py:method:: __hash__() + + Return hash(self). + + + +.. py:class:: ActivityProxyBase(data, *args, **kwargs) + + Bases: :py:obj:`ProxyBase` + + A MutableMapping is a generic container for associating + key/value pairs. + + This class provides concrete generic implementations of all + methods except for __getitem__, __setitem__, __delitem__, + __iter__, and __len__. + + .. py:property:: key + + + .. py:method:: __str__() + + Return str(self). + + + .. py:method:: __eq__(other) + + Return self==value. + + + .. py:method:: __lt__(other) + + Return self", ">=": Mathematical relations + * "is", "not": Identity relations. Work on any Python object. + * "in", "notin": List or string relations. + * "iin", "iis", "inot": Case-insensitive string relations. + * "len": Length relation. + + In addition, any function which defines a relationship between an input and an output can also be used. + + .. rubric:: Examples + + * All ``name`` values are *"foo"*: ``Filter("name", "is", "foo")`` + * All ``name`` values include the string *"foo"*: ``Filter("name", "has", "foo")`` + * Category (a list of categories and subcategories) includes *"foo"*: ``Filter("category", "has", "foo")`` + + :param \* *key*: The field to filter on. + :type \* *key*: str + :param \* *function*: One of the pre-defined filters, or a callable object. + :type \* *function*: str or object + :param \* *value*: The value to test against. + :type \* *value*: object + + :returns: A ``Result`` object which wraps a new data dictionary. + + .. py:method:: __call__(data) + + + +.. py:function:: NF(value) + + Shortcut for a name filter + + +.. py:function:: PF(value) + + Shortcut for a reference product filter + + diff --git a/sphinx/technical/api/bw2data/search/index.rst b/sphinx/technical/api/bw2data/search/index.rst new file mode 100644 index 0000000..0b422a5 --- /dev/null +++ b/sphinx/technical/api/bw2data/search/index.rst @@ -0,0 +1,70 @@ +:py:mod:`bw2data.search` +======================== + +.. py:module:: bw2data.search + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + indices/index.rst + schema/index.rst + search/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.search.IndexManager + bw2data.search.Searcher + + + + +.. py:class:: IndexManager(database_path, dir_name='whoosh') + + .. py:method:: get() + + + .. py:method:: create() + + + .. py:method:: _format_dataset(ds) + + + .. py:method:: add_dataset(ds) + + + .. py:method:: add_datasets(datasets) + + + .. py:method:: update_dataset(ds) + + + .. py:method:: delete_dataset(ds) + + + .. py:method:: delete_database() + + + +.. py:class:: Searcher(database) + + .. py:method:: __enter__() + + + .. py:method:: __exit__(type, value, traceback) + + + .. py:method:: search(string, limit=25, facet=None, proxy=True, boosts=None, filter=None, mask=None, node_class=None) + + + diff --git a/sphinx/technical/api/bw2data/search/indices/index.rst b/sphinx/technical/api/bw2data/search/indices/index.rst new file mode 100644 index 0000000..19b6b7e --- /dev/null +++ b/sphinx/technical/api/bw2data/search/indices/index.rst @@ -0,0 +1,46 @@ +:py:mod:`bw2data.search.indices` +================================ + +.. py:module:: bw2data.search.indices + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.search.indices.IndexManager + + + + +.. py:class:: IndexManager(database_path, dir_name='whoosh') + + .. py:method:: get() + + + .. py:method:: create() + + + .. py:method:: _format_dataset(ds) + + + .. py:method:: add_dataset(ds) + + + .. py:method:: add_datasets(datasets) + + + .. py:method:: update_dataset(ds) + + + .. py:method:: delete_dataset(ds) + + + .. py:method:: delete_database() + + + diff --git a/sphinx/technical/api/bw2data/search/schema/index.rst b/sphinx/technical/api/bw2data/search/schema/index.rst new file mode 100644 index 0000000..8dde95f --- /dev/null +++ b/sphinx/technical/api/bw2data/search/schema/index.rst @@ -0,0 +1,14 @@ +:py:mod:`bw2data.search.schema` +=============================== + +.. py:module:: bw2data.search.schema + + +Module Contents +--------------- + +.. py:data:: bw2_schema + + + + diff --git a/sphinx/technical/api/bw2data/search/search/index.rst b/sphinx/technical/api/bw2data/search/search/index.rst new file mode 100644 index 0000000..cc43294 --- /dev/null +++ b/sphinx/technical/api/bw2data/search/search/index.rst @@ -0,0 +1,44 @@ +:py:mod:`bw2data.search.search` +=============================== + +.. py:module:: bw2data.search.search + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.search.search.Searcher + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.search.search.keysplit + + + +.. py:function:: keysplit(strng) + + Split an activity key joined into a single string using the magic sequence `⊡|⊡` + + +.. py:class:: Searcher(database) + + .. py:method:: __enter__() + + + .. py:method:: __exit__(type, value, traceback) + + + .. py:method:: search(string, limit=25, facet=None, proxy=True, boosts=None, filter=None, mask=None, node_class=None) + + + diff --git a/sphinx/technical/api/bw2data/serialization/index.rst b/sphinx/technical/api/bw2data/serialization/index.rst new file mode 100644 index 0000000..1cc3c35 --- /dev/null +++ b/sphinx/technical/api/bw2data/serialization/index.rst @@ -0,0 +1,210 @@ +:py:mod:`bw2data.serialization` +=============================== + +.. py:module:: bw2data.serialization + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.serialization.JsonWrapper + bw2data.serialization.JsonSanitizer + bw2data.serialization.SerializedDict + bw2data.serialization.PickledDict + bw2data.serialization.CompoundJSONDict + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.serialization.anyjson + + +.. py:data:: anyjson + + + + +.. py:class:: JsonWrapper + + .. py:method:: dump(data, filepath) + :classmethod: + + + .. py:method:: dump_bz2(data, filepath) + :classmethod: + + + .. py:method:: load(file) + :classmethod: + + + .. py:method:: load_bz2(filepath) + :classmethod: + + + .. py:method:: dumps(data) + :classmethod: + + + .. py:method:: loads(data) + :classmethod: + + + +.. py:class:: JsonSanitizer + + .. py:method:: sanitize(data) + :classmethod: + + + .. py:method:: load(data) + :classmethod: + + + +.. py:class:: SerializedDict(dirpath=None) + + Bases: :py:obj:`collections.abc.MutableMapping` + + Base class for dictionary that can be `serialized `_ to or unserialized from disk. Uses JSON as its storage format. Has most of the methods of a dictionary. + + Upon instantiation, the serialized dictionary is read from disk. + + .. py:property:: list + + List the keys of the dictionary. This is a property, and does not need to be called. + + .. py:attribute:: __repr__ + + + + + .. py:method:: load() + + Load the serialized data. Creates the file if not yet present. + + + .. py:method:: flush() + + Serialize the current data to disk. + + + .. py:method:: __getitem__(key) + + + .. py:method:: __setitem__(key, value) + + + .. py:method:: __contains__(key) + + + .. py:method:: __str__() + + Return str(self). + + + .. py:method:: __delitem__(name) + + + .. py:method:: __len__() + + + .. py:method:: __iter__() + + + .. py:method:: __hash__() + + Return hash(self). + + + .. py:method:: keys() + + D.keys() -> a set-like object providing a view on D's keys + + + .. py:method:: values() + + D.values() -> an object providing a view on D's values + + + .. py:method:: serialize(filepath=None) + + Method to do the actual serialization. Can be replaced with other serialization formats. + + :param \* *filepath*: Provide an alternate filepath (e.g. for backup). + :type \* *filepath*: str, optional + + + .. py:method:: deserialize() + + Load the serialized data. Can be replaced with other serialization formats. + + + .. py:method:: pack(data) + + Transform the data, if necessary. Needed because JSON must have strings as dictionary keys. + + + .. py:method:: unpack(data) + + Return serialized data to true form. + + + .. py:method:: random() + + Return a random key. + + + .. py:method:: backup() + + Write a backup version of the data to the ``backups`` directory. + + + +.. py:class:: PickledDict(dirpath=None) + + Bases: :py:obj:`SerializedDict` + + Subclass of ``SerializedDict`` that uses the pickle format instead of JSON. + + .. py:method:: serialize() + + Method to do the actual serialization. Can be replaced with other serialization formats. + + :param \* *filepath*: Provide an alternate filepath (e.g. for backup). + :type \* *filepath*: str, optional + + + .. py:method:: deserialize() + + Load the serialized data. Can be replaced with other serialization formats. + + + +.. py:class:: CompoundJSONDict(dirpath=None) + + Bases: :py:obj:`SerializedDict` + + Subclass of ``SerializedDict`` that allows tuples as dictionary keys (not allowed in JSON). + + .. py:method:: pack(data) + + Transform the dictionary to a list because JSON can't handle lists as keys + + + .. py:method:: unpack(data) + + Transform data back to a dictionary + + + diff --git a/sphinx/technical/api/bw2data/sqlite/index.rst b/sphinx/technical/api/bw2data/sqlite/index.rst new file mode 100644 index 0000000..e88d68b --- /dev/null +++ b/sphinx/technical/api/bw2data/sqlite/index.rst @@ -0,0 +1,80 @@ +:py:mod:`bw2data.sqlite` +======================== + +.. py:module:: bw2data.sqlite + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.sqlite.PickleField + bw2data.sqlite.SubstitutableDatabase + bw2data.sqlite.JSONField + bw2data.sqlite.TupleJSONField + + + + +.. py:class:: PickleField + + Bases: :py:obj:`peewee.BlobField` + + .. py:method:: db_value(value) + + + .. py:method:: python_value(value) + + + +.. py:class:: SubstitutableDatabase(filepath, tables) + + .. py:property:: db + + + .. py:method:: _create_database() + + + .. py:method:: change_path(filepath) + + + .. py:method:: atomic() + + + .. py:method:: execute_sql(*args, **kwargs) + + + .. py:method:: transaction() + + + .. py:method:: vacuum() + + + +.. py:class:: JSONField + + Bases: :py:obj:`peewee.TextField` + + Simpler JSON field that doesn't support advanced querying and is human-readable + + .. py:method:: db_value(value) + + + .. py:method:: python_value(value) + + + +.. py:class:: TupleJSONField + + Bases: :py:obj:`JSONField` + + Simpler JSON field that doesn't support advanced querying and is human-readable + + .. py:method:: python_value(value) + + + diff --git a/sphinx/technical/api/bw2data/tests/index.rst b/sphinx/technical/api/bw2data/tests/index.rst new file mode 100644 index 0000000..caa6197 --- /dev/null +++ b/sphinx/technical/api/bw2data/tests/index.rst @@ -0,0 +1,82 @@ +:py:mod:`bw2data.tests` +======================= + +.. py:module:: bw2data.tests + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.tests.BW2DataTest + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.tests.bw2test + + + +.. py:class:: BW2DataTest(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:attribute:: _multiprocess_can_split_ + :annotation: = True + + + + .. py:method:: setUp() + + Hook method for setting up the test fixture before exercising it. + + + .. py:method:: extra_setup() + + + .. py:method:: test_setup_clean() + + + +.. py:function:: bw2test(wrapped, instance, args, kwargs) + + diff --git a/sphinx/technical/api/bw2data/updates/index.rst b/sphinx/technical/api/bw2data/updates/index.rst new file mode 100644 index 0000000..0538aa8 --- /dev/null +++ b/sphinx/technical/api/bw2data/updates/index.rst @@ -0,0 +1,221 @@ +:py:mod:`bw2data.updates` +========================= + +.. py:module:: bw2data.updates + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.updates.Updates + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.updates.hash_re + bw2data.updates.is_hash + bw2data.updates.UPTODATE_WARNING + bw2data.updates.UPDATE_ACTIVITYDATASET + bw2data.updates.UPDATE_EXCHANGEDATASET + + +.. py:data:: hash_re + + + + +.. py:data:: is_hash + + + + +.. py:data:: UPTODATE_WARNING + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + + + Your data needs to be updated. Please run the following program on the command line: + + bw2-uptodate + + + .. raw:: html + +
+ + + +.. py:data:: UPDATE_ACTIVITYDATASET + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + + BEGIN; + DROP INDEX IF EXISTS "activitydataset_key"; + ALTER TABLE ActivityDataset rename to AD_old; + CREATE TABLE "activitydataset" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "database" TEXT NOT NULL, + "code" TEXT NOT NULL, + "data" BLOB NOT NULL, + "location" TEXT, + "name" TEXT, + "product" TEXT, + "type" TEXT + ); + INSERT INTO ActivityDataset ("database", "code", "data", "location", "name", "product", "type") + SELECT substr(key, 0, instr(key, '⊡')), + substr("key", instr("key", '⊡') + 1), + "data", + "location", + "name", + "product", + "type" + FROM AD_old; + CREATE UNIQUE INDEX "activitydataset_key" ON "activitydataset" ("database", "code"); + DROP TABLE AD_old; + COMMIT; + + + .. raw:: html + +
+ + + +.. py:data:: UPDATE_EXCHANGEDATASET + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + + BEGIN; + DROP INDEX IF EXISTS "exchangedataset_database"; + DROP INDEX IF EXISTS "exchangedataset_input"; + DROP INDEX IF EXISTS "exchangedataset_output"; + ALTER TABLE ExchangeDataset rename to ED_old; + CREATE TABLE "exchangedataset" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "data" BLOB NOT NULL, + "input_database" TEXT NOT NULL, + "input_code" TEXT NOT NULL, + "output_database" TEXT NOT NULL, + "output_code" TEXT NOT NULL, + "type" TEXT NOT NULL + ); + INSERT INTO ExchangeDataset ("data", "input_database", "input_code", "output_database", "output_code", "type") + SELECT "data", + substr("input", 0, instr("input", '⊡')), + substr("input", instr("input", '⊡') + 1), + substr("output", 0, instr("output", '⊡')), + substr("output", instr("output", '⊡') + 1), + "type" + FROM ED_old; + CREATE INDEX "exchangedataset_input" ON "exchangedataset" ("input_database", "input_code"); + CREATE INDEX "exchangedataset_output" ON "exchangedataset" ("output_database", "output_code"); + DROP TABLE ED_old; + COMMIT; + + + .. raw:: html + +
+ + + +.. py:class:: Updates + + .. py:attribute:: UPDATES + + + + + .. py:method:: explain(key) + :classmethod: + + + .. py:method:: do_update(key) + :classmethod: + + + .. py:method:: check_status(verbose=True) + :classmethod: + + Check if updates need to be applied. + + :returns: List of needed updates (strings), if any. + + + .. py:method:: set_initial_updates() + :classmethod: + + + .. py:method:: check_automatic_updates() + :classmethod: + + Get list of automatic updates to be applied + + + .. py:method:: reprocess_all_1_0() + :classmethod: + + 1.0: Reprocess all to make sure default 'loc' value inserted when not specified. + + + .. py:method:: schema_change_20_compound_keys() + :classmethod: + + + .. py:method:: database_search_directories_20() + :classmethod: + + + .. py:method:: processed_data_format_change_23() + :classmethod: + + + .. py:method:: expire_all_processed_data_40() + :classmethod: + + + .. py:method:: fix_migrations_filename() + :classmethod: + + "Fix migration data filenames to use shorter hash. + + See https://github.com/brightway-lca/brightway2-io/issues/115 + + + .. py:method:: _reprocess_all() + :classmethod: + + + diff --git a/sphinx/technical/api/bw2data/utils/index.rst b/sphinx/technical/api/bw2data/utils/index.rst new file mode 100644 index 0000000..8e99a15 --- /dev/null +++ b/sphinx/technical/api/bw2data/utils/index.rst @@ -0,0 +1,207 @@ +:py:mod:`bw2data.utils` +======================= + +.. py:module:: bw2data.utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.utils.safe_filename + bw2data.utils.maybe_path + bw2data.utils.natural_sort + bw2data.utils.random_string + bw2data.utils.combine_methods + bw2data.utils.clean_exchanges + bw2data.utils.as_uncertainty_dict + bw2data.utils.uncertainify + bw2data.utils.recursive_str_to_unicode + bw2data.utils.combine_databases + bw2data.utils.merge_databases + bw2data.utils.download_file + bw2data.utils.web_ui_accessible + bw2data.utils.open_activity_in_webbrowser + bw2data.utils.set_data_dir + bw2data.utils.switch_data_directory + bw2data.utils.create_in_memory_zipfile_from_directory + bw2data.utils.get_node + bw2data.utils.get_activity + bw2data.utils.get_geocollection + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.utils.TYPE_DICTIONARY + bw2data.utils.DOWNLOAD_URL + bw2data.utils.POSITIVE_DISTRIBUTIONS + + +.. py:data:: TYPE_DICTIONARY + + + + +.. py:data:: DOWNLOAD_URL + :annotation: = https://brightway.dev/data/ + + + +.. py:function:: safe_filename(*args, **kwargs) + + +.. py:function:: maybe_path(x) + + +.. py:function:: natural_sort(l) + + Sort the given list in the way that humans expect, e.g. 9 before 10. + + +.. py:function:: random_string(length=8) + + Generate a random string of letters and numbers. + + :param \* *length*: Length of string, default is 8 + :type \* *length*: int + + :returns: A string (not unicode) + + +.. py:function:: combine_methods(name, *ms) + + Combine LCIA methods by adding duplicate characterization factors. + + :param \* *ms*: Any number of method ids, e.g. ``("my method", "wow"), ("another method", "wheee")``. + :type \* *ms*: one or more method id tuples + + :returns: The new Method instance. + + +.. py:function:: clean_exchanges(data) + + Make sure all exchange inputs are tuples, not lists. + + +.. py:data:: POSITIVE_DISTRIBUTIONS + + + + +.. py:function:: as_uncertainty_dict(value) + + Given either a number or a ``stats_arrays`` uncertainty dict, return an uncertainty dict + + +.. py:function:: uncertainify(data, distribution=None, bounds_factor=0.1, sd_factor=0.1) + + Add some rough uncertainty to exchanges. + + .. warning:: This function only changes exchanges with no uncertainty type or uncertainty type ``UndefinedUncertainty``, and does not change production exchanges! + + Can only apply normal or uniform uncertainty distributions; default is uniform. Distribution, if specified, must be a ``stats_array`` uncertainty object. + + ``data`` is a LCI data dictionary. + + If using the normal distribution: + + * ``sd_factor`` will be multiplied by the mean to calculate the standard deviation. + * If no bounds are desired, set ``bounds_factor`` to ``None``. + * Otherwise, the bounds will be ``[(1 - bounds_factor) * mean, (1 + bounds_factor) * mean]``. + + If using the uniform distribution, then the bounds are ``[(1 - bounds_factor) * mean, (1 + bounds_factor) * mean]``. + + Returns the modified data. + + +.. py:function:: recursive_str_to_unicode(data, encoding='utf8') + + Convert the strings inside a (possibly nested) python data structure to unicode strings using `encoding`. + + +.. py:function:: combine_databases(name, *dbs) + + Combine databases into new database called ``name``. + + +.. py:function:: merge_databases(parent_db, other) + + Merge ``other`` into ``parent_db``, including updating exchanges. + + All databases must be SQLite databases. + + ``parent_db`` and ``other`` should be the names of databases. + + Doesn't return anything. + + +.. py:function:: download_file(filename, directory='downloads', url=None) + + Download a file and write it to disk in ``downloads`` directory. + + If ``url`` is None, uses the Brightway2 data base URL. ``url`` should everything up to the filename, such that ``url`` + ``filename`` is the valid complete URL to download from. + + Streams download to reduce memory usage. + + :param \* *filename*: The filename to download. + :type \* *filename*: str + :param \* *directory*: Directory to save the file. Created if it doesn't already exist. + :type \* *directory*: str, optional + :param \* *url*: URL where the file is located, if not the default Brightway data URL. + :type \* *url*: str, optional + + :returns: The path of the created file. + + +.. py:function:: web_ui_accessible() + + Test if ``bw2-web`` is running and accessible. Returns ``True`` or ``False``. + + +.. py:function:: open_activity_in_webbrowser(activity) + + Open a dataset document in the Brightway2 web UI. Requires ``bw2-web`` to be running. + + ``activity`` is a dataset key, e.g. ``("foo", "bar")``. + + +.. py:function:: set_data_dir(dirpath, permanent=True) + + Set the Brightway2 data directory to ``dirpath``. + + If ``permanent`` is ``True``, then set ``dirpath`` as the default data directory. + + Creates ``dirpath`` if needed. Also creates basic directories, and resets metadata. + + + +.. py:function:: switch_data_directory(dirpath) + + +.. py:function:: create_in_memory_zipfile_from_directory(path) + + +.. py:function:: get_node(**kwargs) + + +.. py:function:: get_activity(key=None, **kwargs) + + Support multiple ways to get exactly one activity node. + + ``key`` can be an integer or a key tuple. + + +.. py:function:: get_geocollection(location, default_global_location=False) + + conservative approach to finding geocollections. Won't guess about ecoinvent or other databases. + + diff --git a/sphinx/technical/api/bw2data/validate/index.rst b/sphinx/technical/api/bw2data/validate/index.rst new file mode 100644 index 0000000..687b265 --- /dev/null +++ b/sphinx/technical/api/bw2data/validate/index.rst @@ -0,0 +1,77 @@ +:py:mod:`bw2data.validate` +========================== + +.. py:module:: bw2data.validate + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2data.validate.valid_tuple + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2data.validate.uncertainty_dict + bw2data.validate.exchange + bw2data.validate.lci_dataset + bw2data.validate.db_validator + bw2data.validate.maybe_uncertainty + bw2data.validate.ia_validator + bw2data.validate.weighting_validator + bw2data.validate.normalization_validator + + +.. py:function:: valid_tuple(obj) + + +.. py:data:: uncertainty_dict + + + + +.. py:data:: exchange + + + + +.. py:data:: lci_dataset + + + + +.. py:data:: db_validator + + + + +.. py:data:: maybe_uncertainty + + + + +.. py:data:: ia_validator + + + + +.. py:data:: weighting_validator + + + + +.. py:data:: normalization_validator + + + + diff --git a/sphinx/technical/api/bw2data/version/index.rst b/sphinx/technical/api/bw2data/version/index.rst new file mode 100644 index 0000000..7795a0e --- /dev/null +++ b/sphinx/technical/api/bw2data/version/index.rst @@ -0,0 +1,14 @@ +:py:mod:`bw2data.version` +========================= + +.. py:module:: bw2data.version + + +Module Contents +--------------- + +.. py:data:: version + :annotation: = [4, 0, 'DEV18'] + + + diff --git a/sphinx/technical/api/bw2data/weighting_normalization/index.rst b/sphinx/technical/api/bw2data/weighting_normalization/index.rst new file mode 100644 index 0000000..d32d7b4 --- /dev/null +++ b/sphinx/technical/api/bw2data/weighting_normalization/index.rst @@ -0,0 +1,102 @@ +:py:mod:`bw2data.weighting_normalization` +========================================= + +.. py:module:: bw2data.weighting_normalization + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2data.weighting_normalization.Weighting + bw2data.weighting_normalization.Normalization + + + + +.. py:class:: Weighting + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + LCIA weighting data - used to combine or compare different impact categories. + + The data schema for weighting is a one-element list: + + .. code-block:: python + + Schema(All( + [uncertainty_dict], + Length(min=1, max=1) + )) + + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = weighting_matrix + + + + .. py:method:: write(data) + + Because of DataStore assumptions, need a one-element list + + + .. py:method:: process_row(row) + + Return an empty tuple (as ``dtype_fields`` is empty), and the weighting uncertainty dictionary. + + + +.. py:class:: Normalization + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + LCIA normalization data - used to transform meaningful units, like mass or damage, into "person-equivalents" or some such thing. + + The data schema for IA normalization is: + + .. code-block:: python + + Schema([ + [valid_tuple, maybe_uncertainty] + ]) + + where: + * ``valid_tuple`` is a dataset identifier, like ``("biosphere", "CO2")`` + * ``maybe_uncertainty`` is either a number or an uncertainty dictionary + + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = normalization_matrix + + + + .. py:method:: process_row(row) + + Given ``(flow key, amount)``, return a dictionary for array insertion. + + + diff --git a/sphinx/technical/api/bw2io/backup/index.rst b/sphinx/technical/api/bw2io/backup/index.rst new file mode 100644 index 0000000..81690d1 --- /dev/null +++ b/sphinx/technical/api/bw2io/backup/index.rst @@ -0,0 +1,54 @@ +:py:mod:`bw2io.backup` +====================== + +.. py:module:: bw2io.backup + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.backup.backup_data_directory + bw2io.backup.backup_project_directory + bw2io.backup.restore_project_directory + + + +.. py:function:: backup_data_directory() + + Backup data directory to a ``.tar.gz`` (compressed tar archive). + + Backup archive is saved to the user's home directory. + + Restoration is done manually. Returns the filepath of the backup archive. + + +.. py:function:: backup_project_directory(project) + + Backup project data directory to a ``.tar.gz`` (compressed tar archive). + + ``project`` is the name of a project. + + Backup archive is saved to the user's home directory. + + Restoration is done using ``restore_project_directory``. + + Returns the filepath of the backup archive. + + +.. py:function:: restore_project_directory(fp) + + Restore backup created using ``backup_project_directory``. + + Raises an error is the project already exists. + + ``fp`` is the filepath of the backup archive. + + Returns the name of the newly created project. + + diff --git a/sphinx/technical/api/bw2io/chemidplus/index.rst b/sphinx/technical/api/bw2io/chemidplus/index.rst new file mode 100644 index 0000000..4af86d6 --- /dev/null +++ b/sphinx/technical/api/bw2io/chemidplus/index.rst @@ -0,0 +1,94 @@ +:py:mod:`bw2io.chemidplus` +========================== + +.. py:module:: bw2io.chemidplus + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.chemidplus.ChemIDPlus + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.chemidplus.canonical_cas + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.chemidplus.DIRPATH + + +.. py:data:: DIRPATH + + + + +.. py:function:: canonical_cas(s) + + CAS numbers have up to ten digits; we remove zero padding and add hyphens where needed. + + +.. py:exception:: Multiple + + Bases: :py:obj:`Exception` + + Multiple results for given search query. + + +.. py:exception:: Missing + + Bases: :py:obj:`Exception` + + 404 or other error code returned + + +.. py:class:: ChemIDPlus + + Use the `ChemIDPlus `__ API to lookup synonyms for chemicals, including pesticides. + + Always used to match against a master list. Seeded with names from ecoinvent. + + .. py:attribute:: CAS_TEMPLATE + :annotation: = https://chem.nlm.nih.gov/api/data/search?data=complete&exp=rn%2Feq%2F{cas} + + + + .. py:attribute:: NAME_TEMPLATE + :annotation: = https://chem.nlm.nih.gov/api/data/search?data=complete&exp=na%2Feq%2F{name} + + + + .. py:method:: match(synonym, search=True) + + + .. py:method:: match_cas(number) + + + .. py:method:: add_master_term(term, CAS) + + + .. py:method:: save_cache() + + + .. py:method:: load_cache() + + + .. py:method:: process_request(response) + + + diff --git a/sphinx/technical/api/bw2io/compatibility/index.rst b/sphinx/technical/api/bw2io/compatibility/index.rst new file mode 100644 index 0000000..aae8472 --- /dev/null +++ b/sphinx/technical/api/bw2io/compatibility/index.rst @@ -0,0 +1,29 @@ +:py:mod:`bw2io.compatibility` +============================= + +.. py:module:: bw2io.compatibility + + +Module Contents +--------------- + +.. py:data:: SIMAPRO_BIO_SUBCATEGORIES + + + + +.. py:data:: SIMAPRO_BIOSPHERE + + + + +.. py:data:: SIMAPRO_SYSTEM_MODELS + + + + +.. py:data:: ECOSPOLD_2_3_BIOSPHERE + + + + diff --git a/sphinx/technical/api/bw2io/data/exiopol/index.rst b/sphinx/technical/api/bw2io/data/exiopol/index.rst new file mode 100644 index 0000000..e53bf48 --- /dev/null +++ b/sphinx/technical/api/bw2io/data/exiopol/index.rst @@ -0,0 +1,22 @@ +:py:mod:`bw2io.data.exiopol` +============================ + +.. py:module:: bw2io.data.exiopol + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.data.exiopol.import_exiopol_IO_table + + + +.. py:function:: import_exiopol_IO_table(database_name, dir_path) + + diff --git a/sphinx/technical/api/bw2io/data/index.rst b/sphinx/technical/api/bw2io/data/index.rst new file mode 100644 index 0000000..7d77c4a --- /dev/null +++ b/sphinx/technical/api/bw2io/data/index.rst @@ -0,0 +1,222 @@ +:py:mod:`bw2io.data` +==================== + +.. py:module:: bw2io.data + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + exiopol/index.rst + + +Package Contents +---------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.data.write_json_file + bw2io.data.get_csv_example_filepath + bw2io.data.get_xlsx_example_filepath + bw2io.data.get_sheet + bw2io.data.get_biosphere_2_3_category_migration_data + bw2io.data.get_biosphere_2_3_name_migration_data + bw2io.data.get_simapro_water_migration_data + bw2io.data.get_us_lci_migration_data + bw2io.data.get_exiobase_biosphere_migration_data + bw2io.data.convert_simapro_ecoinvent_elementary_flows + bw2io.data.convert_simapro_ecoinvent_3_migration_data + bw2io.data.get_simapro_ecoinvent_3_migration_data + bw2io.data.convert_ecoinvent_2_301 + bw2io.data._add_new_ecoinvent_biosphere_flows + bw2io.data.convert_lcia_methods_data + bw2io.data.get_valid_geonames + bw2io.data.get_ecoinvent_pre35_migration_data + bw2io.data.update_db_ecoinvent_locations + bw2io.data.add_example_database + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.data.ECOSPOLD_2_3_BIOSPHERE + bw2io.data.SIMAPRO_BIOSPHERE + bw2io.data.normalize_units + bw2io.data.dirpath + bw2io.data.add_ecoinvent_33_biosphere_flows + bw2io.data.add_ecoinvent_34_biosphere_flows + bw2io.data.add_ecoinvent_35_biosphere_flows + bw2io.data.add_ecoinvent_36_biosphere_flows + bw2io.data.add_ecoinvent_37_biosphere_flows + bw2io.data.add_ecoinvent_38_biosphere_flows + bw2io.data.add_ecoinvent_39_biosphere_flows + + +.. py:data:: ECOSPOLD_2_3_BIOSPHERE + + + + +.. py:data:: SIMAPRO_BIOSPHERE + + + + +.. py:data:: normalize_units + + + + +.. py:data:: dirpath + + + + +.. py:function:: write_json_file(data, name) + + +.. py:function:: get_csv_example_filepath() + + +.. py:function:: get_xlsx_example_filepath() + + +.. py:function:: get_sheet(path, name) + + +.. py:function:: get_biosphere_2_3_category_migration_data() + + Get data for 2 -> 3 migration for biosphere flow categories + + +.. py:function:: get_biosphere_2_3_name_migration_data() + + Get migration data for 2 -> 3 biosphere flow names. + + This migration **must** be applied only after categories have been updated. + + Note that the input data excel sheet is **modified** from the raw data provided by ecoinvent - some biosphere flows which had no equivalent in ecospold2 were mapped using my best judgment. Name changes from 3.1 were also included. Modified cells are marked in **dark orange**. + + Note that not all rows have names in ecoinvent 3. There are a few energy resources that we don't update. For water flows, the categories are updated by a different strategy, and the names don't change, so we just ignore them for now. + + +.. py:function:: get_simapro_water_migration_data() + + +.. py:function:: get_us_lci_migration_data() + + Fix US LCI database name inconsistencies + + +.. py:function:: get_exiobase_biosphere_migration_data() + + Migrate to ecoinvent3 flow names + + +.. py:function:: convert_simapro_ecoinvent_elementary_flows() + + Write a correspondence list from SimaPro elementary flow names to ecoinvent 3 flow names to a JSON file. + + Uses custom SimaPro specific data. Ecoinvent 2 -> 3 conversion is in a separate JSON file. + + +.. py:function:: convert_simapro_ecoinvent_3_migration_data() + + +.. py:function:: get_simapro_ecoinvent_3_migration_data(version) + + Write a migrations data file from SimaPro activity names to ecoinvent 3 processes. + + Correspondence file is processed from Pré, and has the following fields: + + #. SimaPro name + #. Ecoinvent flow name + #. Location + #. Ecoinvent activity name + #. System model + #. SimaPro type + + Note that even the official matching data from Pré is incorrect, but works if we cast all strings to lower case. + + SimaPro type is either ``System terminated`` or ``Unit process``. We always match to unit processes regardless of SimaPro type. + + +.. py:function:: convert_ecoinvent_2_301() + + Write a migrations data file from ecoinvent 2 to 3.1. + + This is not simple, unfortunately. We have to deal with at least the following: + * Unit changes (e.g. cubic meters to MJ) + * Some datasets are deleted, and replaced by others + + + +.. py:function:: _add_new_ecoinvent_biosphere_flows(version) + + +.. py:data:: add_ecoinvent_33_biosphere_flows + + + + +.. py:data:: add_ecoinvent_34_biosphere_flows + + + + +.. py:data:: add_ecoinvent_35_biosphere_flows + + + + +.. py:data:: add_ecoinvent_36_biosphere_flows + + + + +.. py:data:: add_ecoinvent_37_biosphere_flows + + + + +.. py:data:: add_ecoinvent_38_biosphere_flows + + + + +.. py:data:: add_ecoinvent_39_biosphere_flows + + + + +.. py:function:: convert_lcia_methods_data() + + +.. py:function:: get_valid_geonames() + + Get list of short location names used in ecoinvent 3 + + +.. py:function:: get_ecoinvent_pre35_migration_data() + + +.. py:function:: update_db_ecoinvent_locations(database_name) + + Update ecoinvent location names for an existing database. + + Returns number of modified datasets. + + +.. py:function:: add_example_database(overwrite=True) + + diff --git a/sphinx/technical/api/bw2io/download_utils/index.rst b/sphinx/technical/api/bw2io/download_utils/index.rst new file mode 100644 index 0000000..d07ed74 --- /dev/null +++ b/sphinx/technical/api/bw2io/download_utils/index.rst @@ -0,0 +1,26 @@ +:py:mod:`bw2io.download_utils` +============================== + +.. py:module:: bw2io.download_utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.download_utils.get_filename + bw2io.download_utils.download_with_progressbar + + + +.. py:function:: get_filename(response) + + +.. py:function:: download_with_progressbar(url, filename=None, dirpath=None, chunk_size=4096 * 8) + + diff --git a/sphinx/technical/api/bw2io/errors/index.rst b/sphinx/technical/api/bw2io/errors/index.rst new file mode 100644 index 0000000..d48144e --- /dev/null +++ b/sphinx/technical/api/bw2io/errors/index.rst @@ -0,0 +1,72 @@ +:py:mod:`bw2io.errors` +====================== + +.. py:module:: bw2io.errors + + +Module Contents +--------------- + +.. py:exception:: InvalidPackage + + Bases: :py:obj:`Exception` + + bw2package data doesn't validate + + +.. py:exception:: UnsafeData + + Bases: :py:obj:`Exception` + + bw2package data comes from a class that isn't recognized by Brightway2 + + +.. py:exception:: UnsupportedExchange + + Bases: :py:obj:`Exception` + + This exchange uncertainty type can't be rescaled automatically + + +.. py:exception:: StrategyError + + Bases: :py:obj:`Exception` + + The strategy could not be applied + + +.. py:exception:: NonuniqueCode + + Bases: :py:obj:`Exception` + + Not all provided codes are unique + + +.. py:exception:: WrongDatabase + + Bases: :py:obj:`Exception` + + Dataset does not belong to this database + + +.. py:exception:: MultiprocessingError + + Bases: :py:obj:`Exception` + + Multiprocessing module error or incompatibility + + +.. py:exception:: UnallocatableDataset + + Bases: :py:obj:`Exception` + + GIven data cannot be sanely or deterministically allocated + + +.. py:exception:: MissingMigration + + Bases: :py:obj:`Exception` + + Needed migration data is missing + + diff --git a/sphinx/technical/api/bw2io/export/csv/index.rst b/sphinx/technical/api/bw2io/export/csv/index.rst new file mode 100644 index 0000000..2cda488 --- /dev/null +++ b/sphinx/technical/api/bw2io/export/csv/index.rst @@ -0,0 +1,142 @@ +:py:mod:`bw2io.export.csv` +========================== + +.. py:module:: bw2io.export.csv + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.export.csv.CSVFormatter + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.export.csv.reformat + bw2io.export.csv.write_lci_csv + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.export.csv.EXCHANGE_COLUMNS + bw2io.export.csv.PARAMETER_COLUMNS + bw2io.export.csv.MAPPING + + +.. py:function:: reformat(value) + + +.. py:data:: EXCHANGE_COLUMNS + :annotation: = ['name', 'amount', 'database', 'location', 'unit', 'categories', 'type', 'formula', 'uncertainty... + + + +.. py:data:: PARAMETER_COLUMNS + :annotation: = ['name', 'amount', 'formula', 'uncertainty type', 'loc', 'scale', 'shape', 'minimum', 'maximum'] + + + +.. py:data:: MAPPING + + + + +.. py:class:: CSVFormatter(database_name, objs=None) + + Bases: :py:obj:`object` + + .. py:method:: get_project_parameters() + + + .. py:method:: get_database_parameters() + + + .. py:method:: get_activity_parameters(act) + + + .. py:method:: get_database_metadata() + + + .. py:method:: get_activity_metadata(act) + + + .. py:method:: exchange_as_dict(exc) + + + .. py:method:: order_dicts(data, kind='exchange') + + + .. py:method:: get_exchanges(act) + + + .. py:method:: get_activity(act) + + + .. py:method:: get_unformatted_data() + + Return all database data as a nested dictionary: + + .. code-block:: python + + { + 'database': { + 'name': name, + 'metadata': [(key, value)], + 'parameters': { + 'columns': [column names], + 'data': [[column values for each row]] + }, + 'project parameters': { + 'columns': [column names], + 'data': [[column values for each row]] + } + }, + 'activities': [{ + 'name': name, + 'metadata': [(key, value)], + 'parameters': { + 'columns': [column names], + 'group': 'group name', + 'data': [[column values for each row]] + }, + 'exchanges': { + 'columns': [column names], + 'data': [[column values for each row]] + } + }] + } + + + + .. py:method:: get_formatted_data(sections=None) + + + +.. py:function:: write_lci_csv(database_name, objs=None, sections=None, dirpath=None) + + Export database `database_name` to a CSV file. + + Not all data can be exported. The following constraints apply: + + * Nested data, e.g. `{'foo': {'bar': 'baz'}}` are excluded. CSV is not a great format for nested data. However, *tuples* are exported, and the characters `::` are used to join elements of the tuple. + * The only well-supported data types are strings, numbers, and booleans. + + Default directory is ``projects.output_dir``, set ``dirpath`` to have save the file somewhere else. + + Returns the filepath of the exported file. + + + diff --git a/sphinx/technical/api/bw2io/export/excel/index.rst b/sphinx/technical/api/bw2io/export/excel/index.rst new file mode 100644 index 0000000..e5da6e7 --- /dev/null +++ b/sphinx/technical/api/bw2io/export/excel/index.rst @@ -0,0 +1,60 @@ +:py:mod:`bw2io.export.excel` +============================ + +.. py:module:: bw2io.export.excel + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.export.excel.create_valid_worksheet_name + bw2io.export.excel.lci_matrices_to_excel + bw2io.export.excel.write_lci_excel + bw2io.export.excel.write_lci_matching + bw2io.export.excel.write_lcia_matching + + + +.. py:function:: create_valid_worksheet_name(string) + + Exclude invalid characters and names. + + Data from http://www.accountingweb.com/technology/excel/seven-characters-you-cant-use-in-worksheet-names. + + +.. py:function:: lci_matrices_to_excel(database_name, include_descendants=True) + + Fake docstring + + +.. py:function:: write_lci_excel(database_name, objs=None, sections=None, dirpath=None) + + Export database `database_name` to an Excel spreadsheet. + + Not all data can be exported. The following constraints apply: + + * Nested data, e.g. `{'foo': {'bar': 'baz'}}` are excluded. Spreadsheets are not a great format for nested data. However, *tuples* are exported, and the characters `::` are used to join elements of the tuple. + * The only well-supported data types are strings, numbers, and booleans. + + Default directory is ``projects.output_dir``, set ``dirpath`` to have save the file somewhere else. + + Returns the filepath of the exported file. + + + +.. py:function:: write_lci_matching(db, database_name, only_unlinked=False, only_activity_names=False) + + Write matched and unmatched exchanges to Excel file + + +.. py:function:: write_lcia_matching(db, name) + + Write matched and unmatched CFs to Excel file + + diff --git a/sphinx/technical/api/bw2io/export/gexf/index.rst b/sphinx/technical/api/bw2io/export/gexf/index.rst new file mode 100644 index 0000000..02f5950 --- /dev/null +++ b/sphinx/technical/api/bw2io/export/gexf/index.rst @@ -0,0 +1,81 @@ +:py:mod:`bw2io.export.gexf` +=========================== + +.. py:module:: bw2io.export.gexf + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.export.gexf.DatabaseToGEXF + bw2io.export.gexf.DatabaseSelectionToGEXF + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.export.gexf.keyword_to_gephi_graph + + + +.. py:class:: DatabaseToGEXF(database, include_descendants=False) + + Bases: :py:obj:`object` + + Export a Gephi graph for a database. + + Call ``.export()`` to export the file after class instantiation. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *include_descendants*: Include databases which are linked from ``database``. + :type \* *include_descendants*: bool + + .. warning:: ``include_descendants`` is not yet implemented. + + + .. py:method:: export() + + Export the Gephi XML file. Returns the filepath of the created file. + + + .. py:method:: get_data(E) + + Get Gephi nodes and edges. + + + +.. py:class:: DatabaseSelectionToGEXF(database, keys) + + Bases: :py:obj:`DatabaseToGEXF` + + Export a Gephi graph for a selection of activities from a database. + + Also includes all inputs for the filtered activities. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *keys*: The activity keys to export. + :type \* *keys*: str + + +.. py:function:: keyword_to_gephi_graph(database, keyword) + + Export a Gephi graph for a database for all activities whose names include the string ``keyword``. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *keyword*: Keyword to search for. + :type \* *keyword*: str + + :returns: The filepath of the exported file. + + diff --git a/sphinx/technical/api/bw2io/export/index.rst b/sphinx/technical/api/bw2io/export/index.rst new file mode 100644 index 0000000..7bd6de2 --- /dev/null +++ b/sphinx/technical/api/bw2io/export/index.rst @@ -0,0 +1,135 @@ +:py:mod:`bw2io.export` +====================== + +.. py:module:: bw2io.export + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + csv/index.rst + excel/index.rst + gexf/index.rst + matlab/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.export.DatabaseSelectionToGEXF + bw2io.export.DatabaseToGEXF + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.export.write_lci_csv + bw2io.export.lci_matrices_to_excel + bw2io.export.write_lci_excel + bw2io.export.keyword_to_gephi_graph + bw2io.export.lci_matrices_to_matlab + + + +.. py:function:: write_lci_csv(database_name, objs=None, sections=None, dirpath=None) + + Export database `database_name` to a CSV file. + + Not all data can be exported. The following constraints apply: + + * Nested data, e.g. `{'foo': {'bar': 'baz'}}` are excluded. CSV is not a great format for nested data. However, *tuples* are exported, and the characters `::` are used to join elements of the tuple. + * The only well-supported data types are strings, numbers, and booleans. + + Default directory is ``projects.output_dir``, set ``dirpath`` to have save the file somewhere else. + + Returns the filepath of the exported file. + + + +.. py:function:: lci_matrices_to_excel(database_name, include_descendants=True) + + Fake docstring + + +.. py:function:: write_lci_excel(database_name, objs=None, sections=None, dirpath=None) + + Export database `database_name` to an Excel spreadsheet. + + Not all data can be exported. The following constraints apply: + + * Nested data, e.g. `{'foo': {'bar': 'baz'}}` are excluded. Spreadsheets are not a great format for nested data. However, *tuples* are exported, and the characters `::` are used to join elements of the tuple. + * The only well-supported data types are strings, numbers, and booleans. + + Default directory is ``projects.output_dir``, set ``dirpath`` to have save the file somewhere else. + + Returns the filepath of the exported file. + + + +.. py:class:: DatabaseSelectionToGEXF(database, keys) + + Bases: :py:obj:`DatabaseToGEXF` + + Export a Gephi graph for a selection of activities from a database. + + Also includes all inputs for the filtered activities. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *keys*: The activity keys to export. + :type \* *keys*: str + + +.. py:class:: DatabaseToGEXF(database, include_descendants=False) + + Bases: :py:obj:`object` + + Export a Gephi graph for a database. + + Call ``.export()`` to export the file after class instantiation. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *include_descendants*: Include databases which are linked from ``database``. + :type \* *include_descendants*: bool + + .. warning:: ``include_descendants`` is not yet implemented. + + + .. py:method:: export() + + Export the Gephi XML file. Returns the filepath of the created file. + + + .. py:method:: get_data(E) + + Get Gephi nodes and edges. + + + +.. py:function:: keyword_to_gephi_graph(database, keyword) + + Export a Gephi graph for a database for all activities whose names include the string ``keyword``. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *keyword*: Keyword to search for. + :type \* *keyword*: str + + :returns: The filepath of the exported file. + + +.. py:function:: lci_matrices_to_matlab(database_name) + + diff --git a/sphinx/technical/api/bw2io/export/matlab/index.rst b/sphinx/technical/api/bw2io/export/matlab/index.rst new file mode 100644 index 0000000..f8e00ba --- /dev/null +++ b/sphinx/technical/api/bw2io/export/matlab/index.rst @@ -0,0 +1,22 @@ +:py:mod:`bw2io.export.matlab` +============================= + +.. py:module:: bw2io.export.matlab + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.export.matlab.lci_matrices_to_matlab + + + +.. py:function:: lci_matrices_to_matlab(database_name) + + diff --git a/sphinx/technical/api/bw2io/extractors/csv/index.rst b/sphinx/technical/api/bw2io/extractors/csv/index.rst new file mode 100644 index 0000000..76640ca --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/csv/index.rst @@ -0,0 +1,28 @@ +:py:mod:`bw2io.extractors.csv` +============================== + +.. py:module:: bw2io.extractors.csv + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.csv.CSVExtractor + + + + +.. py:class:: CSVExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath, encoding='utf-8-sig') + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/ecospold1/index.rst b/sphinx/technical/api/bw2io/extractors/ecospold1/index.rst new file mode 100644 index 0000000..ec368d0 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/ecospold1/index.rst @@ -0,0 +1,87 @@ +:py:mod:`bw2io.extractors.ecospold1` +==================================== + +.. py:module:: bw2io.extractors.ecospold1 + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.ecospold1.Ecospold1DataExtractor + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.ecospold1.getattr2 + + + +.. py:function:: getattr2(obj, attr) + + +.. py:class:: Ecospold1DataExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(path, db_name, use_mp=True) + :classmethod: + + + .. py:method:: process_file(filepath, db_name) + :classmethod: + + + .. py:method:: is_valid_ecospold1(dataset) + :classmethod: + + + .. py:method:: process_dataset(dataset, filename, db_name) + :classmethod: + + + .. py:method:: process_exchanges(dataset) + :classmethod: + + + .. py:method:: process_allocation(exc, dataset) + :classmethod: + + + .. py:method:: process_exchange(exc, dataset) + :classmethod: + + Process exchange. + + Input groups are: + + 1. Materials/fuels + 2. Electricity/Heat + 3. Services + 4. FromNature + 5. FromTechnosphere + + Output groups are: + + 0. Reference product + 1. Include avoided product system + 2. Allocated byproduct + 3. Waste to treatment + 4. ToNature + + A single-output process will have one output group 0; A MO process will have multiple output group 2s. Output groups 1 and 3 are not used in ecoinvent. + + + .. py:method:: process_uncertainty_fields(exc, data) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/ecospold1_lcia/index.rst b/sphinx/technical/api/bw2io/extractors/ecospold1_lcia/index.rst new file mode 100644 index 0000000..077d3b9 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/ecospold1_lcia/index.rst @@ -0,0 +1,49 @@ +:py:mod:`bw2io.extractors.ecospold1_lcia` +========================================= + +.. py:module:: bw2io.extractors.ecospold1_lcia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.ecospold1_lcia.Ecospold1LCIAExtractor + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.ecospold1_lcia._to_unicode + + + +.. py:function:: _to_unicode(data) + + +.. py:class:: Ecospold1LCIAExtractor + + Bases: :py:obj:`object` + + Extract impact assessment methods and weightings data from ecospold XML format. + + .. py:method:: extract(path) + :classmethod: + + + .. py:method:: parse_method(ds, filepath) + :classmethod: + + + .. py:method:: parse_cf(cf) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/ecospold2/index.rst b/sphinx/technical/api/bw2io/extractors/ecospold2/index.rst new file mode 100644 index 0000000..6934712 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/ecospold2/index.rst @@ -0,0 +1,149 @@ +:py:mod:`bw2io.extractors.ecospold2` +==================================== + +.. py:module:: bw2io.extractors.ecospold2 + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.ecospold2.Ecospold2DataExtractor + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.ecospold2.getattr2 + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.ecospold2.PM_MAPPING + bw2io.extractors.ecospold2.ACTIVITY_TYPES + bw2io.extractors.ecospold2.TOO_LOW + bw2io.extractors.ecospold2.TOO_HIGH + + +.. py:data:: PM_MAPPING + + + + +.. py:data:: ACTIVITY_TYPES + + + + +.. py:function:: getattr2(obj, attr) + + +.. py:data:: TOO_LOW + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + Lognormal scale value at or below zero: {}. + Reverting to undefined uncertainty. + + .. raw:: html + +
+ + + +.. py:data:: TOO_HIGH + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + Lognormal scale value impossibly high: {}. + Reverting to undefined uncertainty. + + .. raw:: html + +
+ + + +.. py:class:: Ecospold2DataExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract_technosphere_metadata(dirpath) + :classmethod: + + + .. py:method:: extract(dirpath, db_name, use_mp=True) + :classmethod: + + + .. py:method:: condense_multiline_comment(element) + :classmethod: + + + .. py:method:: extract_activity(dirpath, filename, db_name) + :classmethod: + + + .. py:method:: abort_exchange(exc, comment=None) + :classmethod: + + + .. py:method:: extract_uncertainty_dict(obj) + :classmethod: + + + .. py:method:: extract_parameter(exc) + :classmethod: + + + .. py:method:: extract_properties(exc) + :classmethod: + + + .. py:method:: extract_exchange(exc) + :classmethod: + + Process exchange. + + Input groups are: + + 1. Materials/fuels + 2. Electricity/Heat + 3. Services + 4. From environment (elementary exchange only) + 5. FromTechnosphere + + Output groups are: + + 0. ReferenceProduct + 2. By-product + 3. MaterialForTreatment + 4. To environment (elementary exchange only) + 5. Stock addition + + + + diff --git a/sphinx/technical/api/bw2io/extractors/excel/index.rst b/sphinx/technical/api/bw2io/extractors/excel/index.rst new file mode 100644 index 0000000..5ed20da --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/excel/index.rst @@ -0,0 +1,43 @@ +:py:mod:`bw2io.extractors.excel` +================================ + +.. py:module:: bw2io.extractors.excel + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.excel.ExcelExtractor + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.excel.get_cell_value_handle_error + + + +.. py:function:: get_cell_value_handle_error(cell) + + +.. py:class:: ExcelExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath) + :classmethod: + + + .. py:method:: extract_sheet(wb, name, strip=True) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/exiobase/index.rst b/sphinx/technical/api/bw2io/extractors/exiobase/index.rst new file mode 100644 index 0000000..d24b235 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/exiobase/index.rst @@ -0,0 +1,65 @@ +:py:mod:`bw2io.extractors.exiobase` +=================================== + +.. py:module:: bw2io.extractors.exiobase + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.exiobase.Exiobase3MonetaryDataExtractor + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.exiobase.remove_numerics + + + +.. py:function:: remove_numerics(string) + + Transform names like 'Tobacco products (16)' into 'Tobacco products' + + +.. py:class:: Exiobase3MonetaryDataExtractor + + Bases: :py:obj:`object` + + .. py:method:: _get_path(dirpath) + :classmethod: + + + .. py:method:: _get_production_volumes(dirpath) + :classmethod: + + + .. py:method:: _get_unit_data(dirpath) + :classmethod: + + + .. py:method:: get_flows(dirpath) + :classmethod: + + + .. py:method:: get_products(dirpath) + :classmethod: + + + .. py:method:: get_technosphere_iterator(dirpath, num_products, ignore_small_balancing_corrections=True) + :classmethod: + + + .. py:method:: get_biosphere_iterator(dirpath, ignore_small_balancing_corrections=True) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/index.rst b/sphinx/technical/api/bw2io/extractors/index.rst new file mode 100644 index 0000000..8bf2981 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/index.rst @@ -0,0 +1,448 @@ +:py:mod:`bw2io.extractors` +========================== + +.. py:module:: bw2io.extractors + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + csv/index.rst + ecospold1/index.rst + ecospold1_lcia/index.rst + ecospold2/index.rst + excel/index.rst + exiobase/index.rst + json_ld/index.rst + simapro_csv/index.rst + simapro_lcia_csv/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.CSVExtractor + bw2io.extractors.Ecospold1DataExtractor + bw2io.extractors.Ecospold1LCIAExtractor + bw2io.extractors.Ecospold2DataExtractor + bw2io.extractors.ExcelExtractor + bw2io.extractors.Exiobase3MonetaryDataExtractor + bw2io.extractors.SimaProCSVExtractor + bw2io.extractors.SimaProLCIACSVExtractor + + + + +.. py:class:: CSVExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath, encoding='utf-8-sig') + :classmethod: + + + +.. py:class:: Ecospold1DataExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(path, db_name, use_mp=True) + :classmethod: + + + .. py:method:: process_file(filepath, db_name) + :classmethod: + + + .. py:method:: is_valid_ecospold1(dataset) + :classmethod: + + + .. py:method:: process_dataset(dataset, filename, db_name) + :classmethod: + + + .. py:method:: process_exchanges(dataset) + :classmethod: + + + .. py:method:: process_allocation(exc, dataset) + :classmethod: + + + .. py:method:: process_exchange(exc, dataset) + :classmethod: + + Process exchange. + + Input groups are: + + 1. Materials/fuels + 2. Electricity/Heat + 3. Services + 4. FromNature + 5. FromTechnosphere + + Output groups are: + + 0. Reference product + 1. Include avoided product system + 2. Allocated byproduct + 3. Waste to treatment + 4. ToNature + + A single-output process will have one output group 0; A MO process will have multiple output group 2s. Output groups 1 and 3 are not used in ecoinvent. + + + .. py:method:: process_uncertainty_fields(exc, data) + :classmethod: + + + +.. py:class:: Ecospold1LCIAExtractor + + Bases: :py:obj:`object` + + Extract impact assessment methods and weightings data from ecospold XML format. + + .. py:method:: extract(path) + :classmethod: + + + .. py:method:: parse_method(ds, filepath) + :classmethod: + + + .. py:method:: parse_cf(cf) + :classmethod: + + + +.. py:class:: Ecospold2DataExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract_technosphere_metadata(dirpath) + :classmethod: + + + .. py:method:: extract(dirpath, db_name, use_mp=True) + :classmethod: + + + .. py:method:: condense_multiline_comment(element) + :classmethod: + + + .. py:method:: extract_activity(dirpath, filename, db_name) + :classmethod: + + + .. py:method:: abort_exchange(exc, comment=None) + :classmethod: + + + .. py:method:: extract_uncertainty_dict(obj) + :classmethod: + + + .. py:method:: extract_parameter(exc) + :classmethod: + + + .. py:method:: extract_properties(exc) + :classmethod: + + + .. py:method:: extract_exchange(exc) + :classmethod: + + Process exchange. + + Input groups are: + + 1. Materials/fuels + 2. Electricity/Heat + 3. Services + 4. From environment (elementary exchange only) + 5. FromTechnosphere + + Output groups are: + + 0. ReferenceProduct + 2. By-product + 3. MaterialForTreatment + 4. To environment (elementary exchange only) + 5. Stock addition + + + + +.. py:class:: ExcelExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath) + :classmethod: + + + .. py:method:: extract_sheet(wb, name, strip=True) + :classmethod: + + + +.. py:class:: Exiobase3MonetaryDataExtractor + + Bases: :py:obj:`object` + + .. py:method:: _get_path(dirpath) + :classmethod: + + + .. py:method:: _get_production_volumes(dirpath) + :classmethod: + + + .. py:method:: _get_unit_data(dirpath) + :classmethod: + + + .. py:method:: get_flows(dirpath) + :classmethod: + + + .. py:method:: get_products(dirpath) + :classmethod: + + + .. py:method:: get_technosphere_iterator(dirpath, num_products, ignore_small_balancing_corrections=True) + :classmethod: + + + .. py:method:: get_biosphere_iterator(dirpath, ignore_small_balancing_corrections=True) + :classmethod: + + + +.. py:class:: SimaProCSVExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath, delimiter=';', name=None, encoding='cp1252') + :classmethod: + + + .. py:method:: get_next_process_index(data, index) + :classmethod: + + + .. py:method:: get_project_metadata(data) + :classmethod: + + + .. py:method:: get_global_parameters(data, pm) + :classmethod: + + + .. py:method:: get_project_name(data) + :classmethod: + + + .. py:method:: invalid_uncertainty_data(amount, kind, field1, field2, field3) + :classmethod: + + + .. py:method:: create_distribution(amount, kind, field1, field2, field3) + :classmethod: + + + .. py:method:: parse_calculated_parameter(line, pm) + :classmethod: + + Parse line in `Calculated parameters` section. + + 0. name + 1. formula + 2. comment + + Can include multiline comment in TSV. + + + .. py:method:: parse_input_parameter(line) + :classmethod: + + Parse line in `Input parameters` section. + + 0. name + 1. value (not formula) + 2. uncertainty type + 3. uncert. param. + 4. uncert. param. + 5. uncert. param. + 6. hidden ("Yes" or "No" - we ignore) + 7. comment + + + + .. py:method:: parse_biosphere_flow(line, category, pm) + :classmethod: + + Parse biosphere flow line. + + 0. name + 1. subcategory + 2. unit + 3. value or formula + 4. uncertainty type + 5. uncert. param. + 6. uncert. param. + 7. uncert. param. + 8. comment + + However, sometimes the value is in index 2, and the unit in index 3. Because why not! We assume default ordering unless we find a number in index 2. + + + + .. py:method:: parse_input_line(line, category, pm) + :classmethod: + + Parse technosphere input line. + + 0. name + 1. unit + 2. value or formula + 3. uncertainty type + 4. uncert. param. + 5. uncert. param. + 6. uncert. param. + 7. comment + + However, sometimes the value is in index 1, and the unit in index 2. Because why not! We assume default ordering unless we find a number in index 1. + + + + .. py:method:: parse_final_waste_flow(line, pm) + :classmethod: + + Parse final wate flow line. + + 0: name + 1: subcategory? + 2: unit + 3. value or formula + 4. uncertainty type + 5. uncert. param. + 6. uncert. param. + 7. uncert. param. + + However, sometimes the value is in index 2, and the unit in index 3. Because why not! We assume default ordering unless we find a number in index 2. + + + + .. py:method:: parse_reference_product(line, pm) + :classmethod: + + Parse reference product line. + + 0. name + 1. unit + 2. value or formula + 3. allocation + 4. waste type + 5. category (separated by \) + 6. comment + + However, sometimes the value is in index 1, and the unit in index 2. Because why not! We assume default ordering unless we find a number in index 1. + + + + .. py:method:: parse_waste_treatment(line, pm) + :classmethod: + + Parse reference product line. + + 0. name + 1. unit + 2. value or formula + 3. waste type + 4. category (separated by \) + 5. comment + + + + .. py:method:: read_dataset_metadata(data, index) + :classmethod: + + + .. py:method:: read_data_set(data, index, db_name, filepath, gp, pm, global_precompiled) + :classmethod: + + + +.. py:class:: SimaProLCIACSVExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath, delimiter=';', encoding='cp1252') + :classmethod: + + + .. py:method:: get_next_method_index(data, index) + :classmethod: + + + .. py:method:: skip_to_section_end(data, index) + :classmethod: + + + .. py:method:: parse_cf(line) + :classmethod: + + Parse line in `Substances` section. + + 0. category + 1. subcategory + 2. flow + 3. CAS number + 4. CF + 5. unit + + + + .. py:method:: read_metadata(data, index) + :classmethod: + + + .. py:method:: read_method_data_set(data, index, filepath) + :classmethod: + + + .. py:method:: get_all_cfs(nw_data, category_data) + :classmethod: + + + .. py:method:: get_damage_exchanges(damage_data, category_data) + :classmethod: + + + .. py:method:: get_category_data(data, index) + :classmethod: + + + .. py:method:: get_damage_category_data(data, index) + :classmethod: + + + .. py:method:: get_normalization_weighting_data(data, index) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/json_ld/index.rst b/sphinx/technical/api/bw2io/extractors/json_ld/index.rst new file mode 100644 index 0000000..1d195e2 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/json_ld/index.rst @@ -0,0 +1,47 @@ +:py:mod:`bw2io.extractors.json_ld` +================================== + +.. py:module:: bw2io.extractors.json_ld + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.json_ld.JSONLDExtractor + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.json_ld.FILES_TO_IGNORE + bw2io.extractors.json_ld.DIRECTORIES_TO_IGNORE + + +.. py:data:: FILES_TO_IGNORE + + + + +.. py:data:: DIRECTORIES_TO_IGNORE + + + + +.. py:class:: JSONLDExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath, add_filename=True) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/simapro_csv/index.rst b/sphinx/technical/api/bw2io/extractors/simapro_csv/index.rst new file mode 100644 index 0000000..449b6f1 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/simapro_csv/index.rst @@ -0,0 +1,262 @@ +:py:mod:`bw2io.extractors.simapro_csv` +====================================== + +.. py:module:: bw2io.extractors.simapro_csv + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.simapro_csv.SimaProCSVExtractor + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.simapro_csv.to_number + bw2io.extractors.simapro_csv.replace_with_uppercase + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.simapro_csv.INTRODUCTION + bw2io.extractors.simapro_csv.SIMAPRO_TECHNOSPHERE + bw2io.extractors.simapro_csv.SIMAPRO_PRODUCTS + bw2io.extractors.simapro_csv.SIMAPRO_END_OF_DATASETS + bw2io.extractors.simapro_csv.strip_whitespace_and_delete + bw2io.extractors.simapro_csv.uppercase_expression + + +.. py:data:: INTRODUCTION + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + Starting SimaPro import: + Filepath: %s + Delimiter: %s + Name: %s + + + .. raw:: html + +
+ + + +.. py:data:: SIMAPRO_TECHNOSPHERE + + + + +.. py:data:: SIMAPRO_PRODUCTS + + + + +.. py:data:: SIMAPRO_END_OF_DATASETS + + + + +.. py:exception:: EndOfDatasets + + Bases: :py:obj:`Exception` + + Common base class for all non-exit exceptions. + + +.. py:function:: to_number(obj) + + +.. py:data:: strip_whitespace_and_delete + + + + +.. py:data:: uppercase_expression + :annotation: = (?:^|[^a-zA-Z_])(?P{})(?:[^a-zA-Z_]|$) + + + +.. py:function:: replace_with_uppercase(string, names, precompiled) + + Replace all occurrences of elements of ``names`` in ``string`` with their uppercase equivalents. + + ``names`` is a list of variable name strings that should already all be uppercase. + + Returns a modified ``string``. + + +.. py:class:: SimaProCSVExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath, delimiter=';', name=None, encoding='cp1252') + :classmethod: + + + .. py:method:: get_next_process_index(data, index) + :classmethod: + + + .. py:method:: get_project_metadata(data) + :classmethod: + + + .. py:method:: get_global_parameters(data, pm) + :classmethod: + + + .. py:method:: get_project_name(data) + :classmethod: + + + .. py:method:: invalid_uncertainty_data(amount, kind, field1, field2, field3) + :classmethod: + + + .. py:method:: create_distribution(amount, kind, field1, field2, field3) + :classmethod: + + + .. py:method:: parse_calculated_parameter(line, pm) + :classmethod: + + Parse line in `Calculated parameters` section. + + 0. name + 1. formula + 2. comment + + Can include multiline comment in TSV. + + + .. py:method:: parse_input_parameter(line) + :classmethod: + + Parse line in `Input parameters` section. + + 0. name + 1. value (not formula) + 2. uncertainty type + 3. uncert. param. + 4. uncert. param. + 5. uncert. param. + 6. hidden ("Yes" or "No" - we ignore) + 7. comment + + + + .. py:method:: parse_biosphere_flow(line, category, pm) + :classmethod: + + Parse biosphere flow line. + + 0. name + 1. subcategory + 2. unit + 3. value or formula + 4. uncertainty type + 5. uncert. param. + 6. uncert. param. + 7. uncert. param. + 8. comment + + However, sometimes the value is in index 2, and the unit in index 3. Because why not! We assume default ordering unless we find a number in index 2. + + + + .. py:method:: parse_input_line(line, category, pm) + :classmethod: + + Parse technosphere input line. + + 0. name + 1. unit + 2. value or formula + 3. uncertainty type + 4. uncert. param. + 5. uncert. param. + 6. uncert. param. + 7. comment + + However, sometimes the value is in index 1, and the unit in index 2. Because why not! We assume default ordering unless we find a number in index 1. + + + + .. py:method:: parse_final_waste_flow(line, pm) + :classmethod: + + Parse final wate flow line. + + 0: name + 1: subcategory? + 2: unit + 3. value or formula + 4. uncertainty type + 5. uncert. param. + 6. uncert. param. + 7. uncert. param. + + However, sometimes the value is in index 2, and the unit in index 3. Because why not! We assume default ordering unless we find a number in index 2. + + + + .. py:method:: parse_reference_product(line, pm) + :classmethod: + + Parse reference product line. + + 0. name + 1. unit + 2. value or formula + 3. allocation + 4. waste type + 5. category (separated by \) + 6. comment + + However, sometimes the value is in index 1, and the unit in index 2. Because why not! We assume default ordering unless we find a number in index 1. + + + + .. py:method:: parse_waste_treatment(line, pm) + :classmethod: + + Parse reference product line. + + 0. name + 1. unit + 2. value or formula + 3. waste type + 4. category (separated by \) + 5. comment + + + + .. py:method:: read_dataset_metadata(data, index) + :classmethod: + + + .. py:method:: read_data_set(data, index, db_name, filepath, gp, pm, global_precompiled) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/extractors/simapro_lcia_csv/index.rst b/sphinx/technical/api/bw2io/extractors/simapro_lcia_csv/index.rst new file mode 100644 index 0000000..71ce8f6 --- /dev/null +++ b/sphinx/technical/api/bw2io/extractors/simapro_lcia_csv/index.rst @@ -0,0 +1,126 @@ +:py:mod:`bw2io.extractors.simapro_lcia_csv` +=========================================== + +.. py:module:: bw2io.extractors.simapro_lcia_csv + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.simapro_lcia_csv.SimaProLCIACSVExtractor + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.extractors.simapro_lcia_csv.INTRODUCTION + bw2io.extractors.simapro_lcia_csv.SKIPPABLE_SECTIONS + bw2io.extractors.simapro_lcia_csv.strip_delete + + +.. py:data:: INTRODUCTION + :annotation: = Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: text + :linenos: + + Starting SimaPro import: + Filepath: %s + Delimiter: %s + + + .. raw:: html + +
+ + + +.. py:data:: SKIPPABLE_SECTIONS + + + + +.. py:exception:: EndOfDatasets + + Bases: :py:obj:`Exception` + + Common base class for all non-exit exceptions. + + +.. py:data:: strip_delete + + + + +.. py:class:: SimaProLCIACSVExtractor + + Bases: :py:obj:`object` + + .. py:method:: extract(filepath, delimiter=';', encoding='cp1252') + :classmethod: + + + .. py:method:: get_next_method_index(data, index) + :classmethod: + + + .. py:method:: skip_to_section_end(data, index) + :classmethod: + + + .. py:method:: parse_cf(line) + :classmethod: + + Parse line in `Substances` section. + + 0. category + 1. subcategory + 2. flow + 3. CAS number + 4. CF + 5. unit + + + + .. py:method:: read_metadata(data, index) + :classmethod: + + + .. py:method:: read_method_data_set(data, index, filepath) + :classmethod: + + + .. py:method:: get_all_cfs(nw_data, category_data) + :classmethod: + + + .. py:method:: get_damage_exchanges(damage_data, category_data) + :classmethod: + + + .. py:method:: get_category_data(data, index) + :classmethod: + + + .. py:method:: get_damage_category_data(data, index) + :classmethod: + + + .. py:method:: get_normalization_weighting_data(data, index) + :classmethod: + + + diff --git a/sphinx/technical/api/bw2io/importers/base/index.rst b/sphinx/technical/api/bw2io/importers/base/index.rst new file mode 100644 index 0000000..4776f62 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/base/index.rst @@ -0,0 +1,74 @@ +:py:mod:`bw2io.importers.base` +============================== + +.. py:module:: bw2io.importers.base + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.base.ImportBase + + + + +.. py:class:: ImportBase(*args, **kwargs) + + Bases: :py:obj:`object` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + .. py:property:: unlinked + + Iterate through unique unlinked exchanges. + + Uniqueness is determined by ``activity_hash``. + + .. py:method:: __iter__() + + + .. py:method:: apply_strategy(strategy, verbose=True) + + Apply ``strategy`` transform to ``self.data``. + + Adds strategy name to ``self.applied_strategies``. If ``StrategyError`` is raised, print error message, but don't raise error. + + .. note:: Strategies should not partially modify data before raising ``StrategyError``. + + :param \*strategy*: + :type \*strategy*: callable + + :returns: Nothing, but modifies ``self.data``, and strategy to ``self.applied_strategies``. + + + .. py:method:: apply_strategies(strategies=None, verbose=True) + + Apply a list of strategies. + + Uses the default list ``self.strategies`` if ``strategies`` is ``None``. + + :param \*strategies*: List of strategies to apply. Defaults to ``self.strategies``. + :type \*strategies*: list, optional + + :returns: Nothings, but modifies ``self.data``, and adds each strategy to ``self.applied_strategies``. + + + .. py:method:: write_unlinked(name) + + Write all data to an ``UnlikedData`` data store (not a ``Database``!) + + + .. py:method:: _migrate_datasets(migration_name) + + + .. py:method:: _migrate_exchanges(migration_name) + + + diff --git a/sphinx/technical/api/bw2io/importers/base_lci/index.rst b/sphinx/technical/api/bw2io/importers/base_lci/index.rst new file mode 100644 index 0000000..6b8fedd --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/base_lci/index.rst @@ -0,0 +1,156 @@ +:py:mod:`bw2io.importers.base_lci` +================================== + +.. py:module:: bw2io.importers.base_lci + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.base_lci.LCIImporter + + + + +.. py:class:: LCIImporter(db_name) + + Bases: :py:obj:`bw2io.importers.base.ImportBase` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:property:: all_linked + + + .. py:attribute:: format + :annotation: = Generic LCIImporter + + + + .. py:attribute:: project_parameters + + + + + .. py:attribute:: database_parameters + + + + + .. py:attribute:: metadata + + + + + .. py:method:: statistics(print_stats=True) + + + .. py:method:: write_project_parameters(data=None, delete_existing=True) + + Write global parameters to ``ProjectParameter`` database table. + + ``delete_existing`` controls whether new parameters will delete_existing existing parameters, or just update values. The ``name`` field is used to determine if a parameter exists. + + ``data`` should be a list of dictionaries (``self.project_parameters`` is used by default): + + .. code-block:: python + + [{ + 'name': name of variable (unique), + 'amount': numeric value of variable (optional), + 'formula': formula in Python as string (optional), + optional keys like uncertainty, etc. (no limitations) + }] + + + + .. py:method:: write_database_parameters(activate_parameters=False, delete_existing=True) + + + .. py:method:: _prepare_activity_parameters(data=None, delete_existing=True) + + + .. py:method:: _write_activity_parameters(activity_parameters) + + + .. py:method:: write_database(data=None, delete_existing=True, backend=None, activate_parameters=False, db_name=None, **kwargs) + + Write data to a ``Database``. + + All arguments are optional, and are normally not specified. + + ``delete_existing`` effects both the existing database (it will be emptied prior to writing if True, which is the default), and, if ``activate_parameters`` is True, existing database and activity parameters. Database parameters will only be deleted if the import data specifies a new set of database parameters (i.e. ``database_parameters`` is not ``None``) - the same is true for activity parameters. If you need finer-grained control, please use the ``DatabaseParameter``, etc. objects directly. + + :param \* *data*: The data to write to the ``Database``. Default is ``self.data``. + :type \* *data*: dict, optional + :param \* *delete_existing*: See above. + :type \* *delete_existing*: bool, default ``True`` + :param \* *activate_parameters*: + :type \* *activate_parameters*: bool, default ``False`` + :param \* *backend*: Storage backend to use when creating ``Database``. Default is the default backend. + :type \* *backend*: string, optional + + :returns: ``Database`` instance. + + + .. py:method:: write_excel(only_unlinked=False, only_names=False) + + Write database information to a spreadsheet. + + If ``only_unlinked``, then only write unlinked exchanges. + + If ``only_names``, then write only activity names, no exchange data. + + Returns the filepath to the spreadsheet file. + + + + .. py:method:: match_database(db_name=None, fields=None, ignore_categories=False, relink=False, kind=None) + + Match current database against itself or another database. + + If ``db_name`` is None, match against current data. Otherwise, ``db_name`` should be the name of an existing ``Database``. + + ``fields`` is a list of fields to use for matching. Field values are case-insensitive, but otherwise must match exactly for a link to be valid. If ``fields`` is ``None``, use the default fields of 'name', 'categories', 'unit', 'reference product', and 'location'. + + If ``ignore_categories``, link based only on name, unit and location. ``ignore_categories`` conflicts with ``fields``. + + If ``relink``, relink exchanges even if a link is already present. + + ``kind`` can be a string or a list of strings. Common values are "technosphere", "biosphere", "production", and "substitution". + + Nothing is returned, but ``self.data`` is changed. + + + + .. py:method:: create_new_biosphere(biosphere_name, relink=True) + + Create new biosphere database from biosphere flows in ``self.data``. + + Links all biosphere flows to new bio database if ``relink``. + + + .. py:method:: add_unlinked_flows_to_biosphere_database(biosphere_name=None) + + + .. py:method:: migrate(migration_name) + + + .. py:method:: drop_unlinked(i_am_reckless=False) + + + .. py:method:: add_unlinked_activities() + + Add technosphere flows to ``self.data``. + + + diff --git a/sphinx/technical/api/bw2io/importers/base_lcia/index.rst b/sphinx/technical/api/bw2io/importers/base_lcia/index.rst new file mode 100644 index 0000000..3c3dfae --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/base_lcia/index.rst @@ -0,0 +1,51 @@ +:py:mod:`bw2io.importers.base_lcia` +=================================== + +.. py:module:: bw2io.importers.base_lcia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.base_lcia.LCIAImporter + + + + +.. py:class:: LCIAImporter(filepath, biosphere=None) + + Bases: :py:obj:`bw2io.importers.base.ImportBase` + + .. py:property:: all_linked + + + .. py:method:: write_methods(overwrite=False, verbose=True) + + + .. py:method:: write_excel(name) + + + .. py:method:: drop_unlinked(verbose=True) + + + .. py:method:: _reformat_cfs(ds) + + + .. py:method:: _format_flow(cf) + + + .. py:method:: add_missing_cfs() + + + .. py:method:: statistics(print_stats=True) + + + .. py:method:: migrate(migration_name) + + + diff --git a/sphinx/technical/api/bw2io/importers/ecoinvent_lcia/index.rst b/sphinx/technical/api/bw2io/importers/ecoinvent_lcia/index.rst new file mode 100644 index 0000000..e44b50e --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/ecoinvent_lcia/index.rst @@ -0,0 +1,32 @@ +:py:mod:`bw2io.importers.ecoinvent_lcia` +======================================== + +.. py:module:: bw2io.importers.ecoinvent_lcia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.ecoinvent_lcia.EcoinventLCIAImporter + + + + +.. py:class:: EcoinventLCIAImporter + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:method:: add_rationalize_method_names_strategy() + + + .. py:method:: separate_methods() + + Separate the list of CFs into distinct methods + + + diff --git a/sphinx/technical/api/bw2io/importers/ecospold1/index.rst b/sphinx/technical/api/bw2io/importers/ecospold1/index.rst new file mode 100644 index 0000000..afbd8c2 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/ecospold1/index.rst @@ -0,0 +1,77 @@ +:py:mod:`bw2io.importers.ecospold1` +=================================== + +.. py:module:: bw2io.importers.ecospold1 + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.ecospold1.SingleOutputEcospold1Importer + bw2io.importers.ecospold1.NoIntegerCodesEcospold1Importer + bw2io.importers.ecospold1.MultiOutputEcospold1Importer + + + + +.. py:class:: SingleOutputEcospold1Importer(filepath, db_name, use_mp=True, extractor=Ecospold1DataExtractor) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Import and process single-output datasets in the ecospold 1 format. + + Applies the following strategies: + #. If only one exchange is a production exchange, that is the reference product + #. Delete (unreliable) integer codes from extracted data + #. Drop ``unspecified`` subcategories from biosphere flows + #. Normalize biosphere flow categories to ecoinvent 3.1 standard + #. Normalize biosphere flow names to ecoinvent 3.1 standard + #. Remove locations from biosphere exchanges + #. Create a ``code`` from the activity hash of the dataset + #. Link biosphere exchanges to the default biosphere database + #. Link internal technosphere exchanges + + :param \* *filepath*: Either a file or directory. + :param \* *db_name*: Name of database to create. + + .. py:attribute:: format + :annotation: = Ecospold1 + + + + +.. py:class:: NoIntegerCodesEcospold1Importer(*args, **kwargs) + + Bases: :py:obj:`SingleOutputEcospold1Importer` + + Import and process single-output datasets in the ecospold 1 format. + + Applies the following strategies: + #. If only one exchange is a production exchange, that is the reference product + #. Delete (unreliable) integer codes from extracted data + #. Drop ``unspecified`` subcategories from biosphere flows + #. Normalize biosphere flow categories to ecoinvent 3.1 standard + #. Normalize biosphere flow names to ecoinvent 3.1 standard + #. Remove locations from biosphere exchanges + #. Create a ``code`` from the activity hash of the dataset + #. Link biosphere exchanges to the default biosphere database + #. Link internal technosphere exchanges + + :param \* *filepath*: Either a file or directory. + :param \* *db_name*: Name of database to create. + + +.. py:class:: MultiOutputEcospold1Importer(*args, **kwargs) + + Bases: :py:obj:`SingleOutputEcospold1Importer` + + Import and process mutli-output datasets in the ecospold 1 format. + + Works the same as the single-output importer, but first allocates multioutput datasets. + + diff --git a/sphinx/technical/api/bw2io/importers/ecospold1_lcia/index.rst b/sphinx/technical/api/bw2io/importers/ecospold1_lcia/index.rst new file mode 100644 index 0000000..94663f0 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/ecospold1_lcia/index.rst @@ -0,0 +1,29 @@ +:py:mod:`bw2io.importers.ecospold1_lcia` +======================================== + +.. py:module:: bw2io.importers.ecospold1_lcia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.ecospold1_lcia.Ecospold1LCIAImporter + + + + +.. py:class:: Ecospold1LCIAImporter(filepath, biosphere=None) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:attribute:: format + :annotation: = Ecospold1 LCIA + + + + diff --git a/sphinx/technical/api/bw2io/importers/ecospold2/index.rst b/sphinx/technical/api/bw2io/importers/ecospold2/index.rst new file mode 100644 index 0000000..a9e23c0 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/ecospold2/index.rst @@ -0,0 +1,36 @@ +:py:mod:`bw2io.importers.ecospold2` +=================================== + +.. py:module:: bw2io.importers.ecospold2 + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.ecospold2.SingleOutputEcospold2Importer + + + + +.. py:class:: SingleOutputEcospold2Importer(dirpath, db_name, extractor=Ecospold2DataExtractor, use_mp=True, signal=None) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Ecospold2 + + + + diff --git a/sphinx/technical/api/bw2io/importers/ecospold2_biosphere/index.rst b/sphinx/technical/api/bw2io/importers/ecospold2_biosphere/index.rst new file mode 100644 index 0000000..d6e1ccf --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/ecospold2_biosphere/index.rst @@ -0,0 +1,52 @@ +:py:mod:`bw2io.importers.ecospold2_biosphere` +============================================= + +.. py:module:: bw2io.importers.ecospold2_biosphere + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.ecospold2_biosphere.Ecospold2BiosphereImporter + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.ecospold2_biosphere.EMISSIONS_CATEGORIES + + +.. py:data:: EMISSIONS_CATEGORIES + + + + +.. py:class:: Ecospold2BiosphereImporter(name='biosphere3', version='3.9') + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Ecoinvent XML + + + + .. py:method:: extract(version) + + + diff --git a/sphinx/technical/api/bw2io/importers/excel/index.rst b/sphinx/technical/api/bw2io/importers/excel/index.rst new file mode 100644 index 0000000..1345dc0 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/excel/index.rst @@ -0,0 +1,165 @@ +:py:mod:`bw2io.importers.excel` +=============================== + +.. py:module:: bw2io.importers.excel + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.excel.ExcelImporter + bw2io.importers.excel.CSVImporter + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.excel.valid_first_cell + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.excel.is_empty_line + bw2io.importers.excel.remove_empty + + +.. py:data:: is_empty_line + + + + +.. py:data:: remove_empty + + + + +.. py:function:: valid_first_cell(sheet, data) + + Return boolean if first cell in worksheet is not ``skip``. + + +.. py:class:: ExcelImporter(filepath) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Generic Excel importer. + + See the `generic Excel example spreadsheet `__. + + Excel spreadsheet should follow the following format: + + :: + Project parameters + , , , metadata + + Database, + , + + Parameters + , , , metadata + + Activity, + , + Exchanges + , , + , , + , , + + Neither project parameters, parameters, nor exchanges for each activity are required. + + An activity is marked as finished with a blank line. + + In general, data is imported without modification. However, the following transformations are applied: + + * Numbers are translated from text into actual numbers. + * Tuples, separated in the cell by the ``::`` string, are reconstructed. + * ``True`` and ``False`` are transformed to boolean values. + * Fields with the value ``(Unknown)`` are dropped. + + + .. py:attribute:: format + :annotation: = Excel + + + + .. py:attribute:: extractor + + + + + .. py:method:: get_database(data) + + + .. py:method:: get_database_parameters(data) + + + .. py:method:: get_project_parameters(data) + + Extract project parameters (variables and formulas). + + Project parameters are a section that starts with a line with the string "project parameters" (case-insensitive) in the first cell, and ends with a blank line. There can be multiple project parameter sections. + + + .. py:method:: get_labelled_section(sn, ws, index=0, transform=True) + + Turn a list of rows into a list of dictionaries. + + The first line of ``ws`` is the column labels. All subsequent rows are the data values. Missing columns are dropped. + + ``transform`` is a boolean: perform CSV transformation functions like ``csv_restore_tuples``. + + + .. py:method:: get_metadata_section(sn, ws, index=0, transform=True) + + + .. py:method:: process_activities(data) + + Take list of `(sheet names, raw data)` and process it. + + + .. py:method:: write_activity_parameters(data=None, delete_existing=True) + + + .. py:method:: write_database_parameters(activate_parameters=True, delete_existing=True) + + Same as base ``write_database_parameters`` method, but ``activate_parameters`` is True by default. + + + .. py:method:: write_database(**kwargs) + + Same as base ``write_database`` method, but ``activate_parameters`` is True by default. + + + .. py:method:: get_activity(sn, ws) + + + +.. py:class:: CSVImporter(filepath) + + Bases: :py:obj:`ExcelImporter` + + Generic CSV importer + + .. py:attribute:: format + :annotation: = CSV + + + + .. py:attribute:: extractor + + + + + diff --git a/sphinx/technical/api/bw2io/importers/excel_lcia/index.rst b/sphinx/technical/api/bw2io/importers/excel_lcia/index.rst new file mode 100644 index 0000000..9cf386e --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/excel_lcia/index.rst @@ -0,0 +1,68 @@ +:py:mod:`bw2io.importers.excel_lcia` +==================================== + +.. py:module:: bw2io.importers.excel_lcia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.excel_lcia.ExcelLCIAImporter + bw2io.importers.excel_lcia.CSVLCIAImporter + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.excel_lcia.as_dicts + + + +.. py:function:: as_dicts(obj) + + +.. py:class:: ExcelLCIAImporter(filepath, name, description, unit, **metadata) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + Generic Excel LCIA importer. + + See the `documentation `__. + + + .. py:attribute:: format + :annotation: = Excel + + + + .. py:attribute:: extractor + + + + + +.. py:class:: CSVLCIAImporter(filepath, name, description, unit, **metadata) + + Bases: :py:obj:`ExcelLCIAImporter` + + Generic CSV LCIA importer + + .. py:attribute:: format + :annotation: = CSV + + + + .. py:attribute:: extractor + + + + + diff --git a/sphinx/technical/api/bw2io/importers/exiobase3_hybrid/index.rst b/sphinx/technical/api/bw2io/importers/exiobase3_hybrid/index.rst new file mode 100644 index 0000000..84a1826 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/exiobase3_hybrid/index.rst @@ -0,0 +1,32 @@ +:py:mod:`bw2io.importers.exiobase3_hybrid` +========================================== + +.. py:module:: bw2io.importers.exiobase3_hybrid + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.exiobase3_hybrid.Exiobase3HybridImporter + + + + +.. py:class:: Exiobase3HybridImporter(dirpath, db_name='EXIOBASE 3.3.17 hybrid') + + Bases: :py:obj:`object` + + .. py:attribute:: format + :annotation: = Exiobase 3.3.17 hybrid mrio_common_metadata tidy datapackage + + + + .. py:method:: write_database() + + + diff --git a/sphinx/technical/api/bw2io/importers/exiobase3_monetary/index.rst b/sphinx/technical/api/bw2io/importers/exiobase3_monetary/index.rst new file mode 100644 index 0000000..18f4321 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/exiobase3_monetary/index.rst @@ -0,0 +1,72 @@ +:py:mod:`bw2io.importers.exiobase3_monetary` +============================================ + +.. py:module:: bw2io.importers.exiobase3_monetary + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.exiobase3_monetary.Exiobase3MonetaryImporter + + + + +.. py:class:: Exiobase3MonetaryImporter(dirpath, db_name, ignore_small_balancing_corrections=True) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Exiobase 3 + + + + .. py:method:: apply_strategy(*args, **kwargs) + :abstractmethod: + + + .. py:method:: add_unlinked_flows_to_new_biosphere_database(biosphere_name=None) + + + .. py:method:: write_activities_as_database() + + + .. py:method:: patch_lcia_methods(new_biosphere) + + + .. py:method:: apply_strategies(biosphere=None) + + + .. py:method:: write_database(biosphere=None) + + Write data to a ``Database``. + + All arguments are optional, and are normally not specified. + + ``delete_existing`` effects both the existing database (it will be emptied prior to writing if True, which is the default), and, if ``activate_parameters`` is True, existing database and activity parameters. Database parameters will only be deleted if the import data specifies a new set of database parameters (i.e. ``database_parameters`` is not ``None``) - the same is true for activity parameters. If you need finer-grained control, please use the ``DatabaseParameter``, etc. objects directly. + + :param \* *data*: The data to write to the ``Database``. Default is ``self.data``. + :type \* *data*: dict, optional + :param \* *delete_existing*: See above. + :type \* *delete_existing*: bool, default ``True`` + :param \* *activate_parameters*: + :type \* *activate_parameters*: bool, default ``False`` + :param \* *backend*: Storage backend to use when creating ``Database``. Default is the default backend. + :type \* *backend*: string, optional + + :returns: ``Database`` instance. + + + diff --git a/sphinx/technical/api/bw2io/importers/index.rst b/sphinx/technical/api/bw2io/importers/index.rst new file mode 100644 index 0000000..16c02b0 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/index.rst @@ -0,0 +1,442 @@ +:py:mod:`bw2io.importers` +========================= + +.. py:module:: bw2io.importers + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + base/index.rst + base_lci/index.rst + base_lcia/index.rst + ecoinvent_lcia/index.rst + ecospold1/index.rst + ecospold1_lcia/index.rst + ecospold2/index.rst + ecospold2_biosphere/index.rst + excel/index.rst + excel_lcia/index.rst + exiobase3_hybrid/index.rst + exiobase3_monetary/index.rst + json_ld/index.rst + json_ld_lcia/index.rst + simapro_csv/index.rst + simapro_lcia_csv/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.EcoinventLCIAImporter + bw2io.importers.MultiOutputEcospold1Importer + bw2io.importers.NoIntegerCodesEcospold1Importer + bw2io.importers.SingleOutputEcospold1Importer + bw2io.importers.Ecospold1LCIAImporter + bw2io.importers.SingleOutputEcospold2Importer + bw2io.importers.Ecospold2BiosphereImporter + bw2io.importers.CSVImporter + bw2io.importers.ExcelImporter + bw2io.importers.CSVLCIAImporter + bw2io.importers.ExcelLCIAImporter + bw2io.importers.Exiobase3HybridImporter + bw2io.importers.Exiobase3MonetaryImporter + bw2io.importers.SimaProCSVImporter + bw2io.importers.SimaProLCIACSVImporter + + + + +.. py:class:: EcoinventLCIAImporter + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:method:: add_rationalize_method_names_strategy() + + + .. py:method:: separate_methods() + + Separate the list of CFs into distinct methods + + + +.. py:class:: MultiOutputEcospold1Importer(*args, **kwargs) + + Bases: :py:obj:`SingleOutputEcospold1Importer` + + Import and process mutli-output datasets in the ecospold 1 format. + + Works the same as the single-output importer, but first allocates multioutput datasets. + + +.. py:class:: NoIntegerCodesEcospold1Importer(*args, **kwargs) + + Bases: :py:obj:`SingleOutputEcospold1Importer` + + Import and process single-output datasets in the ecospold 1 format. + + Applies the following strategies: + #. If only one exchange is a production exchange, that is the reference product + #. Delete (unreliable) integer codes from extracted data + #. Drop ``unspecified`` subcategories from biosphere flows + #. Normalize biosphere flow categories to ecoinvent 3.1 standard + #. Normalize biosphere flow names to ecoinvent 3.1 standard + #. Remove locations from biosphere exchanges + #. Create a ``code`` from the activity hash of the dataset + #. Link biosphere exchanges to the default biosphere database + #. Link internal technosphere exchanges + + :param \* *filepath*: Either a file or directory. + :param \* *db_name*: Name of database to create. + + +.. py:class:: SingleOutputEcospold1Importer(filepath, db_name, use_mp=True, extractor=Ecospold1DataExtractor) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Import and process single-output datasets in the ecospold 1 format. + + Applies the following strategies: + #. If only one exchange is a production exchange, that is the reference product + #. Delete (unreliable) integer codes from extracted data + #. Drop ``unspecified`` subcategories from biosphere flows + #. Normalize biosphere flow categories to ecoinvent 3.1 standard + #. Normalize biosphere flow names to ecoinvent 3.1 standard + #. Remove locations from biosphere exchanges + #. Create a ``code`` from the activity hash of the dataset + #. Link biosphere exchanges to the default biosphere database + #. Link internal technosphere exchanges + + :param \* *filepath*: Either a file or directory. + :param \* *db_name*: Name of database to create. + + .. py:attribute:: format + :annotation: = Ecospold1 + + + + +.. py:class:: Ecospold1LCIAImporter(filepath, biosphere=None) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:attribute:: format + :annotation: = Ecospold1 LCIA + + + + +.. py:class:: SingleOutputEcospold2Importer(dirpath, db_name, extractor=Ecospold2DataExtractor, use_mp=True, signal=None) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Ecospold2 + + + + +.. py:class:: Ecospold2BiosphereImporter(name='biosphere3', version='3.9') + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Ecoinvent XML + + + + .. py:method:: extract(version) + + + +.. py:class:: CSVImporter(filepath) + + Bases: :py:obj:`ExcelImporter` + + Generic CSV importer + + .. py:attribute:: format + :annotation: = CSV + + + + .. py:attribute:: extractor + + + + + +.. py:class:: ExcelImporter(filepath) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Generic Excel importer. + + See the `generic Excel example spreadsheet `__. + + Excel spreadsheet should follow the following format: + + :: + Project parameters + , , , metadata + + Database, + , + + Parameters + , , , metadata + + Activity, + , + Exchanges + , , + , , + , , + + Neither project parameters, parameters, nor exchanges for each activity are required. + + An activity is marked as finished with a blank line. + + In general, data is imported without modification. However, the following transformations are applied: + + * Numbers are translated from text into actual numbers. + * Tuples, separated in the cell by the ``::`` string, are reconstructed. + * ``True`` and ``False`` are transformed to boolean values. + * Fields with the value ``(Unknown)`` are dropped. + + + .. py:attribute:: format + :annotation: = Excel + + + + .. py:attribute:: extractor + + + + + .. py:method:: get_database(data) + + + .. py:method:: get_database_parameters(data) + + + .. py:method:: get_project_parameters(data) + + Extract project parameters (variables and formulas). + + Project parameters are a section that starts with a line with the string "project parameters" (case-insensitive) in the first cell, and ends with a blank line. There can be multiple project parameter sections. + + + .. py:method:: get_labelled_section(sn, ws, index=0, transform=True) + + Turn a list of rows into a list of dictionaries. + + The first line of ``ws`` is the column labels. All subsequent rows are the data values. Missing columns are dropped. + + ``transform`` is a boolean: perform CSV transformation functions like ``csv_restore_tuples``. + + + .. py:method:: get_metadata_section(sn, ws, index=0, transform=True) + + + .. py:method:: process_activities(data) + + Take list of `(sheet names, raw data)` and process it. + + + .. py:method:: write_activity_parameters(data=None, delete_existing=True) + + + .. py:method:: write_database_parameters(activate_parameters=True, delete_existing=True) + + Same as base ``write_database_parameters`` method, but ``activate_parameters`` is True by default. + + + .. py:method:: write_database(**kwargs) + + Same as base ``write_database`` method, but ``activate_parameters`` is True by default. + + + .. py:method:: get_activity(sn, ws) + + + +.. py:class:: CSVLCIAImporter(filepath, name, description, unit, **metadata) + + Bases: :py:obj:`ExcelLCIAImporter` + + Generic CSV LCIA importer + + .. py:attribute:: format + :annotation: = CSV + + + + .. py:attribute:: extractor + + + + + +.. py:class:: ExcelLCIAImporter(filepath, name, description, unit, **metadata) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + Generic Excel LCIA importer. + + See the `documentation `__. + + + .. py:attribute:: format + :annotation: = Excel + + + + .. py:attribute:: extractor + + + + + +.. py:class:: Exiobase3HybridImporter(dirpath, db_name='EXIOBASE 3.3.17 hybrid') + + Bases: :py:obj:`object` + + .. py:attribute:: format + :annotation: = Exiobase 3.3.17 hybrid mrio_common_metadata tidy datapackage + + + + .. py:method:: write_database() + + + +.. py:class:: Exiobase3MonetaryImporter(dirpath, db_name, ignore_small_balancing_corrections=True) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Exiobase 3 + + + + .. py:method:: apply_strategy(*args, **kwargs) + :abstractmethod: + + + .. py:method:: add_unlinked_flows_to_new_biosphere_database(biosphere_name=None) + + + .. py:method:: write_activities_as_database() + + + .. py:method:: patch_lcia_methods(new_biosphere) + + + .. py:method:: apply_strategies(biosphere=None) + + + .. py:method:: write_database(biosphere=None) + + Write data to a ``Database``. + + All arguments are optional, and are normally not specified. + + ``delete_existing`` effects both the existing database (it will be emptied prior to writing if True, which is the default), and, if ``activate_parameters`` is True, existing database and activity parameters. Database parameters will only be deleted if the import data specifies a new set of database parameters (i.e. ``database_parameters`` is not ``None``) - the same is true for activity parameters. If you need finer-grained control, please use the ``DatabaseParameter``, etc. objects directly. + + :param \* *data*: The data to write to the ``Database``. Default is ``self.data``. + :type \* *data*: dict, optional + :param \* *delete_existing*: See above. + :type \* *delete_existing*: bool, default ``True`` + :param \* *activate_parameters*: + :type \* *activate_parameters*: bool, default ``False`` + :param \* *backend*: Storage backend to use when creating ``Database``. Default is the default backend. + :type \* *backend*: string, optional + + :returns: ``Database`` instance. + + + +.. py:class:: SimaProCSVImporter(filepath, name=None, delimiter=';', encoding='latin-1', normalize_biosphere=True, biosphere_db=None, extractor=SimaProCSVExtractor) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = SimaPro CSV + + + + .. py:method:: get_db_name() + + + .. py:method:: write_database(data=None, name=None, *args, **kwargs) + + Write data to a ``Database``. + + All arguments are optional, and are normally not specified. + + ``delete_existing`` effects both the existing database (it will be emptied prior to writing if True, which is the default), and, if ``activate_parameters`` is True, existing database and activity parameters. Database parameters will only be deleted if the import data specifies a new set of database parameters (i.e. ``database_parameters`` is not ``None``) - the same is true for activity parameters. If you need finer-grained control, please use the ``DatabaseParameter``, etc. objects directly. + + :param \* *data*: The data to write to the ``Database``. Default is ``self.data``. + :type \* *data*: dict, optional + :param \* *delete_existing*: See above. + :type \* *delete_existing*: bool, default ``True`` + :param \* *activate_parameters*: + :type \* *activate_parameters*: bool, default ``False`` + :param \* *backend*: Storage backend to use when creating ``Database``. Default is the default backend. + :type \* *backend*: string, optional + + :returns: ``Database`` instance. + + + .. py:method:: match_ecoinvent2(db_name) + + + +.. py:class:: SimaProLCIACSVImporter(filepath, biosphere=None, delimiter=';', encoding='latin-1', normalize_biosphere=True) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:attribute:: format + :annotation: = SimaPro CSV LCIA + + + + diff --git a/sphinx/technical/api/bw2io/importers/json_ld/index.rst b/sphinx/technical/api/bw2io/importers/json_ld/index.rst new file mode 100644 index 0000000..d97c954 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/json_ld/index.rst @@ -0,0 +1,56 @@ +:py:mod:`bw2io.importers.json_ld` +================================= + +.. py:module:: bw2io.importers.json_ld + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.json_ld.JSONLDImporter + + + + +.. py:class:: JSONLDImporter(dirpath, database_name, preferred_allocation=None) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Importer for the `OLCD JSON-LD data format `__. + + See `discussion with linked issues here `__. + + + .. py:attribute:: format + :annotation: = OLCA JSON-LD + + + + .. py:attribute:: extractor + + + + + .. py:method:: apply_strategies(*args, **kwargs) + + + .. py:method:: merge_biosphere_flows() + + Add flows in ``self.biosphere_database`` to ``self.data``. + + + .. py:method:: write_separate_biosphere_database() + + + .. py:method:: flows_as_biosphere_database(data, database_name, suffix=' biosphere') + + + .. py:method:: flows_as_products(data) + + + diff --git a/sphinx/technical/api/bw2io/importers/json_ld_lcia/index.rst b/sphinx/technical/api/bw2io/importers/json_ld_lcia/index.rst new file mode 100644 index 0000000..67bfa0b --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/json_ld_lcia/index.rst @@ -0,0 +1,39 @@ +:py:mod:`bw2io.importers.json_ld_lcia` +====================================== + +.. py:module:: bw2io.importers.json_ld_lcia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.json_ld_lcia.JSONLDLCIAImporter + + + + +.. py:class:: JSONLDLCIAImporter(dirpath) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + Importer for the `OLCD JSON-LD LCIA data format `__. + + .. py:attribute:: format + :annotation: = OLCA JSON-LD + + + + .. py:attribute:: extractor + + + + + .. py:method:: match_biosphere_by_id(database_name) + + + diff --git a/sphinx/technical/api/bw2io/importers/simapro_csv/index.rst b/sphinx/technical/api/bw2io/importers/simapro_csv/index.rst new file mode 100644 index 0000000..45f03eb --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/simapro_csv/index.rst @@ -0,0 +1,62 @@ +:py:mod:`bw2io.importers.simapro_csv` +===================================== + +.. py:module:: bw2io.importers.simapro_csv + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.simapro_csv.SimaProCSVImporter + + + + +.. py:class:: SimaProCSVImporter(filepath, name=None, delimiter=';', encoding='latin-1', normalize_biosphere=True, biosphere_db=None, extractor=SimaProCSVExtractor) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = SimaPro CSV + + + + .. py:method:: get_db_name() + + + .. py:method:: write_database(data=None, name=None, *args, **kwargs) + + Write data to a ``Database``. + + All arguments are optional, and are normally not specified. + + ``delete_existing`` effects both the existing database (it will be emptied prior to writing if True, which is the default), and, if ``activate_parameters`` is True, existing database and activity parameters. Database parameters will only be deleted if the import data specifies a new set of database parameters (i.e. ``database_parameters`` is not ``None``) - the same is true for activity parameters. If you need finer-grained control, please use the ``DatabaseParameter``, etc. objects directly. + + :param \* *data*: The data to write to the ``Database``. Default is ``self.data``. + :type \* *data*: dict, optional + :param \* *delete_existing*: See above. + :type \* *delete_existing*: bool, default ``True`` + :param \* *activate_parameters*: + :type \* *activate_parameters*: bool, default ``False`` + :param \* *backend*: Storage backend to use when creating ``Database``. Default is the default backend. + :type \* *backend*: string, optional + + :returns: ``Database`` instance. + + + .. py:method:: match_ecoinvent2(db_name) + + + diff --git a/sphinx/technical/api/bw2io/importers/simapro_lcia_csv/index.rst b/sphinx/technical/api/bw2io/importers/simapro_lcia_csv/index.rst new file mode 100644 index 0000000..6e94967 --- /dev/null +++ b/sphinx/technical/api/bw2io/importers/simapro_lcia_csv/index.rst @@ -0,0 +1,29 @@ +:py:mod:`bw2io.importers.simapro_lcia_csv` +========================================== + +.. py:module:: bw2io.importers.simapro_lcia_csv + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.importers.simapro_lcia_csv.SimaProLCIACSVImporter + + + + +.. py:class:: SimaProLCIACSVImporter(filepath, biosphere=None, delimiter=';', encoding='latin-1', normalize_biosphere=True) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:attribute:: format + :annotation: = SimaPro CSV LCIA + + + + diff --git a/sphinx/technical/api/bw2io/index.rst b/sphinx/technical/api/bw2io/index.rst new file mode 100644 index 0000000..28635be --- /dev/null +++ b/sphinx/technical/api/bw2io/index.rst @@ -0,0 +1,806 @@ +:py:mod:`bw2io` +=============== + +.. py:module:: bw2io + + +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + + data/index.rst + export/index.rst + extractors/index.rst + importers/index.rst + strategies/index.rst + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + backup/index.rst + chemidplus/index.rst + compatibility/index.rst + download_utils/index.rst + errors/index.rst + modified_database/index.rst + package/index.rst + tests/index.rst + units/index.rst + unlinked_data/index.rst + utils/index.rst + validation/index.rst + version/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.ChemIDPlus + bw2io.BW2Package + bw2io.DatabaseToGEXF + bw2io.DatabaseSelectionToGEXF + bw2io.CSVImporter + bw2io.CSVLCIAImporter + bw2io.Ecospold1LCIAImporter + bw2io.ExcelImporter + bw2io.ExcelLCIAImporter + bw2io.MultiOutputEcospold1Importer + bw2io.SimaProCSVImporter + bw2io.SimaProLCIACSVImporter + bw2io.SingleOutputEcospold1Importer + bw2io.SingleOutputEcospold2Importer + bw2io.Exiobase3MonetaryImporter + bw2io.UnlinkedData + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.lci_matrices_to_excel + bw2io.lci_matrices_to_matlab + bw2io.backup_data_directory + bw2io.backup_project_directory + bw2io.restore_project_directory + bw2io.add_example_database + bw2io.get_csv_example_filepath + bw2io.get_xlsx_example_filepath + bw2io.activity_hash + bw2io.es2_activity_hash + bw2io.load_json_data_file + bw2io.create_default_biosphere3 + bw2io.create_default_lcia_methods + bw2io.bw2setup + bw2io.useeio11 + bw2io.exiobase_monetary + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.add_ecoinvent_33_biosphere_flows + bw2io.add_ecoinvent_34_biosphere_flows + bw2io.add_ecoinvent_35_biosphere_flows + bw2io.add_ecoinvent_36_biosphere_flows + bw2io.add_ecoinvent_37_biosphere_flows + bw2io.add_ecoinvent_38_biosphere_flows + bw2io.add_ecoinvent_39_biosphere_flows + bw2io.normalize_units + bw2io.unlinked_data + + +.. py:class:: ChemIDPlus + + Use the `ChemIDPlus `__ API to lookup synonyms for chemicals, including pesticides. + + Always used to match against a master list. Seeded with names from ecoinvent. + + .. py:attribute:: CAS_TEMPLATE + :annotation: = https://chem.nlm.nih.gov/api/data/search?data=complete&exp=rn%2Feq%2F{cas} + + + + .. py:attribute:: NAME_TEMPLATE + :annotation: = https://chem.nlm.nih.gov/api/data/search?data=complete&exp=na%2Feq%2F{name} + + + + .. py:method:: match(synonym, search=True) + + + .. py:method:: match_cas(number) + + + .. py:method:: add_master_term(term, CAS) + + + .. py:method:: save_cache() + + + .. py:method:: load_cache() + + + .. py:method:: process_request(response) + + + +.. py:class:: BW2Package + + Bases: :py:obj:`object` + + This is a format for saving objects which implement the :ref:`datastore` API. Data is stored as a BZip2-compressed file of JSON data. This archive format is compatible across Python versions, and is, at least in theory, programming-language agnostic. + + Validation is done with ``bw2data.validate.bw2package_validator``. + + The data format is: + + .. code-block:: python + + { + 'metadata': {}, # Dictionary of metadata to be written to metadata-store. + 'name': basestring, # Name of object + 'class': { # Data on the underlying class. A new class is instantiated + # based on these strings. See _create_class. + 'module': basestring, # e.g. "bw2data.database" + 'name': basestring # e.g. "Database" + }, + 'unrolled_dict': bool, # Flag indicating if dictionary keys needed to + # be modified for JSON (as JSON keys can't be tuples) + 'data': object # Object data, e.g. LCIA method or LCI database + } + + Perfect roundtrips between machines are not guaranteed: + * All lists are converted to tuples (because JSON does not distinguish between lists and tuples). + * Absolute filepaths in metadata would be specific to a certain computer and user. + + .. note:: This class does not need to be instantiated, as all its methods are ``classmethods``, i.e. do ``BW2Package.import_obj("foo")`` instead of ``BW2Package().import_obj("foo")`` + + + .. py:attribute:: APPROVED + + + + + .. py:method:: _get_class_metadata(obj) + :classmethod: + + + .. py:method:: _is_valid_package(data) + :classmethod: + + + .. py:method:: _is_whitelisted(metadata) + :classmethod: + + + .. py:method:: _create_class(metadata, apply_whitelist=True) + :classmethod: + + + .. py:method:: _prepare_obj(obj, backwards_compatible=False) + :classmethod: + + + .. py:method:: _load_obj(data, whitelist=True) + :classmethod: + + + .. py:method:: _create_obj(data) + :classmethod: + + + .. py:method:: _write_file(filepath, data) + :classmethod: + + + .. py:method:: export_objs(objs, filename, folder='export', backwards_compatible=False) + :classmethod: + + Export a list of objects. Can have heterogeneous types. + + :param \* *objs*: List of objects to export. + :type \* *objs*: list + :param \* *filename*: Name of file to create. + :type \* *filename*: str + :param \* *folder*: Folder to create file in. Default is ``export``. + :type \* *folder*: str, optional + :param \* *backwards_compatible*: Create package compatible with bw2data version 1. + :type \* *backwards_compatible*: bool, optional + + :returns: Filepath of created file. + + + .. py:method:: export_obj(obj, filename=None, folder='export', backwards_compatible=False) + :classmethod: + + Export an object. + + :param \* *obj*: Object to export. + :type \* *obj*: object + :param \* *filename*: Name of file to create. Default is ``obj.name``. + :type \* *filename*: str, optional + :param \* *folder*: Folder to create file in. Default is ``export``. + :type \* *folder*: str, optional + :param \* *backwards_compatible*: Create package compatible with bw2data version 1. + :type \* *backwards_compatible*: bool, optional + + :returns: Filepath of created file. + + + .. py:method:: load_file(filepath, whitelist=True) + :classmethod: + + Load a bw2package file with one or more objects. Does not create new objects. + + :param \* *filepath*: Path of file to import + :type \* *filepath*: str + :param \* *whitelist*: Apply whitelist of approved classes to allowed types. Default is ``True``. + :type \* *whitelist*: bool + + Returns the loaded data in the bw2package dict data format, with the following changes: + * ``"class"`` is an actual Python class object (but not instantiated). + + + + .. py:method:: import_file(filepath, whitelist=True) + :classmethod: + + Import bw2package file, and create the loaded objects, including registering, writing, and processing the created objects. + + :param \* *filepath*: Path of file to import + :type \* *filepath*: str + :param \* *whitelist*: Apply whitelist to allowed types. Default is ``True``. + :type \* *whitelist*: bool + + :returns: Created object or list of created objects. + + + +.. py:class:: DatabaseToGEXF(database, include_descendants=False) + + Bases: :py:obj:`object` + + Export a Gephi graph for a database. + + Call ``.export()`` to export the file after class instantiation. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *include_descendants*: Include databases which are linked from ``database``. + :type \* *include_descendants*: bool + + .. warning:: ``include_descendants`` is not yet implemented. + + + .. py:method:: export() + + Export the Gephi XML file. Returns the filepath of the created file. + + + .. py:method:: get_data(E) + + Get Gephi nodes and edges. + + + +.. py:class:: DatabaseSelectionToGEXF(database, keys) + + Bases: :py:obj:`DatabaseToGEXF` + + Export a Gephi graph for a selection of activities from a database. + + Also includes all inputs for the filtered activities. + + :param \* *database*: Database name. + :type \* *database*: str + :param \* *keys*: The activity keys to export. + :type \* *keys*: str + + +.. py:function:: lci_matrices_to_excel(database_name, include_descendants=True) + + Fake docstring + + +.. py:function:: lci_matrices_to_matlab(database_name) + + +.. py:function:: backup_data_directory() + + Backup data directory to a ``.tar.gz`` (compressed tar archive). + + Backup archive is saved to the user's home directory. + + Restoration is done manually. Returns the filepath of the backup archive. + + +.. py:function:: backup_project_directory(project) + + Backup project data directory to a ``.tar.gz`` (compressed tar archive). + + ``project`` is the name of a project. + + Backup archive is saved to the user's home directory. + + Restoration is done using ``restore_project_directory``. + + Returns the filepath of the backup archive. + + +.. py:function:: restore_project_directory(fp) + + Restore backup created using ``backup_project_directory``. + + Raises an error is the project already exists. + + ``fp`` is the filepath of the backup archive. + + Returns the name of the newly created project. + + +.. py:data:: add_ecoinvent_33_biosphere_flows + + + + +.. py:data:: add_ecoinvent_34_biosphere_flows + + + + +.. py:data:: add_ecoinvent_35_biosphere_flows + + + + +.. py:data:: add_ecoinvent_36_biosphere_flows + + + + +.. py:data:: add_ecoinvent_37_biosphere_flows + + + + +.. py:data:: add_ecoinvent_38_biosphere_flows + + + + +.. py:data:: add_ecoinvent_39_biosphere_flows + + + + +.. py:function:: add_example_database(overwrite=True) + + +.. py:function:: get_csv_example_filepath() + + +.. py:function:: get_xlsx_example_filepath() + + +.. py:class:: CSVImporter(filepath) + + Bases: :py:obj:`ExcelImporter` + + Generic CSV importer + + .. py:attribute:: format + :annotation: = CSV + + + + .. py:attribute:: extractor + + + + + +.. py:class:: CSVLCIAImporter(filepath, name, description, unit, **metadata) + + Bases: :py:obj:`ExcelLCIAImporter` + + Generic CSV LCIA importer + + .. py:attribute:: format + :annotation: = CSV + + + + .. py:attribute:: extractor + + + + + +.. py:class:: Ecospold1LCIAImporter(filepath, biosphere=None) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:attribute:: format + :annotation: = Ecospold1 LCIA + + + + +.. py:class:: ExcelImporter(filepath) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Generic Excel importer. + + See the `generic Excel example spreadsheet `__. + + Excel spreadsheet should follow the following format: + + :: + Project parameters + , , , metadata + + Database, + , + + Parameters + , , , metadata + + Activity, + , + Exchanges + , , + , , + , , + + Neither project parameters, parameters, nor exchanges for each activity are required. + + An activity is marked as finished with a blank line. + + In general, data is imported without modification. However, the following transformations are applied: + + * Numbers are translated from text into actual numbers. + * Tuples, separated in the cell by the ``::`` string, are reconstructed. + * ``True`` and ``False`` are transformed to boolean values. + * Fields with the value ``(Unknown)`` are dropped. + + + .. py:attribute:: format + :annotation: = Excel + + + + .. py:attribute:: extractor + + + + + .. py:method:: get_database(data) + + + .. py:method:: get_database_parameters(data) + + + .. py:method:: get_project_parameters(data) + + Extract project parameters (variables and formulas). + + Project parameters are a section that starts with a line with the string "project parameters" (case-insensitive) in the first cell, and ends with a blank line. There can be multiple project parameter sections. + + + .. py:method:: get_labelled_section(sn, ws, index=0, transform=True) + + Turn a list of rows into a list of dictionaries. + + The first line of ``ws`` is the column labels. All subsequent rows are the data values. Missing columns are dropped. + + ``transform`` is a boolean: perform CSV transformation functions like ``csv_restore_tuples``. + + + .. py:method:: get_metadata_section(sn, ws, index=0, transform=True) + + + .. py:method:: process_activities(data) + + Take list of `(sheet names, raw data)` and process it. + + + .. py:method:: write_activity_parameters(data=None, delete_existing=True) + + + .. py:method:: write_database_parameters(activate_parameters=True, delete_existing=True) + + Same as base ``write_database_parameters`` method, but ``activate_parameters`` is True by default. + + + .. py:method:: write_database(**kwargs) + + Same as base ``write_database`` method, but ``activate_parameters`` is True by default. + + + .. py:method:: get_activity(sn, ws) + + + +.. py:class:: ExcelLCIAImporter(filepath, name, description, unit, **metadata) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + Generic Excel LCIA importer. + + See the `documentation `__. + + + .. py:attribute:: format + :annotation: = Excel + + + + .. py:attribute:: extractor + + + + + +.. py:class:: MultiOutputEcospold1Importer(*args, **kwargs) + + Bases: :py:obj:`SingleOutputEcospold1Importer` + + Import and process mutli-output datasets in the ecospold 1 format. + + Works the same as the single-output importer, but first allocates multioutput datasets. + + +.. py:class:: SimaProCSVImporter(filepath, name=None, delimiter=';', encoding='latin-1', normalize_biosphere=True, biosphere_db=None, extractor=SimaProCSVExtractor) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = SimaPro CSV + + + + .. py:method:: get_db_name() + + + .. py:method:: write_database(data=None, name=None, *args, **kwargs) + + Write data to a ``Database``. + + All arguments are optional, and are normally not specified. + + ``delete_existing`` effects both the existing database (it will be emptied prior to writing if True, which is the default), and, if ``activate_parameters`` is True, existing database and activity parameters. Database parameters will only be deleted if the import data specifies a new set of database parameters (i.e. ``database_parameters`` is not ``None``) - the same is true for activity parameters. If you need finer-grained control, please use the ``DatabaseParameter``, etc. objects directly. + + :param \* *data*: The data to write to the ``Database``. Default is ``self.data``. + :type \* *data*: dict, optional + :param \* *delete_existing*: See above. + :type \* *delete_existing*: bool, default ``True`` + :param \* *activate_parameters*: + :type \* *activate_parameters*: bool, default ``False`` + :param \* *backend*: Storage backend to use when creating ``Database``. Default is the default backend. + :type \* *backend*: string, optional + + :returns: ``Database`` instance. + + + .. py:method:: match_ecoinvent2(db_name) + + + +.. py:class:: SimaProLCIACSVImporter(filepath, biosphere=None, delimiter=';', encoding='latin-1', normalize_biosphere=True) + + Bases: :py:obj:`bw2io.importers.base_lcia.LCIAImporter` + + .. py:attribute:: format + :annotation: = SimaPro CSV LCIA + + + + +.. py:class:: SingleOutputEcospold1Importer(filepath, db_name, use_mp=True, extractor=Ecospold1DataExtractor) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Import and process single-output datasets in the ecospold 1 format. + + Applies the following strategies: + #. If only one exchange is a production exchange, that is the reference product + #. Delete (unreliable) integer codes from extracted data + #. Drop ``unspecified`` subcategories from biosphere flows + #. Normalize biosphere flow categories to ecoinvent 3.1 standard + #. Normalize biosphere flow names to ecoinvent 3.1 standard + #. Remove locations from biosphere exchanges + #. Create a ``code`` from the activity hash of the dataset + #. Link biosphere exchanges to the default biosphere database + #. Link internal technosphere exchanges + + :param \* *filepath*: Either a file or directory. + :param \* *db_name*: Name of database to create. + + .. py:attribute:: format + :annotation: = Ecospold1 + + + + +.. py:class:: SingleOutputEcospold2Importer(dirpath, db_name, extractor=Ecospold2DataExtractor, use_mp=True, signal=None) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Ecospold2 + + + + +.. py:class:: Exiobase3MonetaryImporter(dirpath, db_name, ignore_small_balancing_corrections=True) + + Bases: :py:obj:`bw2io.importers.base_lci.LCIImporter` + + Base class for format-specific importers. + + Defines workflow for applying strategies. + + Takes a database name (string) as initialization parameter. + + + .. py:attribute:: format + :annotation: = Exiobase 3 + + + + .. py:method:: apply_strategy(*args, **kwargs) + :abstractmethod: + + + .. py:method:: add_unlinked_flows_to_new_biosphere_database(biosphere_name=None) + + + .. py:method:: write_activities_as_database() + + + .. py:method:: patch_lcia_methods(new_biosphere) + + + .. py:method:: apply_strategies(biosphere=None) + + + .. py:method:: write_database(biosphere=None) + + Write data to a ``Database``. + + All arguments are optional, and are normally not specified. + + ``delete_existing`` effects both the existing database (it will be emptied prior to writing if True, which is the default), and, if ``activate_parameters`` is True, existing database and activity parameters. Database parameters will only be deleted if the import data specifies a new set of database parameters (i.e. ``database_parameters`` is not ``None``) - the same is true for activity parameters. If you need finer-grained control, please use the ``DatabaseParameter``, etc. objects directly. + + :param \* *data*: The data to write to the ``Database``. Default is ``self.data``. + :type \* *data*: dict, optional + :param \* *delete_existing*: See above. + :type \* *delete_existing*: bool, default ``True`` + :param \* *activate_parameters*: + :type \* *activate_parameters*: bool, default ``False`` + :param \* *backend*: Storage backend to use when creating ``Database``. Default is the default backend. + :type \* *backend*: string, optional + + :returns: ``Database`` instance. + + + +.. py:data:: normalize_units + + + + +.. py:data:: unlinked_data + + + + +.. py:class:: UnlinkedData(name) + + Bases: :py:obj:`bw2data.data_store.DataStore` + + Base class for all Brightway2 data stores. Subclasses should define: + + * **metadata**: A :ref:`serialized-dict` instance, e.g. ``databases`` or ``methods``. The custom is that each type of data store has a new metadata store, so the data store ``Foo`` would have a metadata store ``foos``. + * **validator**: A data validator. Optional. See bw2data.validate. + + + .. py:attribute:: _metadata + + + + + .. py:attribute:: _intermediate_dir + :annotation: = unlinked + + + + .. py:method:: validate(*args, **kwargs) + + Validate data. Must be called manually. + + + +.. py:function:: activity_hash(data, fields=None, case_insensitive=True) + + Hash an activity dataset. + + Used to import data formats like ecospold 1 (ecoinvent v1-2) and SimaPro, where no unique attributes for datasets are given. This is clearly an imperfect and brittle solution, but there is no other obvious approach at this time. + + The fields used can be optionally specified in ``fields``. + + No fields are required; an empty string is used if a field isn't present. All fields are cast to lower case. + + By default, uses the following, in order: + * name + * categories + * unit + * reference product + * location + + :param \* *data*: The :ref:`activity dataset data `. + :type \* *data*: dict + :param \* *fields*: Optional list of fields to hash together. Default is ``('name', 'categories', 'unit', 'reference product', 'location')``. + :type \* *fields*: list + :param \* *case_insensitive*: Cast everything to lowercase before computing hash. Default is ``True``. + :type \* *case_insensitive*: bool + + :returns: A MD5 hash string, hex-encoded. + + +.. py:function:: es2_activity_hash(activity, flow) + + Generate unique ID for ecoinvent3 dataset. + + Despite using a million UUIDs, there is actually no unique ID in an ecospold2 dataset. Datasets are uniquely identified by the combination of activity and flow UUIDs. + + +.. py:function:: load_json_data_file(filename) + + +.. py:function:: create_default_biosphere3(overwrite=False) + + +.. py:function:: create_default_lcia_methods(overwrite=False, rationalize_method_names=False, shortcut=True) + + +.. py:function:: bw2setup() + + +.. py:function:: useeio11(name='US EEIO 1.1') + + +.. py:function:: exiobase_monetary(version=(3, 8, 1), year=2017, products=False, name=None, ignore_small_balancing_corrections=True) + + diff --git a/sphinx/technical/api/bw2io/modified_database/index.rst b/sphinx/technical/api/bw2io/modified_database/index.rst new file mode 100644 index 0000000..3d0fdf9 --- /dev/null +++ b/sphinx/technical/api/bw2io/modified_database/index.rst @@ -0,0 +1,87 @@ +:py:mod:`bw2io.modified_database` +================================= + +.. py:module:: bw2io.modified_database + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.modified_database.ModifiedDatabase + + + + +.. py:class:: ModifiedDatabase(data, ref_database_name, from_simapro=False) + + Bases: :py:obj:`object` + + Find relationships between foreground data ``data`` and background database named ``ref_database_name``. + + Each activity and exchange is summarized in a *hash*, a small set of letters that summarizes all relevant attributes. + + foreground_activities_mapping: + hash: dataset + + foreground_exchanges_mapping: + hash: exchange + + foreground_activities: + activity hash: set of (exchange hash, amount) exchange tuples. + + background_activities_mapping: + hash: Activity + + background_exchanges_mapping: + hash: Exchange + + background_activities: + activity hash: set of (Exchange hash, amount) exchange tuples + + .. py:method:: assert_data_fully_linked() + + + .. py:method:: iterate_unmatched() + + Return data on activities in ``data`` which can't be found in ``ref_database_name``. + + + .. py:method:: get_reason(exc_tuple, data) + + Get reason why exc_tuple not in data. Reasons are: + 1) Changed amount + 2) Missing + + + .. py:method:: iterate_modified() + + Return data on modified activities + + + .. py:method:: load_datasets() + + Determine which datasets are modified by comparing the exchanges values. + + Specifically, compare the set of ``(input activity hashes, amount_as_string)`` values. + + If the name or other important attributes changed, then there won't be a correspondence at all, so the dataset is treated as modified in any case. + + + .. py:method:: add_to_background_exchanges_mapping(exc) + + + .. py:method:: hash_background_exchanges(activity) + + + .. py:method:: hash_foreground_exchanges(activity) + + + .. py:method:: prune() + + + diff --git a/sphinx/technical/api/bw2io/package/index.rst b/sphinx/technical/api/bw2io/package/index.rst new file mode 100644 index 0000000..76df027 --- /dev/null +++ b/sphinx/technical/api/bw2io/package/index.rst @@ -0,0 +1,166 @@ +:py:mod:`bw2io.package` +======================= + +.. py:module:: bw2io.package + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.package.BW2Package + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.package.download_biosphere + bw2io.package.download_methods + + + +.. py:class:: BW2Package + + Bases: :py:obj:`object` + + This is a format for saving objects which implement the :ref:`datastore` API. Data is stored as a BZip2-compressed file of JSON data. This archive format is compatible across Python versions, and is, at least in theory, programming-language agnostic. + + Validation is done with ``bw2data.validate.bw2package_validator``. + + The data format is: + + .. code-block:: python + + { + 'metadata': {}, # Dictionary of metadata to be written to metadata-store. + 'name': basestring, # Name of object + 'class': { # Data on the underlying class. A new class is instantiated + # based on these strings. See _create_class. + 'module': basestring, # e.g. "bw2data.database" + 'name': basestring # e.g. "Database" + }, + 'unrolled_dict': bool, # Flag indicating if dictionary keys needed to + # be modified for JSON (as JSON keys can't be tuples) + 'data': object # Object data, e.g. LCIA method or LCI database + } + + Perfect roundtrips between machines are not guaranteed: + * All lists are converted to tuples (because JSON does not distinguish between lists and tuples). + * Absolute filepaths in metadata would be specific to a certain computer and user. + + .. note:: This class does not need to be instantiated, as all its methods are ``classmethods``, i.e. do ``BW2Package.import_obj("foo")`` instead of ``BW2Package().import_obj("foo")`` + + + .. py:attribute:: APPROVED + + + + + .. py:method:: _get_class_metadata(obj) + :classmethod: + + + .. py:method:: _is_valid_package(data) + :classmethod: + + + .. py:method:: _is_whitelisted(metadata) + :classmethod: + + + .. py:method:: _create_class(metadata, apply_whitelist=True) + :classmethod: + + + .. py:method:: _prepare_obj(obj, backwards_compatible=False) + :classmethod: + + + .. py:method:: _load_obj(data, whitelist=True) + :classmethod: + + + .. py:method:: _create_obj(data) + :classmethod: + + + .. py:method:: _write_file(filepath, data) + :classmethod: + + + .. py:method:: export_objs(objs, filename, folder='export', backwards_compatible=False) + :classmethod: + + Export a list of objects. Can have heterogeneous types. + + :param \* *objs*: List of objects to export. + :type \* *objs*: list + :param \* *filename*: Name of file to create. + :type \* *filename*: str + :param \* *folder*: Folder to create file in. Default is ``export``. + :type \* *folder*: str, optional + :param \* *backwards_compatible*: Create package compatible with bw2data version 1. + :type \* *backwards_compatible*: bool, optional + + :returns: Filepath of created file. + + + .. py:method:: export_obj(obj, filename=None, folder='export', backwards_compatible=False) + :classmethod: + + Export an object. + + :param \* *obj*: Object to export. + :type \* *obj*: object + :param \* *filename*: Name of file to create. Default is ``obj.name``. + :type \* *filename*: str, optional + :param \* *folder*: Folder to create file in. Default is ``export``. + :type \* *folder*: str, optional + :param \* *backwards_compatible*: Create package compatible with bw2data version 1. + :type \* *backwards_compatible*: bool, optional + + :returns: Filepath of created file. + + + .. py:method:: load_file(filepath, whitelist=True) + :classmethod: + + Load a bw2package file with one or more objects. Does not create new objects. + + :param \* *filepath*: Path of file to import + :type \* *filepath*: str + :param \* *whitelist*: Apply whitelist of approved classes to allowed types. Default is ``True``. + :type \* *whitelist*: bool + + Returns the loaded data in the bw2package dict data format, with the following changes: + * ``"class"`` is an actual Python class object (but not instantiated). + + + + .. py:method:: import_file(filepath, whitelist=True) + :classmethod: + + Import bw2package file, and create the loaded objects, including registering, writing, and processing the created objects. + + :param \* *filepath*: Path of file to import + :type \* *filepath*: str + :param \* *whitelist*: Apply whitelist to allowed types. Default is ``True``. + :type \* *whitelist*: bool + + :returns: Created object or list of created objects. + + + +.. py:function:: download_biosphere() + + +.. py:function:: download_methods() + + diff --git a/sphinx/technical/api/bw2io/strategies/biosphere/index.rst b/sphinx/technical/api/bw2io/strategies/biosphere/index.rst new file mode 100644 index 0000000..4922872 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/biosphere/index.rst @@ -0,0 +1,53 @@ +:py:mod:`bw2io.strategies.biosphere` +==================================== + +.. py:module:: bw2io.strategies.biosphere + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.biosphere.drop_unspecified_subcategories + bw2io.strategies.biosphere.normalize_biosphere_names + bw2io.strategies.biosphere.normalize_biosphere_categories + bw2io.strategies.biosphere.strip_biosphere_exc_locations + bw2io.strategies.biosphere.ensure_categories_are_tuples + + + +.. py:function:: drop_unspecified_subcategories(db) + + Drop subcategories if they are in the following: + * ``unspecified`` + * ``(unspecified)`` + * ``''`` (empty string) + * ``None`` + + + +.. py:function:: normalize_biosphere_names(db, lcia=False) + + Normalize biosphere flow names to ecoinvent 3.1 standard. + + Assumes that each dataset and each exchange have a ``name``. Will change names even if exchange is already linked. + + +.. py:function:: normalize_biosphere_categories(db, lcia=False) + + Normalize biosphere categories to ecoinvent 3.1 standard + + +.. py:function:: strip_biosphere_exc_locations(db) + + Biosphere flows don't have locations - if any are included they can confuse linking + + +.. py:function:: ensure_categories_are_tuples(db) + + diff --git a/sphinx/technical/api/bw2io/strategies/csv/index.rst b/sphinx/technical/api/bw2io/strategies/csv/index.rst new file mode 100644 index 0000000..778ef7b --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/csv/index.rst @@ -0,0 +1,46 @@ +:py:mod:`bw2io.strategies.csv` +============================== + +.. py:module:: bw2io.strategies.csv + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.csv.csv_restore_tuples + bw2io.strategies.csv.csv_restore_booleans + bw2io.strategies.csv.csv_numerize + bw2io.strategies.csv.csv_drop_unknown + bw2io.strategies.csv.csv_add_missing_exchanges_section + + + +.. py:function:: csv_restore_tuples(data) + + Restore tuples separated by `::` string + + +.. py:function:: csv_restore_booleans(data) + + Turn `True` and `False` into proper booleans, where possible + + +.. py:function:: csv_numerize(data) + + Turns strings into numbers where possible + + +.. py:function:: csv_drop_unknown(data) + + Drop keys whose values are `(Unknown)`. + + +.. py:function:: csv_add_missing_exchanges_section(data) + + diff --git a/sphinx/technical/api/bw2io/strategies/ecospold1/index.rst b/sphinx/technical/api/bw2io/strategies/ecospold1/index.rst new file mode 100644 index 0000000..ae6afd9 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/ecospold1/index.rst @@ -0,0 +1,6 @@ +:py:mod:`bw2io.strategies.ecospold1` +==================================== + +.. py:module:: bw2io.strategies.ecospold1 + + diff --git a/sphinx/technical/api/bw2io/strategies/ecospold1_allocation/index.rst b/sphinx/technical/api/bw2io/strategies/ecospold1_allocation/index.rst new file mode 100644 index 0000000..f53bfd2 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/ecospold1_allocation/index.rst @@ -0,0 +1,64 @@ +:py:mod:`bw2io.strategies.ecospold1_allocation` +=============================================== + +.. py:module:: bw2io.strategies.ecospold1_allocation + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.ecospold1_allocation.delete_integer_codes + bw2io.strategies.ecospold1_allocation.clean_integer_codes + bw2io.strategies.ecospold1_allocation.es1_allocate_multioutput + bw2io.strategies.ecospold1_allocation.allocate_exchanges + bw2io.strategies.ecospold1_allocation.rescale_exchange + + + +.. py:function:: delete_integer_codes(data) + + Delete integer codes completely from extracted ecospold1 datasets + + +.. py:function:: clean_integer_codes(data) + + Convert integer activity codes to strings and delete integer codes from exchanges (they can't be believed). + + +.. py:function:: es1_allocate_multioutput(data) + + This strategy allocates multioutput datasets to new datasets. + + This deletes the multioutput dataset, breaking any existing linking. This shouldn't be a concern, as you shouldn't link to a multioutput dataset in any case. + + Note that multiple allocations for the same product and input will result in undefined behavior. + + + +.. py:function:: allocate_exchanges(ds) + + Take a dataset, which has multiple outputs, and return a list of allocated datasets. + + The allocation data structure looks like: + + .. code-block:: python + + { + 'exchanges': [integer codes for biosphere flows, ...], + 'fraction': out of 100, + 'reference': integer codes + } + + We assume that the allocation factor for each coproduct is always 100 percent. + + + +.. py:function:: rescale_exchange(exc, scale) + + diff --git a/sphinx/technical/api/bw2io/strategies/ecospold2/index.rst b/sphinx/technical/api/bw2io/strategies/ecospold2/index.rst new file mode 100644 index 0000000..fab661b --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/ecospold2/index.rst @@ -0,0 +1,151 @@ +:py:mod:`bw2io.strategies.ecospold2` +==================================== + +.. py:module:: bw2io.strategies.ecospold2 + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.ecospold2.link_biosphere_by_flow_uuid + bw2io.strategies.ecospold2.remove_zero_amount_coproducts + bw2io.strategies.ecospold2.remove_zero_amount_inputs_with_no_activity + bw2io.strategies.ecospold2.remove_unnamed_parameters + bw2io.strategies.ecospold2.es2_assign_only_product_with_amount_as_reference_product + bw2io.strategies.ecospold2.assign_single_product_as_activity + bw2io.strategies.ecospold2.create_composite_code + bw2io.strategies.ecospold2.link_internal_technosphere_by_composite_code + bw2io.strategies.ecospold2.delete_exchanges_missing_activity + bw2io.strategies.ecospold2.delete_ghost_exchanges + bw2io.strategies.ecospold2.remove_uncertainty_from_negative_loss_exchanges + bw2io.strategies.ecospold2.set_lognormal_loc_value + bw2io.strategies.ecospold2.fix_unreasonably_high_lognormal_uncertainties + bw2io.strategies.ecospold2.fix_ecoinvent_flows_pre35 + bw2io.strategies.ecospold2.drop_temporary_outdated_biosphere_flows + bw2io.strategies.ecospold2.add_cpc_classification_from_single_reference_product + bw2io.strategies.ecospold2.delete_none_synonyms + bw2io.strategies.ecospold2.update_social_flows_in_older_consequential + + + +.. py:function:: link_biosphere_by_flow_uuid(db, biosphere='biosphere3') + + +.. py:function:: remove_zero_amount_coproducts(db) + + Remove coproducts with zero production amounts from ``exchanges`` + + +.. py:function:: remove_zero_amount_inputs_with_no_activity(db) + + Remove technosphere exchanges with amount of zero and no uncertainty. + + Input exchanges with zero amounts are the result of the ecoinvent linking algorithm, and can be safely discarded. + + +.. py:function:: remove_unnamed_parameters(db) + + Remove parameters which have no name. They can't be used in formulas or referenced. + + +.. py:function:: es2_assign_only_product_with_amount_as_reference_product(db) + + If a multioutput process has one product with a non-zero amount, assign that product as reference product. + + This is by default called after ``remove_zero_amount_coproducts``, which will delete the zero-amount coproducts in any case. However, we still keep the zero-amount logic in case people want to keep all coproducts. + + +.. py:function:: assign_single_product_as_activity(db) + + +.. py:function:: create_composite_code(db) + + Create composite code from activity and flow names + + +.. py:function:: link_internal_technosphere_by_composite_code(db) + + Link internal technosphere inputs by ``code``. + + Only links to process datasets actually in the database document. + + +.. py:function:: delete_exchanges_missing_activity(db) + + Delete exchanges that weren't linked correctly by ecoinvent. + + These exchanges are missing the "activityLinkId" attribute, and the flow they want to consume is not produced as the reference product of any activity. See the `known data issues `__ report. + + + +.. py:function:: delete_ghost_exchanges(db) + + Delete technosphere which can't be linked due to ecoinvent errors. + + A ghost exchange is one which links to a combination of *activity* and *flow* which aren't provided in the database. + + +.. py:function:: remove_uncertainty_from_negative_loss_exchanges(db) + + Remove uncertainty from negative lognormal exchanges. + + There are 15699 of these in ecoinvent 3.3 cutoff. + + The basic uncertainty and pedigree matrix are applied rather blindly, + and the can produce strange net production values. It makes much more + sense to assume that these loss factors are static. + + Only applies to exchanges which decrease net production. + + + +.. py:function:: set_lognormal_loc_value(db) + + Make sure ``loc`` value is correct for lognormal uncertainty distributions + + +.. py:function:: fix_unreasonably_high_lognormal_uncertainties(db, cutoff=2.5, replacement=0.25) + + Fix unreasonably high uncertainty values. + + With the default cutoff value of 2.5 and a median of 1, the 95% confidence + interval has a high to low ratio of 20.000. + + +.. py:function:: fix_ecoinvent_flows_pre35(db) + + +.. py:function:: drop_temporary_outdated_biosphere_flows(db) + + Drop biosphere exchanges which aren't used and are outdated + + +.. py:function:: add_cpc_classification_from_single_reference_product(db) + + +.. py:function:: delete_none_synonyms(db) + + +.. py:function:: update_social_flows_in_older_consequential(db, biosphere_db) + + The consequential system model automatically generates new biosphere flows with the category ``social`` (even though they aren't social flows) which are not really used and definitely not characterized, and whose UUID seems to change with each release. They are: + + * residual wood, dry + * venting of argon, crude, liquid + * venting of nitrogen, liquid + + The ecoinvent centre `recommends that they be dropped `__: + + Consequential system model issues + Three elementary exchanges are found in the compartment “social”. These exchanges can be ignored, both at the unit process and the inventory level, as ecoinvent does not yet account for social impacts. + + However, we can just look up the new UUIDs. + + + diff --git a/sphinx/technical/api/bw2io/strategies/exiobase/index.rst b/sphinx/technical/api/bw2io/strategies/exiobase/index.rst new file mode 100644 index 0000000..5960f91 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/exiobase/index.rst @@ -0,0 +1,50 @@ +:py:mod:`bw2io.strategies.exiobase` +=================================== + +.. py:module:: bw2io.strategies.exiobase + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.exiobase.normalize_units + bw2io.strategies.exiobase.remove_numeric_codes + bw2io.strategies.exiobase.add_stam_labels + bw2io.strategies.exiobase.rename_exiobase_co2_eq_flows + bw2io.strategies.exiobase.get_exiobase_biosphere_correspondence + bw2io.strategies.exiobase.get_categories + bw2io.strategies.exiobase.add_biosphere_ids + bw2io.strategies.exiobase.add_product_ids + + + +.. py:function:: normalize_units(data, label='unit') + + +.. py:function:: remove_numeric_codes(products) + + +.. py:function:: add_stam_labels(data) + + +.. py:function:: rename_exiobase_co2_eq_flows(flows) + + +.. py:function:: get_exiobase_biosphere_correspondence() + + +.. py:function:: get_categories(x) + + +.. py:function:: add_biosphere_ids(correspondence, biospheres=None) + + +.. py:function:: add_product_ids(products, db_name) + + diff --git a/sphinx/technical/api/bw2io/strategies/generic/index.rst b/sphinx/technical/api/bw2io/strategies/generic/index.rst new file mode 100644 index 0000000..8b53214 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/generic/index.rst @@ -0,0 +1,163 @@ +:py:mod:`bw2io.strategies.generic` +================================== + +.. py:module:: bw2io.strategies.generic + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.generic.format_nonunique_key_error + bw2io.strategies.generic.link_iterable_by_fields + bw2io.strategies.generic.assign_only_product_as_production + bw2io.strategies.generic.link_technosphere_by_activity_hash + bw2io.strategies.generic.set_code_by_activity_hash + bw2io.strategies.generic.tupleize_categories + bw2io.strategies.generic.drop_unlinked + bw2io.strategies.generic.normalize_units + bw2io.strategies.generic.add_database_name + bw2io.strategies.generic.convert_uncertainty_types_to_integers + bw2io.strategies.generic.drop_falsey_uncertainty_fields_but_keep_zeros + bw2io.strategies.generic.convert_activity_parameters_to_list + bw2io.strategies.generic.split_exchanges + + + +.. py:function:: format_nonunique_key_error(obj, fields, others) + + +.. py:function:: link_iterable_by_fields(unlinked, other=None, fields=None, kind=None, internal=False, relink=False) + + Generic function to link objects in ``unlinked`` to objects in ``other`` using fields ``fields``. + + The database to be linked must have uniqueness for each object for the given ``fields``. + + If ``kind``, limit the exchanges in ``unlinked`` objects to types in ``kind``. + + If ``relink``, link to objects which already have an ``input``. Otherwise, skip already linked objects. + + If ``internal``, linked ``unlinked`` to other objects in ``unlinked``. Each object must have the attributes ``database`` and ``code``. + + +.. py:function:: assign_only_product_as_production(db) + + Assign only product as reference product. + + Skips datasets that already have a reference product or no production exchanges. Production exchanges must have a ``name`` and an amount. + + Will replace the following activity fields, if not already specified: + + * 'name' - name of reference product + * 'unit' - unit of reference product + * 'production amount' - amount of reference product + + + +.. py:function:: link_technosphere_by_activity_hash(db, external_db_name=None, fields=None) + + Link technosphere exchanges using ``activity_hash`` function. + + If ``external_db_name``, link against a different database; otherwise link internally. + + If ``fields``, link using only certain fields. + + +.. py:function:: set_code_by_activity_hash(db, overwrite=False) + + Use ``activity_hash`` to set dataset code. + + By default, won't overwrite existing codes, but will if ``overwrite`` is ``True``. + + +.. py:function:: tupleize_categories(db) + + +.. py:function:: drop_unlinked(db) + + This is the nuclear option - use at your own risk! + + +.. py:function:: normalize_units(db) + + Normalize units in datasets and their exchanges + + +.. py:function:: add_database_name(db, name) + + Add database name to datasets + + +.. py:function:: convert_uncertainty_types_to_integers(db) + + Generic number conversion function convert to floats. Return to integers. + + +.. py:function:: drop_falsey_uncertainty_fields_but_keep_zeros(db) + + Drop fields like '' but keep zero and NaN. + + Note that this doesn't strip `False`, which behaves *exactly* like 0. + + + +.. py:function:: convert_activity_parameters_to_list(data) + + Convert activity parameters from dictionary to list of dictionaries + + +.. py:function:: split_exchanges(data, filter_params, changed_attributes, allocation_factors=None) + + Split unlinked exchanges in ``data`` which satisfy ``filter_params`` into new exchanges with changed attributes. + + ``changed_attributes`` is a list of dictionaries with the attributes that should be changed. + + ``allocation_factors`` is an optional list of floats to allocate the original exchange amount to the respective copies defined in ``changed_attributes``. They don't have to sum to one. If ``allocation_factors`` are not defined, then exchanges are split equally. + + Resets uncertainty to ``UndefinedUncertainty`` (0). + + To use this function as a strategy, you will need to curry it first using ``functools.partial``. + + Example usage:: + + split_exchanges( + [ + {'exchanges': [{ + 'name': 'foo', + 'location': 'bar', + 'amount': 20 + }, { + 'name': 'food', + 'location': 'bar', + 'amount': 12 + }]} + ], + {'name': 'foo'}, + [{'location': 'A'}, {'location': 'B', 'cat': 'dog'} + ] + >>> [ + {'exchanges': [{ + 'name': 'food', + 'location': 'bar', + 'amount': 12 + }, { + 'name': 'foo', + 'location': 'A', + 'amount': 12., + 'uncertainty_type': 0 + }, { + 'name': 'foo', + 'location': 'B', + 'amount': 8., + 'uncertainty_type': 0, + 'cat': 'dog', + }]} + ] + + + diff --git a/sphinx/technical/api/bw2io/strategies/index.rst b/sphinx/technical/api/bw2io/strategies/index.rst new file mode 100644 index 0000000..2d3db94 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/index.rst @@ -0,0 +1,609 @@ +:py:mod:`bw2io.strategies` +========================== + +.. py:module:: bw2io.strategies + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + biosphere/index.rst + csv/index.rst + ecospold1/index.rst + ecospold1_allocation/index.rst + ecospold2/index.rst + exiobase/index.rst + generic/index.rst + json_ld/index.rst + json_ld_allocation/index.rst + json_ld_lcia/index.rst + lcia/index.rst + locations/index.rst + simapro/index.rst + special/index.rst + + +Package Contents +---------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.drop_unspecified_subcategories + bw2io.strategies.ensure_categories_are_tuples + bw2io.strategies.normalize_biosphere_categories + bw2io.strategies.normalize_biosphere_names + bw2io.strategies.strip_biosphere_exc_locations + bw2io.strategies.csv_add_missing_exchanges_section + bw2io.strategies.csv_drop_unknown + bw2io.strategies.csv_numerize + bw2io.strategies.csv_restore_booleans + bw2io.strategies.csv_restore_tuples + bw2io.strategies.clean_integer_codes + bw2io.strategies.delete_integer_codes + bw2io.strategies.es1_allocate_multioutput + bw2io.strategies.add_cpc_classification_from_single_reference_product + bw2io.strategies.assign_single_product_as_activity + bw2io.strategies.create_composite_code + bw2io.strategies.delete_exchanges_missing_activity + bw2io.strategies.delete_ghost_exchanges + bw2io.strategies.delete_none_synonyms + bw2io.strategies.drop_temporary_outdated_biosphere_flows + bw2io.strategies.es2_assign_only_product_with_amount_as_reference_product + bw2io.strategies.fix_ecoinvent_flows_pre35 + bw2io.strategies.fix_unreasonably_high_lognormal_uncertainties + bw2io.strategies.link_biosphere_by_flow_uuid + bw2io.strategies.link_internal_technosphere_by_composite_code + bw2io.strategies.remove_uncertainty_from_negative_loss_exchanges + bw2io.strategies.remove_unnamed_parameters + bw2io.strategies.remove_zero_amount_coproducts + bw2io.strategies.remove_zero_amount_inputs_with_no_activity + bw2io.strategies.set_lognormal_loc_value + bw2io.strategies.update_social_flows_in_older_consequential + bw2io.strategies.add_database_name + bw2io.strategies.assign_only_product_as_production + bw2io.strategies.convert_activity_parameters_to_list + bw2io.strategies.convert_uncertainty_types_to_integers + bw2io.strategies.drop_falsey_uncertainty_fields_but_keep_zeros + bw2io.strategies.drop_unlinked + bw2io.strategies.link_iterable_by_fields + bw2io.strategies.link_technosphere_by_activity_hash + bw2io.strategies.normalize_units + bw2io.strategies.set_code_by_activity_hash + bw2io.strategies.split_exchanges + bw2io.strategies.tupleize_categories + bw2io.strategies.json_ld_add_activity_unit + bw2io.strategies.json_ld_add_products_as_activities + bw2io.strategies.json_ld_convert_unit_to_reference_unit + bw2io.strategies.json_ld_fix_process_type + bw2io.strategies.json_ld_get_activities_list_from_rawdata + bw2io.strategies.json_ld_get_normalized_exchange_locations + bw2io.strategies.json_ld_get_normalized_exchange_units + bw2io.strategies.json_ld_label_exchange_type + bw2io.strategies.json_ld_location_name + bw2io.strategies.json_ld_prepare_exchange_fields_for_linking + bw2io.strategies.json_ld_remove_fields + bw2io.strategies.json_ld_rename_metadata_fields + bw2io.strategies.json_ld_allocate_datasets + bw2io.strategies.json_ld_lcia_add_method_metadata + bw2io.strategies.json_ld_lcia_convert_to_list + bw2io.strategies.json_ld_lcia_reformat_cfs_as_exchanges + bw2io.strategies.json_ld_lcia_set_method_metadata + bw2io.strategies.add_activity_hash_code + bw2io.strategies.drop_unlinked_cfs + bw2io.strategies.match_subcategories + bw2io.strategies.set_biosphere_type + bw2io.strategies.update_ecoinvent_locations + bw2io.strategies.change_electricity_unit_mj_to_kwh + bw2io.strategies.fix_localized_water_flows + bw2io.strategies.fix_zero_allocation_products + bw2io.strategies.link_technosphere_based_on_name_unit_location + bw2io.strategies.normalize_simapro_biosphere_categories + bw2io.strategies.normalize_simapro_biosphere_names + bw2io.strategies.sp_allocate_products + bw2io.strategies.split_simapro_name_geo + + + +.. py:function:: drop_unspecified_subcategories(db) + + Drop subcategories if they are in the following: + * ``unspecified`` + * ``(unspecified)`` + * ``''`` (empty string) + * ``None`` + + + +.. py:function:: ensure_categories_are_tuples(db) + + +.. py:function:: normalize_biosphere_categories(db, lcia=False) + + Normalize biosphere categories to ecoinvent 3.1 standard + + +.. py:function:: normalize_biosphere_names(db, lcia=False) + + Normalize biosphere flow names to ecoinvent 3.1 standard. + + Assumes that each dataset and each exchange have a ``name``. Will change names even if exchange is already linked. + + +.. py:function:: strip_biosphere_exc_locations(db) + + Biosphere flows don't have locations - if any are included they can confuse linking + + +.. py:function:: csv_add_missing_exchanges_section(data) + + +.. py:function:: csv_drop_unknown(data) + + Drop keys whose values are `(Unknown)`. + + +.. py:function:: csv_numerize(data) + + Turns strings into numbers where possible + + +.. py:function:: csv_restore_booleans(data) + + Turn `True` and `False` into proper booleans, where possible + + +.. py:function:: csv_restore_tuples(data) + + Restore tuples separated by `::` string + + +.. py:function:: clean_integer_codes(data) + + Convert integer activity codes to strings and delete integer codes from exchanges (they can't be believed). + + +.. py:function:: delete_integer_codes(data) + + Delete integer codes completely from extracted ecospold1 datasets + + +.. py:function:: es1_allocate_multioutput(data) + + This strategy allocates multioutput datasets to new datasets. + + This deletes the multioutput dataset, breaking any existing linking. This shouldn't be a concern, as you shouldn't link to a multioutput dataset in any case. + + Note that multiple allocations for the same product and input will result in undefined behavior. + + + +.. py:function:: add_cpc_classification_from_single_reference_product(db) + + +.. py:function:: assign_single_product_as_activity(db) + + +.. py:function:: create_composite_code(db) + + Create composite code from activity and flow names + + +.. py:function:: delete_exchanges_missing_activity(db) + + Delete exchanges that weren't linked correctly by ecoinvent. + + These exchanges are missing the "activityLinkId" attribute, and the flow they want to consume is not produced as the reference product of any activity. See the `known data issues `__ report. + + + +.. py:function:: delete_ghost_exchanges(db) + + Delete technosphere which can't be linked due to ecoinvent errors. + + A ghost exchange is one which links to a combination of *activity* and *flow* which aren't provided in the database. + + +.. py:function:: delete_none_synonyms(db) + + +.. py:function:: drop_temporary_outdated_biosphere_flows(db) + + Drop biosphere exchanges which aren't used and are outdated + + +.. py:function:: es2_assign_only_product_with_amount_as_reference_product(db) + + If a multioutput process has one product with a non-zero amount, assign that product as reference product. + + This is by default called after ``remove_zero_amount_coproducts``, which will delete the zero-amount coproducts in any case. However, we still keep the zero-amount logic in case people want to keep all coproducts. + + +.. py:function:: fix_ecoinvent_flows_pre35(db) + + +.. py:function:: fix_unreasonably_high_lognormal_uncertainties(db, cutoff=2.5, replacement=0.25) + + Fix unreasonably high uncertainty values. + + With the default cutoff value of 2.5 and a median of 1, the 95% confidence + interval has a high to low ratio of 20.000. + + +.. py:function:: link_biosphere_by_flow_uuid(db, biosphere='biosphere3') + + +.. py:function:: link_internal_technosphere_by_composite_code(db) + + Link internal technosphere inputs by ``code``. + + Only links to process datasets actually in the database document. + + +.. py:function:: remove_uncertainty_from_negative_loss_exchanges(db) + + Remove uncertainty from negative lognormal exchanges. + + There are 15699 of these in ecoinvent 3.3 cutoff. + + The basic uncertainty and pedigree matrix are applied rather blindly, + and the can produce strange net production values. It makes much more + sense to assume that these loss factors are static. + + Only applies to exchanges which decrease net production. + + + +.. py:function:: remove_unnamed_parameters(db) + + Remove parameters which have no name. They can't be used in formulas or referenced. + + +.. py:function:: remove_zero_amount_coproducts(db) + + Remove coproducts with zero production amounts from ``exchanges`` + + +.. py:function:: remove_zero_amount_inputs_with_no_activity(db) + + Remove technosphere exchanges with amount of zero and no uncertainty. + + Input exchanges with zero amounts are the result of the ecoinvent linking algorithm, and can be safely discarded. + + +.. py:function:: set_lognormal_loc_value(db) + + Make sure ``loc`` value is correct for lognormal uncertainty distributions + + +.. py:function:: update_social_flows_in_older_consequential(db, biosphere_db) + + The consequential system model automatically generates new biosphere flows with the category ``social`` (even though they aren't social flows) which are not really used and definitely not characterized, and whose UUID seems to change with each release. They are: + + * residual wood, dry + * venting of argon, crude, liquid + * venting of nitrogen, liquid + + The ecoinvent centre `recommends that they be dropped `__: + + Consequential system model issues + Three elementary exchanges are found in the compartment “social”. These exchanges can be ignored, both at the unit process and the inventory level, as ecoinvent does not yet account for social impacts. + + However, we can just look up the new UUIDs. + + + +.. py:function:: add_database_name(db, name) + + Add database name to datasets + + +.. py:function:: assign_only_product_as_production(db) + + Assign only product as reference product. + + Skips datasets that already have a reference product or no production exchanges. Production exchanges must have a ``name`` and an amount. + + Will replace the following activity fields, if not already specified: + + * 'name' - name of reference product + * 'unit' - unit of reference product + * 'production amount' - amount of reference product + + + +.. py:function:: convert_activity_parameters_to_list(data) + + Convert activity parameters from dictionary to list of dictionaries + + +.. py:function:: convert_uncertainty_types_to_integers(db) + + Generic number conversion function convert to floats. Return to integers. + + +.. py:function:: drop_falsey_uncertainty_fields_but_keep_zeros(db) + + Drop fields like '' but keep zero and NaN. + + Note that this doesn't strip `False`, which behaves *exactly* like 0. + + + +.. py:function:: drop_unlinked(db) + + This is the nuclear option - use at your own risk! + + +.. py:function:: link_iterable_by_fields(unlinked, other=None, fields=None, kind=None, internal=False, relink=False) + + Generic function to link objects in ``unlinked`` to objects in ``other`` using fields ``fields``. + + The database to be linked must have uniqueness for each object for the given ``fields``. + + If ``kind``, limit the exchanges in ``unlinked`` objects to types in ``kind``. + + If ``relink``, link to objects which already have an ``input``. Otherwise, skip already linked objects. + + If ``internal``, linked ``unlinked`` to other objects in ``unlinked``. Each object must have the attributes ``database`` and ``code``. + + +.. py:function:: link_technosphere_by_activity_hash(db, external_db_name=None, fields=None) + + Link technosphere exchanges using ``activity_hash`` function. + + If ``external_db_name``, link against a different database; otherwise link internally. + + If ``fields``, link using only certain fields. + + +.. py:function:: normalize_units(db) + + Normalize units in datasets and their exchanges + + +.. py:function:: set_code_by_activity_hash(db, overwrite=False) + + Use ``activity_hash`` to set dataset code. + + By default, won't overwrite existing codes, but will if ``overwrite`` is ``True``. + + +.. py:function:: split_exchanges(data, filter_params, changed_attributes, allocation_factors=None) + + Split unlinked exchanges in ``data`` which satisfy ``filter_params`` into new exchanges with changed attributes. + + ``changed_attributes`` is a list of dictionaries with the attributes that should be changed. + + ``allocation_factors`` is an optional list of floats to allocate the original exchange amount to the respective copies defined in ``changed_attributes``. They don't have to sum to one. If ``allocation_factors`` are not defined, then exchanges are split equally. + + Resets uncertainty to ``UndefinedUncertainty`` (0). + + To use this function as a strategy, you will need to curry it first using ``functools.partial``. + + Example usage:: + + split_exchanges( + [ + {'exchanges': [{ + 'name': 'foo', + 'location': 'bar', + 'amount': 20 + }, { + 'name': 'food', + 'location': 'bar', + 'amount': 12 + }]} + ], + {'name': 'foo'}, + [{'location': 'A'}, {'location': 'B', 'cat': 'dog'} + ] + >>> [ + {'exchanges': [{ + 'name': 'food', + 'location': 'bar', + 'amount': 12 + }, { + 'name': 'foo', + 'location': 'A', + 'amount': 12., + 'uncertainty_type': 0 + }, { + 'name': 'foo', + 'location': 'B', + 'amount': 8., + 'uncertainty_type': 0, + 'cat': 'dog', + }]} + ] + + + +.. py:function:: tupleize_categories(db) + + +.. py:function:: json_ld_add_activity_unit(db) + + Add units to activities from their reference products. + + +.. py:function:: json_ld_add_products_as_activities(db, products) + + +.. py:function:: json_ld_convert_unit_to_reference_unit(db) + + Convert the units to their reference unit. Also changes the format to eliminate unnecessary complexity. + + Changes: + + { + 'flow': {'refUnit': 'MJ', ...}, + 'unit': { + '@type': 'Unit', + '@id': '86ad2244-1f0e-4912-af53-7865283103e4', + 'name': 'kWh' + } + + To: + + { + 'flow': {...}, + 'unit': 'MJ' + } + + + + +.. py:function:: json_ld_fix_process_type(db) + + +.. py:function:: json_ld_get_activities_list_from_rawdata(data) + + Return list of processes from raw data. + + +.. py:function:: json_ld_get_normalized_exchange_locations(data) + + The exchanges location strings are not necessarily the same as those given in the process or the master metadata. Fix this inconsistency. + + This has to happen before we transform the input data from a dictionary to a list of activities, as it uses the ``locations`` data. + + +.. py:function:: json_ld_get_normalized_exchange_units(data) + + The exchanges unit strings are not necessarily the same as BW units. Fix this inconsistency. + + +.. py:function:: json_ld_label_exchange_type(db) + + +.. py:function:: json_ld_location_name(db) + + +.. py:function:: json_ld_prepare_exchange_fields_for_linking(db) + + +.. py:function:: json_ld_remove_fields(db) + + +.. py:function:: json_ld_rename_metadata_fields(db) + + Change metadata field names from the JSON-LD `processes` to BW schema. + + BW schema: https://wurst.readthedocs.io/#internal-data-format + + + +.. py:function:: json_ld_allocate_datasets(db, preferred_allocation=None) + + Perform allocation on multifunctional datasets. + + Uses the ``preferred_allocation`` method if available; otherwise, the default method. + + Here are the allocation methods listed in the JSON-LD spec: + + * PHYSICAL_ALLOCATION + * ECONOMIC_ALLOCATION + * CAUSAL_ALLOCATION (Can be exchange-specific) + * USE_DEFAULT_ALLOCATION + * NO_ALLOCATION + + We can't use ``@id`` values as codes after allocation, so we combine the process id and the flow id for the allocated dataset. + + + +.. py:function:: json_ld_lcia_add_method_metadata(data) + + +.. py:function:: json_ld_lcia_convert_to_list(data) + + +.. py:function:: json_ld_lcia_reformat_cfs_as_exchanges(data) + + +.. py:function:: json_ld_lcia_set_method_metadata(data) + + +.. py:function:: add_activity_hash_code(data) + + Add ``code`` field to characterization factors using ``activity_hash``, if ``code`` not already present. + + +.. py:function:: drop_unlinked_cfs(data) + + Drop CFs which don't have ``input`` attribute + + +.. py:function:: match_subcategories(data, biosphere_db_name, remove=True) + + Given a characterization with a top-level category, e.g. ``('air',)``, find all biosphere flows with the same top-level categories, and add CFs for these flows as well. Doesn't replace CFs for existing flows with multi-level categories. If ``remove``, also delete the top-level CF, but only if it is unlinked. + + +.. py:function:: set_biosphere_type(data) + + Set CF types to 'biosphere', to keep compatibility with LCI strategies. + + This will overwrite existing ``type`` values. + + +.. py:function:: update_ecoinvent_locations(db) + + Update old ecoinvent location codes + + +.. py:function:: change_electricity_unit_mj_to_kwh(db) + + Change datasets with the string ``electricity`` in their name from units of MJ to kilowatt hour. + + +.. py:function:: fix_localized_water_flows(db) + + Change ``Water, BR`` to ``Water``. + + Biosphere flows can't have locations - locations are defined by the activity dataset. + + +.. py:function:: fix_zero_allocation_products(db) + + Drop all inputs from allocated products which had zero allocation factors. + + The final production amount is the initial amount times the allocation factor. If this is zero, a singular technosphere matrix is created. We fix this by setting the production amount to one, and deleting all inputs. + + Does not modify datasets with more than one production exchange. + + +.. py:function:: link_technosphere_based_on_name_unit_location(db, external_db_name=None) + + Link technosphere exchanges based on name, unit, and location. Can't use categories because we can't reliably extract categories from SimaPro exports, only exchanges. + + If ``external_db_name``, link against a different database; otherwise link internally. + + +.. py:function:: normalize_simapro_biosphere_categories(db) + + Normalize biosphere categories to ecoinvent standard. + + +.. py:function:: normalize_simapro_biosphere_names(db) + + Normalize biosphere flow names to ecoinvent standard + + +.. py:function:: sp_allocate_products(db) + + Create a dataset from each product in a raw SimaPro dataset + + +.. py:function:: split_simapro_name_geo(db) + + Split a name like 'foo/CH U' into name and geo components. + + Sets original name to ``simapro name``. + + diff --git a/sphinx/technical/api/bw2io/strategies/json_ld/index.rst b/sphinx/technical/api/bw2io/strategies/json_ld/index.rst new file mode 100644 index 0000000..8344c90 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/json_ld/index.rst @@ -0,0 +1,102 @@ +:py:mod:`bw2io.strategies.json_ld` +================================== + +.. py:module:: bw2io.strategies.json_ld + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.json_ld.json_ld_get_normalized_exchange_locations + bw2io.strategies.json_ld.json_ld_add_products_as_activities + bw2io.strategies.json_ld.json_ld_convert_unit_to_reference_unit + bw2io.strategies.json_ld.json_ld_get_normalized_exchange_units + bw2io.strategies.json_ld.json_ld_add_activity_unit + bw2io.strategies.json_ld.json_ld_get_activities_list_from_rawdata + bw2io.strategies.json_ld.json_ld_rename_metadata_fields + bw2io.strategies.json_ld.json_ld_remove_fields + bw2io.strategies.json_ld.json_ld_location_name + bw2io.strategies.json_ld.json_ld_fix_process_type + bw2io.strategies.json_ld.json_ld_prepare_exchange_fields_for_linking + bw2io.strategies.json_ld.json_ld_label_exchange_type + + + +.. py:function:: json_ld_get_normalized_exchange_locations(data) + + The exchanges location strings are not necessarily the same as those given in the process or the master metadata. Fix this inconsistency. + + This has to happen before we transform the input data from a dictionary to a list of activities, as it uses the ``locations`` data. + + +.. py:function:: json_ld_add_products_as_activities(db, products) + + +.. py:function:: json_ld_convert_unit_to_reference_unit(db) + + Convert the units to their reference unit. Also changes the format to eliminate unnecessary complexity. + + Changes: + + { + 'flow': {'refUnit': 'MJ', ...}, + 'unit': { + '@type': 'Unit', + '@id': '86ad2244-1f0e-4912-af53-7865283103e4', + 'name': 'kWh' + } + + To: + + { + 'flow': {...}, + 'unit': 'MJ' + } + + + + +.. py:function:: json_ld_get_normalized_exchange_units(data) + + The exchanges unit strings are not necessarily the same as BW units. Fix this inconsistency. + + +.. py:function:: json_ld_add_activity_unit(db) + + Add units to activities from their reference products. + + +.. py:function:: json_ld_get_activities_list_from_rawdata(data) + + Return list of processes from raw data. + + +.. py:function:: json_ld_rename_metadata_fields(db) + + Change metadata field names from the JSON-LD `processes` to BW schema. + + BW schema: https://wurst.readthedocs.io/#internal-data-format + + + +.. py:function:: json_ld_remove_fields(db) + + +.. py:function:: json_ld_location_name(db) + + +.. py:function:: json_ld_fix_process_type(db) + + +.. py:function:: json_ld_prepare_exchange_fields_for_linking(db) + + +.. py:function:: json_ld_label_exchange_type(db) + + diff --git a/sphinx/technical/api/bw2io/strategies/json_ld_allocation/index.rst b/sphinx/technical/api/bw2io/strategies/json_ld_allocation/index.rst new file mode 100644 index 0000000..4dedc47 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/json_ld_allocation/index.rst @@ -0,0 +1,74 @@ +:py:mod:`bw2io.strategies.json_ld_allocation` +============================================= + +.. py:module:: bw2io.strategies.json_ld_allocation + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.json_ld_allocation.allocation_needed + bw2io.strategies.json_ld_allocation.allocatable_exchanges + bw2io.strategies.json_ld_allocation.get_allocation_dict + bw2io.strategies.json_ld_allocation.get_production_exchanges + bw2io.strategies.json_ld_allocation.get_production_exchange + bw2io.strategies.json_ld_allocation.causal_allocation + bw2io.strategies.json_ld_allocation.json_ld_allocate_datasets + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.json_ld_allocation.VALID_METHODS + + +.. py:data:: VALID_METHODS + + + + +.. py:function:: allocation_needed(ds) + + +.. py:function:: allocatable_exchanges(exchanges) + + +.. py:function:: get_allocation_dict(factors) + + +.. py:function:: get_production_exchanges(exchanges) + + +.. py:function:: get_production_exchange(exchanges, flow_id) + + +.. py:function:: causal_allocation(exchanges, ad) + + +.. py:function:: json_ld_allocate_datasets(db, preferred_allocation=None) + + Perform allocation on multifunctional datasets. + + Uses the ``preferred_allocation`` method if available; otherwise, the default method. + + Here are the allocation methods listed in the JSON-LD spec: + + * PHYSICAL_ALLOCATION + * ECONOMIC_ALLOCATION + * CAUSAL_ALLOCATION (Can be exchange-specific) + * USE_DEFAULT_ALLOCATION + * NO_ALLOCATION + + We can't use ``@id`` values as codes after allocation, so we combine the process id and the flow id for the allocated dataset. + + + diff --git a/sphinx/technical/api/bw2io/strategies/json_ld_lcia/index.rst b/sphinx/technical/api/bw2io/strategies/json_ld_lcia/index.rst new file mode 100644 index 0000000..795f0b5 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/json_ld_lcia/index.rst @@ -0,0 +1,34 @@ +:py:mod:`bw2io.strategies.json_ld_lcia` +======================================= + +.. py:module:: bw2io.strategies.json_ld_lcia + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.json_ld_lcia.json_ld_lcia_add_method_metadata + bw2io.strategies.json_ld_lcia.json_ld_lcia_set_method_metadata + bw2io.strategies.json_ld_lcia.json_ld_lcia_convert_to_list + bw2io.strategies.json_ld_lcia.json_ld_lcia_reformat_cfs_as_exchanges + + + +.. py:function:: json_ld_lcia_add_method_metadata(data) + + +.. py:function:: json_ld_lcia_set_method_metadata(data) + + +.. py:function:: json_ld_lcia_convert_to_list(data) + + +.. py:function:: json_ld_lcia_reformat_cfs_as_exchanges(data) + + diff --git a/sphinx/technical/api/bw2io/strategies/lcia/index.rst b/sphinx/technical/api/bw2io/strategies/lcia/index.rst new file mode 100644 index 0000000..55f800b --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/lcia/index.rst @@ -0,0 +1,56 @@ +:py:mod:`bw2io.strategies.lcia` +=============================== + +.. py:module:: bw2io.strategies.lcia + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.lcia.add_activity_hash_code + bw2io.strategies.lcia.drop_unlinked_cfs + bw2io.strategies.lcia.set_biosphere_type + bw2io.strategies.lcia.rationalize_method_names + bw2io.strategies.lcia.match_subcategories + bw2io.strategies.lcia.fix_ecoinvent_38_lcia_implementation + + + +.. py:function:: add_activity_hash_code(data) + + Add ``code`` field to characterization factors using ``activity_hash``, if ``code`` not already present. + + +.. py:function:: drop_unlinked_cfs(data) + + Drop CFs which don't have ``input`` attribute + + +.. py:function:: set_biosphere_type(data) + + Set CF types to 'biosphere', to keep compatibility with LCI strategies. + + This will overwrite existing ``type`` values. + + +.. py:function:: rationalize_method_names(data) + + +.. py:function:: match_subcategories(data, biosphere_db_name, remove=True) + + Given a characterization with a top-level category, e.g. ``('air',)``, find all biosphere flows with the same top-level categories, and add CFs for these flows as well. Doesn't replace CFs for existing flows with multi-level categories. If ``remove``, also delete the top-level CF, but only if it is unlinked. + + +.. py:function:: fix_ecoinvent_38_lcia_implementation(data) + + Ecoinvent 3.8 LCIA implmentation uses some flow names from 3.7. + + Update these when possible, delete when not. + + diff --git a/sphinx/technical/api/bw2io/strategies/locations/index.rst b/sphinx/technical/api/bw2io/strategies/locations/index.rst new file mode 100644 index 0000000..04a3968 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/locations/index.rst @@ -0,0 +1,37 @@ +:py:mod:`bw2io.strategies.locations` +==================================== + +.. py:module:: bw2io.strategies.locations + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.locations.update_ecoinvent_locations + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.locations.GEO_UPDATE + + +.. py:data:: GEO_UPDATE + + + + +.. py:function:: update_ecoinvent_locations(db) + + Update old ecoinvent location codes + + diff --git a/sphinx/technical/api/bw2io/strategies/simapro/index.rst b/sphinx/technical/api/bw2io/strategies/simapro/index.rst new file mode 100644 index 0000000..e410e12 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/simapro/index.rst @@ -0,0 +1,139 @@ +:py:mod:`bw2io.strategies.simapro` +================================== + +.. py:module:: bw2io.strategies.simapro + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.simapro.sp_allocate_products + bw2io.strategies.simapro.fix_zero_allocation_products + bw2io.strategies.simapro.link_technosphere_based_on_name_unit_location + bw2io.strategies.simapro.split_simapro_name_geo + bw2io.strategies.simapro.normalize_simapro_biosphere_categories + bw2io.strategies.simapro.normalize_simapro_biosphere_names + bw2io.strategies.simapro.fix_iff_formula + bw2io.strategies.simapro.normalize_simapro_formulae + bw2io.strategies.simapro.change_electricity_unit_mj_to_kwh + bw2io.strategies.simapro.fix_localized_water_flows + bw2io.strategies.simapro.set_lognormal_loc_value_uncertainty_safe + bw2io.strategies.simapro.flip_sign_on_waste + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.simapro.detoxify_pattern + bw2io.strategies.simapro.detoxify_re + bw2io.strategies.simapro.iff_exp + + +.. py:data:: detoxify_pattern + :annotation: = ^(?P.+?)/(?P[A-Za-z]{2,10})(/I)? [SU]$ + + + +.. py:data:: detoxify_re + + + + +.. py:function:: sp_allocate_products(db) + + Create a dataset from each product in a raw SimaPro dataset + + +.. py:function:: fix_zero_allocation_products(db) + + Drop all inputs from allocated products which had zero allocation factors. + + The final production amount is the initial amount times the allocation factor. If this is zero, a singular technosphere matrix is created. We fix this by setting the production amount to one, and deleting all inputs. + + Does not modify datasets with more than one production exchange. + + +.. py:function:: link_technosphere_based_on_name_unit_location(db, external_db_name=None) + + Link technosphere exchanges based on name, unit, and location. Can't use categories because we can't reliably extract categories from SimaPro exports, only exchanges. + + If ``external_db_name``, link against a different database; otherwise link internally. + + +.. py:function:: split_simapro_name_geo(db) + + Split a name like 'foo/CH U' into name and geo components. + + Sets original name to ``simapro name``. + + +.. py:function:: normalize_simapro_biosphere_categories(db) + + Normalize biosphere categories to ecoinvent standard. + + +.. py:function:: normalize_simapro_biosphere_names(db) + + Normalize biosphere flow names to ecoinvent standard + + +.. py:data:: iff_exp + + + + +.. py:function:: fix_iff_formula(string) + + +.. py:function:: normalize_simapro_formulae(formula, settings) + + Convert SimaPro formulae to Python + + +.. py:function:: change_electricity_unit_mj_to_kwh(db) + + Change datasets with the string ``electricity`` in their name from units of MJ to kilowatt hour. + + +.. py:function:: fix_localized_water_flows(db) + + Change ``Water, BR`` to ``Water``. + + Biosphere flows can't have locations - locations are defined by the activity dataset. + + +.. py:function:: set_lognormal_loc_value_uncertainty_safe(db) + + Make sure ``loc`` value is correct for lognormal uncertainty distributions + + +.. py:function:: flip_sign_on_waste(db, other) + + Flip sign on waste exchanges in imported database + + Rationale: The convention for waste exchanges in some databases, notably the + ecoivent database, is the following: + - waste exchanges produced by an activity are stored as negative inputs + - waste exchanges flowing into a waste treatment activity have a negative + In SimaPro, produced waste are stored as positive inputs, and waste + treatment datasets have positive outputs. + If left as are, the supply of waste treatment services from the linked-to + database "other" to imported activities would have the wrong sign. + This function flips the sign on all inputs from database "other" if the + production amount for that exchange in the activity for which it is the + reference flow is negative. + + Note: the strategy needs to be run *after* matching with ecoinvent. + Strategy should be run as follows: + sp_imported.apply_strategy(functools.partial(flip_sign_on_waste, other="name_of_other")) + + diff --git a/sphinx/technical/api/bw2io/strategies/special/index.rst b/sphinx/technical/api/bw2io/strategies/special/index.rst new file mode 100644 index 0000000..ddea278 --- /dev/null +++ b/sphinx/technical/api/bw2io/strategies/special/index.rst @@ -0,0 +1,24 @@ +:py:mod:`bw2io.strategies.special` +================================== + +.. py:module:: bw2io.strategies.special + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.strategies.special.add_dummy_processes_and_rename_exchanges + + + +.. py:function:: add_dummy_processes_and_rename_exchanges(db) + + Add new processes to link to so-called "dummy" processes in the US LCI database. + + diff --git a/sphinx/technical/api/bw2io/tests/index.rst b/sphinx/technical/api/bw2io/tests/index.rst new file mode 100644 index 0000000..900e29d --- /dev/null +++ b/sphinx/technical/api/bw2io/tests/index.rst @@ -0,0 +1,72 @@ +:py:mod:`bw2io.tests` +===================== + +.. py:module:: bw2io.tests + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.tests.MockMetadata + bw2io.tests.MockDS + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.tests.mocks + + +.. py:class:: MockMetadata(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.SerializedDict` + + Base class for dictionary that can be `serialized `_ to or unserialized from disk. Uses JSON as its storage format. Has most of the methods of a dictionary. + + Upon instantiation, the serialized dictionary is read from disk. + + .. py:attribute:: filename + :annotation: = mock-meta.json + + + + +.. py:data:: mocks + + + + +.. py:class:: MockDS(name) + + Bases: :py:obj:`bw2data.data_store.DataStore` + + Mock DataStore for testing + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: dtype_fields + :annotation: = [] + + + + .. py:method:: process_data(row) + + + diff --git a/sphinx/technical/api/bw2io/units/index.rst b/sphinx/technical/api/bw2io/units/index.rst new file mode 100644 index 0000000..a7ab399 --- /dev/null +++ b/sphinx/technical/api/bw2io/units/index.rst @@ -0,0 +1,59 @@ +:py:mod:`bw2io.units` +===================== + +.. py:module:: bw2io.units + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.units.get_default_units_migration_data + bw2io.units.get_unusual_units_migration_data + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.units.UNITS_NORMALIZATION + bw2io.units.normalize_units + bw2io.units.DEFAULT_UNITS_CONVERSION + bw2io.units._USED_IN_ECOINVENT + + +.. py:data:: UNITS_NORMALIZATION + + + + +.. py:data:: normalize_units + + + + +.. py:data:: DEFAULT_UNITS_CONVERSION + :annotation: = [['PJ', 'megajoule', 1000000000.0], ['J', 'megajoule', 1e-06], ['Nm3 Europe', 'megajoule',... + + + +.. py:function:: get_default_units_migration_data() + + +.. py:data:: _USED_IN_ECOINVENT + + + + +.. py:function:: get_unusual_units_migration_data() + + Only convert units that are not used in ecoinvent at all + + diff --git a/sphinx/technical/api/bw2io/unlinked_data/index.rst b/sphinx/technical/api/bw2io/unlinked_data/index.rst new file mode 100644 index 0000000..bb6fc8e --- /dev/null +++ b/sphinx/technical/api/bw2io/unlinked_data/index.rst @@ -0,0 +1,73 @@ +:py:mod:`bw2io.unlinked_data` +============================= + +.. py:module:: bw2io.unlinked_data + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2io.unlinked_data._UnlinkedData + bw2io.unlinked_data.UnlinkedData + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.unlinked_data.unlinked_data + + +.. py:class:: _UnlinkedData(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.SerializedDict` + + Base class for dictionary that can be `serialized `_ to or unserialized from disk. Uses JSON as its storage format. Has most of the methods of a dictionary. + + Upon instantiation, the serialized dictionary is read from disk. + + .. py:attribute:: filename + :annotation: = unlinked_data.json + + + + +.. py:data:: unlinked_data + + + + +.. py:class:: UnlinkedData(name) + + Bases: :py:obj:`bw2data.data_store.DataStore` + + Base class for all Brightway2 data stores. Subclasses should define: + + * **metadata**: A :ref:`serialized-dict` instance, e.g. ``databases`` or ``methods``. The custom is that each type of data store has a new metadata store, so the data store ``Foo`` would have a metadata store ``foos``. + * **validator**: A data validator. Optional. See bw2data.validate. + + + .. py:attribute:: _metadata + + + + + .. py:attribute:: _intermediate_dir + :annotation: = unlinked + + + + .. py:method:: validate(*args, **kwargs) + + Validate data. Must be called manually. + + + diff --git a/sphinx/technical/api/bw2io/utils/index.rst b/sphinx/technical/api/bw2io/utils/index.rst new file mode 100644 index 0000000..a1eef15 --- /dev/null +++ b/sphinx/technical/api/bw2io/utils/index.rst @@ -0,0 +1,94 @@ +:py:mod:`bw2io.utils` +===================== + +.. py:module:: bw2io.utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2io.utils.activity_hash + bw2io.utils.es2_activity_hash + bw2io.utils.load_json_data_file + bw2io.utils.format_for_logging + bw2io.utils.rescale_exchange + bw2io.utils.standardize_method_to_len_3 + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2io.utils.DEFAULT_FIELDS + + +.. py:data:: DEFAULT_FIELDS + :annotation: = ['name', 'categories', 'unit', 'reference product', 'location'] + + + +.. py:function:: activity_hash(data, fields=None, case_insensitive=True) + + Hash an activity dataset. + + Used to import data formats like ecospold 1 (ecoinvent v1-2) and SimaPro, where no unique attributes for datasets are given. This is clearly an imperfect and brittle solution, but there is no other obvious approach at this time. + + The fields used can be optionally specified in ``fields``. + + No fields are required; an empty string is used if a field isn't present. All fields are cast to lower case. + + By default, uses the following, in order: + * name + * categories + * unit + * reference product + * location + + :param \* *data*: The :ref:`activity dataset data `. + :type \* *data*: dict + :param \* *fields*: Optional list of fields to hash together. Default is ``('name', 'categories', 'unit', 'reference product', 'location')``. + :type \* *fields*: list + :param \* *case_insensitive*: Cast everything to lowercase before computing hash. Default is ``True``. + :type \* *case_insensitive*: bool + + :returns: A MD5 hash string, hex-encoded. + + +.. py:function:: es2_activity_hash(activity, flow) + + Generate unique ID for ecoinvent3 dataset. + + Despite using a million UUIDs, there is actually no unique ID in an ecospold2 dataset. Datasets are uniquely identified by the combination of activity and flow UUIDs. + + +.. py:function:: load_json_data_file(filename) + + +.. py:function:: format_for_logging(obj) + + +.. py:function:: rescale_exchange(exc, factor) + + Rescale exchanges, including formulas and uncertainty values, by a constant factor. + + No generally recommended, but needed for use in unit conversions. Not well tested. + + + +.. py:function:: standardize_method_to_len_3(name, padding='--', joiner=',') + + Standardize an LCIA method name to a length 3 tuple. + + ``name`` is the current name. + ``padding`` is the string to use for missing fields. + + + diff --git a/sphinx/technical/api/bw2io/validation/index.rst b/sphinx/technical/api/bw2io/validation/index.rst new file mode 100644 index 0000000..35b092d --- /dev/null +++ b/sphinx/technical/api/bw2io/validation/index.rst @@ -0,0 +1,14 @@ +:py:mod:`bw2io.validation` +========================== + +.. py:module:: bw2io.validation + + +Module Contents +--------------- + +.. py:data:: bw2package_validator + + + + diff --git a/sphinx/technical/api/bw2io/version/index.rst b/sphinx/technical/api/bw2io/version/index.rst new file mode 100644 index 0000000..a2622c4 --- /dev/null +++ b/sphinx/technical/api/bw2io/version/index.rst @@ -0,0 +1,14 @@ +:py:mod:`bw2io.version` +======================= + +.. py:module:: bw2io.version + + +Module Contents +--------------- + +.. py:data:: version + :annotation: = [0, 9, 'DEV10'] + + + diff --git a/sphinx/technical/api/bw2regional/base_data/index.rst b/sphinx/technical/api/bw2regional/base_data/index.rst new file mode 100644 index 0000000..3c39620 --- /dev/null +++ b/sphinx/technical/api/bw2regional/base_data/index.rst @@ -0,0 +1,43 @@ +:py:mod:`bw2regional.base_data` +=============================== + +.. py:module:: bw2regional.base_data + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.base_data.create_world_collections + bw2regional.base_data.create_ecoinvent_collections + bw2regional.base_data.create_restofworlds_collections + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.base_data.COUNTRIES + + +.. py:data:: COUNTRIES + + + + +.. py:function:: create_world_collections() + + +.. py:function:: create_ecoinvent_collections() + + +.. py:function:: create_restofworlds_collections() + + diff --git a/sphinx/technical/api/bw2regional/databases/index.rst b/sphinx/technical/api/bw2regional/databases/index.rst new file mode 100644 index 0000000..fbc49cd --- /dev/null +++ b/sphinx/technical/api/bw2regional/databases/index.rst @@ -0,0 +1,26 @@ +:py:mod:`bw2regional.databases` +=============================== + +.. py:module:: bw2regional.databases + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.databases.label_activity_geocollections + + + +.. py:function:: label_activity_geocollections(name) + + Add geocollections to activity ``location`` fields. + + ``name`` is the name of an existing LCI database. + + diff --git a/sphinx/technical/api/bw2regional/density/index.rst b/sphinx/technical/api/bw2regional/density/index.rst new file mode 100644 index 0000000..2f74df0 --- /dev/null +++ b/sphinx/technical/api/bw2regional/density/index.rst @@ -0,0 +1,40 @@ +:py:mod:`bw2regional.density` +============================= + +.. py:module:: bw2regional.density + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.density.get_area + bw2regional.density.get_column_array + bw2regional.density.divide_by_area + + + +.. py:function:: get_area(lat1, lat2, width) + + Get area of a spherical quadrangle. + + lat1, lat2, and width should all be in degrees. + + Uses the formula derived and demonstrated in https://gis.stackexchange.com/questions/127165/more-accurate-way-to-calculate-area-of-rasters. + + +.. py:function:: get_column_array(affine, rows, width) + + +.. py:function:: divide_by_area(source_fp, destination_fp) + + Create a new raster file at ``destination_fp``, dividing the values in ``source_fp`` by their cell's area. + + Will raise an error is the CRS is not geographic, or the raster is rotated. + + diff --git a/sphinx/technical/api/bw2regional/errors/index.rst b/sphinx/technical/api/bw2regional/errors/index.rst new file mode 100644 index 0000000..ed4e3c7 --- /dev/null +++ b/sphinx/technical/api/bw2regional/errors/index.rst @@ -0,0 +1,72 @@ +:py:mod:`bw2regional.errors` +============================ + +.. py:module:: bw2regional.errors + + +Module Contents +--------------- + +.. py:exception:: BW2RegionalizationError + + Bases: :py:obj:`Exception` + + Base class for BW2 regionalization errors + + +.. py:exception:: UnprocessedDatabase + + Bases: :py:obj:`BW2RegionalizationError` + + A ``Database`` object doesn't have a list of reference geocollections. + + +.. py:exception:: SiteGenericMethod + + Bases: :py:obj:`BW2RegionalizationError` + + This ``Method`` doesn't have links to ``geocollections``, making it site-generic. + + +.. py:exception:: MissingIntersection + + Bases: :py:obj:`BW2RegionalizationError` + + Missing an ``Intersection`` object and its data needed for regionalized LCA + + +.. py:exception:: GeocollectionsMismatch + + Bases: :py:obj:`BW2RegionalizationError` + + Base class for BW2 regionalization errors + + +.. py:exception:: MissingSpatialSourceData + + Bases: :py:obj:`BW2RegionalizationError` + + Base class for BW2 regionalization errors + + +.. py:exception:: TopologyError + + Bases: :py:obj:`BW2RegionalizationError` + + Inventory includes locations for which no topology data is available + + +.. py:exception:: IncompleteSpatialDefinition + + Bases: :py:obj:`BW2RegionalizationError` + + Given metadata is not enough to understand a spatial data source + + +.. py:exception:: WindowsPathCharacterLimit + + Bases: :py:obj:`BW2RegionalizationError` + + Windows has an absolute limit of 255 characters in a filepath + + diff --git a/sphinx/technical/api/bw2regional/export/index.rst b/sphinx/technical/api/bw2regional/export/index.rst new file mode 100644 index 0000000..9019e8b --- /dev/null +++ b/sphinx/technical/api/bw2regional/export/index.rst @@ -0,0 +1,71 @@ +:py:mod:`bw2regional.export` +============================ + +.. py:module:: bw2regional.export + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.export.add_attributes + bw2regional.export.unplottable + bw2regional.export.create_geodataframe + bw2regional.export._generic_exporter + bw2regional.export._hash_feature + bw2regional.export.add_two_geojson_results + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.export.as_inv_spatial_scale + bw2regional.export.as_ia_spatial_scale + bw2regional.export.as_xt_spatial_scale + + +.. py:function:: add_attributes(dct, func, row_index, col_index) + + +.. py:function:: unplottable(key) + + +.. py:function:: create_geodataframe(matrix, used_geocollections, row_dict, col_dict, spatial_dim='col', attribute_adder=None, cutoff=None) + + +.. py:function:: _generic_exporter(lca, geocollection, filepath, spatial_dict, spatial_func, score_column_absolute='score_abs', score_column_relative='score_rel', cutoff=0.001) + + +.. py:data:: as_inv_spatial_scale + + + + +.. py:data:: as_ia_spatial_scale + + + + +.. py:data:: as_xt_spatial_scale + + + + +.. py:function:: _hash_feature(feature) + + Calculate SHA256 hash of feature geometry as WKT + + +.. py:function:: add_two_geojson_results(first, second, output_filepath, first_column_name='score_abs', second_column_name='score_abs', score_column_absolute='score_abs', score_column_relative='score_rel', cutoff=0.0001) + + Sum results from two regionalized LCA calculations on the same spatial scale. + + diff --git a/sphinx/technical/api/bw2regional/gis_tasks/index.rst b/sphinx/technical/api/bw2regional/gis_tasks/index.rst new file mode 100644 index 0000000..6b3da48 --- /dev/null +++ b/sphinx/technical/api/bw2regional/gis_tasks/index.rst @@ -0,0 +1,47 @@ +:py:mod:`bw2regional.gis_tasks` +=============================== + +.. py:module:: bw2regional.gis_tasks + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.gis_tasks.raster_as_extension_table + bw2regional.gis_tasks.calculate_intersection + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.gis_tasks.pandarus + bw2regional.gis_tasks.CPU_COUNT + + +.. py:data:: pandarus + + + + +.. py:data:: CPU_COUNT + + + + +.. py:function:: raster_as_extension_table(vector, raster, name=None, engine=remote, overwrite=False) + + +.. py:function:: calculate_intersection(first, second, engine=remote, overwrite=False, cpus=None) + + Calculate and write areal intersections between two vector geocollections + + diff --git a/sphinx/technical/api/bw2regional/hashing/index.rst b/sphinx/technical/api/bw2regional/hashing/index.rst new file mode 100644 index 0000000..40c44b7 --- /dev/null +++ b/sphinx/technical/api/bw2regional/hashing/index.rst @@ -0,0 +1,24 @@ +:py:mod:`bw2regional.hashing` +============================= + +.. py:module:: bw2regional.hashing + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.hashing.sha256 + + + +.. py:function:: sha256(filepath, blocksize=65536) + + Generate SHA 256 hash for file at `filepath` + + diff --git a/sphinx/technical/api/bw2regional/index.rst b/sphinx/technical/api/bw2regional/index.rst new file mode 100644 index 0000000..6615bc7 --- /dev/null +++ b/sphinx/technical/api/bw2regional/index.rst @@ -0,0 +1,688 @@ +:py:mod:`bw2regional` +===================== + +.. py:module:: bw2regional + + +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + + lca/index.rst + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + base_data/index.rst + databases/index.rst + density/index.rst + errors/index.rst + export/index.rst + gis_tasks/index.rst + hashing/index.rst + intersection/index.rst + loading/index.rst + meta/index.rst + pandarus/index.rst + pandarus_remote/index.rst + topography/index.rst + utils/index.rst + validate/index.rst + version/index.rst + xtables/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.Topography + bw2regional.Loading + bw2regional.Intersection + bw2regional.ExtensionTable + bw2regional.ExtensionTablesLCA + bw2regional.OneSpatialScaleLCA + bw2regional.TwoSpatialScalesLCA + bw2regional.TwoSpatialScalesWithGenericLoadingLCA + bw2regional.PandarusRemote + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.divide_by_area + bw2regional.create_ecoinvent_collections + bw2regional.create_restofworlds_collections + bw2regional.create_world_collections + bw2regional.calculate_intersection + bw2regional.raster_as_extension_table + bw2regional.sha256 + bw2regional.import_from_pandarus + bw2regional.create_empty_intersection + bw2regional.get_spatial_dataset_kind + bw2regional.hash_collection + bw2regional.import_regionalized_cfs + bw2regional.reset_all_geo + bw2regional.reset_geo_meta + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.cg + bw2regional.extension_tables + bw2regional.geocollections + bw2regional.intersections + bw2regional.loadings + bw2regional.topocollections + bw2regional.remote + + +.. py:data:: cg + + + + +.. py:class:: Topography(name) + + Bases: :py:obj:`bw2data.DataStore` + + A topographical description of a ``geocollection``. + + A topography must be registered to exactly one geocollection. + + For example, the geocollection 'countries' could have the location 'Ghana', and include a spatial data file which defines 'Ghana'. The `Topography` 'countries-topo' would have two files: + + #. A mapping file that linked each location in the geocollection 'countries' to a set of topographical faces + #. A spatial dataset which defines the topographical faces + + Topographies can make some calculations much quicker for two reasons: + + #. Large features are split into smaller faces, making GIS calculations easier and spatial index queries more efficient, as bounding boxes are smaller + #. You only do GIS work on each topographical face once + + The LCA classes in ``bw2regional`` don't work directly with the `Topography` objects; rather, GIS calculations on topographies can be merged to the spatial features of the `geocollection`. + + The usual order of operations with a `geocollection` that has a `Topography` is the following: + + #. Create a `geocollection`, including defining a spatial dataset + #. Create a new `topocollection` and specify the linked `geocollection`. You can also optionally define another spatial dataset, using the same format as for `geocollections`. + #. Instantiate the new `Topography` object created in the earlier step, and write mapping data from spatial features in the `geocollection`(s) to face ids in the `Topography` spatial data set. + + The data format for mapping data is ``{feature field value: [list of topo field values (usually id numbers)]}``. + + Here is a code sample for using the test data in `bw2regional`: + + .. code-block:: python + + from bw2regional import Topography, geocollections + import json + + geocollections['countries'] = { + 'filepath': os.path.join(data_dir, "test_countries.gpkg"), + 'field': 'name' + } + topocollections['countries-topo'] = { + 'geocollection': 'countries', + 'filepath': os.path.join(data_dir, "test_provinces.gpkg"), + 'field': 'OBJECTID_1' + } + topo = Topography('countries-topo') + topo.write(json.load(open(os.path.join(data_dir, "test_topo_mapping.json")))) + + + .. py:property:: geocollection + + + .. py:attribute:: _metadata + + + + + .. py:method:: add_geomappings(data) + + + .. py:method:: write(data) + + Serialize intermediate data to disk. + + :param \* *data*: The data + :type \* *data*: object + + + +.. py:class:: Loading(name) + + Bases: :py:obj:`bw2data.data_store.ProcessedDataStore` + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = loading_matrix + + + + .. py:method:: add_geomappings(data) + + In theory, this shouldn't do anything, as all spatial units should be in defined by the method. + + + .. py:method:: process(**extra_metadata) + + Process intermediate data from a Python dictionary to a `stats_arrays `_ array, which is a `NumPy `_ `Structured `_ `Array `_. A structured array (also called record array) is a heterogeneous array, where each column has a different label and data type. + + Processed arrays are saved in the ``processed`` directory. + + If the uncertainty type is no uncertainty, undefined, or not specified, then the 'amount' value is used for 'loc' as well. This is needed for the random number generator. + + Doesn't return anything, but writes a file to disk. + + + + +.. py:data:: extension_tables + + + + +.. py:data:: geocollections + + + + +.. py:data:: intersections + + + + +.. py:data:: loadings + + + + +.. py:data:: topocollections + + + + +.. py:class:: Intersection(name) + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = intersection_matrix + + + + .. py:method:: add_geomappings(data) + + Add all geographic units in both geocollections to ``geomapping``, the master location list. + + Called automatically when data is written. + + + .. py:method:: create_reversed_intersection() + + Create (B, A) intersection from (A, B). + + + .. py:method:: process(**extra_metadata) + + Process intermediate data from a Python dictionary to a `stats_arrays `_ array, which is a `NumPy `_ `Structured `_ `Array `_. A structured array (also called record array) is a heterogeneous array, where each column has a different label and data type. + + Processed arrays are saved in the ``processed`` directory. + + If the uncertainty type is no uncertainty, undefined, or not specified, then the 'amount' value is used for 'loc' as well. This is needed for the random number generator. + + Doesn't return anything, but writes a file to disk. + + + + +.. py:class:: ExtensionTable(name) + + Bases: :py:obj:`bw2regional.loading.Loading` + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = xtable_matrix + + + + .. py:method:: write_to_map(*args, **kwargs) + :abstractmethod: + + + .. py:method:: import_from_map(mask=None) + + + +.. py:function:: divide_by_area(source_fp, destination_fp) + + Create a new raster file at ``destination_fp``, dividing the values in ``source_fp`` by their cell's area. + + Will raise an error is the CRS is not geographic, or the raster is rotated. + + +.. py:class:: ExtensionTablesLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'distribution_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm',... + + + + .. py:method:: needed_inv_xtable_intersections() + + + .. py:method:: needed_xtable_ia_intersections() + + + .. py:method:: check_geocollection_intersections() + + + .. py:method:: get_xtable_geodata() + + + .. py:method:: create_distribution_matrix() + + Get distribution matrix, **D**, which provides the area of inventory spatial units in each extension table spatial unit. Rows are inventory spatial units and columns are extension table spatial units. + + + .. py:method:: create_xtable_matrix() + + Diagonal extension table matrix that indicates the extension table density value in each extension table spatial unit. + + + .. py:method:: build_distribution_normalization_matrix() + + Get normalization matrix for inventory-xtable mapping. Normalizes to + + .. math:: + ( \textbf{N}_{dx} )_{i, i} = \left[ \sum_{j} \textbf{DX}_{i, j} \right]^{-1} + + + + .. py:method:: build_geo_transform_normalization_matrix() + + Get normalization value for areas in each IA spatial unit: + + .. math:: + ( \textbf{N}_{g} )_{i, i} = \left[ \sum_{j} \textbf{G}_{i, j} \right]^{-1} + + + + .. py:method:: create_geo_transform_matrix() + + Get geographic transform matrix **G**, which gives the intersecting areas of inventory and impact assessment spatial units. Rows are inventory spatial units, and columns are impact assessment spatial units. + + Uses ``self.inv_spatial_dict`` and ``self.ia_spatial_dict``. + + :returns: Parameter array with row/col of inventory and IA locations + * ``geo_transform_matrix``: The matrix **G** + :rtype: * ``geo_transform_params`` + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: apply_inv_mappinig_limitations() + + + .. py:method:: apply_cf_matrix_limitations() + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + .. py:method:: results_xtable_spatial_scale() + + + +.. py:class:: OneSpatialScaleLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + :abstractmethod: + + + .. py:method:: results_inv_spatial_scale() + + + +.. py:class:: TwoSpatialScalesLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: build_normalization_matrix() + + Get normalization matrix, a diagonal matrix. + + .. math:: + \textbf{N}_{i,i} = \left[ \sum_{j} \left( \textbf{G} \right)_{i,j} \right]^{-1} + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + +.. py:class:: TwoSpatialScalesWithGenericLoadingLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm', 'loading_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: build_normalization_matrix() + + Get normalization matrix, a diagonal matrix. + + .. math:: + \textbf{N}_{i,i} = \left[ \sum_{j} \left( \textbf{GL} \right)_{i,j} \right]^{-1} + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + +.. py:function:: create_ecoinvent_collections() + + +.. py:function:: create_restofworlds_collections() + + +.. py:function:: create_world_collections() + + +.. py:function:: calculate_intersection(first, second, engine=remote, overwrite=False, cpus=None) + + Calculate and write areal intersections between two vector geocollections + + +.. py:function:: raster_as_extension_table(vector, raster, name=None, engine=remote, overwrite=False) + + +.. py:function:: sha256(filepath, blocksize=65536) + + Generate SHA 256 hash for file at `filepath` + + +.. py:function:: import_from_pandarus(fp) + + Load output file from Pandarus job. + + This function will: + + * Load pandarus output file + * Locate the appropriate geo- or topocollection + * Check to make sure that SHA256 hashes and other metadata match + * If ``first`` is a topocollection, make sure the appropriate ``Topology`` exists, and squash the pandarus results to the linked geocollection(s). + + + +.. py:class:: PandarusRemote(url=None) + + Bases: :py:obj:`object` + + Interaction with `pandarus_remote `__ web service. + + Default URL is `https://pandarus.brightway.dev`. + + .. py:property:: alive + + + .. py:method:: _download_file(resp) + + + .. py:method:: catalog() + + + .. py:method:: status(url) + + + .. py:method:: upload(collection) + + + .. py:method:: intersection(collection_one, collection_two) + + + .. py:method:: intersection_as_new_geocollection(collection_one, collection_two, new_name) + + + .. py:method:: rasterstats_as_xt(vector, raster, name) + + + .. py:method:: calculate_rasterstats(vector, raster) + + + .. py:method:: calculate_intersection(collection_one, collection_two) + + + .. py:method:: hash_and_upload(collection, catalog=None) + + + .. py:method:: handle_errors(response) + + + +.. py:data:: remote + + + + +.. py:function:: create_empty_intersection(name) + + Shortcut to create Intersection object with no data + + +.. py:function:: get_spatial_dataset_kind(filepath) + + Get kind of spatial dataset at `filepath`. + + Returns one of "vector", "raster", None. + + + +.. py:function:: hash_collection(name) + + Return SHA256 hash for a topo- or geocollection. + + Prefers topocollection if available. + + +.. py:function:: import_regionalized_cfs(geocollection, method_tuple, mapping, scaling_factor=1, global_cfs=None, nan_value=None) + + Import data from a vector geospatial dataset into a ``Method``. + + A ``Method`` can have both site-generic and regionalized characterization factors. + + The ``mapping`` defines which field (vector) maps to which biosphere flows. Some geocollections may only define regionalized chracterization factors for a single biosphere flow, but it is much more common to have each field or band map to multiple biosphere flows. Therefore, mapping should be defined as: + + .. code-block:: python + + { + field name (str): [list of biosphere flows (tuples)] + } + + :param \* *geocollection*: A ``geocollection`` name. + :param \* *method_tuple*: A method tuple. + :param \* *mapping*: Mapping from fields or bands to biosphere flows. See above. + :param \* *scaling_factor*: Optional. Rescale the values in the spatial data source. + :param \* *global_cfs*: An optional list of CFs to add when writing the method. + :param \* *nan_value*: Sentinel value for missing values if ``NaN`` is not used directly. + + +.. py:function:: reset_all_geo() + + Reset all bw2regional data and metadata + + +.. py:function:: reset_geo_meta() + + diff --git a/sphinx/technical/api/bw2regional/intersection/index.rst b/sphinx/technical/api/bw2regional/intersection/index.rst new file mode 100644 index 0000000..d6830c0 --- /dev/null +++ b/sphinx/technical/api/bw2regional/intersection/index.rst @@ -0,0 +1,63 @@ +:py:mod:`bw2regional.intersection` +================================== + +.. py:module:: bw2regional.intersection + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.intersection.Intersection + + + + +.. py:class:: Intersection(name) + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = intersection_matrix + + + + .. py:method:: add_geomappings(data) + + Add all geographic units in both geocollections to ``geomapping``, the master location list. + + Called automatically when data is written. + + + .. py:method:: create_reversed_intersection() + + Create (B, A) intersection from (A, B). + + + .. py:method:: process(**extra_metadata) + + Process intermediate data from a Python dictionary to a `stats_arrays `_ array, which is a `NumPy `_ `Structured `_ `Array `_. A structured array (also called record array) is a heterogeneous array, where each column has a different label and data type. + + Processed arrays are saved in the ``processed`` directory. + + If the uncertainty type is no uncertainty, undefined, or not specified, then the 'amount' value is used for 'loc' as well. This is needed for the random number generator. + + Doesn't return anything, but writes a file to disk. + + + + diff --git a/sphinx/technical/api/bw2regional/lca/base_class/index.rst b/sphinx/technical/api/bw2regional/lca/base_class/index.rst new file mode 100644 index 0000000..49c9add --- /dev/null +++ b/sphinx/technical/api/bw2regional/lca/base_class/index.rst @@ -0,0 +1,131 @@ +:py:mod:`bw2regional.lca.base_class` +==================================== + +.. py:module:: bw2regional.lca.base_class + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.lca.base_class.RegionalizationBase + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.lca.base_class.get_dependent_databases + bw2regional.lca.base_class.annotate_flow + + + +.. py:function:: get_dependent_databases(demand_dict) + + Demand can be activitiy ids or tuple keys. + + +.. py:function:: annotate_flow(flow_id, _) + + +.. py:class:: RegionalizationBase(demand, *args, **kwargs) + + Bases: :py:obj:`bw2calc.lca.LCA` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:method:: get_inventory_geocollections() + + Get the set of all needed inventory geocollections. + + Raise UnprocessedDatabase if any database is missing the required metadata. + + + .. py:method:: get_ia_geocollections() + + Retrieve the geocollections linked to the impact assessment method + + + .. py:method:: create_inventory_mapping_matrix() + + Get inventory mapping matrix, **M**, which maps inventory activities to inventory locations. Rows are inventory activities and columns are inventory spatial units. + + Uses ``self.technosphere_mm.row_mapper`` and ``self.databases``. + + Creates ``self.inv_mapping_mm``, ``self.inv_mapping_matrix``, and ``self.dicts.inv_spatial``/ + + + + .. py:method:: needed_intersections() + + Figure out which ``Intersection`` objects are needed bsed on ``self.inventory_geocollections`` and ``self.ia_geocollections``. + + Raise ``MissingIntersection`` if an intersection is required, but not available. + + + .. py:method:: create_geo_transform_matrix() + + Get geographic transform matrix **G**, which gives the intersecting areas of inventory and impact assessment spatial units. Rows are inventory spatial units, and columns are impact assessment spatial units. + + Uses ``self.inv_spatial_dict`` and ``self.ia_spatial_dict``. + + :returns: Parameter array with row/col of inventory and IA locations + * ``geo_transform_matrix``: The matrix **G** + :rtype: * ``geo_transform_params`` + + + .. py:method:: create_regionalized_characterization_matrix(row_mapper=None) + + Get regionalized characterization matrix, **R**, which gives location- and biosphere flow-specific characterization factors. + + Rows are impact assessment spatial units, and columns are biosphere flows. However, we build it transverse and transpose it, as the characterization matrix indices are provided that way. + + Uses ``self._biosphere_dict`` and ``self.method``. + + :returns: Parameter array with row/col of IA locations/biosphere flows + * ``ia_spatial_dict``: Dictionary linking impact assessment locations to matrix rows + * ``reg_cf_matrix``: The matrix **R** + :rtype: * ``reg_cf_params`` + + + .. py:method:: create_loading_matrix() + + Get diagonal regionalized loading matrix, **L**, which gives location-specific background loading factors. Dimensions are impact assessment spatial units. + + Uses ``self.dicts.ia_spatial``. + + + + .. py:method:: _results_new_scale(matrix, flow) + + + .. py:method:: results_ia_spatial_scale() + :abstractmethod: + + + .. py:method:: results_inv_spatial_scale() + :abstractmethod: + + + .. py:method:: __geodataframe(matrix, sum_flows, annotate_flows, col_dict, used_geocollections, cutoff) + + + .. py:method:: geodataframe_xtable_spatial_scale(sum_flows=True, annotate_flows=None, cutoff=None) + + + .. py:method:: geodataframe_ia_spatial_scale(sum_flows=True, annotate_flows=None, cutoff=None) + + + .. py:method:: geodataframe_inv_spatial_scale(sum_flows=True, annotate_flows=None, cutoff=None) + + + diff --git a/sphinx/technical/api/bw2regional/lca/extension_tables/index.rst b/sphinx/technical/api/bw2regional/lca/extension_tables/index.rst new file mode 100644 index 0000000..4b3bb07 --- /dev/null +++ b/sphinx/technical/api/bw2regional/lca/extension_tables/index.rst @@ -0,0 +1,119 @@ +:py:mod:`bw2regional.lca.extension_tables` +========================================== + +.. py:module:: bw2regional.lca.extension_tables + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.lca.extension_tables.ExtensionTablesLCA + + + + +.. py:class:: ExtensionTablesLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'distribution_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm',... + + + + .. py:method:: needed_inv_xtable_intersections() + + + .. py:method:: needed_xtable_ia_intersections() + + + .. py:method:: check_geocollection_intersections() + + + .. py:method:: get_xtable_geodata() + + + .. py:method:: create_distribution_matrix() + + Get distribution matrix, **D**, which provides the area of inventory spatial units in each extension table spatial unit. Rows are inventory spatial units and columns are extension table spatial units. + + + .. py:method:: create_xtable_matrix() + + Diagonal extension table matrix that indicates the extension table density value in each extension table spatial unit. + + + .. py:method:: build_distribution_normalization_matrix() + + Get normalization matrix for inventory-xtable mapping. Normalizes to + + .. math:: + ( \textbf{N}_{dx} )_{i, i} = \left[ \sum_{j} \textbf{DX}_{i, j} \right]^{-1} + + + + .. py:method:: build_geo_transform_normalization_matrix() + + Get normalization value for areas in each IA spatial unit: + + .. math:: + ( \textbf{N}_{g} )_{i, i} = \left[ \sum_{j} \textbf{G}_{i, j} \right]^{-1} + + + + .. py:method:: create_geo_transform_matrix() + + Get geographic transform matrix **G**, which gives the intersecting areas of inventory and impact assessment spatial units. Rows are inventory spatial units, and columns are impact assessment spatial units. + + Uses ``self.inv_spatial_dict`` and ``self.ia_spatial_dict``. + + :returns: Parameter array with row/col of inventory and IA locations + * ``geo_transform_matrix``: The matrix **G** + :rtype: * ``geo_transform_params`` + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: apply_inv_mappinig_limitations() + + + .. py:method:: apply_cf_matrix_limitations() + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + .. py:method:: results_xtable_spatial_scale() + + + diff --git a/sphinx/technical/api/bw2regional/lca/index.rst b/sphinx/technical/api/bw2regional/lca/index.rst new file mode 100644 index 0000000..278f249 --- /dev/null +++ b/sphinx/technical/api/bw2regional/lca/index.rst @@ -0,0 +1,271 @@ +:py:mod:`bw2regional.lca` +========================= + +.. py:module:: bw2regional.lca + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + base_class/index.rst + extension_tables/index.rst + one_spatial_scale/index.rst + two_spatial_scales/index.rst + two_spatial_scales_weighting/index.rst + + +Package Contents +---------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.lca.ExtensionTablesLCA + bw2regional.lca.OneSpatialScaleLCA + bw2regional.lca.TwoSpatialScalesLCA + bw2regional.lca.TwoSpatialScalesWithGenericLoadingLCA + + + + +.. py:class:: ExtensionTablesLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'distribution_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm',... + + + + .. py:method:: needed_inv_xtable_intersections() + + + .. py:method:: needed_xtable_ia_intersections() + + + .. py:method:: check_geocollection_intersections() + + + .. py:method:: get_xtable_geodata() + + + .. py:method:: create_distribution_matrix() + + Get distribution matrix, **D**, which provides the area of inventory spatial units in each extension table spatial unit. Rows are inventory spatial units and columns are extension table spatial units. + + + .. py:method:: create_xtable_matrix() + + Diagonal extension table matrix that indicates the extension table density value in each extension table spatial unit. + + + .. py:method:: build_distribution_normalization_matrix() + + Get normalization matrix for inventory-xtable mapping. Normalizes to + + .. math:: + ( \textbf{N}_{dx} )_{i, i} = \left[ \sum_{j} \textbf{DX}_{i, j} \right]^{-1} + + + + .. py:method:: build_geo_transform_normalization_matrix() + + Get normalization value for areas in each IA spatial unit: + + .. math:: + ( \textbf{N}_{g} )_{i, i} = \left[ \sum_{j} \textbf{G}_{i, j} \right]^{-1} + + + + .. py:method:: create_geo_transform_matrix() + + Get geographic transform matrix **G**, which gives the intersecting areas of inventory and impact assessment spatial units. Rows are inventory spatial units, and columns are impact assessment spatial units. + + Uses ``self.inv_spatial_dict`` and ``self.ia_spatial_dict``. + + :returns: Parameter array with row/col of inventory and IA locations + * ``geo_transform_matrix``: The matrix **G** + :rtype: * ``geo_transform_params`` + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: apply_inv_mappinig_limitations() + + + .. py:method:: apply_cf_matrix_limitations() + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + .. py:method:: results_xtable_spatial_scale() + + + +.. py:class:: OneSpatialScaleLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + :abstractmethod: + + + .. py:method:: results_inv_spatial_scale() + + + +.. py:class:: TwoSpatialScalesLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: build_normalization_matrix() + + Get normalization matrix, a diagonal matrix. + + .. math:: + \textbf{N}_{i,i} = \left[ \sum_{j} \left( \textbf{G} \right)_{i,j} \right]^{-1} + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + +.. py:class:: TwoSpatialScalesWithGenericLoadingLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm', 'loading_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: build_normalization_matrix() + + Get normalization matrix, a diagonal matrix. + + .. math:: + \textbf{N}_{i,i} = \left[ \sum_{j} \left( \textbf{GL} \right)_{i,j} \right]^{-1} + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + diff --git a/sphinx/technical/api/bw2regional/lca/one_spatial_scale/index.rst b/sphinx/technical/api/bw2regional/lca/one_spatial_scale/index.rst new file mode 100644 index 0000000..6445cfb --- /dev/null +++ b/sphinx/technical/api/bw2regional/lca/one_spatial_scale/index.rst @@ -0,0 +1,57 @@ +:py:mod:`bw2regional.lca.one_spatial_scale` +=========================================== + +.. py:module:: bw2regional.lca.one_spatial_scale + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.lca.one_spatial_scale.OneSpatialScaleLCA + + + + +.. py:class:: OneSpatialScaleLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + :abstractmethod: + + + .. py:method:: results_inv_spatial_scale() + + + diff --git a/sphinx/technical/api/bw2regional/lca/two_spatial_scales/index.rst b/sphinx/technical/api/bw2regional/lca/two_spatial_scales/index.rst new file mode 100644 index 0000000..3f47351 --- /dev/null +++ b/sphinx/technical/api/bw2regional/lca/two_spatial_scales/index.rst @@ -0,0 +1,68 @@ +:py:mod:`bw2regional.lca.two_spatial_scales` +============================================ + +.. py:module:: bw2regional.lca.two_spatial_scales + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.lca.two_spatial_scales.TwoSpatialScalesLCA + + + + +.. py:class:: TwoSpatialScalesLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: build_normalization_matrix() + + Get normalization matrix, a diagonal matrix. + + .. math:: + \textbf{N}_{i,i} = \left[ \sum_{j} \left( \textbf{G} \right)_{i,j} \right]^{-1} + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + diff --git a/sphinx/technical/api/bw2regional/lca/two_spatial_scales_weighting/index.rst b/sphinx/technical/api/bw2regional/lca/two_spatial_scales_weighting/index.rst new file mode 100644 index 0000000..dcff28d --- /dev/null +++ b/sphinx/technical/api/bw2regional/lca/two_spatial_scales_weighting/index.rst @@ -0,0 +1,68 @@ +:py:mod:`bw2regional.lca.two_spatial_scales_weighting` +====================================================== + +.. py:module:: bw2regional.lca.two_spatial_scales_weighting + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.lca.two_spatial_scales_weighting.TwoSpatialScalesWithGenericLoadingLCA + + + + +.. py:class:: TwoSpatialScalesWithGenericLoadingLCA(*args, **kwargs) + + Bases: :py:obj:`bw2regional.lca.base_class.RegionalizationBase` + + An LCI or LCIA calculation. + + Compatible with Brightway2 and 2.5 semantics. Can be static, stochastic, or iterative (scenario-based), depending on the ``data_objs`` input data.. + + + .. py:attribute:: matrix_labels + :annotation: = ['biosphere_mm', 'geo_transform_mm', 'inv_mapping_mm', 'reg_cf_mm', 'technosphere_mm', 'loading_mm'] + + + + .. py:method:: load_lcia_data() + + Load data and create characterization matrix. + + This method will filter out regionalized characterization factors. + + + + .. py:method:: after_matrix_iteration() + + + .. py:method:: build_normalization_matrix() + + Get normalization matrix, a diagonal matrix. + + .. math:: + \textbf{N}_{i,i} = \left[ \sum_{j} \left( \textbf{GL} \right)_{i,j} \right]^{-1} + + + + .. py:method:: lcia_calculation() + + Do regionalized LCA calculation. + + Creates ``self.characterized_inventory``. + + + + .. py:method:: results_ia_spatial_scale() + + + .. py:method:: results_inv_spatial_scale() + + + diff --git a/sphinx/technical/api/bw2regional/loading/index.rst b/sphinx/technical/api/bw2regional/loading/index.rst new file mode 100644 index 0000000..3dbe704 --- /dev/null +++ b/sphinx/technical/api/bw2regional/loading/index.rst @@ -0,0 +1,60 @@ +:py:mod:`bw2regional.loading` +============================= + +.. py:module:: bw2regional.loading + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.loading.Loading + + + + +.. py:class:: Loading(name) + + Bases: :py:obj:`bw2data.data_store.ProcessedDataStore` + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = loading_matrix + + + + .. py:method:: add_geomappings(data) + + In theory, this shouldn't do anything, as all spatial units should be in defined by the method. + + + .. py:method:: process(**extra_metadata) + + Process intermediate data from a Python dictionary to a `stats_arrays `_ array, which is a `NumPy `_ `Structured `_ `Array `_. A structured array (also called record array) is a heterogeneous array, where each column has a different label and data type. + + Processed arrays are saved in the ``processed`` directory. + + If the uncertainty type is no uncertainty, undefined, or not specified, then the 'amount' value is used for 'loc' as well. This is needed for the random number generator. + + Doesn't return anything, but writes a file to disk. + + + + diff --git a/sphinx/technical/api/bw2regional/meta/index.rst b/sphinx/technical/api/bw2regional/meta/index.rst new file mode 100644 index 0000000..244752f --- /dev/null +++ b/sphinx/technical/api/bw2regional/meta/index.rst @@ -0,0 +1,126 @@ +:py:mod:`bw2regional.meta` +========================== + +.. py:module:: bw2regional.meta + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.meta.Loadings + bw2regional.meta.Intersections + bw2regional.meta.Geocollections + bw2regional.meta.Topocollections + bw2regional.meta.ExtensionTables + + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.meta.extension_tables + bw2regional.meta.geocollections + bw2regional.meta.intersections + bw2regional.meta.loadings + bw2regional.meta.topocollections + + +.. py:class:: Loadings(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.SerializedDict` + + Metadata on regionalized LCIA weightings. + + .. py:attribute:: filename + :annotation: = loadings.json + + + + +.. py:class:: Intersections(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.CompoundJSONDict` + + Areal intersections between the elements of two geo- or topocollections + + .. py:attribute:: filename + :annotation: = intersections.json + + + + +.. py:class:: Geocollections(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.SerializedDict` + + Metadata for spatial data sets. + + .. py:attribute:: filename + :annotation: = geocollections.json + + + + .. py:method:: __setitem__(key, value) + + + +.. py:class:: Topocollections(dirpath=None) + + Bases: :py:obj:`Geocollections` + + Mappings from geocollections to a set of topographical face ids. + + .. py:attribute:: filename + :annotation: = topocollections.json + + + + .. py:method:: __setitem__(key, value) + + + +.. py:class:: ExtensionTables(dirpath=None) + + Bases: :py:obj:`bw2data.serialization.SerializedDict` + + Metadata for extension tables that give loadings on a third spatial scale. + + .. py:attribute:: filename + :annotation: = extension-tables.json + + + + +.. py:data:: extension_tables + + + + +.. py:data:: geocollections + + + + +.. py:data:: intersections + + + + +.. py:data:: loadings + + + + +.. py:data:: topocollections + + + + diff --git a/sphinx/technical/api/bw2regional/pandarus/index.rst b/sphinx/technical/api/bw2regional/pandarus/index.rst new file mode 100644 index 0000000..03e6c2b --- /dev/null +++ b/sphinx/technical/api/bw2regional/pandarus/index.rst @@ -0,0 +1,77 @@ +:py:mod:`bw2regional.pandarus` +============================== + +.. py:module:: bw2regional.pandarus + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.pandarus.relabel + bw2regional.pandarus.load_file + bw2regional.pandarus.get_possible_collections + bw2regional.pandarus.import_from_pandarus + bw2regional.pandarus.handle_topographical_intersection + bw2regional.pandarus.import_xt_from_rasterstats + + + +.. py:function:: relabel(data, first, second) + + Add geocollection names to geo identifiers + + +.. py:function:: load_file(filepath) + + Load Pandarus JSON output file. + + Returns metadata and calculation results. + + +.. py:function:: get_possible_collections(kwargs) + + Return all geo- and topocollections for a file hash. + + Returns list of (collection name, collection type) tuples. + + +.. py:function:: import_from_pandarus(fp) + + Load output file from Pandarus job. + + This function will: + + * Load pandarus output file + * Locate the appropriate geo- or topocollection + * Check to make sure that SHA256 hashes and other metadata match + * If ``first`` is a topocollection, make sure the appropriate ``Topology`` exists, and squash the pandarus results to the linked geocollection(s). + + + +.. py:function:: handle_topographical_intersection(metadata, data, first_collections, second_collections, filepath) + + Handle an intersection between one or more topographies and a single geocollection. + + Each topography is associated with exactly one geocollection. + + Each topography is not empty, i.e. we can use the topographical definitions to filter. + + The procedure is: + #. Check metadata validity, and make sure the topography ids are in the first column + #. To split data into each topography + #. Squash the topography to geocollections + #. Create a new intersection for each geocollection/topography pair + + We use Pandas DataFrames to do aggregation in a resource efficient way. We also write the processed Intersection arrays directly. + + + +.. py:function:: import_xt_from_rasterstats(fp, name, gc, **kwargs) + + diff --git a/sphinx/technical/api/bw2regional/pandarus_remote/index.rst b/sphinx/technical/api/bw2regional/pandarus_remote/index.rst new file mode 100644 index 0000000..542531d --- /dev/null +++ b/sphinx/technical/api/bw2regional/pandarus_remote/index.rst @@ -0,0 +1,129 @@ +:py:mod:`bw2regional.pandarus_remote` +===================================== + +.. py:module:: bw2regional.pandarus_remote + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.pandarus_remote.PendingJob + bw2regional.pandarus_remote.PandarusRemote + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.pandarus_remote.run_job + bw2regional.pandarus_remote.check_alive + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.pandarus_remote.remote + + +.. py:exception:: RemoteError + + Bases: :py:obj:`Exception` + + Can't reach pandarus-remote web service + + +.. py:exception:: NotYetCalculated + + Bases: :py:obj:`Exception` + + Resource hasn't been calculated yet + + +.. py:exception:: AlreadyExists + + Bases: :py:obj:`Exception` + + Resource has already been calculated + + +.. py:class:: PendingJob(url) + + Bases: :py:obj:`object` + + A calculation job enqueued on a remote server + + .. py:property:: status + + + .. py:method:: poll(interval=10) + + + +.. py:function:: run_job(job) + + Handler that blocks until job is finished. + + +.. py:function:: check_alive(wrapped, instance, args, kwargs) + + +.. py:class:: PandarusRemote(url=None) + + Bases: :py:obj:`object` + + Interaction with `pandarus_remote `__ web service. + + Default URL is `https://pandarus.brightway.dev`. + + .. py:property:: alive + + + .. py:method:: _download_file(resp) + + + .. py:method:: catalog() + + + .. py:method:: status(url) + + + .. py:method:: upload(collection) + + + .. py:method:: intersection(collection_one, collection_two) + + + .. py:method:: intersection_as_new_geocollection(collection_one, collection_two, new_name) + + + .. py:method:: rasterstats_as_xt(vector, raster, name) + + + .. py:method:: calculate_rasterstats(vector, raster) + + + .. py:method:: calculate_intersection(collection_one, collection_two) + + + .. py:method:: hash_and_upload(collection, catalog=None) + + + .. py:method:: handle_errors(response) + + + +.. py:data:: remote + + + + diff --git a/sphinx/technical/api/bw2regional/topography/index.rst b/sphinx/technical/api/bw2regional/topography/index.rst new file mode 100644 index 0000000..8dac9ec --- /dev/null +++ b/sphinx/technical/api/bw2regional/topography/index.rst @@ -0,0 +1,87 @@ +:py:mod:`bw2regional.topography` +================================ + +.. py:module:: bw2regional.topography + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.topography.Topography + + + + +.. py:class:: Topography(name) + + Bases: :py:obj:`bw2data.DataStore` + + A topographical description of a ``geocollection``. + + A topography must be registered to exactly one geocollection. + + For example, the geocollection 'countries' could have the location 'Ghana', and include a spatial data file which defines 'Ghana'. The `Topography` 'countries-topo' would have two files: + + #. A mapping file that linked each location in the geocollection 'countries' to a set of topographical faces + #. A spatial dataset which defines the topographical faces + + Topographies can make some calculations much quicker for two reasons: + + #. Large features are split into smaller faces, making GIS calculations easier and spatial index queries more efficient, as bounding boxes are smaller + #. You only do GIS work on each topographical face once + + The LCA classes in ``bw2regional`` don't work directly with the `Topography` objects; rather, GIS calculations on topographies can be merged to the spatial features of the `geocollection`. + + The usual order of operations with a `geocollection` that has a `Topography` is the following: + + #. Create a `geocollection`, including defining a spatial dataset + #. Create a new `topocollection` and specify the linked `geocollection`. You can also optionally define another spatial dataset, using the same format as for `geocollections`. + #. Instantiate the new `Topography` object created in the earlier step, and write mapping data from spatial features in the `geocollection`(s) to face ids in the `Topography` spatial data set. + + The data format for mapping data is ``{feature field value: [list of topo field values (usually id numbers)]}``. + + Here is a code sample for using the test data in `bw2regional`: + + .. code-block:: python + + from bw2regional import Topography, geocollections + import json + + geocollections['countries'] = { + 'filepath': os.path.join(data_dir, "test_countries.gpkg"), + 'field': 'name' + } + topocollections['countries-topo'] = { + 'geocollection': 'countries', + 'filepath': os.path.join(data_dir, "test_provinces.gpkg"), + 'field': 'OBJECTID_1' + } + topo = Topography('countries-topo') + topo.write(json.load(open(os.path.join(data_dir, "test_topo_mapping.json")))) + + + .. py:property:: geocollection + + + .. py:attribute:: _metadata + + + + + .. py:method:: add_geomappings(data) + + + .. py:method:: write(data) + + Serialize intermediate data to disk. + + :param \* *data*: The data + :type \* *data*: object + + + diff --git a/sphinx/technical/api/bw2regional/utils/index.rst b/sphinx/technical/api/bw2regional/utils/index.rst new file mode 100644 index 0000000..c9d08a7 --- /dev/null +++ b/sphinx/technical/api/bw2regional/utils/index.rst @@ -0,0 +1,120 @@ +:py:mod:`bw2regional.utils` +=========================== + +.. py:module:: bw2regional.utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.utils.filter_fiona_metadata + bw2regional.utils.import_regionalized_cfs + bw2regional.utils.get_pandarus_map + bw2regional.utils.get_pandarus_map_for_method + bw2regional.utils.hash_collection + bw2regional.utils.create_empty_intersection + bw2regional.utils.get_spatial_dataset_kind + bw2regional.utils.reset_all_geo + bw2regional.utils.reset_geo_meta + bw2regional.utils.filter_rows + bw2regional.utils.filter_columns + bw2regional.utils.create_certain_datapackage + bw2regional.utils.dp + + + +.. py:function:: filter_fiona_metadata(dct) + + Include only valid Fiona keywords for opening a feature collection + + +.. py:function:: import_regionalized_cfs(geocollection, method_tuple, mapping, scaling_factor=1, global_cfs=None, nan_value=None) + + Import data from a vector geospatial dataset into a ``Method``. + + A ``Method`` can have both site-generic and regionalized characterization factors. + + The ``mapping`` defines which field (vector) maps to which biosphere flows. Some geocollections may only define regionalized chracterization factors for a single biosphere flow, but it is much more common to have each field or band map to multiple biosphere flows. Therefore, mapping should be defined as: + + .. code-block:: python + + { + field name (str): [list of biosphere flows (tuples)] + } + + :param \* *geocollection*: A ``geocollection`` name. + :param \* *method_tuple*: A method tuple. + :param \* *mapping*: Mapping from fields or bands to biosphere flows. See above. + :param \* *scaling_factor*: Optional. Rescale the values in the spatial data source. + :param \* *global_cfs*: An optional list of CFs to add when writing the method. + :param \* *nan_value*: Sentinel value for missing values if ``NaN`` is not used directly. + + +.. py:function:: get_pandarus_map(geocollection) + + +.. py:function:: get_pandarus_map_for_method(method, geocollection=None) + + +.. py:function:: hash_collection(name) + + Return SHA256 hash for a topo- or geocollection. + + Prefers topocollection if available. + + +.. py:function:: create_empty_intersection(name) + + Shortcut to create Intersection object with no data + + +.. py:function:: get_spatial_dataset_kind(filepath) + + Get kind of spatial dataset at `filepath`. + + Returns one of "vector", "raster", None. + + + +.. py:function:: reset_all_geo() + + Reset all bw2regional data and metadata + + +.. py:function:: reset_geo_meta() + + +.. py:function:: filter_rows(matrix, row_indices, exclude=True) + + Filter a sparse matrix, either excluding or taking only the rows in ``row_indices``. + + * ``matrix``: A Scipy sparse matrix. + * ``row_indices``: An iterable of integer row indices + * ``exclude``: Boolean. If true, exclude rows in ``row_indices``. Otherwise, include only rows in ``row_indices``. + + Returns a sparse matrix. + + +.. py:function:: filter_columns(matrix, col_indices, exclude=True) + + Filter a sparse matrix, either excluding or taking only the columns in ``col_indices``. + + * ``matrix``: A Scipy sparse matrix. + * ``col_indices``: An iterable of integer column indices + * ``exclude``: Boolean. If true, exclude rows in ``row_indices``. Otherwise, include only rows in ``row_indices``. + + Returns a sparse matrix. + + +.. py:function:: create_certain_datapackage(indices, data, data_store, **extra_metadata) + + +.. py:function:: dp(fp) + + diff --git a/sphinx/technical/api/bw2regional/validate/index.rst b/sphinx/technical/api/bw2regional/validate/index.rst new file mode 100644 index 0000000..f95d4be --- /dev/null +++ b/sphinx/technical/api/bw2regional/validate/index.rst @@ -0,0 +1,67 @@ +:py:mod:`bw2regional.validate` +============================== + +.. py:module:: bw2regional.validate + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.validate.uncertainty_list + bw2regional.validate.float_as_last + bw2regional.validate.xtable_data + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + bw2regional.validate._maybe_uncertainty + bw2regional.validate._loading_value + bw2regional.validate.loading_validator + bw2regional.validate.intersection_validator + bw2regional.validate.xtable_validator + + +.. py:data:: _maybe_uncertainty + + + + +.. py:data:: _loading_value + + + + +.. py:function:: uncertainty_list(obj) + + +.. py:function:: float_as_last(obj) + + +.. py:function:: xtable_data(obj) + + +.. py:data:: loading_validator + + + + +.. py:data:: intersection_validator + + + + +.. py:data:: xtable_validator + + + + diff --git a/sphinx/technical/api/bw2regional/version/index.rst b/sphinx/technical/api/bw2regional/version/index.rst new file mode 100644 index 0000000..e45cc7c --- /dev/null +++ b/sphinx/technical/api/bw2regional/version/index.rst @@ -0,0 +1,14 @@ +:py:mod:`bw2regional.version` +============================= + +.. py:module:: bw2regional.version + + +Module Contents +--------------- + +.. py:data:: version + :annotation: = [0, 6, 'DEV6'] + + + diff --git a/sphinx/technical/api/bw2regional/xtables/index.rst b/sphinx/technical/api/bw2regional/xtables/index.rst new file mode 100644 index 0000000..af0100b --- /dev/null +++ b/sphinx/technical/api/bw2regional/xtables/index.rst @@ -0,0 +1,50 @@ +:py:mod:`bw2regional.xtables` +============================= + +.. py:module:: bw2regional.xtables + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + bw2regional.xtables.ExtensionTable + + + + +.. py:class:: ExtensionTable(name) + + Bases: :py:obj:`bw2regional.loading.Loading` + + .. py:property:: filename + + Remove filesystem-unsafe characters and perform unicode normalization on ``self.name`` using :func:`.filesystem.safe_filename`. + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:attribute:: matrix + :annotation: = xtable_matrix + + + + .. py:method:: write_to_map(*args, **kwargs) + :abstractmethod: + + + .. py:method:: import_from_map(mask=None) + + + diff --git a/sphinx/technical/api/complicated/index.rst b/sphinx/technical/api/complicated/index.rst new file mode 100644 index 0000000..418f9fc --- /dev/null +++ b/sphinx/technical/api/complicated/index.rst @@ -0,0 +1,35 @@ +:py:mod:`complicated` +===================== + +.. py:module:: complicated + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + complicated.test_no_valid_worksheets + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + complicated.CSV_FIXTURES_DIR + + +.. py:data:: CSV_FIXTURES_DIR + + + + +.. py:function:: test_no_valid_worksheets() + + diff --git a/sphinx/technical/api/conf/index.rst b/sphinx/technical/api/conf/index.rst new file mode 100644 index 0000000..be66172 --- /dev/null +++ b/sphinx/technical/api/conf/index.rst @@ -0,0 +1,173 @@ +:py:mod:`conf` +============== + +.. py:module:: conf + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + conf.Mock + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + conf.skip + conf.setup + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + conf.MOCK_MODULES + conf.version + conf.release + conf.extensions + conf.mathjax_path + conf.templates_path + conf.source_suffix + conf.master_doc + conf.project + conf.copyright + conf.exclude_patterns + conf.pygments_style + conf.html_theme + conf.html_static_path + conf.htmlhelp_basename + conf.latex_elements + conf.latex_documents + conf.man_pages + conf.texinfo_documents + + +.. py:class:: Mock(/, *args, **kw) + + Bases: :py:obj:`unittest.mock.MagicMock` + + MagicMock is a subclass of Mock with default implementations + of most of the magic methods. You can use MagicMock without having to + configure the magic methods yourself. + + If you use the `spec` or `spec_set` arguments then *only* magic + methods that exist in the spec will be created. + + Attributes and the return value of a `MagicMock` will also be `MagicMocks`. + + .. py:method:: __getattr__(name) + :classmethod: + + + +.. py:data:: MOCK_MODULES + :annotation: = ['pyprind', 'brightway2', 'bw2calc.lca', 'bw2calc.matrices', 'bw2data', 'bw2data.data_store',... + + + +.. py:data:: version + :annotation: = 0.4 + + + +.. py:data:: release + :annotation: = 0.4.2 + + + +.. py:function:: skip(app, what, name, obj, skip, options) + + +.. py:function:: setup(app) + + +.. py:data:: extensions + :annotation: = ['sphinx.ext.autodoc', 'sphinx.ext.mathjax'] + + + +.. py:data:: mathjax_path + :annotation: = https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML + + + +.. py:data:: templates_path + :annotation: = ['_templates'] + + + +.. py:data:: source_suffix + :annotation: = .rst + + + +.. py:data:: master_doc + :annotation: = index + + + +.. py:data:: project + :annotation: = bw2-regional + + + +.. py:data:: copyright + :annotation: = 2014, Chris Mutel + + + +.. py:data:: exclude_patterns + :annotation: = ['_build'] + + + +.. py:data:: pygments_style + :annotation: = sphinx + + + +.. py:data:: html_theme + :annotation: = default + + + +.. py:data:: html_static_path + :annotation: = ['_static'] + + + +.. py:data:: htmlhelp_basename + :annotation: = bw2-regionaldoc + + + +.. py:data:: latex_elements + + + + +.. py:data:: latex_documents + :annotation: = [['index', 'bw2-regional.tex', 'bw2-regional Documentation', 'Chris Mutel', 'manual']] + + + +.. py:data:: man_pages + :annotation: = [['index', 'bw2-regional', 'bw2-regional Documentation', ['Chris Mutel'], 1]] + + + +.. py:data:: texinfo_documents + :annotation: = [['index', 'bw2-regional', 'bw2-regional Documentation', 'Chris Mutel', 'bw2-regional', 'One... + + + diff --git a/sphinx/technical/api/csv_excel_exporters/index.rst b/sphinx/technical/api/csv_excel_exporters/index.rst new file mode 100644 index 0000000..f1adb18 --- /dev/null +++ b/sphinx/technical/api/csv_excel_exporters/index.rst @@ -0,0 +1,89 @@ +:py:mod:`csv_excel_exporters` +============================= + +.. py:module:: csv_excel_exporters + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + csv_excel_exporters.setup + csv_excel_exporters.test_write_lci_csv_complicated + csv_excel_exporters.test_write_lci_excel_complicated + csv_excel_exporters.test_write_lci_excel_complicated_custom_directory + csv_excel_exporters.test_write_lci_excel_rich_data_skipped + csv_excel_exporters.test_roundtrip_excel_complicated + csv_excel_exporters.test_excel_roundtrip_update + csv_excel_exporters.test_write_lci_sections + csv_excel_exporters.test_write_lci_csv_custom_directory + csv_excel_exporters.test_excel_export_all + csv_excel_exporters.test_excel_export_no_ap + csv_excel_exporters.test_excel_export_no_dp + csv_excel_exporters.test_excel_export_no_params + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + csv_excel_exporters.CSV_FIXTURES_DIR + csv_excel_exporters.EXCEL_FIXTURES_DIR + + +.. py:data:: CSV_FIXTURES_DIR + + + + +.. py:data:: EXCEL_FIXTURES_DIR + + + + +.. py:function:: setup() + + +.. py:function:: test_write_lci_csv_complicated(setup) + + +.. py:function:: test_write_lci_excel_complicated(setup) + + +.. py:function:: test_write_lci_excel_complicated_custom_directory(setup) + + +.. py:function:: test_write_lci_excel_rich_data_skipped() + + +.. py:function:: test_roundtrip_excel_complicated(setup) + + +.. py:function:: test_excel_roundtrip_update(setup) + + +.. py:function:: test_write_lci_sections(setup) + + +.. py:function:: test_write_lci_csv_custom_directory(setup) + + +.. py:function:: test_excel_export_all(setup) + + +.. py:function:: test_excel_export_no_ap(setup) + + +.. py:function:: test_excel_export_no_dp(setup) + + +.. py:function:: test_excel_export_no_params(setup) + + diff --git a/sphinx/technical/api/csv_lcia/index.rst b/sphinx/technical/api/csv_lcia/index.rst new file mode 100644 index 0000000..0fade69 --- /dev/null +++ b/sphinx/technical/api/csv_lcia/index.rst @@ -0,0 +1,43 @@ +:py:mod:`csv_lcia` +================== + +.. py:module:: csv_lcia + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + csv_lcia.test_csv_lcia_extraction + csv_lcia.test_import_initial_data + csv_lcia.test_csv_lcia_integration + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + csv_lcia.CSV_FIXTURES_DIR + + +.. py:data:: CSV_FIXTURES_DIR + + + + +.. py:function:: test_csv_lcia_extraction() + + +.. py:function:: test_import_initial_data() + + +.. py:function:: test_csv_lcia_integration() + + diff --git a/sphinx/technical/api/default_data/index.rst b/sphinx/technical/api/default_data/index.rst new file mode 100644 index 0000000..b6db16e --- /dev/null +++ b/sphinx/technical/api/default_data/index.rst @@ -0,0 +1,22 @@ +:py:mod:`default_data` +====================== + +.. py:module:: default_data + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + default_data.test_ensure_base_biosphere_flows_have_tuples + + + +.. py:function:: test_ensure_base_biosphere_flows_have_tuples() + + diff --git a/sphinx/technical/api/ecospold1_importer/index.rst b/sphinx/technical/api/ecospold1_importer/index.rst new file mode 100644 index 0000000..3bb3d7f --- /dev/null +++ b/sphinx/technical/api/ecospold1_importer/index.rst @@ -0,0 +1,47 @@ +:py:mod:`ecospold1_importer` +============================ + +.. py:module:: ecospold1_importer + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + ecospold1_importer.test_importer_mp_error + ecospold1_importer.test_ecospold1_extractor_working + ecospold1_importer.test_ecospold1_extractor_invalid_tag + ecospold1_importer.test_ecospold1_extractor_missing_tag + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + ecospold1_importer.FIXTURES + + +.. py:data:: FIXTURES + + + + +.. py:function:: test_importer_mp_error(tmpdir) + + +.. py:function:: test_ecospold1_extractor_working() + + +.. py:function:: test_ecospold1_extractor_invalid_tag() + + +.. py:function:: test_ecospold1_extractor_missing_tag() + + diff --git a/sphinx/technical/api/ecospold2_extractor/index.rst b/sphinx/technical/api/ecospold2_extractor/index.rst new file mode 100644 index 0000000..65534c4 --- /dev/null +++ b/sphinx/technical/api/ecospold2_extractor/index.rst @@ -0,0 +1,39 @@ +:py:mod:`ecospold2_extractor` +============================= + +.. py:module:: ecospold2_extractor + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + ecospold2_extractor.test_extraction_without_synonyms + ecospold2_extractor.test_extraction_with_synonyms + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + ecospold2_extractor.FIXTURES + + +.. py:data:: FIXTURES + + + + +.. py:function:: test_extraction_without_synonyms() + + +.. py:function:: test_extraction_with_synonyms() + + diff --git a/sphinx/technical/api/ecospold2_importer/index.rst b/sphinx/technical/api/ecospold2_importer/index.rst new file mode 100644 index 0000000..2cac813 --- /dev/null +++ b/sphinx/technical/api/ecospold2_importer/index.rst @@ -0,0 +1,43 @@ +:py:mod:`ecospold2_importer` +============================ + +.. py:module:: ecospold2_importer + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + ecospold2_importer.test_importer_custom_extractor + ecospold2_importer.test_importer_mp_error + ecospold2_importer.test_importer_signals + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + ecospold2_importer.FIXTURES + + +.. py:data:: FIXTURES + + + + +.. py:function:: test_importer_custom_extractor() + + +.. py:function:: test_importer_mp_error() + + +.. py:function:: test_importer_signals() + + diff --git a/sphinx/technical/api/extraction/index.rst b/sphinx/technical/api/extraction/index.rst new file mode 100644 index 0000000..c212734 --- /dev/null +++ b/sphinx/technical/api/extraction/index.rst @@ -0,0 +1,38 @@ +:py:mod:`extraction` +==================== + +.. py:module:: extraction + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + extraction.test_extraction + extraction.test_extraction_missing_database + extraction.test_extraction_input_formats + extraction.test_extraction_with_properties + extraction.test_extraction_with_identifiers + + + +.. py:function:: test_extraction(test_bw2_database) + + +.. py:function:: test_extraction_missing_database() + + +.. py:function:: test_extraction_input_formats(test_bw2_database) + + +.. py:function:: test_extraction_with_properties() + + +.. py:function:: test_extraction_with_identifiers() + + diff --git a/sphinx/technical/api/fixtures/index.rst b/sphinx/technical/api/fixtures/index.rst new file mode 100644 index 0000000..20b572c --- /dev/null +++ b/sphinx/technical/api/fixtures/index.rst @@ -0,0 +1,41 @@ +:py:mod:`fixtures` +================== + +.. py:module:: fixtures + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + fixtures.test_bw2_database + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + fixtures.biosphere + fixtures.food + + +.. py:data:: biosphere + + + + +.. py:data:: food + + + + +.. py:function:: test_bw2_database() + + diff --git a/sphinx/technical/api/formula_parsing/index.rst b/sphinx/technical/api/formula_parsing/index.rst new file mode 100644 index 0000000..35d2fe9 --- /dev/null +++ b/sphinx/technical/api/formula_parsing/index.rst @@ -0,0 +1,35 @@ +:py:mod:`formula_parsing` +========================= + +.. py:module:: formula_parsing + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + formula_parsing.test_parse_formulas + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + formula_parsing.EXCEL_FIXTURES_DIR + + +.. py:data:: EXCEL_FIXTURES_DIR + + + + +.. py:function:: test_parse_formulas() + + diff --git a/sphinx/technical/api/generate_fixture/index.rst b/sphinx/technical/api/generate_fixture/index.rst new file mode 100644 index 0000000..fd4ea00 --- /dev/null +++ b/sphinx/technical/api/generate_fixture/index.rst @@ -0,0 +1,22 @@ +:py:mod:`generate_fixture` +========================== + +.. py:module:: generate_fixture + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + generate_fixture.generate_fixture + + + +.. py:function:: generate_fixture() + + diff --git a/sphinx/technical/api/generic_excel/index.rst b/sphinx/technical/api/generic_excel/index.rst new file mode 100644 index 0000000..d02aa61 --- /dev/null +++ b/sphinx/technical/api/generic_excel/index.rst @@ -0,0 +1,71 @@ +:py:mod:`generic_excel` +======================= + +.. py:module:: generic_excel + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + generic_excel.no_init + generic_excel.test_write_only_database_parameters + generic_excel.test_write_only_activity_parameters + generic_excel.test_write_only_activity_parameters_no_activate_others + generic_excel.test_empty_activity_parameters_dont_delete + generic_excel.test_no_valid_worksheets + generic_excel.test_no_valid_worksheets_all_columns_cutoff + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + generic_excel.EXCEL_FIXTURES_DIR + generic_excel.DATA + generic_excel.DB + + +.. py:data:: EXCEL_FIXTURES_DIR + + + + +.. py:data:: DATA + + + + +.. py:data:: DB + + + + +.. py:function:: no_init(monkeypatch) + + +.. py:function:: test_write_only_database_parameters(no_init) + + +.. py:function:: test_write_only_activity_parameters(no_init) + + +.. py:function:: test_write_only_activity_parameters_no_activate_others(no_init) + + +.. py:function:: test_empty_activity_parameters_dont_delete(no_init) + + +.. py:function:: test_no_valid_worksheets() + + +.. py:function:: test_no_valid_worksheets_all_columns_cutoff() + + diff --git a/sphinx/technical/api/index.rst b/sphinx/technical/api/index.rst new file mode 100644 index 0000000..95ec061 --- /dev/null +++ b/sphinx/technical/api/index.rst @@ -0,0 +1,52 @@ +API Reference +============= + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + /technical/api/setup/index + /technical/api/tests/index + /technical/api/bw2analyzer/index + /technical/api/generate_fixture/index + /technical/api/lca_old/index + /technical/api/bw2calc/index + /technical/api/noxfile/index + /technical/api/extraction/index + /technical/api/fixtures/index + /technical/api/bw2data/index + /technical/api/ecospold2_importer/index + /technical/api/ecospold2_extractor/index + /technical/api/default_data/index + /technical/api/lcia/index + /technical/api/formula_parsing/index + /technical/api/blank_lines/index + /technical/api/with_products/index + /technical/api/subcomponents/index + /technical/api/simple/index + /technical/api/generic_excel/index + /technical/api/parameterized/index + /technical/api/ecospold1_importer/index + /technical/api/complicated/index + /technical/api/csv_lcia/index + /technical/api/csv_excel_exporters/index + /technical/api/json_ld/index + /technical/api/bw2io/index + /technical/api/bw2regional/index + /technical/api/test_two_spatial_scales/index + /technical/api/test_utils/index + /technical/api/test_setup/index + /technical/api/test_one_spatial_scale/index + /technical/api/test_loading/index + /technical/api/test_lca/index + /technical/api/test_xtables/index + /technical/api/test_pandarus/index + /technical/api/test_two_spatial_scales_weighting/index + /technical/api/test_intersections/index + /technical/api/test_density/index + /technical/api/test_meta/index + /technical/api/test_topography/index + /technical/api/conf/index + +.. [#f1] Created with `sphinx-autoapi `_ \ No newline at end of file diff --git a/sphinx/technical/api/json_ld/index.rst b/sphinx/technical/api/json_ld/index.rst new file mode 100644 index 0000000..4f25a8f --- /dev/null +++ b/sphinx/technical/api/json_ld/index.rst @@ -0,0 +1,44 @@ +:py:mod:`json_ld` +================= + +.. py:module:: json_ld + + +Module Contents +--------------- + +.. py:data:: fp + :annotation: = /Users/akim/Documents/LCA_files/US_Forest_Service_Forest_Products_Lab-Woody_biomass + + + +.. py:data:: data + + + + +.. py:data:: data + + + + +.. py:data:: data + + + + +.. py:data:: db + + + + +.. py:data:: db + + + + +.. py:data:: db + + + + diff --git a/sphinx/technical/api/lca_old/index.rst b/sphinx/technical/api/lca_old/index.rst new file mode 100644 index 0000000..37f0a9e --- /dev/null +++ b/sphinx/technical/api/lca_old/index.rst @@ -0,0 +1,117 @@ +:py:mod:`lca_old` +================= + +.. py:module:: lca_old + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + lca_old.LCACalculationTestCase + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + lca_old.test_empty_biosphere_lcia + lca_old.test_warning_empty_biosphere + + + +.. py:function:: test_empty_biosphere_lcia() + + +.. py:function:: test_warning_empty_biosphere() + + +.. py:class:: LCACalculationTestCase + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + .. py:method:: add_basic_biosphere() + + + .. py:method:: test_basic() + + + .. py:method:: test_redo_lci_fails_if_activity_outside_technosphere() + + + .. py:method:: test_redo_lci_with_no_new_demand_no_error() + + + .. py:method:: test_passing_falsey_key() + + + .. py:method:: test_pass_object_as_demand() + + + .. py:method:: test_production_values() + + + .. py:method:: test_substitution() + + + .. py:method:: test_substitution_no_type() + + + .. py:method:: test_circular_chains() + + + .. py:method:: test_only_products() + + + .. py:method:: test_activity_product_dict() + + + .. py:method:: test_process_product_split() + + + .. py:method:: test_activity_as_fu_raises_error() + + + .. py:method:: test_nonsquare_technosphere_error() + + + .. py:method:: test_multiple_lci_calculations() + + + .. py:method:: test_dependent_databases() + + + .. py:method:: test_filepaths_full() + + + .. py:method:: test_filepaths_empty() + + + .. py:method:: test_demand_type() + + + .. py:method:: test_decomposed_uses_solver() + + + .. py:method:: test_fix_dictionaries() + + + .. py:method:: test_redo_lci_switches_demand() + + + .. py:method:: test_basic_lcia() + + + .. py:method:: test_redo_lcia_switches_demand() + + + .. py:method:: test_lcia_regionalized_ignored() + + + diff --git a/sphinx/technical/api/lcia/index.rst b/sphinx/technical/api/lcia/index.rst new file mode 100644 index 0000000..eb923da --- /dev/null +++ b/sphinx/technical/api/lcia/index.rst @@ -0,0 +1,43 @@ +:py:mod:`lcia` +============== + +.. py:module:: lcia + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + lcia.test_excel_lcia_extraction + lcia.test_import_initial_data + lcia.test_excel_lcia_integration + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + lcia.EXCEL_FIXTURES_DIR + + +.. py:data:: EXCEL_FIXTURES_DIR + + + + +.. py:function:: test_excel_lcia_extraction() + + +.. py:function:: test_import_initial_data() + + +.. py:function:: test_excel_lcia_integration() + + diff --git a/sphinx/technical/api/noxfile/index.rst b/sphinx/technical/api/noxfile/index.rst new file mode 100644 index 0000000..7508845 --- /dev/null +++ b/sphinx/technical/api/noxfile/index.rst @@ -0,0 +1,22 @@ +:py:mod:`noxfile` +================= + +.. py:module:: noxfile + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + noxfile.tests + + + +.. py:function:: tests(session) + + diff --git a/sphinx/technical/api/parameterized/index.rst b/sphinx/technical/api/parameterized/index.rst new file mode 100644 index 0000000..1830c78 --- /dev/null +++ b/sphinx/technical/api/parameterized/index.rst @@ -0,0 +1,43 @@ +:py:mod:`parameterized` +======================= + +.. py:module:: parameterized + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + parameterized.test_parameterized_import + parameterized.test_example_notebook + parameterized.test_parameterized_import_activate_later + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + parameterized.EXCEL_FIXTURES_DIR + + +.. py:data:: EXCEL_FIXTURES_DIR + + + + +.. py:function:: test_parameterized_import() + + +.. py:function:: test_example_notebook() + + +.. py:function:: test_parameterized_import_activate_later() + + diff --git a/sphinx/technical/api/setup/index.rst b/sphinx/technical/api/setup/index.rst new file mode 100644 index 0000000..6e316bd --- /dev/null +++ b/sphinx/technical/api/setup/index.rst @@ -0,0 +1,24 @@ +:py:mod:`setup` +=============== + +.. py:module:: setup + + +Module Contents +--------------- + +.. py:data:: requirements + :annotation: = ['bw2calc>=2.0.dev6', 'bw2data>=4.0.dev12', 'constructive_geometries', 'fiona', 'geopandas',... + + + +.. py:data:: v_temp + + + + +.. py:data:: version + + + + diff --git a/sphinx/technical/api/simple/index.rst b/sphinx/technical/api/simple/index.rst new file mode 100644 index 0000000..1af3e91 --- /dev/null +++ b/sphinx/technical/api/simple/index.rst @@ -0,0 +1,45 @@ +:py:mod:`simple` +================ + +.. py:module:: simple + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + simple.test_parameterized_import + simple.test_correct_reference_product + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + simple.EXCEL_FIXTURES_DIR + simple.expected + + +.. py:data:: EXCEL_FIXTURES_DIR + + + + +.. py:data:: expected + + + + +.. py:function:: test_parameterized_import() + + +.. py:function:: test_correct_reference_product() + + diff --git a/sphinx/technical/api/subcomponents/index.rst b/sphinx/technical/api/subcomponents/index.rst new file mode 100644 index 0000000..7a2752d --- /dev/null +++ b/sphinx/technical/api/subcomponents/index.rst @@ -0,0 +1,38 @@ +:py:mod:`subcomponents` +======================= + +.. py:module:: subcomponents + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + subcomponents.no_init + subcomponents.test_get_labelled_section + subcomponents.test_process_activities + subcomponents.test_get_activity + subcomponents.test_get_activity_metadata_indexing + + + +.. py:function:: no_init(monkeypatch) + + +.. py:function:: test_get_labelled_section(no_init) + + +.. py:function:: test_process_activities(no_init, monkeypatch) + + +.. py:function:: test_get_activity(no_init) + + +.. py:function:: test_get_activity_metadata_indexing(no_init) + + diff --git a/sphinx/technical/api/test_density/index.rst b/sphinx/technical/api/test_density/index.rst new file mode 100644 index 0000000..0a43b1e --- /dev/null +++ b/sphinx/technical/api/test_density/index.rst @@ -0,0 +1,45 @@ +:py:mod:`test_density` +====================== + +.. py:module:: test_density + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_density.test_divide_by_area + test_density.write_test_raster + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + test_density.AREAS + test_density.FIXTURE + + +.. py:data:: AREAS + + + + +.. py:data:: FIXTURE + + + + +.. py:function:: test_divide_by_area(tmpdir) + + +.. py:function:: write_test_raster() + + diff --git a/sphinx/technical/api/test_intersections/index.rst b/sphinx/technical/api/test_intersections/index.rst new file mode 100644 index 0000000..efe2e6c --- /dev/null +++ b/sphinx/technical/api/test_intersections/index.rst @@ -0,0 +1,26 @@ +:py:mod:`test_intersections` +============================ + +.. py:module:: test_intersections + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_intersections.test_add_geomappings + test_intersections.test_validation + + + +.. py:function:: test_add_geomappings() + + +.. py:function:: test_validation() + + diff --git a/sphinx/technical/api/test_lca/index.rst b/sphinx/technical/api/test_lca/index.rst new file mode 100644 index 0000000..f3a859e --- /dev/null +++ b/sphinx/technical/api/test_lca/index.rst @@ -0,0 +1,26 @@ +:py:mod:`test_lca` +================== + +.. py:module:: test_lca + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_lca.test_site_generic_method_error + test_lca.test_missing_intersection_error + + + +.. py:function:: test_site_generic_method_error() + + +.. py:function:: test_missing_intersection_error() + + diff --git a/sphinx/technical/api/test_loading/index.rst b/sphinx/technical/api/test_loading/index.rst new file mode 100644 index 0000000..97302e2 --- /dev/null +++ b/sphinx/technical/api/test_loading/index.rst @@ -0,0 +1,34 @@ +:py:mod:`test_loading` +====================== + +.. py:module:: test_loading + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_loading.test_add_geomappings + test_loading.test_validation + test_loading.test_filename + test_loading.test_allow_zero_loadings + + + +.. py:function:: test_add_geomappings() + + +.. py:function:: test_validation() + + +.. py:function:: test_filename() + + +.. py:function:: test_allow_zero_loadings() + + diff --git a/sphinx/technical/api/test_meta/index.rst b/sphinx/technical/api/test_meta/index.rst new file mode 100644 index 0000000..1294a20 --- /dev/null +++ b/sphinx/technical/api/test_meta/index.rst @@ -0,0 +1,71 @@ +:py:mod:`test_meta` +=================== + +.. py:module:: test_meta + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_meta.test_geocollections_filename + test_meta.test_geocollections_unicode + test_meta.test_geocollectoins_vector_test_data + test_meta.test_geocollectioins_raster_test_data + test_meta.test_intersectioins_filename + test_meta.test_intersectioins_unicode + test_meta.test_intersectioins_load_test_data_1 + test_meta.test_intersectioins_load_test_data_2 + test_meta.test_loadings_filename + test_meta.test_loadings_unicode + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + test_meta.data_dir + + +.. py:data:: data_dir + + + + +.. py:function:: test_geocollections_filename() + + +.. py:function:: test_geocollections_unicode() + + +.. py:function:: test_geocollectoins_vector_test_data() + + +.. py:function:: test_geocollectioins_raster_test_data() + + +.. py:function:: test_intersectioins_filename() + + +.. py:function:: test_intersectioins_unicode() + + +.. py:function:: test_intersectioins_load_test_data_1() + + +.. py:function:: test_intersectioins_load_test_data_2() + + +.. py:function:: test_loadings_filename() + + +.. py:function:: test_loadings_unicode() + + diff --git a/sphinx/technical/api/test_one_spatial_scale/index.rst b/sphinx/technical/api/test_one_spatial_scale/index.rst new file mode 100644 index 0000000..92a974d --- /dev/null +++ b/sphinx/technical/api/test_one_spatial_scale/index.rst @@ -0,0 +1,50 @@ +:py:mod:`test_one_spatial_scale` +================================ + +.. py:module:: test_one_spatial_scale + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_one_spatial_scale.test_value_error_no_method + test_one_spatial_scale.test_geocollections_mismatch + test_one_spatial_scale.import_data + test_one_spatial_scale.test_import_data + test_one_spatial_scale.get_lca + test_one_spatial_scale.test_inventory + test_one_spatial_scale.test_characterization_matrix + test_one_spatial_scale.test_lca_score + + + +.. py:function:: test_value_error_no_method() + + +.. py:function:: test_geocollections_mismatch() + + +.. py:function:: import_data() + + +.. py:function:: test_import_data() + + +.. py:function:: get_lca() + + +.. py:function:: test_inventory() + + +.. py:function:: test_characterization_matrix() + + +.. py:function:: test_lca_score() + + diff --git a/sphinx/technical/api/test_pandarus/index.rst b/sphinx/technical/api/test_pandarus/index.rst new file mode 100644 index 0000000..934fb36 --- /dev/null +++ b/sphinx/technical/api/test_pandarus/index.rst @@ -0,0 +1,43 @@ +:py:mod:`test_pandarus` +======================= + +.. py:module:: test_pandarus + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_pandarus.test_relabel + test_pandarus.test_import_topo_intersection_without_error + test_pandarus.test_import_intersection_without_error + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + test_pandarus.data_dir + + +.. py:data:: data_dir + + + + +.. py:function:: test_relabel() + + +.. py:function:: test_import_topo_intersection_without_error() + + +.. py:function:: test_import_intersection_without_error() + + diff --git a/sphinx/technical/api/test_setup/index.rst b/sphinx/technical/api/test_setup/index.rst new file mode 100644 index 0000000..c8d11ee --- /dev/null +++ b/sphinx/technical/api/test_setup/index.rst @@ -0,0 +1,42 @@ +:py:mod:`test_setup` +==================== + +.. py:module:: test_setup + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_setup.nope + test_setup.no_download + test_setup.test_row_only + test_setup.test_world_only + test_setup.test_ecoinvent_only + test_setup.test_all_basic_data + + + +.. py:function:: nope(*args, **kwargs) + + +.. py:function:: no_download(monkeypatch) + + +.. py:function:: test_row_only() + + +.. py:function:: test_world_only(no_download) + + +.. py:function:: test_ecoinvent_only(no_download) + + +.. py:function:: test_all_basic_data(no_download) + + diff --git a/sphinx/technical/api/test_topography/index.rst b/sphinx/technical/api/test_topography/index.rst new file mode 100644 index 0000000..5e4b6ce --- /dev/null +++ b/sphinx/technical/api/test_topography/index.rst @@ -0,0 +1,14 @@ +:py:mod:`test_topography` +========================= + +.. py:module:: test_topography + + +Module Contents +--------------- + +.. py:data:: data_dir + + + + diff --git a/sphinx/technical/api/test_two_spatial_scales/index.rst b/sphinx/technical/api/test_two_spatial_scales/index.rst new file mode 100644 index 0000000..645565b --- /dev/null +++ b/sphinx/technical/api/test_two_spatial_scales/index.rst @@ -0,0 +1,58 @@ +:py:mod:`test_two_spatial_scales` +================================= + +.. py:module:: test_two_spatial_scales + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_two_spatial_scales.test_value_error_no_method + test_two_spatial_scales.import_data + test_two_spatial_scales.test_import_data + test_two_spatial_scales.get_lca + test_two_spatial_scales.test_inventory + test_two_spatial_scales.test_geo_transform_matrix + test_two_spatial_scales.test_characterization_matrix + test_two_spatial_scales.test_inv_mapping_matrix + test_two_spatial_scales.test_normalization_matrix + test_two_spatial_scales.test_lca_score + + + +.. py:function:: test_value_error_no_method() + + +.. py:function:: import_data() + + +.. py:function:: test_import_data() + + +.. py:function:: get_lca() + + +.. py:function:: test_inventory() + + +.. py:function:: test_geo_transform_matrix() + + +.. py:function:: test_characterization_matrix() + + +.. py:function:: test_inv_mapping_matrix() + + +.. py:function:: test_normalization_matrix() + + +.. py:function:: test_lca_score() + + diff --git a/sphinx/technical/api/test_two_spatial_scales_weighting/index.rst b/sphinx/technical/api/test_two_spatial_scales_weighting/index.rst new file mode 100644 index 0000000..8d8257d --- /dev/null +++ b/sphinx/technical/api/test_two_spatial_scales_weighting/index.rst @@ -0,0 +1,62 @@ +:py:mod:`test_two_spatial_scales_weighting` +=========================================== + +.. py:module:: test_two_spatial_scales_weighting + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_two_spatial_scales_weighting.test_value_error_no_loadings + test_two_spatial_scales_weighting.import_data + test_two_spatial_scales_weighting.test_import_data + test_two_spatial_scales_weighting.get_lca + test_two_spatial_scales_weighting.test_inventory + test_two_spatial_scales_weighting.test_geo_transform_matrix + test_two_spatial_scales_weighting.test_loading_matrix + test_two_spatial_scales_weighting.test_characterization_matrix + test_two_spatial_scales_weighting.test_inv_mapping_matrix + test_two_spatial_scales_weighting.test_normalization_matrix + test_two_spatial_scales_weighting.test_lca_score + + + +.. py:function:: test_value_error_no_loadings() + + +.. py:function:: import_data() + + +.. py:function:: test_import_data() + + +.. py:function:: get_lca() + + +.. py:function:: test_inventory() + + +.. py:function:: test_geo_transform_matrix() + + +.. py:function:: test_loading_matrix() + + +.. py:function:: test_characterization_matrix() + + +.. py:function:: test_inv_mapping_matrix() + + +.. py:function:: test_normalization_matrix() + + +.. py:function:: test_lca_score() + + diff --git a/sphinx/technical/api/test_utils/index.rst b/sphinx/technical/api/test_utils/index.rst new file mode 100644 index 0000000..abf62ef --- /dev/null +++ b/sphinx/technical/api/test_utils/index.rst @@ -0,0 +1,46 @@ +:py:mod:`test_utils` +==================== + +.. py:module:: test_utils + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_utils.M + test_utils.test_setup + test_utils.test_filter_rows_inclusive + test_utils.test_filter_rows_exclusive + test_utils.test_filter_columns_inclusive + test_utils.test_filter_columns_exclusive + test_utils.test_filter_fiona_metadata + + + +.. py:function:: M() + + +.. py:function:: test_setup(M) + + +.. py:function:: test_filter_rows_inclusive(M) + + +.. py:function:: test_filter_rows_exclusive(M) + + +.. py:function:: test_filter_columns_inclusive(M) + + +.. py:function:: test_filter_columns_exclusive(M) + + +.. py:function:: test_filter_fiona_metadata() + + diff --git a/sphinx/technical/api/test_xtables/index.rst b/sphinx/technical/api/test_xtables/index.rst new file mode 100644 index 0000000..88ad743 --- /dev/null +++ b/sphinx/technical/api/test_xtables/index.rst @@ -0,0 +1,22 @@ +:py:mod:`test_xtables` +====================== + +.. py:module:: test_xtables + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + test_xtables.test_xtable_filename + + + +.. py:function:: test_xtable_filename() + + diff --git a/sphinx/technical/api/tests/activity_proxy/index.rst b/sphinx/technical/api/tests/activity_proxy/index.rst new file mode 100644 index 0000000..791f00d --- /dev/null +++ b/sphinx/technical/api/tests/activity_proxy/index.rst @@ -0,0 +1,126 @@ +:py:mod:`tests.activity_proxy` +============================== + +.. py:module:: tests.activity_proxy + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.activity_proxy.test_change_code_not_unique_raises_error + tests.activity_proxy.test_save_invalid_activity_raises_error + tests.activity_proxy.activity + tests.activity_proxy.test_set_item + tests.activity_proxy.test_key + tests.activity_proxy.test_change_code + tests.activity_proxy.test_change_code_same_code + tests.activity_proxy.test_change_database + tests.activity_proxy.test_change_database_not_exist + tests.activity_proxy.test_database_same_database + tests.activity_proxy.test_delete + tests.activity_proxy.test_delete_activity_only_self_references + tests.activity_proxy.test_copy + tests.activity_proxy.test_copy_with_kwargs + tests.activity_proxy.test_delete_activity_parameters + tests.activity_proxy.test_get_classifications_ref_product + tests.activity_proxy.test_get_classifications_main_activity_dict + tests.activity_proxy.test_get_classifications_main_activity_list + tests.activity_proxy.test_get_classifications_also_in_activity + tests.activity_proxy.test_get_properties_ref_product + tests.activity_proxy.test_get_properties_missing_property + tests.activity_proxy.test_get_properties_no_rp_exchange + tests.activity_proxy.test_rp_exchange_single_production_wrong_rp_name + tests.activity_proxy.test_rp_exchange_multiple_produuction_match_rp_name + tests.activity_proxy.test_rp_exchange_value_error_multiple + tests.activity_proxy.test_rp_exchange_value_error_no_production + tests.activity_proxy.test_rp_exchange_value_error_only_substitution + + + +.. py:function:: test_change_code_not_unique_raises_error() + + +.. py:function:: test_save_invalid_activity_raises_error() + + +.. py:function:: activity() + + +.. py:function:: test_set_item(activity) + + +.. py:function:: test_key(activity) + + +.. py:function:: test_change_code(activity) + + +.. py:function:: test_change_code_same_code(activity) + + +.. py:function:: test_change_database(activity) + + +.. py:function:: test_change_database_not_exist(activity) + + +.. py:function:: test_database_same_database(activity) + + +.. py:function:: test_delete(activity) + + +.. py:function:: test_delete_activity_only_self_references() + + +.. py:function:: test_copy(activity) + + +.. py:function:: test_copy_with_kwargs(activity) + + +.. py:function:: test_delete_activity_parameters() + + +.. py:function:: test_get_classifications_ref_product() + + +.. py:function:: test_get_classifications_main_activity_dict() + + +.. py:function:: test_get_classifications_main_activity_list() + + +.. py:function:: test_get_classifications_also_in_activity() + + +.. py:function:: test_get_properties_ref_product() + + +.. py:function:: test_get_properties_missing_property() + + +.. py:function:: test_get_properties_no_rp_exchange() + + +.. py:function:: test_rp_exchange_single_production_wrong_rp_name() + + +.. py:function:: test_rp_exchange_multiple_produuction_match_rp_name() + + +.. py:function:: test_rp_exchange_value_error_multiple() + + +.. py:function:: test_rp_exchange_value_error_no_production() + + +.. py:function:: test_rp_exchange_value_error_only_substitution() + + diff --git a/sphinx/technical/api/tests/base/index.rst b/sphinx/technical/api/tests/base/index.rst new file mode 100644 index 0000000..02ae309 --- /dev/null +++ b/sphinx/technical/api/tests/base/index.rst @@ -0,0 +1,22 @@ +:py:mod:`tests.base` +==================== + +.. py:module:: tests.base + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.base.test_bw2test_decorator + + + +.. py:function:: test_bw2test_decorator() + + diff --git a/sphinx/technical/api/tests/base_lci_importer/index.rst b/sphinx/technical/api/tests/base_lci_importer/index.rst new file mode 100644 index 0000000..25e846e --- /dev/null +++ b/sphinx/technical/api/tests/base_lci_importer/index.rst @@ -0,0 +1,123 @@ +:py:mod:`tests.base_lci_importer` +================================= + +.. py:module:: tests.base_lci_importer + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.base_lci_importer.lci + tests.base_lci_importer.test_write_database_no_activate + tests.base_lci_importer.test_write_database + tests.base_lci_importer.test_no_delete_project_parameters + tests.base_lci_importer.test_delete_project_parameters + tests.base_lci_importer.test_update_project_parameters + tests.base_lci_importer.test_no_delete_database_parameters + tests.base_lci_importer.test_delete_database_parameters + tests.base_lci_importer.test_update_database_parameters + tests.base_lci_importer.test_activity_parameters_with_group_name + tests.base_lci_importer.test_activity_multiple_activities_same_group_name + tests.base_lci_importer.test_wrongdatabase_error_code + tests.base_lci_importer.test_nonuniquecode_error_code + tests.base_lci_importer.test_database_update_existing_data + tests.base_lci_importer.test_update_activity_parameters + tests.base_lci_importer.test_activity_parameters_delete_old_groupname + tests.base_lci_importer.test_delete_activity_parameters_delete_existing + tests.base_lci_importer.test_no_delete_pe_no_activate_parameters + tests.base_lci_importer.test_delete_pe_delete_existing + tests.base_lci_importer.test_delete_pe_update_still_deletes + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.base_lci_importer.DATA + tests.base_lci_importer.DATA_NO_PARAMS + tests.base_lci_importer.DB + + +.. py:data:: DATA + + + + +.. py:data:: DATA_NO_PARAMS + + + + +.. py:data:: DB + + + + +.. py:function:: lci() + + +.. py:function:: test_write_database_no_activate(lci) + + +.. py:function:: test_write_database(lci) + + +.. py:function:: test_no_delete_project_parameters(lci) + + +.. py:function:: test_delete_project_parameters(lci) + + +.. py:function:: test_update_project_parameters(lci) + + +.. py:function:: test_no_delete_database_parameters(lci) + + +.. py:function:: test_delete_database_parameters(lci) + + +.. py:function:: test_update_database_parameters(lci) + + +.. py:function:: test_activity_parameters_with_group_name() + + +.. py:function:: test_activity_multiple_activities_same_group_name() + + +.. py:function:: test_wrongdatabase_error_code() + + +.. py:function:: test_nonuniquecode_error_code() + + +.. py:function:: test_database_update_existing_data(lci) + + +.. py:function:: test_update_activity_parameters(lci) + + +.. py:function:: test_activity_parameters_delete_old_groupname(lci) + + +.. py:function:: test_delete_activity_parameters_delete_existing(lci) + + +.. py:function:: test_no_delete_pe_no_activate_parameters(lci) + + +.. py:function:: test_delete_pe_delete_existing(lci) + + +.. py:function:: test_delete_pe_update_still_deletes() + + diff --git a/sphinx/technical/api/tests/base_lcia_importer/index.rst b/sphinx/technical/api/tests/base_lcia_importer/index.rst new file mode 100644 index 0000000..f4fd634 --- /dev/null +++ b/sphinx/technical/api/tests/base_lcia_importer/index.rst @@ -0,0 +1,26 @@ +:py:mod:`tests.base_lcia_importer` +================================== + +.. py:module:: tests.base_lcia_importer + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.base_lcia_importer.initial_biosphere + tests.base_lcia_importer.test_add_missing_cfs + + + +.. py:function:: initial_biosphere() + + +.. py:function:: test_add_missing_cfs() + + diff --git a/sphinx/technical/api/tests/chemidplus/index.rst b/sphinx/technical/api/tests/chemidplus/index.rst new file mode 100644 index 0000000..ef67896 --- /dev/null +++ b/sphinx/technical/api/tests/chemidplus/index.rst @@ -0,0 +1,6 @@ +:py:mod:`tests.chemidplus` +========================== + +.. py:module:: tests.chemidplus + + diff --git a/sphinx/technical/api/tests/comparisons/index.rst b/sphinx/technical/api/tests/comparisons/index.rst new file mode 100644 index 0000000..d143d5e --- /dev/null +++ b/sphinx/technical/api/tests/comparisons/index.rst @@ -0,0 +1,82 @@ +:py:mod:`tests.comparisons` +=========================== + +.. py:module:: tests.comparisons + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.comparisons.cabls + tests.comparisons.test_compare_activities_by_lcia_score_similar + tests.comparisons.test_compare_activities_by_lcia_score_different + tests.comparisons.test_compare_activities_by_lcia_score_band + tests.comparisons.fdii + tests.comparisons.test_find_differences_in_inputs + tests.comparisons.test_find_differences_in_inputs_tolerances + tests.comparisons.test_find_differences_in_inputs_locations + tests.comparisons.test_find_differences_in_inputs_dataframe + tests.comparisons.test_find_differences_in_inputs_errors + tests.comparisons.cabgl + tests.comparisons.test_compare_activities_by_grouped_leaves + tests.comparisons.test_compare_activities_by_grouped_leaves_html + tests.comparisons.test_compare_activities_by_grouped_leaves_pandas + tests.comparisons.test_compare_activities_by_grouped_leaves_max_level + tests.comparisons.test_compare_activities_by_grouped_leaves_max_cutoff + + + +.. py:function:: cabls(capsys) + + +.. py:function:: test_compare_activities_by_lcia_score_similar(capsys) + + +.. py:function:: test_compare_activities_by_lcia_score_different(cabls, capsys) + + +.. py:function:: test_compare_activities_by_lcia_score_band(cabls, capsys) + + +.. py:function:: fdii() + + +.. py:function:: test_find_differences_in_inputs(fdii) + + +.. py:function:: test_find_differences_in_inputs_tolerances(fdii) + + +.. py:function:: test_find_differences_in_inputs_locations(fdii) + + +.. py:function:: test_find_differences_in_inputs_dataframe(fdii) + + +.. py:function:: test_find_differences_in_inputs_errors(fdii) + + +.. py:function:: cabgl() + + +.. py:function:: test_compare_activities_by_grouped_leaves(cabgl) + + +.. py:function:: test_compare_activities_by_grouped_leaves_html(cabgl) + + +.. py:function:: test_compare_activities_by_grouped_leaves_pandas(cabgl) + + +.. py:function:: test_compare_activities_by_grouped_leaves_max_level(cabgl) + + +.. py:function:: test_compare_activities_by_grouped_leaves_max_cutoff(cabgl) + + diff --git a/sphinx/technical/api/tests/compatibility/index.rst b/sphinx/technical/api/tests/compatibility/index.rst new file mode 100644 index 0000000..2d31198 --- /dev/null +++ b/sphinx/technical/api/tests/compatibility/index.rst @@ -0,0 +1,50 @@ +:py:mod:`tests.compatibility` +============================= + +.. py:module:: tests.compatibility + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.compatibility.test_repr_str_unicode + tests.compatibility.test_registered_database_repr + tests.compatibility.setup + tests.compatibility.test_prepare_lca_inputs_basic + tests.compatibility.test_prepare_lca_inputs_only_method + tests.compatibility.test_prepare_lca_inputs_multiple_demands + tests.compatibility.test_prepare_lca_inputs_database_ordering + tests.compatibility.test_prepare_lca_inputs_remapping + + + +.. py:function:: test_repr_str_unicode() + + +.. py:function:: test_registered_database_repr() + + +.. py:function:: setup() + + +.. py:function:: test_prepare_lca_inputs_basic() + + +.. py:function:: test_prepare_lca_inputs_only_method() + + +.. py:function:: test_prepare_lca_inputs_multiple_demands() + + +.. py:function:: test_prepare_lca_inputs_database_ordering() + + +.. py:function:: test_prepare_lca_inputs_remapping() + + diff --git a/sphinx/technical/api/tests/config/index.rst b/sphinx/technical/api/tests/config/index.rst new file mode 100644 index 0000000..bad6b29 --- /dev/null +++ b/sphinx/technical/api/tests/config/index.rst @@ -0,0 +1,26 @@ +:py:mod:`tests.config` +====================== + +.. py:module:: tests.config + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.config.test_default_biosphere + tests.config.test_default_geo + + + +.. py:function:: test_default_biosphere() + + +.. py:function:: test_default_geo() + + diff --git a/sphinx/technical/api/tests/contribution/index.rst b/sphinx/technical/api/tests/contribution/index.rst new file mode 100644 index 0000000..4427f0c --- /dev/null +++ b/sphinx/technical/api/tests/contribution/index.rst @@ -0,0 +1,88 @@ +:py:mod:`tests.contribution` +============================ + +.. py:module:: tests.contribution + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.contribution.ContributionTestCase + tests.contribution.Contribution2TestCase + + + + +.. py:class:: ContributionTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_sort_array_number() + + + .. py:method:: test_sort_array_percentage() + + + .. py:method:: test_sort_array_percentage_negative() + + + .. py:method:: test_sort_array_errors() + + + .. py:method:: test_top_matrix_array() + + + .. py:method:: test_top_matrix_matrix() + + + +.. py:class:: Contribution2TestCase + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + .. py:method:: install_fixtures() + + + .. py:method:: test_hinton_matrix_no_error() + + + .. py:method:: test_d3_treemap_no_error() + + + diff --git a/sphinx/technical/api/tests/data/index.rst b/sphinx/technical/api/tests/data/index.rst new file mode 100644 index 0000000..2732037 --- /dev/null +++ b/sphinx/technical/api/tests/data/index.rst @@ -0,0 +1,15 @@ +:py:mod:`tests.data` +==================== + +.. py:module:: tests.data + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + init_file/index.rst + + diff --git a/sphinx/technical/api/tests/data/init_file/index.rst b/sphinx/technical/api/tests/data/init_file/index.rst new file mode 100644 index 0000000..6d82a6a --- /dev/null +++ b/sphinx/technical/api/tests/data/init_file/index.rst @@ -0,0 +1,22 @@ +:py:mod:`tests.data.init_file` +============================== + +.. py:module:: tests.data.init_file + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.data.init_file.test_geodata + + + +.. py:function:: test_geodata() + + diff --git a/sphinx/technical/api/tests/data_store/index.rst b/sphinx/technical/api/tests/data_store/index.rst new file mode 100644 index 0000000..cb961b9 --- /dev/null +++ b/sphinx/technical/api/tests/data_store/index.rst @@ -0,0 +1,124 @@ +:py:mod:`tests.data_store` +========================== + +.. py:module:: tests.data_store + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.data_store.Metadata + tests.data_store.MockDS + tests.data_store.MockPDS + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.data_store.reset + tests.data_store.test_data_store_repr + tests.data_store.test_data_store_unicode + tests.data_store.test_data_store_deregister + tests.data_store.test_data_store_metadata_keyerror + tests.data_store.test_data_store_metadata_readable_writable + tests.data_store.test_data_store_write_load + tests.data_store.test_data_store_copy + tests.data_store.test_data_store_validation + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.data_store.metadata + + +.. py:class:: Metadata + + Bases: :py:obj:`bw2data.serialization.SerializedDict` + + .. py:attribute:: filename + :annotation: = mock-meta.json + + + + +.. py:data:: metadata + + + + +.. py:class:: MockDS + + Bases: :py:obj:`bw2data.data_store.DataStore` + + Mock DataStore for testing + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + +.. py:class:: MockPDS + + Bases: :py:obj:`bw2data.data_store.ProcessedDataStore` + + Mock DataStore for testing + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:method:: process_row(row) + + + +.. py:function:: reset() + + +.. py:function:: test_data_store_repr(reset) + + +.. py:function:: test_data_store_unicode(reset) + + +.. py:function:: test_data_store_deregister(reset) + + +.. py:function:: test_data_store_metadata_keyerror(reset) + + +.. py:function:: test_data_store_metadata_readable_writable(reset) + + +.. py:function:: test_data_store_write_load(reset) + + +.. py:function:: test_data_store_copy(reset) + + +.. py:function:: test_data_store_validation(reset) + + diff --git a/sphinx/technical/api/tests/database/index.rst b/sphinx/technical/api/tests/database/index.rst new file mode 100644 index 0000000..30ca908 --- /dev/null +++ b/sphinx/technical/api/tests/database/index.rst @@ -0,0 +1,238 @@ +:py:mod:`tests.database` +======================== + +.. py:module:: tests.database + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.database.food + tests.database.test_food + tests.database.test_get_code + tests.database.test_get_kwargs + tests.database.test_iter + tests.database.test_get_random + tests.database.test_copy + tests.database.test_copy_does_deepcopy + tests.database.test_raise_wrong_database + tests.database.test_deletes_from_database + tests.database.test_relabel_data + tests.database.test_find_graph_dependents + tests.database.test_register + tests.database.test_deregister + tests.database.test_write_sets_databases_number_attribute + tests.database.test_process_unknown_object + tests.database.test_naughty_activity_codes + tests.database.test_setup + tests.database.test_rename + tests.database.test_exchange_save + tests.database.test_dirty_activities + tests.database.test_process_invalid_exchange_value + tests.database.test_untyped_exchange_error + tests.database.test_no_input_raises_invalid_exchange + tests.database.test_no_amount_raises_invalid_exchange + tests.database.test_zero_amount_is_valid_exchange + tests.database.test_process_checks_process_type + tests.database.test_geomapping_array_includes_only_processes + tests.database.test_processed_array + tests.database.test_processed_array_with_metadata + tests.database.test_base_class + tests.database.test_find_dependents + tests.database.test_set_dependents + tests.database.test_process_without_exchanges_still_in_processed_array + tests.database.test_random_empty + tests.database.test_new_node + tests.database.test_new_node_no_code + tests.database.test_new_node_error + tests.database.test_new_activity + tests.database.test_can_split_processes_products + tests.database.test_sqlite_processed_array_order + tests.database.test_no_distributions_if_no_uncertainty + tests.database.test_database_delete_parameters + tests.database.test_delete_duplicate_exchanges + tests.database.test_add_geocollections + tests.database.test_set_geocollections + tests.database.test_add_geocollections_unable + tests.database.test_add_geocollections_no_unable_for_product + tests.database.df_fixture + tests.database.test_edges_to_dataframe_simple + tests.database.test_edges_to_dataframe_categorical + tests.database.test_edges_to_dataframe_formatters + tests.database.test_nodes_to_dataframe_simple + tests.database.test_nodes_to_dataframe_columns + tests.database.test_nodes_to_dataframe_unsorted + + + +.. py:function:: food() + + +.. py:function:: test_food(food) + + +.. py:function:: test_get_code() + + +.. py:function:: test_get_kwargs() + + +.. py:function:: test_iter() + + +.. py:function:: test_get_random() + + +.. py:function:: test_copy(food) + + +.. py:function:: test_copy_does_deepcopy() + + +.. py:function:: test_raise_wrong_database() + + +.. py:function:: test_deletes_from_database() + + +.. py:function:: test_relabel_data() + + +.. py:function:: test_find_graph_dependents() + + +.. py:function:: test_register() + + +.. py:function:: test_deregister() + + +.. py:function:: test_write_sets_databases_number_attribute() + + +.. py:function:: test_process_unknown_object() + + +.. py:function:: test_naughty_activity_codes() + + +.. py:function:: test_setup() + + +.. py:function:: test_rename() + + +.. py:function:: test_exchange_save() + + +.. py:function:: test_dirty_activities() + + +.. py:function:: test_process_invalid_exchange_value() + + +.. py:function:: test_untyped_exchange_error() + + +.. py:function:: test_no_input_raises_invalid_exchange() + + +.. py:function:: test_no_amount_raises_invalid_exchange() + + +.. py:function:: test_zero_amount_is_valid_exchange() + + +.. py:function:: test_process_checks_process_type() + + +.. py:function:: test_geomapping_array_includes_only_processes() + + +.. py:function:: test_processed_array() + + +.. py:function:: test_processed_array_with_metadata() + + +.. py:function:: test_base_class() + + +.. py:function:: test_find_dependents() + + +.. py:function:: test_set_dependents() + + +.. py:function:: test_process_without_exchanges_still_in_processed_array() + + +.. py:function:: test_random_empty() + + +.. py:function:: test_new_node() + + +.. py:function:: test_new_node_no_code() + + +.. py:function:: test_new_node_error() + + +.. py:function:: test_new_activity() + + +.. py:function:: test_can_split_processes_products() + + +.. py:function:: test_sqlite_processed_array_order() + + +.. py:function:: test_no_distributions_if_no_uncertainty() + + +.. py:function:: test_database_delete_parameters() + + +.. py:function:: test_delete_duplicate_exchanges() + + +.. py:function:: test_add_geocollections(capsys) + + +.. py:function:: test_set_geocollections(capsys) + + +.. py:function:: test_add_geocollections_unable(capsys) + + +.. py:function:: test_add_geocollections_no_unable_for_product(capsys) + + +.. py:function:: df_fixture() + + +.. py:function:: test_edges_to_dataframe_simple(df_fixture) + + +.. py:function:: test_edges_to_dataframe_categorical(df_fixture) + + +.. py:function:: test_edges_to_dataframe_formatters(df_fixture) + + +.. py:function:: test_nodes_to_dataframe_simple(df_fixture) + + +.. py:function:: test_nodes_to_dataframe_columns(df_fixture) + + +.. py:function:: test_nodes_to_dataframe_unsorted(df_fixture) + + diff --git a/sphinx/technical/api/tests/database_querying/index.rst b/sphinx/technical/api/tests/database_querying/index.rst new file mode 100644 index 0000000..a5e1ba7 --- /dev/null +++ b/sphinx/technical/api/tests/database_querying/index.rst @@ -0,0 +1,72 @@ +:py:mod:`tests.database_querying` +================================= + +.. py:module:: tests.database_querying + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.database_querying.DatabaseQuerysetTest + + + + +.. py:class:: DatabaseQuerysetTest + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + .. py:method:: extra_setup() + + + .. py:method:: test_setup_clean() + + + .. py:method:: test_random_with_global_filters() + + + .. py:method:: test_random_with_local_filters() + + + .. py:method:: test_random_with_local_and_global_filters() + + + .. py:method:: test_contains_respects_filters() + + + .. py:method:: test_get_ignores_filters() + + + .. py:method:: test_filter() + + + .. py:method:: test_order_by() + + + .. py:method:: test_order_by_bad_field() + + + .. py:method:: test_filter_bad_field() + + + .. py:method:: test_filter_not_dict() + + + .. py:method:: test_reset_order_by() + + + .. py:method:: test_reset_filters() + + + .. py:method:: test_len_respects_filters() + + + .. py:method:: test_make_searchable_unknown_object() + + + diff --git a/sphinx/technical/api/tests/dict_man/index.rst b/sphinx/technical/api/tests/dict_man/index.rst new file mode 100644 index 0000000..c957b72 --- /dev/null +++ b/sphinx/technical/api/tests/dict_man/index.rst @@ -0,0 +1,102 @@ +:py:mod:`tests.dict_man` +======================== + +.. py:module:: tests.dict_man + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.dict_man.test_dm_initiation + tests.dict_man.test_dm_setting + tests.dict_man.test_dm_getting + tests.dict_man.test_dm_str + tests.dict_man.test_dm_iter + tests.dict_man.test_dm_len + tests.dict_man.test_rrd_input_error + tests.dict_man.test_rrd_basic + tests.dict_man.test_rrd_reversed + tests.dict_man.test_rrd_reversed_create_on_demand + tests.dict_man.test_rrd_remapping_multiple + tests.dict_man.test_rrd_remapping + tests.dict_man.test_rrd_remapping_deletes_reversed + tests.dict_man.test_rrd_remapping_sets_original + tests.dict_man.test_rrd_remapping_multiple_original + tests.dict_man.test_rrd_str + tests.dict_man.test_rrd_unmap + tests.dict_man.test_rrd_unmap_reversed + tests.dict_man.test_rrd_unmap_original + tests.dict_man.test_rrd_iter + tests.dict_man.test_rrd_len + + + +.. py:function:: test_dm_initiation() + + +.. py:function:: test_dm_setting() + + +.. py:function:: test_dm_getting() + + +.. py:function:: test_dm_str() + + +.. py:function:: test_dm_iter() + + +.. py:function:: test_dm_len() + + +.. py:function:: test_rrd_input_error() + + +.. py:function:: test_rrd_basic() + + +.. py:function:: test_rrd_reversed() + + +.. py:function:: test_rrd_reversed_create_on_demand() + + +.. py:function:: test_rrd_remapping_multiple() + + +.. py:function:: test_rrd_remapping() + + +.. py:function:: test_rrd_remapping_deletes_reversed() + + +.. py:function:: test_rrd_remapping_sets_original() + + +.. py:function:: test_rrd_remapping_multiple_original() + + +.. py:function:: test_rrd_str() + + +.. py:function:: test_rrd_unmap() + + +.. py:function:: test_rrd_unmap_reversed() + + +.. py:function:: test_rrd_unmap_original() + + +.. py:function:: test_rrd_iter() + + +.. py:function:: test_rrd_len() + + diff --git a/sphinx/technical/api/tests/econ/index.rst b/sphinx/technical/api/tests/econ/index.rst new file mode 100644 index 0000000..6c2a963 --- /dev/null +++ b/sphinx/technical/api/tests/econ/index.rst @@ -0,0 +1,73 @@ +:py:mod:`tests.econ` +==================== + +.. py:module:: tests.econ + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.econ.EconometricsTestCase + + + + +.. py:class:: EconometricsTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_concentration_ratio() + + + .. py:method:: test_concentration_ratio_normalization() + + + .. py:method:: test_concentration_ratio_number() + + + .. py:method:: test_herfindahl() + + + .. py:method:: test_gini() + + + .. py:method:: test_theil() + + + diff --git a/sphinx/technical/api/tests/exchange_proxy/index.rst b/sphinx/technical/api/tests/exchange_proxy/index.rst new file mode 100644 index 0000000..1a0c991 --- /dev/null +++ b/sphinx/technical/api/tests/exchange_proxy/index.rst @@ -0,0 +1,123 @@ +:py:mod:`tests.exchange_proxy` +============================== + +.. py:module:: tests.exchange_proxy + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.exchange_proxy.activity + tests.exchange_proxy.activity_and_method + tests.exchange_proxy.test_setup_clean + tests.exchange_proxy.test_production + tests.exchange_proxy.test_substitution + tests.exchange_proxy.test_biosphere + tests.exchange_proxy.test_technosphere + tests.exchange_proxy.test_technosphere_include_substitution + tests.exchange_proxy.test_technosphere_exclude_substitution + tests.exchange_proxy.test_upstream + tests.exchange_proxy.test_upstream_no_kinds + tests.exchange_proxy.test_upstream_bio + tests.exchange_proxy.test_ordering_consistency + tests.exchange_proxy.test_exchanges_to_dataframe + tests.exchange_proxy.test_uncertainty + tests.exchange_proxy.test_uncertainty_type + tests.exchange_proxy.test_uncertainty_type_missing + tests.exchange_proxy.test_random_sample + tests.exchange_proxy.test_random_sample_negative + tests.exchange_proxy.test_lca + tests.exchange_proxy.test_delete_parameterized_exchange + tests.exchange_proxy.test_exchange_eq + tests.exchange_proxy.test_exchange_hash + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.exchange_proxy.bw2calc + + +.. py:data:: bw2calc + + + + +.. py:function:: activity() + + +.. py:function:: activity_and_method() + + +.. py:function:: test_setup_clean(activity) + + +.. py:function:: test_production(activity) + + +.. py:function:: test_substitution(activity) + + +.. py:function:: test_biosphere(activity) + + +.. py:function:: test_technosphere(activity) + + +.. py:function:: test_technosphere_include_substitution(activity) + + +.. py:function:: test_technosphere_exclude_substitution(activity) + + +.. py:function:: test_upstream(activity) + + +.. py:function:: test_upstream_no_kinds(activity) + + +.. py:function:: test_upstream_bio(activity) + + +.. py:function:: test_ordering_consistency(activity) + + +.. py:function:: test_exchanges_to_dataframe(activity) + + +.. py:function:: test_uncertainty() + + +.. py:function:: test_uncertainty_type() + + +.. py:function:: test_uncertainty_type_missing() + + +.. py:function:: test_random_sample() + + +.. py:function:: test_random_sample_negative() + + +.. py:function:: test_lca(activity_and_method) + + +.. py:function:: test_delete_parameterized_exchange() + + +.. py:function:: test_exchange_eq(activity) + + +.. py:function:: test_exchange_hash(activity) + + diff --git a/sphinx/technical/api/tests/fixtures/create_fixtures/index.rst b/sphinx/technical/api/tests/fixtures/create_fixtures/index.rst new file mode 100644 index 0000000..cd323f7 --- /dev/null +++ b/sphinx/technical/api/tests/fixtures/create_fixtures/index.rst @@ -0,0 +1,67 @@ +:py:mod:`tests.fixtures.create_fixtures` +======================================== + +.. py:module:: tests.fixtures.create_fixtures + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.fixtures.create_fixtures.bw2io_example_database + tests.fixtures.create_fixtures.empty_biosphere + tests.fixtures.create_fixtures._create_basic_fixture + tests.fixtures.create_fixtures.create_basic_fixture_zipfile + tests.fixtures.create_fixtures.create_basic_fixture_directory + tests.fixtures.create_fixtures.create_svdm_fixtures + tests.fixtures.create_fixtures.create_array_fixtures + tests.fixtures.create_fixtures.create_mc_basic + tests.fixtures.create_fixtures.create_mc_complete + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.fixtures.create_fixtures.fixture_dir + + +.. py:data:: fixture_dir + + + + +.. py:function:: bw2io_example_database() + + +.. py:function:: empty_biosphere() + + +.. py:function:: _create_basic_fixture(fs) + + +.. py:function:: create_basic_fixture_zipfile() + + +.. py:function:: create_basic_fixture_directory() + + +.. py:function:: create_svdm_fixtures() + + +.. py:function:: create_array_fixtures() + + +.. py:function:: create_mc_basic() + + +.. py:function:: create_mc_complete() + + diff --git a/sphinx/technical/api/tests/fixtures/index.rst b/sphinx/technical/api/tests/fixtures/index.rst new file mode 100644 index 0000000..e7aa842 --- /dev/null +++ b/sphinx/technical/api/tests/fixtures/index.rst @@ -0,0 +1,35 @@ +:py:mod:`tests.fixtures` +======================== + +.. py:module:: tests.fixtures + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + create_fixtures/index.rst + presamples_basic/index.rst + simapro_reference/index.rst + + +Package Contents +---------------- + +.. py:data:: biosphere + + + + +.. py:data:: food + + + + +.. py:data:: food2 + + + + diff --git a/sphinx/technical/api/tests/fixtures/presamples_basic/index.rst b/sphinx/technical/api/tests/fixtures/presamples_basic/index.rst new file mode 100644 index 0000000..3519d6d --- /dev/null +++ b/sphinx/technical/api/tests/fixtures/presamples_basic/index.rst @@ -0,0 +1,57 @@ +:py:mod:`tests.fixtures.presamples_basic` +========================================= + +.. py:module:: tests.fixtures.presamples_basic + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.fixtures.presamples_basic.write_database + tests.fixtures.presamples_basic.build_single_presample_array + tests.fixtures.presamples_basic.build_multi_presample_array_unseeded + tests.fixtures.presamples_basic.build_multi_presample_array + tests.fixtures.presamples_basic.build_multi_presample_sequential_array + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.fixtures.presamples_basic.basedir + tests.fixtures.presamples_basic.name + + +.. py:data:: basedir + + + + +.. py:function:: write_database() + + +.. py:function:: build_single_presample_array() + + +.. py:function:: build_multi_presample_array_unseeded() + + +.. py:function:: build_multi_presample_array() + + +.. py:function:: build_multi_presample_sequential_array() + + +.. py:data:: name + + + + diff --git a/sphinx/technical/api/tests/fixtures/simapro_reference/index.rst b/sphinx/technical/api/tests/fixtures/simapro_reference/index.rst new file mode 100644 index 0000000..4ae4250 --- /dev/null +++ b/sphinx/technical/api/tests/fixtures/simapro_reference/index.rst @@ -0,0 +1,14 @@ +:py:mod:`tests.fixtures.simapro_reference` +========================================== + +.. py:module:: tests.fixtures.simapro_reference + + +Module Contents +--------------- + +.. py:data:: background + + + + diff --git a/sphinx/technical/api/tests/geo/index.rst b/sphinx/technical/api/tests/geo/index.rst new file mode 100644 index 0000000..2efb7de --- /dev/null +++ b/sphinx/technical/api/tests/geo/index.rst @@ -0,0 +1,54 @@ +:py:mod:`tests.geo` +=================== + +.. py:module:: tests.geo + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.geo.add_biosphere + tests.geo.add_method + tests.geo.test_geomapping_retrieval + tests.geo.test_glo_always_present + tests.geo.test_method_process_adds_correct_geo + tests.geo.test_database_process_adds_correct_geo + tests.geo.test_database_process_adds_default_geo + tests.geo.test_method_write_adds_to_geomapping + tests.geo.test_database_write_adds_to_geomapping + + + +.. py:function:: add_biosphere() + + +.. py:function:: add_method(add_biosphere) + + +.. py:function:: test_geomapping_retrieval() + + +.. py:function:: test_glo_always_present() + + +.. py:function:: test_method_process_adds_correct_geo(add_method) + + +.. py:function:: test_database_process_adds_correct_geo(add_biosphere) + + +.. py:function:: test_database_process_adds_default_geo(add_biosphere) + + +.. py:function:: test_method_write_adds_to_geomapping(add_method) + + +.. py:function:: test_database_write_adds_to_geomapping(add_biosphere) + + diff --git a/sphinx/technical/api/tests/health_check/index.rst b/sphinx/technical/api/tests/health_check/index.rst new file mode 100644 index 0000000..f0cdfee --- /dev/null +++ b/sphinx/technical/api/tests/health_check/index.rst @@ -0,0 +1,36 @@ +:py:mod:`tests.health_check` +============================ + +.. py:module:: tests.health_check + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.health_check.DHCMock + tests.health_check.HealthCheckTestCase + + + + +.. py:class:: DHCMock + + Bases: :py:obj:`bw2analyzer.health_check.DatabaseHealthCheck` + + .. py:method:: make_graphs(foo) + + + +.. py:class:: HealthCheckTestCase + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + .. py:method:: test_health_check() + + + diff --git a/sphinx/technical/api/tests/ia/index.rst b/sphinx/technical/api/tests/ia/index.rst new file mode 100644 index 0000000..6a5a351 --- /dev/null +++ b/sphinx/technical/api/tests/ia/index.rst @@ -0,0 +1,162 @@ +:py:mod:`tests.ia` +================== + +.. py:module:: tests.ia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.ia.Metadata + tests.ia.MockIADS + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.ia.reset + tests.ia.test_unicode + tests.ia.test_abbreviate + tests.ia.test_copy_no_name + tests.ia.test_copy_with_name + tests.ia.test_register_adds_abbreviation + tests.ia.test_method_write_adds_num_cfs_to_metadata + tests.ia.test_method_processed_array + tests.ia.test_method_missing_reference + tests.ia.test_method_missing_location + tests.ia.test_method_missing_global_location + tests.ia.test_method_base_class + tests.ia.test_method_validator + tests.ia.test_weighting_write_good_data + tests.ia.test_weighting_write_invalid_data + tests.ia.test_weighting_process + tests.ia.test_weighting_base_class + tests.ia.test_weighting_validator + tests.ia.test_base_normalization_class + tests.ia.test_normalization_process_row + tests.ia.test_method_geocollection + tests.ia.test_method_geocollection_missing_ok + tests.ia.test_method_geocollection_warning + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.ia.metadata + + +.. py:class:: Metadata + + Bases: :py:obj:`bw2data.serialization.CompoundJSONDict` + + .. py:attribute:: filename + :annotation: = mock-meta.json + + + + +.. py:data:: metadata + + + + +.. py:class:: MockIADS + + Bases: :py:obj:`bw2data.ia_data_store.ImpactAssessmentDataStore` + + Mock IADS for testing + + .. py:attribute:: _metadata + + + + + .. py:attribute:: validator + + + + + .. py:method:: process_row(row) + + + +.. py:function:: reset() + + +.. py:function:: test_unicode(reset) + + +.. py:function:: test_abbreviate(reset) + + +.. py:function:: test_copy_no_name(reset) + + +.. py:function:: test_copy_with_name(reset) + + +.. py:function:: test_register_adds_abbreviation(reset) + + +.. py:function:: test_method_write_adds_num_cfs_to_metadata(reset) + + +.. py:function:: test_method_processed_array(reset) + + +.. py:function:: test_method_missing_reference() + + +.. py:function:: test_method_missing_location() + + +.. py:function:: test_method_missing_global_location() + + +.. py:function:: test_method_base_class(reset) + + +.. py:function:: test_method_validator(reset) + + +.. py:function:: test_weighting_write_good_data(reset) + + +.. py:function:: test_weighting_write_invalid_data(reset) + + +.. py:function:: test_weighting_process(reset) + + +.. py:function:: test_weighting_base_class(reset) + + +.. py:function:: test_weighting_validator(reset) + + +.. py:function:: test_base_normalization_class(reset) + + +.. py:function:: test_normalization_process_row(reset) + + +.. py:function:: test_method_geocollection() + + +.. py:function:: test_method_geocollection_missing_ok() + + +.. py:function:: test_method_geocollection_warning() + + diff --git a/sphinx/technical/api/tests/index.rst b/sphinx/technical/api/tests/index.rst new file mode 100644 index 0000000..204a72f --- /dev/null +++ b/sphinx/technical/api/tests/index.rst @@ -0,0 +1,66 @@ +:py:mod:`tests` +=============== + +.. py:module:: tests + + +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + + data/index.rst + fixtures/index.rst + json_ld/index.rst + strategies/index.rst + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + activity_proxy/index.rst + base/index.rst + base_lci_importer/index.rst + base_lcia_importer/index.rst + chemidplus/index.rst + comparisons/index.rst + compatibility/index.rst + config/index.rst + contribution/index.rst + data_store/index.rst + database/index.rst + database_querying/index.rst + dict_man/index.rst + econ/index.rst + exchange_proxy/index.rst + geo/index.rst + health_check/index.rst + ia/index.rst + iotable/index.rst + lca/index.rst + lci/index.rst + matrix_grapher/index.rst + monte_carlo/index.rst + multifunctional_graph_traversal/index.rst + packaging/index.rst + parameters/index.rst + presamples/index.rst + projects/index.rst + sc_graph/index.rst + search/index.rst + serialization/index.rst + simapro/index.rst + single_matrix/index.rst + sqlite/index.rst + svdm/index.rst + tagged/index.rst + to_dataframe/index.rst + updates/index.rst + utils/index.rst + validation/index.rst + + diff --git a/sphinx/technical/api/tests/iotable/index.rst b/sphinx/technical/api/tests/iotable/index.rst new file mode 100644 index 0000000..3004ee0 --- /dev/null +++ b/sphinx/technical/api/tests/iotable/index.rst @@ -0,0 +1,108 @@ +:py:mod:`tests.iotable` +======================= + +.. py:module:: tests.iotable + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.iotable.iotable_fixture + tests.iotable.test_iotable_setup_clean + tests.iotable.test_iotable_matrix_construction + tests.iotable.test_iotable_process_method + tests.iotable.test_iotable_edges_to_dataframe + tests.iotable.test_iotable_nodes_to_dataframe + tests.iotable.test_iotable_get_methods_correct_class + tests.iotable.test_iotable_activity + tests.iotable.test_iotable_activity_edges_to_dataframe + tests.iotable.test_correct_backend_fixture + tests.iotable.test_iotable_edges_production + tests.iotable.test_iotable_edges_technosphere + tests.iotable.test_iotable_edges_biosphere + tests.iotable.test_substitution + tests.iotable.test_iotabe_readonlyexchange + tests.iotable.test_iotabe_readonlyexchange_missing_methods + tests.iotable.test_iotabe_readonlyexchange_not_setitem + tests.iotable.test_iotable_filtered_datapackage + + + +.. py:function:: iotable_fixture() + + Technosphere matrix: + + a b c + a 2 0 -3 + b -1 1 0 + c 4 0.2 -1 + + Biosphere matrix: + + a b c + d 0 1 2 + + Characterization matrix: + + d + d 42 + + + +.. py:function:: test_iotable_setup_clean(iotable_fixture) + + +.. py:function:: test_iotable_matrix_construction(iotable_fixture) + + +.. py:function:: test_iotable_process_method(iotable_fixture) + + +.. py:function:: test_iotable_edges_to_dataframe(iotable_fixture) + + +.. py:function:: test_iotable_nodes_to_dataframe(iotable_fixture) + + +.. py:function:: test_iotable_get_methods_correct_class(iotable_fixture) + + +.. py:function:: test_iotable_activity(iotable_fixture) + + +.. py:function:: test_iotable_activity_edges_to_dataframe(iotable_fixture) + + +.. py:function:: test_correct_backend_fixture(iotable_fixture) + + +.. py:function:: test_iotable_edges_production(iotable_fixture) + + +.. py:function:: test_iotable_edges_technosphere(iotable_fixture) + + +.. py:function:: test_iotable_edges_biosphere(iotable_fixture) + + +.. py:function:: test_substitution(iotable_fixture) + + +.. py:function:: test_iotabe_readonlyexchange(iotable_fixture) + + +.. py:function:: test_iotabe_readonlyexchange_missing_methods(iotable_fixture) + + +.. py:function:: test_iotabe_readonlyexchange_not_setitem(iotable_fixture) + + +.. py:function:: test_iotable_filtered_datapackage(iotable_fixture) + + diff --git a/sphinx/technical/api/tests/json_ld/index.rst b/sphinx/technical/api/tests/json_ld/index.rst new file mode 100644 index 0000000..66db0fa --- /dev/null +++ b/sphinx/technical/api/tests/json_ld/index.rst @@ -0,0 +1,6 @@ +:py:mod:`tests.json_ld` +======================= + +.. py:module:: tests.json_ld + + diff --git a/sphinx/technical/api/tests/lca/index.rst b/sphinx/technical/api/tests/lca/index.rst new file mode 100644 index 0000000..6c28e0a --- /dev/null +++ b/sphinx/technical/api/tests/lca/index.rst @@ -0,0 +1,235 @@ +:py:mod:`tests.lca` +=================== + +.. py:module:: tests.lca + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.lca.test_example_db_basic + tests.lca.test_basic + tests.lca.test_basic_negative_production + tests.lca.test_basic_substitution + tests.lca.test_basic_nonunitary_production + tests.lca.test_circular_inputs + tests.lca.test_invalid_datapackage + tests.lca.test_demand_not_mapping + tests.lca.test_demand_mapping_but_not_dict + tests.lca.test_next_data_array + tests.lca.test_data_array_keep_first + tests.lca.test_next_only_vectors + tests.lca.test_next_plain_monte_carlo + tests.lca.test_next_monte_carlo_as_iterator + tests.lca.test_next_monte_carlo_all_matrices_change + tests.lca.test_build_demand_array + tests.lca.test_build_demand_array_pass_dict + tests.lca.test_build_demand_array_outside_technosphere + tests.lca.test_build_demand_array_activity_not_product + tests.lca.test_build_demand_array_pass_object + tests.lca.test_load_lci_data + tests.lca.test_load_lci_data_nonsquare_technosphere + tests.lca.test_load_lci_data_empty_biosphere_warning + tests.lca.test_remap_inventory_dicts + tests.lca.test_load_lcia_data + tests.lca.test_load_lcia_data_multiple_characterization_packages + tests.lca.test_load_lcia_data_inconsistent_globals + tests.lca.test_load_lcia_data_none_global_value + tests.lca.test_load_lcia_data_nonglobal_filtered + tests.lca.test_empty_biosphere_lcia + tests.lca.test_lca_has + tests.lca.test_lca_with_normalization + tests.lca.test_lca_with_weighting + tests.lca.test_lca_with_weighting_deprecation + tests.lca.test_lca_with_weighting_and_normalization + tests.lca.test_switch_method + tests.lca.test_switch_normalization + tests.lca.test_switch_weighting + tests.lca.test_invert_technosphere + tests.lca.test_redo_lci + tests.lca.test_redo_lci_same_fu + tests.lca.test_redo_lci_deprecated + tests.lca.test_redo_lci_fails_if_activity_outside_technosphere + tests.lca.test_redo_lci_fails_if_passed_bw2_key_tuple + tests.lca.test_redo_lci_with_no_new_demand_no_error + tests.lca.test_redo_lcia + tests.lca.test_redo_lcia_keyerror_bw2_key + tests.lca.test_redo_lcia_outside_technosphere + tests.lca.test_redo_lcia_same_fu + tests.lca.test_redo_lcia_deprecated + tests.lca.test_has + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.lca.fixture_dir + + +.. py:data:: fixture_dir + + + + +.. py:function:: test_example_db_basic() + + +.. py:function:: test_basic() + + +.. py:function:: test_basic_negative_production() + + +.. py:function:: test_basic_substitution() + + +.. py:function:: test_basic_nonunitary_production() + + +.. py:function:: test_circular_inputs() + + +.. py:function:: test_invalid_datapackage() + + +.. py:function:: test_demand_not_mapping() + + +.. py:function:: test_demand_mapping_but_not_dict() + + +.. py:function:: test_next_data_array() + + +.. py:function:: test_data_array_keep_first() + + +.. py:function:: test_next_only_vectors() + + +.. py:function:: test_next_plain_monte_carlo() + + +.. py:function:: test_next_monte_carlo_as_iterator() + + +.. py:function:: test_next_monte_carlo_all_matrices_change() + + +.. py:function:: test_build_demand_array() + + +.. py:function:: test_build_demand_array_pass_dict() + + +.. py:function:: test_build_demand_array_outside_technosphere() + + +.. py:function:: test_build_demand_array_activity_not_product() + + +.. py:function:: test_build_demand_array_pass_object() + + +.. py:function:: test_load_lci_data() + + +.. py:function:: test_load_lci_data_nonsquare_technosphere() + + +.. py:function:: test_load_lci_data_empty_biosphere_warning() + + +.. py:function:: test_remap_inventory_dicts() + + +.. py:function:: test_load_lcia_data() + + +.. py:function:: test_load_lcia_data_multiple_characterization_packages() + + +.. py:function:: test_load_lcia_data_inconsistent_globals() + + +.. py:function:: test_load_lcia_data_none_global_value() + + +.. py:function:: test_load_lcia_data_nonglobal_filtered() + + +.. py:function:: test_empty_biosphere_lcia() + + +.. py:function:: test_lca_has() + + +.. py:function:: test_lca_with_normalization() + + +.. py:function:: test_lca_with_weighting() + + +.. py:function:: test_lca_with_weighting_deprecation() + + +.. py:function:: test_lca_with_weighting_and_normalization() + + +.. py:function:: test_switch_method() + + +.. py:function:: test_switch_normalization() + + +.. py:function:: test_switch_weighting() + + +.. py:function:: test_invert_technosphere() + + +.. py:function:: test_redo_lci() + + +.. py:function:: test_redo_lci_same_fu() + + +.. py:function:: test_redo_lci_deprecated() + + +.. py:function:: test_redo_lci_fails_if_activity_outside_technosphere() + + +.. py:function:: test_redo_lci_fails_if_passed_bw2_key_tuple() + + +.. py:function:: test_redo_lci_with_no_new_demand_no_error() + + +.. py:function:: test_redo_lcia() + + +.. py:function:: test_redo_lcia_keyerror_bw2_key() + + +.. py:function:: test_redo_lcia_outside_technosphere() + + +.. py:function:: test_redo_lcia_same_fu() + + +.. py:function:: test_redo_lcia_deprecated() + + +.. py:function:: test_has() + + diff --git a/sphinx/technical/api/tests/lci/index.rst b/sphinx/technical/api/tests/lci/index.rst new file mode 100644 index 0000000..c8fadb4 --- /dev/null +++ b/sphinx/technical/api/tests/lci/index.rst @@ -0,0 +1,26 @@ +:py:mod:`tests.lci` +=================== + +.. py:module:: tests.lci + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.lci.test_labeled_inventory_before_lci + tests.lci.test_labeled_inventory + + + +.. py:function:: test_labeled_inventory_before_lci(tagged_fixture) + + +.. py:function:: test_labeled_inventory(tagged_fixture) + + diff --git a/sphinx/technical/api/tests/matrix_grapher/index.rst b/sphinx/technical/api/tests/matrix_grapher/index.rst new file mode 100644 index 0000000..b3049b5 --- /dev/null +++ b/sphinx/technical/api/tests/matrix_grapher/index.rst @@ -0,0 +1,39 @@ +:py:mod:`tests.matrix_grapher` +============================== + +.. py:module:: tests.matrix_grapher + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.matrix_grapher.MatrixGrapherTestCase + + + + +.. py:class:: MatrixGrapherTestCase + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + .. py:method:: get_lca() + + + .. py:method:: test_graph_no_file() + + + .. py:method:: test_graph() + + + .. py:method:: test_ordered_graph_no_file() + + + .. py:method:: test_ordered_graph() + + + diff --git a/sphinx/technical/api/tests/monte_carlo/index.rst b/sphinx/technical/api/tests/monte_carlo/index.rst new file mode 100644 index 0000000..0a1c91f --- /dev/null +++ b/sphinx/technical/api/tests/monte_carlo/index.rst @@ -0,0 +1,6 @@ +:py:mod:`tests.monte_carlo` +=========================== + +.. py:module:: tests.monte_carlo + + diff --git a/sphinx/technical/api/tests/multifunctional_graph_traversal/index.rst b/sphinx/technical/api/tests/multifunctional_graph_traversal/index.rst new file mode 100644 index 0000000..7e37692 --- /dev/null +++ b/sphinx/technical/api/tests/multifunctional_graph_traversal/index.rst @@ -0,0 +1,50 @@ +:py:mod:`tests.multifunctional_graph_traversal` +=============================================== + +.. py:module:: tests.multifunctional_graph_traversal + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.multifunctional_graph_traversal.compare_dict + tests.multifunctional_graph_traversal.ordered + tests.multifunctional_graph_traversal.compare_list_of_dicts + tests.multifunctional_graph_traversal.test_multifunctional_x_shape_one_path + tests.multifunctional_graph_traversal.test_multifunctional_coproduction + tests.multifunctional_graph_traversal.test_multifunctional_x_path_two_paths + tests.multifunctional_graph_traversal.test_multifunctional_scaling + tests.multifunctional_graph_traversal.test_multifunctional_multiple_paths + + + +.. py:function:: compare_dict(one, two) + + +.. py:function:: ordered(edges) + + +.. py:function:: compare_list_of_dicts(one, two) + + +.. py:function:: test_multifunctional_x_shape_one_path() + + +.. py:function:: test_multifunctional_coproduction() + + +.. py:function:: test_multifunctional_x_path_two_paths() + + +.. py:function:: test_multifunctional_scaling() + + +.. py:function:: test_multifunctional_multiple_paths() + + diff --git a/sphinx/technical/api/tests/packaging/index.rst b/sphinx/technical/api/tests/packaging/index.rst new file mode 100644 index 0000000..455da50 --- /dev/null +++ b/sphinx/technical/api/tests/packaging/index.rst @@ -0,0 +1,85 @@ +:py:mod:`tests.packaging` +========================= + +.. py:module:: tests.packaging + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.packaging.BW2PackageTest + + + + +.. py:class:: BW2PackageTest(methodName='runTest') + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: extra_setup() + + + .. py:method:: test_class_metadata() + + + .. py:method:: test_validation() + + + .. py:method:: test_whitelist() + + + .. py:method:: test_create_class_whitelist() + + + .. py:method:: test_create_class() + + + .. py:method:: test_load_obj() + + + .. py:method:: test_create_obj() + + + .. py:method:: test_roundtrip_obj() + + + .. py:method:: test_roundtrip_objs() + + + diff --git a/sphinx/technical/api/tests/parameters/index.rst b/sphinx/technical/api/tests/parameters/index.rst new file mode 100644 index 0000000..328f937 --- /dev/null +++ b/sphinx/technical/api/tests/parameters/index.rst @@ -0,0 +1,536 @@ +:py:mod:`tests.parameters` +========================== + +.. py:module:: tests.parameters + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.parameters.test_project_parameters + tests.parameters.test_project_parameter_autocreate_group + tests.parameters.test_expire_downstream + tests.parameters.test_project_parameters_ordering + tests.parameters.test_project_parameters_dict + tests.parameters.test_project_parameters_load + tests.parameters.test_project_parameters_static + tests.parameters.test_project_parameters_expired + tests.parameters.test_project_parameters_recalculate + tests.parameters.test_project_parameters_expire_downstream + tests.parameters.test_project_autoupdate_triggers + tests.parameters.test_project_name_uniqueness + tests.parameters.test_project_parameter_dependency_chain + tests.parameters.test_project_parameter_dependency_chain_missing + tests.parameters.test_project_parameter_depend_within_group + tests.parameters.test_project_parameter_is_deletable + tests.parameters.test_project_parameter_is_not_deletable_project + tests.parameters.test_project_parameter_is_not_deletable_database + tests.parameters.test_project_parameter_is_not_deletable_activity + tests.parameters.test_project_parameter_formula_update + tests.parameters.test_create_database_parameters + tests.parameters.test_database_parameters_group_autocreated + tests.parameters.test_database_parameters_expired + tests.parameters.test_database_parameters_dict + tests.parameters.test_database_parameters_load + tests.parameters.test_database_parameters_static + tests.parameters.test_database_parameters_check + tests.parameters.test_database_autoupdate_triggers + tests.parameters.test_database_uniqueness_constraint + tests.parameters.test_database_parameter_cross_database_constraint + tests.parameters.test_update_database_parameters + tests.parameters.test_database_parameter_dependency_chain + tests.parameters.test_database_parameter_dependency_chain_missing + tests.parameters.test_database_parameter_dependency_chain_include_self + tests.parameters.test_database_parameter_depend_within_group + tests.parameters.test_database_parameter_is_deletable + tests.parameters.test_database_parameter_is_not_deletable_database + tests.parameters.test_database_parameter_is_not_deletable_activity + tests.parameters.test_database_parameter_is_dependent_on + tests.parameters.test_database_parameter_formula_update_project + tests.parameters.test_database_parameter_formula_update_database + tests.parameters.test_create_parameterized_exchange_missing_group + tests.parameters.test_create_parameterized_exchange + tests.parameters.test_create_parameterized_exchange_nonunique + tests.parameters.chain + tests.parameters.test_create_activity_parameter + tests.parameters.test_activity_parameters_group_autocreated + tests.parameters.test_activity_parameter_expired + tests.parameters.test_activity_parameter_dict + tests.parameters.test_activity_parameter_load + tests.parameters.test_activity_parameter_static + tests.parameters.test_activity_parameter_recalculate_shortcut + tests.parameters.test_activity_parameter_dependency_chain + tests.parameters.test_activity_parameter_dependency_chain_missing + tests.parameters.test_activity_parameter_dependency_chain_includes_exchanges + tests.parameters.test_activity_parameter_dependency_chain_include_self + tests.parameters.test_activity_parameter_dependency_chain_include_self_exchanges + tests.parameters.test_activity_parameter_depend_within_group + tests.parameters.test_activity_parameter_depend_within_group_include + tests.parameters.test_activity_parameter_dummy + tests.parameters.test_activity_parameter_multiple_dummies + tests.parameters.test_activity_parameter_static_dependencies + tests.parameters.test_activity_parameter_recalculate_exchanges + tests.parameters.test_pe_no_activities_parameter_group_error + tests.parameters.test_recalculate_exchanges_no_activities_parameters + tests.parameters.test_activity_parameter_recalculate + tests.parameters.test_activity_parameter_is_deletable + tests.parameters.test_activity_parameter_is_dependent_on + tests.parameters.test_activity_parameter_formula_update_project + tests.parameters.test_activity_parameter_formula_update_database + tests.parameters.test_activity_parameter_formula_update_activity + tests.parameters.test_activity_parameter_formula_update_activity_include + tests.parameters.test_activity_parameter_crossdatabase_triggers + tests.parameters.test_activity_parameter_crossgroup_triggers + tests.parameters.test_activity_parameter_autoupdate_triggers + tests.parameters.test_activity_parameter_checks_uniqueness_constraints + tests.parameters.test_activity_parameter_checks + tests.parameters.test_group + tests.parameters.test_group_purging + tests.parameters.test_group_dependency + tests.parameters.test_group_dependency_save_checks + tests.parameters.test_group_dependency_constraints + tests.parameters.test_group_dependency_circular + tests.parameters.test_group_dependency_override + tests.parameters.test_parameters_new_project_parameters_uniqueness + tests.parameters.test_parameters_new_project_parameters + tests.parameters.test_parameters_new_project_parameters_no_overwrite + tests.parameters.test_parameters_repr + tests.parameters.test_parameters_recalculate + tests.parameters.test_parameters_new_database_parameters + tests.parameters.test_parameters_new_database_parameters_no_overwrite + tests.parameters.test_parameters_new_activity_parameters_errors + tests.parameters.test_parameters_new_activity_parameters + tests.parameters.test_parameters_new_activity_parameters_no_overlap + tests.parameters.test_parameters_rename_project_parameter + tests.parameters.test_parameters_rename_project_parameter_incorrect_type + tests.parameters.test_parameters_rename_project_parameter_dependencies + tests.parameters.test_parameters_rename_project_parameter_dependencies_fail + tests.parameters.test_parameters_rename_project_parameter_dependencies_full + tests.parameters.test_parameters_rename_database_parameter + tests.parameters.test_parameters_rename_database_parameter_dependencies + tests.parameters.test_parameters_rename_activity_parameter + tests.parameters.test_parameters_rename_activity_parameter_dependencies + tests.parameters.test_parameters_rename_activity_parameter_group_exchange + tests.parameters.test_parameters_rename_activity_parameter_order_exchange + tests.parameters.test_parameters_add_to_group_empty + tests.parameters.test_parameters_add_to_group + tests.parameters.test_parameters_remove_from_group + tests.parameters.test_parameters_save_restore_exchange_amount + tests.parameters.test_parameters_save_keep_changed_exchange_amount + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.parameters.uuid4hex + + +.. py:data:: uuid4hex + + + + +.. py:function:: test_project_parameters() + + +.. py:function:: test_project_parameter_autocreate_group() + + +.. py:function:: test_expire_downstream() + + +.. py:function:: test_project_parameters_ordering() + + +.. py:function:: test_project_parameters_dict() + + +.. py:function:: test_project_parameters_load() + + +.. py:function:: test_project_parameters_static() + + +.. py:function:: test_project_parameters_expired() + + +.. py:function:: test_project_parameters_recalculate() + + +.. py:function:: test_project_parameters_expire_downstream() + + +.. py:function:: test_project_autoupdate_triggers() + + +.. py:function:: test_project_name_uniqueness() + + +.. py:function:: test_project_parameter_dependency_chain() + + +.. py:function:: test_project_parameter_dependency_chain_missing() + + +.. py:function:: test_project_parameter_depend_within_group() + + +.. py:function:: test_project_parameter_is_deletable() + + Project parameters can be deleted if they are no dependencies. + + +.. py:function:: test_project_parameter_is_not_deletable_project() + + +.. py:function:: test_project_parameter_is_not_deletable_database() + + +.. py:function:: test_project_parameter_is_not_deletable_activity() + + +.. py:function:: test_project_parameter_formula_update() + + Update formulas only where the name of the parameter is an exact match. + + +.. py:function:: test_create_database_parameters() + + +.. py:function:: test_database_parameters_group_autocreated() + + +.. py:function:: test_database_parameters_expired() + + +.. py:function:: test_database_parameters_dict() + + +.. py:function:: test_database_parameters_load() + + +.. py:function:: test_database_parameters_static() + + +.. py:function:: test_database_parameters_check() + + +.. py:function:: test_database_autoupdate_triggers() + + +.. py:function:: test_database_uniqueness_constraint() + + +.. py:function:: test_database_parameter_cross_database_constraint() + + Database parameters cannot use parameters on other databases. + + +.. py:function:: test_update_database_parameters() + + +.. py:function:: test_database_parameter_dependency_chain() + + +.. py:function:: test_database_parameter_dependency_chain_missing() + + +.. py:function:: test_database_parameter_dependency_chain_include_self() + + +.. py:function:: test_database_parameter_depend_within_group() + + +.. py:function:: test_database_parameter_is_deletable() + + Database parameters can be deleted if they are no dependencies. + + +.. py:function:: test_database_parameter_is_not_deletable_database() + + +.. py:function:: test_database_parameter_is_not_deletable_activity() + + +.. py:function:: test_database_parameter_is_dependent_on() + + Databases parameters can be dependent on project parameters. + + +.. py:function:: test_database_parameter_formula_update_project() + + Update formulas of database parameters, only update the formulas + where the actual ProjectParameter is referenced. + + +.. py:function:: test_database_parameter_formula_update_database() + + Update formulas of database parameters, only update the formulas + where the actual DatabaseParameter is referenced. + + +.. py:function:: test_create_parameterized_exchange_missing_group() + + +.. py:function:: test_create_parameterized_exchange() + + +.. py:function:: test_create_parameterized_exchange_nonunique() + + +.. py:function:: chain() + + +.. py:function:: test_create_activity_parameter() + + +.. py:function:: test_activity_parameters_group_autocreated() + + +.. py:function:: test_activity_parameter_expired() + + +.. py:function:: test_activity_parameter_dict() + + +.. py:function:: test_activity_parameter_load() + + +.. py:function:: test_activity_parameter_static(chain) + + +.. py:function:: test_activity_parameter_recalculate_shortcut() + + +.. py:function:: test_activity_parameter_dependency_chain(chain) + + +.. py:function:: test_activity_parameter_dependency_chain_missing(chain) + + Use unknown parameter 'K' in formula to test for MissingName error. + + +.. py:function:: test_activity_parameter_dependency_chain_includes_exchanges(chain) + + +.. py:function:: test_activity_parameter_dependency_chain_include_self(chain) + + Out of the parameters 'D' and 'F' in group 'A', only 'D' counts + as a dependency for group 'A'. + + This means that 'F' can be freely deleted, after which 'D' is no longer + a dependency for group 'A' (as 'D' was a dependency of 'F') and can now + also be deleted. + + +.. py:function:: test_activity_parameter_dependency_chain_include_self_exchanges(chain) + + Out of the parameters 'J' and 'H' in group 'G', only 'H' counts + as a dependency as 'J' is not used by either 'H' or by any exchanges. + + +.. py:function:: test_activity_parameter_depend_within_group(chain) + + When considering only dependencies within the given group. 'D' is + a dependency within the group 'A', while 'F' is not. + + +.. py:function:: test_activity_parameter_depend_within_group_include(chain) + + The 'J' parameter in group 'G' depends on the 'F' parameter in group + 'A'. 'F' doesn't exist within the 'G' group but is instead linked to the + 'J' parameter through the 'G' group order. + + +.. py:function:: test_activity_parameter_dummy() + + +.. py:function:: test_activity_parameter_multiple_dummies() + + +.. py:function:: test_activity_parameter_static_dependencies(chain) + + +.. py:function:: test_activity_parameter_recalculate_exchanges() + + +.. py:function:: test_pe_no_activities_parameter_group_error() + + +.. py:function:: test_recalculate_exchanges_no_activities_parameters() + + +.. py:function:: test_activity_parameter_recalculate() + + +.. py:function:: test_activity_parameter_is_deletable(chain) + + An activity parameter is deletable if it is not a dependency of another + activity parameter. + + +.. py:function:: test_activity_parameter_is_dependent_on(chain) + + An activity parameter can be dependent on any other type of parameter. + + +.. py:function:: test_activity_parameter_formula_update_project(chain) + + +.. py:function:: test_activity_parameter_formula_update_database(chain) + + +.. py:function:: test_activity_parameter_formula_update_activity(chain) + + +.. py:function:: test_activity_parameter_formula_update_activity_include(chain) + + +.. py:function:: test_activity_parameter_crossdatabase_triggers() + + +.. py:function:: test_activity_parameter_crossgroup_triggers() + + +.. py:function:: test_activity_parameter_autoupdate_triggers() + + +.. py:function:: test_activity_parameter_checks_uniqueness_constraints() + + +.. py:function:: test_activity_parameter_checks() + + +.. py:function:: test_group() + + +.. py:function:: test_group_purging() + + +.. py:function:: test_group_dependency() + + +.. py:function:: test_group_dependency_save_checks() + + +.. py:function:: test_group_dependency_constraints() + + +.. py:function:: test_group_dependency_circular() + + +.. py:function:: test_group_dependency_override() + + GroupDependency can be overridden by having a parameter with the same + name within the group. + + +.. py:function:: test_parameters_new_project_parameters_uniqueness() + + +.. py:function:: test_parameters_new_project_parameters() + + +.. py:function:: test_parameters_new_project_parameters_no_overwrite() + + +.. py:function:: test_parameters_repr() + + +.. py:function:: test_parameters_recalculate() + + +.. py:function:: test_parameters_new_database_parameters() + + +.. py:function:: test_parameters_new_database_parameters_no_overwrite() + + +.. py:function:: test_parameters_new_activity_parameters_errors() + + +.. py:function:: test_parameters_new_activity_parameters() + + +.. py:function:: test_parameters_new_activity_parameters_no_overlap() + + +.. py:function:: test_parameters_rename_project_parameter() + + Project parameters can be renamed. + + +.. py:function:: test_parameters_rename_project_parameter_incorrect_type() + + +.. py:function:: test_parameters_rename_project_parameter_dependencies() + + Updating downstream parameters will update all relevant formulas + to use the new name for the parameter. + + +.. py:function:: test_parameters_rename_project_parameter_dependencies_fail() + + An exception is raised if rename is attempted without updating + downstream if other parameters depend on that parameter. + + +.. py:function:: test_parameters_rename_project_parameter_dependencies_full(chain) + + Updating downstream parameters will update all relevant formulas + to use the new name for the parameter. + + Parameter amounts do no change as only the name is altered. + + +.. py:function:: test_parameters_rename_database_parameter() + + +.. py:function:: test_parameters_rename_database_parameter_dependencies(chain) + + +.. py:function:: test_parameters_rename_activity_parameter(chain) + + +.. py:function:: test_parameters_rename_activity_parameter_dependencies(chain) + + +.. py:function:: test_parameters_rename_activity_parameter_group_exchange() + + Rename 'D' from group 'A' updates ParameterizedExchange and + underlying exchange. + + +.. py:function:: test_parameters_rename_activity_parameter_order_exchange() + + Rename 'D' from group 'A' updates ParameterizedExchange and + underlying exchange in group 'G' + + +.. py:function:: test_parameters_add_to_group_empty() + + +.. py:function:: test_parameters_add_to_group() + + +.. py:function:: test_parameters_remove_from_group() + + +.. py:function:: test_parameters_save_restore_exchange_amount() + + The original amount of the exchange is restored when it is no + longer parameterized. + + +.. py:function:: test_parameters_save_keep_changed_exchange_amount() + + diff --git a/sphinx/technical/api/tests/presamples/index.rst b/sphinx/technical/api/tests/presamples/index.rst new file mode 100644 index 0000000..1ff85ca --- /dev/null +++ b/sphinx/technical/api/tests/presamples/index.rst @@ -0,0 +1,6 @@ +:py:mod:`tests.presamples` +========================== + +.. py:module:: tests.presamples + + diff --git a/sphinx/technical/api/tests/projects/index.rst b/sphinx/technical/api/tests/projects/index.rst new file mode 100644 index 0000000..851c917 --- /dev/null +++ b/sphinx/technical/api/tests/projects/index.rst @@ -0,0 +1,158 @@ +:py:mod:`tests.projects` +======================== + +.. py:module:: tests.projects + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.projects.test_project_directories + tests.projects.test_from_env_var + tests.projects.test_invalid_env_var + tests.projects.test_invalid_output_env_dir + tests.projects.test_output_env_dir + tests.projects.test_output_dir_from_preferences + tests.projects.test_invalid_output_dir_from_preferences + tests.projects.test_directories + tests.projects.test_default_project_created + tests.projects.test_repeatedly_set_name_same_value + tests.projects.test_funny_project_names + tests.projects.test_report + tests.projects.test_request_directory + tests.projects.test_delete_current_project_with_name + tests.projects.test_delete_project_remove_directory + tests.projects.test_delete_project_keep_directory + tests.projects.test_delete_project + tests.projects.test_delete_last_project + tests.projects.test_delete_current_project_no_name + tests.projects.test_error_outdated_set_project + tests.projects.test_set_project_creates_new_project + tests.projects.test_set_project + tests.projects.test_set_project_default_writable + tests.projects.test_set_project_writable_even_if_writable_false + tests.projects.test_set_readonly_project + tests.projects.test_set_readonly_project_first_time + tests.projects.test_set_current_reset_metadata + tests.projects.test_representation + tests.projects.test_contains + tests.projects.test_len + tests.projects.test_iterating_over_projects_no_error + tests.projects.test_create_lock_file + tests.projects.test_lockable_config_missing + tests.projects.test_copy_project + tests.projects.test_copy_project_switch_current + + + +.. py:function:: test_project_directories() + + +.. py:function:: test_from_env_var() + + +.. py:function:: test_invalid_env_var() + + +.. py:function:: test_invalid_output_env_dir() + + +.. py:function:: test_output_env_dir() + + +.. py:function:: test_output_dir_from_preferences() + + +.. py:function:: test_invalid_output_dir_from_preferences() + + +.. py:function:: test_directories() + + +.. py:function:: test_default_project_created() + + +.. py:function:: test_repeatedly_set_name_same_value() + + +.. py:function:: test_funny_project_names() + + +.. py:function:: test_report() + + +.. py:function:: test_request_directory() + + +.. py:function:: test_delete_current_project_with_name() + + +.. py:function:: test_delete_project_remove_directory() + + +.. py:function:: test_delete_project_keep_directory() + + +.. py:function:: test_delete_project() + + +.. py:function:: test_delete_last_project() + + +.. py:function:: test_delete_current_project_no_name() + + +.. py:function:: test_error_outdated_set_project() + + +.. py:function:: test_set_project_creates_new_project() + + +.. py:function:: test_set_project() + + +.. py:function:: test_set_project_default_writable() + + +.. py:function:: test_set_project_writable_even_if_writable_false() + + +.. py:function:: test_set_readonly_project() + + +.. py:function:: test_set_readonly_project_first_time() + + +.. py:function:: test_set_current_reset_metadata() + + +.. py:function:: test_representation() + + +.. py:function:: test_contains() + + +.. py:function:: test_len() + + +.. py:function:: test_iterating_over_projects_no_error() + + +.. py:function:: test_create_lock_file() + + +.. py:function:: test_lockable_config_missing() + + +.. py:function:: test_copy_project() + + +.. py:function:: test_copy_project_switch_current() + + diff --git a/sphinx/technical/api/tests/sc_graph/index.rst b/sphinx/technical/api/tests/sc_graph/index.rst new file mode 100644 index 0000000..a72d2aa --- /dev/null +++ b/sphinx/technical/api/tests/sc_graph/index.rst @@ -0,0 +1,192 @@ +:py:mod:`tests.sc_graph` +======================== + +.. py:module:: tests.sc_graph + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.sc_graph.UnrollGraphTestCase + tests.sc_graph.MetadataTestCase + tests.sc_graph.SimplifyTestCase + + + + +.. py:class:: UnrollGraphTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_simple_chain() + + + .. py:method:: test_multiple_inputs() + + + .. py:method:: test_pruning() + + + .. py:method:: test_unroll_circular() + + + .. py:method:: test_max_links() + + + .. py:method:: test_diamond() + + + .. py:method:: test_circle_with_branches() + + + +.. py:class:: MetadataTestCase + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + .. py:class:: LCAMock + + + .. py:method:: extra_setup() + + + .. py:method:: test_setup_clean() + + + .. py:method:: test_without_row() + + + .. py:method:: test_with_functional_unit() + + + .. py:method:: test_with_row() + + + +.. py:class:: SimplifyTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_nodes_dont_change() + + + .. py:method:: test_linear() + + Test supply chain graph like this: + + o + | o + x => | + | o + o + + + + .. py:method:: test_y() + + Test supply chain graph like this: + + o o o o + \ / \ / + x => o + | + o + + + + .. py:method:: test_no_self_edge() + + Test that collapsed edges from a -> a are deleted. + + + .. py:method:: test_diamond() + + Test supply chain graph like this: + + o + / \ o + x x => | + \ / o + o + + + + .. py:method:: test_x() + + Test supply chain graph like this: + + o o + \ / o o + x => |\/| + / \ |/\| + o o o o + + + diff --git a/sphinx/technical/api/tests/search/index.rst b/sphinx/technical/api/tests/search/index.rst new file mode 100644 index 0000000..b20a7cd --- /dev/null +++ b/sphinx/technical/api/tests/search/index.rst @@ -0,0 +1,110 @@ +:py:mod:`tests.search` +====================== + +.. py:module:: tests.search + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.search.test_search_dataset_containing_stop_word + tests.search.test_add_dataset + tests.search.test_search_dataset + tests.search.test_search_geocollection_location + tests.search.test_update_dataset + tests.search.test_delete_dataset + tests.search.test_add_datasets + tests.search.test_add_database + tests.search.test_add_searchable_database + tests.search.test_modify_database + tests.search.test_delete_database + tests.search.test_reset_index + tests.search.test_basic_search + tests.search.test_product_term + tests.search.test_comment_term + tests.search.test_categories_term + tests.search.test_limit + tests.search.test_search_faceting + tests.search.test_copy_save_propogates_to_search_index + tests.search.test_case_sensitivity_convert_lowercase + tests.search.test_case_sensitivity_filter + tests.search.test_case_sensitivity_mask + tests.search.test_synonym_search + + + +.. py:function:: test_search_dataset_containing_stop_word() + + +.. py:function:: test_add_dataset() + + +.. py:function:: test_search_dataset() + + +.. py:function:: test_search_geocollection_location() + + +.. py:function:: test_update_dataset() + + +.. py:function:: test_delete_dataset() + + +.. py:function:: test_add_datasets() + + +.. py:function:: test_add_database() + + +.. py:function:: test_add_searchable_database() + + +.. py:function:: test_modify_database() + + +.. py:function:: test_delete_database() + + +.. py:function:: test_reset_index() + + +.. py:function:: test_basic_search() + + +.. py:function:: test_product_term() + + +.. py:function:: test_comment_term() + + +.. py:function:: test_categories_term() + + +.. py:function:: test_limit() + + +.. py:function:: test_search_faceting() + + +.. py:function:: test_copy_save_propogates_to_search_index() + + +.. py:function:: test_case_sensitivity_convert_lowercase() + + +.. py:function:: test_case_sensitivity_filter() + + +.. py:function:: test_case_sensitivity_mask() + + +.. py:function:: test_synonym_search() + + diff --git a/sphinx/technical/api/tests/serialization/index.rst b/sphinx/technical/api/tests/serialization/index.rst new file mode 100644 index 0000000..f7d4b01 --- /dev/null +++ b/sphinx/technical/api/tests/serialization/index.rst @@ -0,0 +1,64 @@ +:py:mod:`tests.serialization` +============================= + +.. py:module:: tests.serialization + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.serialization.JsonSantizierTestCase + + + + +.. py:class:: JsonSantizierTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_tuple() + + + .. py:method:: test_dict() + + + .. py:method:: test_nested() + + + diff --git a/sphinx/technical/api/tests/simapro/index.rst b/sphinx/technical/api/tests/simapro/index.rst new file mode 100644 index 0000000..0cd7b46 --- /dev/null +++ b/sphinx/technical/api/tests/simapro/index.rst @@ -0,0 +1,95 @@ +:py:mod:`tests.simapro` +======================= + +.. py:module:: tests.simapro + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.simapro.SimaProCSVImporterTest + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.simapro.test_sp_import_allocation + tests.simapro.test_sp_wrong_field_ordering + tests.simapro.test_sp_python_builtin_as_unit_name + tests.simapro.test_damage_category_import + tests.simapro.test_set_lognormal_loc_value_on_import + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.simapro.SP_FIXTURES_DIR + + +.. py:data:: SP_FIXTURES_DIR + + + + +.. py:function:: test_sp_import_allocation() + + +.. py:function:: test_sp_wrong_field_ordering() + + +.. py:function:: test_sp_python_builtin_as_unit_name() + + +.. py:function:: test_damage_category_import() + + +.. py:function:: test_set_lognormal_loc_value_on_import() + + +.. py:class:: SimaProCSVImporterTest(methodName='runTest') + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + diff --git a/sphinx/technical/api/tests/single_matrix/index.rst b/sphinx/technical/api/tests/single_matrix/index.rst new file mode 100644 index 0000000..e6c4e6a --- /dev/null +++ b/sphinx/technical/api/tests/single_matrix/index.rst @@ -0,0 +1,6 @@ +:py:mod:`tests.single_matrix` +============================= + +.. py:module:: tests.single_matrix + + diff --git a/sphinx/technical/api/tests/sqlite/index.rst b/sphinx/technical/api/tests/sqlite/index.rst new file mode 100644 index 0000000..194d02e --- /dev/null +++ b/sphinx/technical/api/tests/sqlite/index.rst @@ -0,0 +1,22 @@ +:py:mod:`tests.sqlite` +====================== + +.. py:module:: tests.sqlite + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.sqlite.test_switch_project_correctly_switches_database_objects + + + +.. py:function:: test_switch_project_correctly_switches_database_objects() + + diff --git a/sphinx/technical/api/tests/strategies/allocation/index.rst b/sphinx/technical/api/tests/strategies/allocation/index.rst new file mode 100644 index 0000000..a95e94d --- /dev/null +++ b/sphinx/technical/api/tests/strategies/allocation/index.rst @@ -0,0 +1,58 @@ +:py:mod:`tests.strategies.allocation` +===================================== + +.. py:module:: tests.strategies.allocation + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.strategies.allocation.Ecospold1AllocationTestCase + + + + +.. py:class:: Ecospold1AllocationTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_allocation() + + + diff --git a/sphinx/technical/api/tests/strategies/biosphere/index.rst b/sphinx/technical/api/tests/strategies/biosphere/index.rst new file mode 100644 index 0000000..6149f57 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/biosphere/index.rst @@ -0,0 +1,241 @@ +:py:mod:`tests.strategies.biosphere` +==================================== + +.. py:module:: tests.strategies.biosphere + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.strategies.biosphere.BiosphereNameNormalizationTestCase + tests.strategies.biosphere.BiosphereCategoryNormalizationTestCase + tests.strategies.biosphere.UnspecifiedCategoryTestCase + tests.strategies.biosphere.BiosphereLinkingTestCase + + + + +.. py:class:: BiosphereNameNormalizationTestCase(methodName='runTest') + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: extra_setup() + + + .. py:method:: test_normalize_ds_name() + + + .. py:method:: test_normalize_ds_name_no_unit() + + + .. py:method:: test_normalize_ds_name_not_emission() + + + .. py:method:: test_normalize_ds_name_no_category() + + + .. py:method:: test_normalize_exc_name() + + + .. py:method:: test_normalize_exc_name_already_linked() + + + .. py:method:: test_normalize_exc_name_not_biosphere() + + + .. py:method:: test_normalize_exc_name_no_category() + + + +.. py:class:: BiosphereCategoryNormalizationTestCase(methodName='runTest') + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: extra_setup() + + + .. py:method:: test_no_categories() + + + .. py:method:: test_ds_wrong_type() + + + .. py:method:: test_ds() + + + .. py:method:: test_exc_no_categories() + + + .. py:method:: test_exc() + + + +.. py:class:: UnspecifiedCategoryTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_ds_no_categories() + + + .. py:method:: test_ds_multilevel() + + + .. py:method:: test_ds_final_subcategory_ok() + + + .. py:method:: test_ds() + + + .. py:method:: test_exc_no_categories() + + + .. py:method:: test_exc_multilevel() + + + .. py:method:: test_exc_final_subcategory_ok() + + + .. py:method:: test_exc() + + + +.. py:class:: BiosphereLinkingTestCase(methodName='runTest') + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: create_biosphere() + + + .. py:method:: test_strip_biosphere_exc_location() + + + diff --git a/sphinx/technical/api/tests/strategies/ecospold2/index.rst b/sphinx/technical/api/tests/strategies/ecospold2/index.rst new file mode 100644 index 0000000..75c6306 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/ecospold2/index.rst @@ -0,0 +1,42 @@ +:py:mod:`tests.strategies.ecospold2` +==================================== + +.. py:module:: tests.strategies.ecospold2 + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.ecospold2.test_fix_unreasonably_high_lognormal_uncertainties + tests.strategies.ecospold2.test_set_lognormal_loc_value + tests.strategies.ecospold2.test_remove_uncertainty_from_negative_loss_exchanges + tests.strategies.ecospold2.test_drop_temporary_outdated_biosphere_flows + tests.strategies.ecospold2.test_add_cpc_classification_from_single_reference_product + tests.strategies.ecospold2.test_delete_none_synonyms + + + +.. py:function:: test_fix_unreasonably_high_lognormal_uncertainties() + + +.. py:function:: test_set_lognormal_loc_value() + + +.. py:function:: test_remove_uncertainty_from_negative_loss_exchanges() + + +.. py:function:: test_drop_temporary_outdated_biosphere_flows() + + +.. py:function:: test_add_cpc_classification_from_single_reference_product() + + +.. py:function:: test_delete_none_synonyms() + + diff --git a/sphinx/technical/api/tests/strategies/generic/index.rst b/sphinx/technical/api/tests/strategies/generic/index.rst new file mode 100644 index 0000000..116a2e6 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/generic/index.rst @@ -0,0 +1,102 @@ +:py:mod:`tests.strategies.generic` +================================== + +.. py:module:: tests.strategies.generic + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.generic.test_tupleize_exchanges + tests.strategies.generic.test_tupleize_datasets + tests.strategies.generic.test_assign_only_product_as_production + tests.strategies.generic.test_assign_only_product_no_name + tests.strategies.generic.test_assign_only_product_leave_fields + tests.strategies.generic.test_assign_only_product_already_reference_product + tests.strategies.generic.test_assign_only_product_no_products + tests.strategies.generic.test_assign_only_product_multiple_products + tests.strategies.generic.test_set_code_by_activity_hash + tests.strategies.generic.test_set_code_by_activity_hash_overwrite + tests.strategies.generic.test_link_technosphere_wrong_type + tests.strategies.generic.test_link_technosphere_fields + tests.strategies.generic.test_link_technosphere_external + tests.strategies.generic.test_link_technosphere_external_untyped + tests.strategies.generic.test_convert_uncertainty_types_to_integers + tests.strategies.generic.test_drop_falsey_uncertainty_fields_but_keep_zeros + tests.strategies.generic.test_split_exchanges_normal + tests.strategies.generic.test_split_exchanges_default_allocation + tests.strategies.generic.test_split_exchanges_length_mismatch + tests.strategies.generic.test_split_exchanges_multiple + tests.strategies.generic.test_split_exchanges_no_changes + + + +.. py:function:: test_tupleize_exchanges() + + +.. py:function:: test_tupleize_datasets() + + +.. py:function:: test_assign_only_product_as_production() + + +.. py:function:: test_assign_only_product_no_name() + + +.. py:function:: test_assign_only_product_leave_fields() + + +.. py:function:: test_assign_only_product_already_reference_product() + + +.. py:function:: test_assign_only_product_no_products() + + +.. py:function:: test_assign_only_product_multiple_products() + + +.. py:function:: test_set_code_by_activity_hash() + + +.. py:function:: test_set_code_by_activity_hash_overwrite() + + +.. py:function:: test_link_technosphere_wrong_type() + + +.. py:function:: test_link_technosphere_fields() + + +.. py:function:: test_link_technosphere_external() + + +.. py:function:: test_link_technosphere_external_untyped() + + +.. py:function:: test_convert_uncertainty_types_to_integers() + + +.. py:function:: test_drop_falsey_uncertainty_fields_but_keep_zeros() + + +.. py:function:: test_split_exchanges_normal() + + +.. py:function:: test_split_exchanges_default_allocation() + + +.. py:function:: test_split_exchanges_length_mismatch() + + +.. py:function:: test_split_exchanges_multiple() + + +.. py:function:: test_split_exchanges_no_changes() + + diff --git a/sphinx/technical/api/tests/strategies/index.rst b/sphinx/technical/api/tests/strategies/index.rst new file mode 100644 index 0000000..0058bee --- /dev/null +++ b/sphinx/technical/api/tests/strategies/index.rst @@ -0,0 +1,26 @@ +:py:mod:`tests.strategies` +========================== + +.. py:module:: tests.strategies + + +Submodules +---------- +.. toctree:: + :titlesonly: + :maxdepth: 1 + + allocation/index.rst + biosphere/index.rst + ecospold2/index.rst + generic/index.rst + json_ld/index.rst + lcia/index.rst + link_iterable/index.rst + locations/index.rst + simapro/index.rst + simapro_flip_sign_on_waste/index.rst + simapro_name_splitting/index.rst + simapro_normalization/index.rst + + diff --git a/sphinx/technical/api/tests/strategies/json_ld/index.rst b/sphinx/technical/api/tests/strategies/json_ld/index.rst new file mode 100644 index 0000000..e4a56d7 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/json_ld/index.rst @@ -0,0 +1,87 @@ +:py:mod:`tests.strategies.json_ld` +================================== + +.. py:module:: tests.strategies.json_ld + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.json_ld.test_extraction + tests.strategies.json_ld.test_exchange_locations + tests.strategies.json_ld.test_exchange_units + tests.strategies.json_ld.test_activities_list + tests.strategies.json_ld.test_conversion_to_ref_unit + tests.strategies.json_ld.test_allocation_default + tests.strategies.json_ld.get_exchange_dict + tests.strategies.json_ld.test_allocation_physical + tests.strategies.json_ld.test_allocation_causal + tests.strategies.json_ld.test_metadata_fields + tests.strategies.json_ld.test_basic_exchange_type_labelling + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.json_ld.FIXTURES + tests.strategies.json_ld.CATTLE + tests.strategies.json_ld.FPL + + +.. py:data:: FIXTURES + + + + +.. py:data:: CATTLE + + + + +.. py:data:: FPL + + + + +.. py:function:: test_extraction() + + +.. py:function:: test_exchange_locations() + + +.. py:function:: test_exchange_units() + + +.. py:function:: test_activities_list() + + +.. py:function:: test_conversion_to_ref_unit() + + +.. py:function:: test_allocation_default() + + +.. py:function:: get_exchange_dict(ds, exclude=None) + + +.. py:function:: test_allocation_physical() + + +.. py:function:: test_allocation_causal() + + +.. py:function:: test_metadata_fields() + + +.. py:function:: test_basic_exchange_type_labelling() + + diff --git a/sphinx/technical/api/tests/strategies/lcia/index.rst b/sphinx/technical/api/tests/strategies/lcia/index.rst new file mode 100644 index 0000000..64033c0 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/lcia/index.rst @@ -0,0 +1,146 @@ +:py:mod:`tests.strategies.lcia` +=============================== + +.. py:module:: tests.strategies.lcia + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.strategies.lcia.LCIATestCase + tests.strategies.lcia.LCIATestCase2 + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.lcia.test_rationalize_method_names_no_remove_lt + tests.strategies.lcia.test_rationalize_method_names_remove_lt + tests.strategies.lcia.test_rationalize_method_names_remove_lt_2 + tests.strategies.lcia.test_rationalize_method_names_total_total + tests.strategies.lcia.test_rationalize_method_names_middle_total + tests.strategies.lcia.test_rationalize_method_names_unneeded_total + + + +.. py:class:: LCIATestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_add_activity_hash_code() + + + .. py:method:: test_drop_unlinked_cfs() + + + .. py:method:: test_set_biosphere_type() + + + +.. py:class:: LCIATestCase2(methodName='runTest') + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_match_subcategories() + + + .. py:method:: test_match_subcategories_remove() + + + .. py:method:: test_match_subcategories_not_remove() + + + .. py:method:: test_match_subcategories_makes_copies() + + Should copy data instead of creating references, so that there are different amounts for different methods. + + + +.. py:function:: test_rationalize_method_names_no_remove_lt() + + +.. py:function:: test_rationalize_method_names_remove_lt() + + +.. py:function:: test_rationalize_method_names_remove_lt_2() + + +.. py:function:: test_rationalize_method_names_total_total() + + +.. py:function:: test_rationalize_method_names_middle_total() + + +.. py:function:: test_rationalize_method_names_unneeded_total() + + diff --git a/sphinx/technical/api/tests/strategies/link_iterable/index.rst b/sphinx/technical/api/tests/strategies/link_iterable/index.rst new file mode 100644 index 0000000..fb9f1c7 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/link_iterable/index.rst @@ -0,0 +1,88 @@ +:py:mod:`tests.strategies.link_iterable` +======================================== + +.. py:module:: tests.strategies.link_iterable + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.strategies.link_iterable.LinkIterableTestCase + + + + +.. py:class:: LinkIterableTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_all_datasets_in_target_have_database_field() + + + .. py:method:: test_all_datasets_in_target_have_code_field() + + + .. py:method:: test_nonunique_target_but_not_linked_no_error() + + + .. py:method:: test_nonunique_target_raises_error() + + + .. py:method:: test_generic_linking_no_kind_no_relink() + + + .. py:method:: test_internal_linking() + + + .. py:method:: test_kind_filter() + + + .. py:method:: test_kind_filter_and_relink() + + + .. py:method:: test_relink() + + + .. py:method:: test_linking_with_fields() + + + .. py:method:: test_no_relink_skips_linking() + + + diff --git a/sphinx/technical/api/tests/strategies/locations/index.rst b/sphinx/technical/api/tests/strategies/locations/index.rst new file mode 100644 index 0000000..d44258f --- /dev/null +++ b/sphinx/technical/api/tests/strategies/locations/index.rst @@ -0,0 +1,26 @@ +:py:mod:`tests.strategies.locations` +==================================== + +.. py:module:: tests.strategies.locations + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.locations.test_locations_update + tests.strategies.locations.test_existing_db_locations_update + + + +.. py:function:: test_locations_update() + + +.. py:function:: test_existing_db_locations_update() + + diff --git a/sphinx/technical/api/tests/strategies/simapro/index.rst b/sphinx/technical/api/tests/strategies/simapro/index.rst new file mode 100644 index 0000000..8e833c5 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/simapro/index.rst @@ -0,0 +1,34 @@ +:py:mod:`tests.strategies.simapro` +================================== + +.. py:module:: tests.strategies.simapro + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.simapro.test_set_lognormal_loc_value_uncertainty_safe + tests.strategies.simapro.test_localized_water_flows + tests.strategies.simapro.test_change_electricity_units + tests.strategies.simapro.test_fix_zero_allocation_products + + + +.. py:function:: test_set_lognormal_loc_value_uncertainty_safe() + + +.. py:function:: test_localized_water_flows() + + +.. py:function:: test_change_electricity_units() + + +.. py:function:: test_fix_zero_allocation_products() + + diff --git a/sphinx/technical/api/tests/strategies/simapro_flip_sign_on_waste/index.rst b/sphinx/technical/api/tests/strategies/simapro_flip_sign_on_waste/index.rst new file mode 100644 index 0000000..3538622 --- /dev/null +++ b/sphinx/technical/api/tests/strategies/simapro_flip_sign_on_waste/index.rst @@ -0,0 +1,22 @@ +:py:mod:`tests.strategies.simapro_flip_sign_on_waste` +===================================================== + +.. py:module:: tests.strategies.simapro_flip_sign_on_waste + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.strategies.simapro_flip_sign_on_waste.test_waste_sign_changed + + + +.. py:function:: test_waste_sign_changed() + + diff --git a/sphinx/technical/api/tests/strategies/simapro_name_splitting/index.rst b/sphinx/technical/api/tests/strategies/simapro_name_splitting/index.rst new file mode 100644 index 0000000..f929d7c --- /dev/null +++ b/sphinx/technical/api/tests/strategies/simapro_name_splitting/index.rst @@ -0,0 +1,67 @@ +:py:mod:`tests.strategies.simapro_name_splitting` +================================================= + +.. py:module:: tests.strategies.simapro_name_splitting + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.strategies.simapro_name_splitting.NameSplittingTestCase + + + + +.. py:class:: NameSplittingTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_detoxify_re() + + + .. py:method:: test_detoxify_re2() + + + .. py:method:: test_splitting_datasets() + + + .. py:method:: test_splitting_exchanges() + + + diff --git a/sphinx/technical/api/tests/strategies/simapro_normalization/index.rst b/sphinx/technical/api/tests/strategies/simapro_normalization/index.rst new file mode 100644 index 0000000..f96524f --- /dev/null +++ b/sphinx/technical/api/tests/strategies/simapro_normalization/index.rst @@ -0,0 +1,58 @@ +:py:mod:`tests.strategies.simapro_normalization` +================================================ + +.. py:module:: tests.strategies.simapro_normalization + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.strategies.simapro_normalization.SPNormalizationTestCase + + + + +.. py:class:: SPNormalizationTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_sp_biosphere_category_normalization() + + + diff --git a/sphinx/technical/api/tests/svdm/index.rst b/sphinx/technical/api/tests/svdm/index.rst new file mode 100644 index 0000000..7caf70f --- /dev/null +++ b/sphinx/technical/api/tests/svdm/index.rst @@ -0,0 +1,63 @@ +:py:mod:`tests.svdm` +==================== + +.. py:module:: tests.svdm + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.svdm.test_svdm_missing_dimension_kwarg + tests.svdm.test_svdm_no_data + tests.svdm.test_svdm_multiple_dataresources + tests.svdm.test_svdm_multiple_values + tests.svdm.test_svdm_basic + tests.svdm.test_svdm_iteration + tests.svdm.test_svdm_distributions + tests.svdm.test_svdm_arrays + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.svdm.fixture_dir + + +.. py:data:: fixture_dir + + + + +.. py:function:: test_svdm_missing_dimension_kwarg() + + +.. py:function:: test_svdm_no_data() + + +.. py:function:: test_svdm_multiple_dataresources() + + +.. py:function:: test_svdm_multiple_values() + + +.. py:function:: test_svdm_basic() + + +.. py:function:: test_svdm_iteration() + + +.. py:function:: test_svdm_distributions() + + +.. py:function:: test_svdm_arrays() + + diff --git a/sphinx/technical/api/tests/tagged/index.rst b/sphinx/technical/api/tests/tagged/index.rst new file mode 100644 index 0000000..76f8da6 --- /dev/null +++ b/sphinx/technical/api/tests/tagged/index.rst @@ -0,0 +1,62 @@ +:py:mod:`tests.tagged` +====================== + +.. py:module:: tests.tagged + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.tagged.tagged_fixture + tests.tagged.test_fixture_no_errors + tests.tagged.test_traverse_tagged_databases_scores + tests.tagged.test_traverse_tagged_databases_graph + tests.tagged.test_traverse_tagged_databases_graph_nonunitary_production + tests.tagged.test_multi_traverse_tagged_databases_scores + tests.tagged.test_multi_traverse_tagged_databases_graph + tests.tagged.test_traverse_tagged_databases_graph_secondary_tag + tests.tagged.test_multi_traverse_tagged_databases_graph_secondary_tag + tests.tagged.test_get_cum_impact + tests.tagged.test_get_multi_cum_impact + + + +.. py:function:: tagged_fixture() + + +.. py:function:: test_fixture_no_errors(tagged_fixture) + + +.. py:function:: test_traverse_tagged_databases_scores(tagged_fixture) + + +.. py:function:: test_traverse_tagged_databases_graph(tagged_fixture) + + +.. py:function:: test_traverse_tagged_databases_graph_nonunitary_production() + + +.. py:function:: test_multi_traverse_tagged_databases_scores(tagged_fixture) + + +.. py:function:: test_multi_traverse_tagged_databases_graph(tagged_fixture) + + +.. py:function:: test_traverse_tagged_databases_graph_secondary_tag(tagged_fixture) + + +.. py:function:: test_multi_traverse_tagged_databases_graph_secondary_tag(tagged_fixture) + + +.. py:function:: test_get_cum_impact(tagged_fixture) + + +.. py:function:: test_get_multi_cum_impact(tagged_fixture) + + diff --git a/sphinx/technical/api/tests/to_dataframe/index.rst b/sphinx/technical/api/tests/to_dataframe/index.rst new file mode 100644 index 0000000..98e057f --- /dev/null +++ b/sphinx/technical/api/tests/to_dataframe/index.rst @@ -0,0 +1,79 @@ +:py:mod:`tests.to_dataframe` +============================ + +.. py:module:: tests.to_dataframe + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.to_dataframe.bw2test + tests.to_dataframe.frames + tests.to_dataframe.basic_example + tests.to_dataframe.test_to_dataframe_basic + tests.to_dataframe.test_to_dataframe_inventory_matrix + tests.to_dataframe.test_to_dataframe_characterization_matrix + tests.to_dataframe.test_to_dataframe_technosphere_matrix + tests.to_dataframe.test_to_dataframe_biosphere_matrix + tests.to_dataframe.test_to_dataframe_number_cutoff + tests.to_dataframe.test_to_dataframe_fraction_cutoff + tests.to_dataframe.test_to_dataframe_custom_mappings + tests.to_dataframe.test_to_dataframe_annotated + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + tests.to_dataframe.fixture_dir + + +.. py:function:: bw2test(func) + + +.. py:data:: fixture_dir + + + + +.. py:function:: frames(one, two) + + +.. py:function:: basic_example() + + +.. py:function:: test_to_dataframe_basic(basic_example) + + +.. py:function:: test_to_dataframe_inventory_matrix(basic_example) + + +.. py:function:: test_to_dataframe_characterization_matrix(basic_example) + + +.. py:function:: test_to_dataframe_technosphere_matrix(basic_example) + + +.. py:function:: test_to_dataframe_biosphere_matrix(basic_example) + + +.. py:function:: test_to_dataframe_number_cutoff(basic_example) + + +.. py:function:: test_to_dataframe_fraction_cutoff(basic_example) + + +.. py:function:: test_to_dataframe_custom_mappings(basic_example) + + +.. py:function:: test_to_dataframe_annotated(basic_example) + + diff --git a/sphinx/technical/api/tests/updates/index.rst b/sphinx/technical/api/tests/updates/index.rst new file mode 100644 index 0000000..9118298 --- /dev/null +++ b/sphinx/technical/api/tests/updates/index.rst @@ -0,0 +1,33 @@ +:py:mod:`tests.updates` +======================= + +.. py:module:: tests.updates + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.updates.UpdatesTest + + + + +.. py:class:: UpdatesTest + + Bases: :py:obj:`bw2data.tests.BW2DataTest` + + .. py:method:: test_set_updates_clean_install() + + + .. py:method:: test_explain() + + + .. py:method:: test_do_updates() + + + diff --git a/sphinx/technical/api/tests/utils/index.rst b/sphinx/technical/api/tests/utils/index.rst new file mode 100644 index 0000000..d16bf8c --- /dev/null +++ b/sphinx/technical/api/tests/utils/index.rst @@ -0,0 +1,78 @@ +:py:mod:`tests.utils` +===================== + +.. py:module:: tests.utils + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.utils.UtilsTestCase + + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + tests.utils.test_standardize_method_to_len_3 + + + +.. py:class:: UtilsTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_load_json_data() + + + .. py:method:: test_activity_hash() + + + .. py:method:: test_format_for_logging() + + + .. py:method:: test_es2_activity_hash() + + + +.. py:function:: test_standardize_method_to_len_3() + + diff --git a/sphinx/technical/api/tests/validation/index.rst b/sphinx/technical/api/tests/validation/index.rst new file mode 100644 index 0000000..3bf5c99 --- /dev/null +++ b/sphinx/technical/api/tests/validation/index.rst @@ -0,0 +1,82 @@ +:py:mod:`tests.validation` +========================== + +.. py:module:: tests.validation + + +Module Contents +--------------- + +Classes +~~~~~~~ + +.. autoapisummary:: + + tests.validation.ValidationTestCase + + + + +.. py:class:: ValidationTestCase(methodName='runTest') + + Bases: :py:obj:`unittest.TestCase` + + A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + + When subclassing TestCase, you can set these attributes: + * failureException: determines which exception will be raised when + the instance's assertion methods fail; test methods raising this + exception will be deemed to have 'failed' rather than 'errored'. + * longMessage: determines whether long messages (including repr of + objects used in assert methods) will be printed on failure in *addition* + to any explicit message passed. + * maxDiff: sets the maximum length of a diff in failure messages + by assert methods using difflib. It is looked up as an instance + attribute so can be configured by individual tests if required. + + .. py:method:: test_valid_tuple() + + + .. py:method:: test_uncertainty_dict() + + + .. py:method:: test_maybe_uncertainty() + + + .. py:method:: test_exchange() + + + .. py:method:: test_db_validator() + + + .. py:method:: test_ia_validator() + + + .. py:method:: test_weighting_too_long() + + + .. py:method:: test_weighting_too_short() + + + .. py:method:: test_weighting() + + + diff --git a/sphinx/technical/api/with_products/index.rst b/sphinx/technical/api/with_products/index.rst new file mode 100644 index 0000000..225e788 --- /dev/null +++ b/sphinx/technical/api/with_products/index.rst @@ -0,0 +1,39 @@ +:py:mod:`with_products` +======================= + +.. py:module:: with_products + + +Module Contents +--------------- + + +Functions +~~~~~~~~~ + +.. autoapisummary:: + + with_products.test_excel_products_import + with_products.test_excel_products_lca + + + +Attributes +~~~~~~~~~~ + +.. autoapisummary:: + + with_products.EXCEL_FIXTURES_DIR + + +.. py:data:: EXCEL_FIXTURES_DIR + + + + +.. py:function:: test_excel_products_import() + + +.. py:function:: test_excel_products_lca() + +