technologydata 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,519 @@
1
+ # SPDX-FileCopyrightText: technologydata contributors
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """Submodule containing pint.UnitRegistry subclasses and utility functions for handling units, conversions, and currency units."""
6
+
7
+ import json
8
+ import logging
9
+ import re
10
+ import typing
11
+ from functools import lru_cache
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ import pandas as pd
16
+ import pint
17
+ import pydeflate
18
+ from frozendict import frozendict
19
+ from hdx.location.country import Country
20
+ from platformdirs import user_cache_dir
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ pydeflate.set_pydeflate_path("./pydeflate_data")
25
+
26
+ CURRENCY_UNIT_PATTERN = re.compile(r"\b(?P<cu_iso3>[A-Z]{3})_(?P<year>\d{4})\b")
27
+
28
+ # Set up cache directory and file for currency codes
29
+ CACHE_DIR = Path(user_cache_dir("technologydata")) # TODO move to commons?
30
+ CACHE_DIR.mkdir(parents=True, exist_ok=True) # TODO move to commons?
31
+ CURRENCY_CODES_CACHE = CACHE_DIR / "iso3_to_currency_codes.json"
32
+
33
+
34
+ SPECIAL_CASES_CURRENCY_CODE_TO_ISO3 = frozendict(
35
+ {
36
+ # Multi-country currencies (return codes directly)
37
+ "EUR": "EUR", # Eurozone
38
+ # Single primary countries
39
+ "AUD": "AUS", # Australia
40
+ "CHF": "CHE", # Switzerland
41
+ "DKK": "DNK", # Denmark
42
+ "GBP": "GBR", # United Kingdom (largest GBP economy)
43
+ "ILS": "ISR", # Israel
44
+ "MAD": "MAR", # Morocco
45
+ "NOK": "NOR", # Norway
46
+ "NZD": "NZL", # New Zealand
47
+ "USD": "USA", # Dollar-ized economies
48
+ # Special regional cases with proxy selection criteria
49
+ "ANG": "CUW", # Curaçao (GDP-weighted proxy)
50
+ "XAF": "CAF", # Central African Republic (lowest inflation differential)
51
+ "XCD": "GRD", # Grenada (lowest inflation differential)
52
+ "XOF": "NER", # Niger (lowest inflation differential 2015-2023)
53
+ "XPF": "PYF", # French Polynesia (data availability)
54
+ }
55
+ )
56
+
57
+
58
+ def get_iso3_to_currency_codes(
59
+ refresh: bool = False, ignore_cache: bool = False
60
+ ) -> dict[str, str]:
61
+ """
62
+ Get all 3-letter currency codes from official UN data.
63
+
64
+ Uses a persistent local cache to avoid unnecessary network requests.
65
+
66
+ Parameters
67
+ ----------
68
+ refresh : bool, optional
69
+ If True, the cache will be updated from the internet, by default False.
70
+ ignore_cache : bool, optional
71
+ If True, the cache will not be used and the data will always be fetched from the live feed.
72
+
73
+ Returns
74
+ -------
75
+ dict[str, str]
76
+ A dictionary mapping ISO3 country codes to their corresponding 3-letter currency codes.
77
+
78
+ """
79
+ currencies: dict[str, str] = {}
80
+
81
+ if refresh:
82
+ logger.debug("Deleting existing currency codes cache to refresh it.")
83
+ CURRENCY_CODES_CACHE.unlink(missing_ok=True)
84
+
85
+ if ignore_cache:
86
+ logger.debug("Ignoring cache and fetching live currency codes.")
87
+ currencies = Country.countriesdata()["currencies"]
88
+ elif not CURRENCY_CODES_CACHE.exists():
89
+ logger.debug(
90
+ "Cache does not exist. Fetching live currency codes and creating cache."
91
+ )
92
+ currencies = Country.countriesdata()["currencies"]
93
+ with open(CURRENCY_CODES_CACHE, "w") as f:
94
+ json.dump(currencies, f)
95
+ else:
96
+ logger.debug("Reading currency codes from cache.")
97
+ with open(CURRENCY_CODES_CACHE) as f:
98
+ currencies = json.load(f)
99
+
100
+ return currencies
101
+
102
+
103
+ def extract_currency_units(units: str | pint.Unit) -> list[str]:
104
+ """
105
+ Extract currency-like strings from a string or pint.Unit.
106
+
107
+ Parameters
108
+ ----------
109
+ units : str or pint.Unit
110
+ The units string or pint.Unit from which to extract currency-like strings.
111
+
112
+ Returns
113
+ -------
114
+ list[str]
115
+ A list of currency-like strings found in the input, formatted as "{3-letter currency code}_{year as YYYY}".
116
+ If no matches are found, an empty list is returned.
117
+
118
+ Examples
119
+ --------
120
+ >>> extract_currency_units("USD_2020/kW")
121
+ ["USD_2020"]
122
+
123
+ >>> extract_currency_units("EUR_2015/USD_2020")
124
+ ["EUR_2015", "USD_2020"]
125
+
126
+ """
127
+ # Ensure that the input is a string
128
+ units = str(units)
129
+
130
+ # Get the 3-letter currency codes for all officially recognized currencies
131
+ logger.debug("Retrieving all 3-letter currency codes from the `hdx-country`.")
132
+ all_currency_codes = set(get_iso3_to_currency_codes().values())
133
+
134
+ # Check if the units contain a currency-like string, defined as "{3-letter currency code}_{year as YYYY}"
135
+ matches = CURRENCY_UNIT_PATTERN.findall(units)
136
+ if len(matches) == 0:
137
+ logger.debug("No currency-like string found in the units.")
138
+ return []
139
+
140
+ # Extract the currency codes from the matches using the regex groups
141
+ logger.debug(f"Found currency-like strings in the units: {matches}")
142
+ currency_codes = {code for code, year in matches}
143
+
144
+ # Ensure that all currency codes are legitimate 3-letter currency codes
145
+ invalid_codes = currency_codes - all_currency_codes
146
+ if invalid_codes:
147
+ invalid_currencies = [
148
+ f"{code}_{year}" for code, year in matches if code in invalid_codes
149
+ ]
150
+ raise ValueError(
151
+ f"The following unit(s) appear to be currency units, but have invalid 3-letter currency codes: {', '.join(invalid_currencies)}. "
152
+ )
153
+
154
+ # Reconstruct currency units from the matches
155
+ matches = [f"{code}_{year}" for code, year in matches]
156
+
157
+ return matches
158
+
159
+
160
+ @lru_cache
161
+ def get_conversion_rate(
162
+ from_iso3: str,
163
+ to_iso3: str,
164
+ country: str,
165
+ from_year: int,
166
+ to_year: int,
167
+ source: str = "worldbank",
168
+ ) -> float:
169
+ """
170
+ Get the conversion rate from one currency (year, ISO3) to another currency (year, ISO3) from pydeflate.
171
+
172
+ Parameters
173
+ ----------
174
+ from_iso3 : str
175
+ The ISO3 code of the country of the currency to convert from (e.g., 'USA' if the source currency is USD).
176
+ to_iso3 : str
177
+ The ISO3 code of the country of the currency to convert to (e.g., 'DEU' if the target currency is EUR).
178
+ country : str
179
+ The ISO3 code of the country to adjust for inflation.
180
+ from_year : int
181
+ The julian year (YYYY) of the source currency.
182
+ to_year : int
183
+ The julian year (YYYY) of the target currency.
184
+ source : str
185
+ The source of the inflation data ('worldbank'/'wb' or 'international_monetary_fund'/'imf').
186
+
187
+ """
188
+ # Choose the deflation function based on the source
189
+ deflation_function = {
190
+ "worldbank": pydeflate.wb_gdp_deflate,
191
+ "wb": pydeflate.wb_gdp_deflate,
192
+ "international_monetary_fund": pydeflate.imf_gdp_deflate,
193
+ "imf": pydeflate.imf_gdp_deflate,
194
+ }[source]
195
+
196
+ # Ensure that `country` is a valid ISO3 code; to_iso3 and from_iso3 should already have been parsed before the function was called
197
+ if country not in get_iso3_to_currency_codes().keys():
198
+ raise ValueError(f"Unknown ISO3 code for `country`: {country}.")
199
+
200
+ # pydeflate only operates on pandas.DataFrame
201
+ data = pd.DataFrame(
202
+ {
203
+ "iso3": [country],
204
+ "from_year": [from_year],
205
+ "value": [1],
206
+ }
207
+ )
208
+
209
+ # Deflate values include currency conversion
210
+ conversion_rates = deflation_function(
211
+ data,
212
+ source_currency=from_iso3,
213
+ target_currency=to_iso3,
214
+ id_column="iso3",
215
+ year_column="from_year",
216
+ base_year=to_year,
217
+ value_column="value",
218
+ target_value_column="new_value",
219
+ )
220
+
221
+ if conversion_rates.isna().any().any():
222
+ raise ValueError(
223
+ f"Conversion rate from {from_iso3} ({from_year}) to {to_iso3} ({to_year}) with inflation rate for {country} not found. "
224
+ )
225
+
226
+ return float(conversion_rates.loc[0, "new_value"])
227
+
228
+
229
+ @lru_cache
230
+ def get_iso3_from_currency_code(
231
+ currency_code: str,
232
+ special_cases: frozendict[str, str] = SPECIAL_CASES_CURRENCY_CODE_TO_ISO3,
233
+ ) -> str:
234
+ """
235
+ Get the ISO3 country code from a 3-letter currency code using official UN data and opinionated assumptions.
236
+
237
+ Parameters
238
+ ----------
239
+ currency_code : str
240
+ The 3-letter currency code (e.g., 'USD', 'EUR').
241
+ special_cases : dict[str, str], optional
242
+ A dictionary mapping specific currency codes to their ISO3 country codes for special cases.
243
+ Defaults to `technologydata.utils.units.SPECIAL_CASES_CURRENCY_CODE_TO_ISO3`.
244
+
245
+ Returns
246
+ -------
247
+ str
248
+ The ISO 3166 alpha 3 country code of the `currency_code`.
249
+
250
+ Raises
251
+ ------
252
+ ValueError
253
+ If the currency code is not found in the official list of currencies.
254
+
255
+ """
256
+ # Build reverse mapping: currency code -> list of ISO3 codes
257
+ iso3_to_currency_codes = pd.DataFrame.from_dict(
258
+ get_iso3_to_currency_codes(), orient="index", columns=["currency"]
259
+ )
260
+ iso3_to_currency_codes = iso3_to_currency_codes.reset_index(drop=False).rename(
261
+ columns={"index": "iso3"}
262
+ )
263
+ currency_codes_to_iso3 = iso3_to_currency_codes.groupby("currency", as_index=False)[
264
+ "iso3"
265
+ ].agg(list)
266
+
267
+ # Handle special cases for specific currency codes
268
+
269
+ # Remove all currencies that are in the special cases from the mapping
270
+ currency_codes_to_iso3 = currency_codes_to_iso3.loc[
271
+ ~currency_codes_to_iso3["currency"].isin(special_cases.keys())
272
+ ]
273
+
274
+ # Mapping should now only contain unique currency codes to ISO3 codes mappings, safe to explode
275
+ currency_codes_to_iso3 = currency_codes_to_iso3.explode("iso3")
276
+
277
+ # Add the special cases to the mapping
278
+ currency_codes_to_iso3 = pd.concat(
279
+ [
280
+ currency_codes_to_iso3,
281
+ pd.DataFrame(
282
+ list(special_cases.items()),
283
+ columns=["currency", "iso3"],
284
+ ),
285
+ ],
286
+ ignore_index=False,
287
+ )
288
+
289
+ # Special cases should handle all non-unique currency codes, check to make sure
290
+ # and return the ones that are not handled in special cases
291
+
292
+ if (
293
+ duplicated_iso3 := currency_codes_to_iso3.explode("iso3")[
294
+ "currency"
295
+ ].duplicated()
296
+ ).any():
297
+ raise ValueError(
298
+ "Some currency codes are used by multiple ISO3 codes but are not handled in `special_cases` "
299
+ "and need to be added to the mapping: "
300
+ f"{currency_codes_to_iso3[duplicated_iso3]}"
301
+ )
302
+
303
+ currency_codes_to_iso3 = currency_codes_to_iso3.set_index("currency")[
304
+ "iso3"
305
+ ].to_dict()
306
+
307
+ try:
308
+ return str(currency_codes_to_iso3[currency_code])
309
+ except KeyError as e:
310
+ raise ValueError(
311
+ f"Currency code '{currency_code}' not found in the list of currencies. "
312
+ "Please ensure it is a valid 3-letter currency code."
313
+ ) from e
314
+
315
+
316
+ def patch_pint_registry_error_handling(registry: pint.registry.UnitRegistry) -> None:
317
+ """
318
+ Patch a Pint registry to use CustomUndefinedUnitError.
319
+
320
+ Parameters
321
+ ----------
322
+ registry : pint.registry.UnitRegistry
323
+ The Pint unit registry to patch.
324
+
325
+ """
326
+ # Store the original method
327
+ original_get_name = registry.get_name
328
+
329
+ def patched_get_name(
330
+ self: pint.registry.UnitRegistry, name: str, *args: Any, **kwargs: Any
331
+ ) -> Any:
332
+ try:
333
+ return original_get_name(name, *args, **kwargs)
334
+ except pint.errors.UndefinedUnitError as e:
335
+ # Raise the custom error with the same arguments
336
+ raise CustomUndefinedUnitError(e.args[0]) from e
337
+
338
+ # Replace the method
339
+ registry.get_name = patched_get_name.__get__(registry)
340
+
341
+
342
+ class CustomUndefinedUnitError(pint.errors.UndefinedUnitError): # type: ignore
343
+ """
344
+ Custom message for undefined unit errors.
345
+
346
+ This custom error is raised when a unit is not defined in the unit registry.
347
+ It provides more specific error messages, especially for currency units
348
+ that are missing the currency year.
349
+
350
+ Parameters
351
+ ----------
352
+ unit_names : list of str
353
+ The names of the units that are not defined in the unit registry.
354
+
355
+ Attributes
356
+ ----------
357
+ unit_names : list of str
358
+ The names of the units that are not defined in the unit registry.
359
+
360
+ Notes
361
+ -----
362
+ This error is a subclass of `pint.errors.UndefinedUnitError` and is designed
363
+ to provide more specific error messages for currency units that are missing
364
+ the currency year.
365
+
366
+ """
367
+
368
+ def __init__(self, unit_names: str | typing.Iterable[str]) -> None:
369
+ """
370
+ Initialize a CustomUndefinedUnitError instance.
371
+
372
+ This constructor creates a new `CustomUndefinedUnitError` object,
373
+ inheriting all default behaviors from `pint.errors.UndefinedUnitError`.
374
+
375
+ Parameters
376
+ ----------
377
+ unit_names : str or iterable of str
378
+ The name or names of the undefined units that caused the error.
379
+
380
+ """
381
+ # Use all defaults definitions from a standard pint.errors.UndefinedUnitError
382
+ super().__init__(unit_names)
383
+
384
+ def __str__(self) -> str:
385
+ """
386
+ Generate a custom error message string.
387
+
388
+ This method generates a custom error message string based on the
389
+ unit names that are not defined in the unit registry. It provides
390
+ specific messages for currency units that are missing the currency year.
391
+
392
+ Returns
393
+ -------
394
+ str
395
+ The custom error message string.
396
+
397
+ """
398
+ # Retrieve unit names, defaulting to an empty list if not present
399
+ unit_names = getattr(self, "unit_names", [])
400
+
401
+ # Precompute valid currency codes for efficiency
402
+ all_currency_codes = set(get_iso3_to_currency_codes().values())
403
+
404
+ # Identify currency units without a year specification
405
+ currency_errors = [
406
+ code
407
+ for unit in unit_names
408
+ for code in re.findall(r"[A-Z]{3}", str(unit))
409
+ if code in all_currency_codes
410
+ and not CURRENCY_UNIT_PATTERN.search(str(unit))
411
+ ]
412
+
413
+ # Generate specific error message for currency units
414
+ if currency_errors:
415
+ missing_code = currency_errors[0]
416
+ return f"Currency unit '{missing_code}' is missing the 4-digit currency year (e.g. {missing_code}_2020)."
417
+
418
+ # Fallback to parent class error message if no specific currency error found
419
+ return super().__str__() # type: ignore
420
+
421
+
422
+ class SpecialUnitRegistry(pint.UnitRegistry): # type: ignore
423
+ """A special pint.UnitRegistry subclass that includes methods for handling currency units and conversion using pydeflate."""
424
+
425
+ def __init__(self, *args: tuple[Any, ...], **kwargs: dict[str, Any]) -> None:
426
+ """
427
+ Initialize a SpecialUnitRegistry instance.
428
+
429
+ This constructor creates a new `SpecialUnitRegistry` object,
430
+ inheriting all default behaviors from `pint.UnitRegistry`.
431
+ It also defines a reference currency unit (`USD_2020`) for
432
+ handling currency conversions and related operations.
433
+
434
+ Parameters
435
+ ----------
436
+ *args : tuple
437
+ Positional arguments passed to the base `pint.UnitRegistry` constructor.
438
+ **kwargs : dict
439
+ Keyword arguments passed to the base `pint.UnitRegistry` constructor.
440
+
441
+ """
442
+ # Use all defaults definitions from a standard pint.UnitRegistry
443
+ super().__init__(*args, **kwargs)
444
+
445
+ # Define the reference currency unit
446
+ # This is the base currency unit that all other currency units will be defined relative to
447
+ # We use USD_2020 as the base currency unit because it is a currency that we can relate all other currencies to
448
+ self.define("USD_2020 = [currency]")
449
+
450
+ def get_reference_currency(self) -> str:
451
+ """Get the reference currency from the unit registry."""
452
+ reference_currency = [
453
+ self._units[u].name
454
+ for u in self._units
455
+ if "[currency]" in self._units[u].reference
456
+ ]
457
+ if not reference_currency or len(reference_currency) != 1:
458
+ raise ValueError(
459
+ "The unit registry does not have a unique base currency defined as '[currency]'. Please define a base currency unit to proceed."
460
+ )
461
+
462
+ return str(reference_currency[0])
463
+
464
+ def ensure_currency_is_unit(self, units: str) -> None:
465
+ """
466
+ Ensure that all currency units in the given string are valid units in the registry.
467
+
468
+ Extracts all currency-like strings from the input and checks if they are defined
469
+ in the unit registry. If they are not defined, they are added as valid units
470
+ relative to the reference currency but without a conversion factor.
471
+
472
+ Parameters
473
+ ----------
474
+ units : str
475
+ The units string to check for currency units.
476
+
477
+ Examples
478
+ --------
479
+ >>> ureg.ensure_currency_is_unit("USD_2020/kW")
480
+ >>> ureg.ensure_currency_is_unit("EUR_2015/USD_2020")
481
+
482
+ """
483
+ logger.debug(f"Ensuring currency units of '{units}' are defined in `ureg`")
484
+ currency_units = extract_currency_units(units)
485
+ logger.debug(f"Found currency-like strings in the units: {currency_units}")
486
+
487
+ if not currency_units:
488
+ # Nothing to do
489
+ return
490
+
491
+ reference_currency = self.get_reference_currency()
492
+ logger.debug(f"Reference currency is '{reference_currency}'.")
493
+
494
+ # Check if the currency unit is already defined in the unit registry
495
+ # if not, define it relative to the base currency USD_2015
496
+ for currency_unit in currency_units:
497
+ if currency_unit in self._units:
498
+ logger.debug(
499
+ f"Currency unit '{currency_unit}' is already defined in the unit registry. Not redefining it."
500
+ )
501
+ continue
502
+
503
+ logger.debug(
504
+ f"Currency unit '{currency_unit}' not found in the unit registry. "
505
+ f"Defining it without a conversion factor relative to the base currency '{reference_currency}'."
506
+ )
507
+ self.define(f"{currency_unit} = nan {reference_currency}")
508
+
509
+
510
+ # Unit registries used throughout the package for different purposes
511
+ ureg = SpecialUnitRegistry() # For handling units, conversions, and currency units
512
+ creg = pint.UnitRegistry(
513
+ filename=Path(__file__).parent / "carriers.txt"
514
+ ) # For tracking carriers and ensuring compatibility between them
515
+ hvreg = pint.UnitRegistry(
516
+ filename=Path(__file__).parent / "heating_values.txt"
517
+ ) # For tracking heating values and ensuring compatibility between them
518
+
519
+ patch_pint_registry_error_handling(ureg)
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: technologydata
3
+ Version: 0.1.0
4
+ Summary: Package for providing common data assumptions for energy system modelling on techno-economics and macro-economics.
5
+ Author-email: Contributors to technologydata <johannes.hampp@openenergytransition.org>, Contributors to technologydata <fabrizio.finozzi.business@gmail.com>
6
+ Project-URL: Homepage, https://github.com/open-energy-transition/technology-data
7
+ Project-URL: Source, https://github.com/open-energy-transition/technology-data
8
+ Project-URL: BugTracker, https://github.com/open-energy-transition/technology-data/issues
9
+ Project-URL: Changelog, https://github.com/open-energy-transition/technology-data/releases
10
+ Keywords: energy,modelling,techno-economics,macroeconomics
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Natural Language :: English
17
+ Classifier: Operating System :: OS Independent
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: frozendict>=2.4.6
22
+ Requires-Dist: hdx-python-country>=3.9.6
23
+ Requires-Dist: mypy>=1.15.0
24
+ Requires-Dist: pandas>=2.2.3
25
+ Requires-Dist: pint>=0.24.4
26
+ Requires-Dist: pydantic>=2.11.7
27
+ Requires-Dist: pydeflate>=2.3.3
28
+ Requires-Dist: requests>=2.32.3
29
+ Requires-Dist: savepagenow>=1.3.0
30
+ Requires-Dist: scipy>=1.16.1
31
+ Dynamic: license-file
32
+
33
+ # technologydata
34
+
35
+ <!--
36
+
37
+ TODO: Add badges
38
+ **Suggestions:**
39
+
40
+ - Use http://shields.io or a similar service to create and host the images.
41
+ - Add the [Standard Readme badge](https://github.com/RichardLitt/standard-readme#badge).
42
+
43
+ -->
44
+
45
+ A Python package to manage techno-economic assumptions for energy system models.
46
+
47
+ ## Overview
48
+
49
+ `technologydata` is a Python package that supports the management of techno-economic assumptions for energy system models.
50
+ It provides a structured way to store, retrieve, and manipulate data related to various technologies used in energy systems,
51
+ including unit-ful parameters, currency conversions, inflation adjustment, and temporal modelling.
52
+
53
+ The package currently includes a pre-parsed dataset from the DEA's Technology Catalogue focusing on energy storage technologies and a dedicated parser (see [here](docs/examples/dea_storage.md)). In the future it will include other common public data sources such NREL's ATB.
54
+
55
+ The goal of this package is to make energy system modelling easier and more efficient,
56
+ automating common tasks and transformations to reduce errors and allowing for easier data exchange between models.
57
+
58
+ ## Table of Contents
59
+
60
+ 1. [Background](#background)
61
+ 2. [Install](#install)
62
+ 3. [Usage](#usage)
63
+ 4. [Maintainers](#maintainers)
64
+ 5. [Thanks](#thanks)
65
+ 6. [Contributing](#contributing)
66
+ 7. [License](#license)
67
+
68
+ ## Background
69
+
70
+ > Modelling is 10% science, 10% art, and 80% finding the right data and getting it into the right format.
71
+ > — Every energy modeller ever
72
+
73
+ Modelling energy systems requires a lot of data.
74
+ Techno-economic data, i.e. data about the costs for building operating technologies and their technical
75
+ characteristics, is a key input to many energy system models today.
76
+
77
+ Techno-economic data is usually collected from a variety of scattered sources and then manually processed
78
+ into the format required by a specific model.
79
+ This is repeated for every new modelling project, leading to a lot of duplicated effort.
80
+ The manual processing also carries a high risk of errors, which can lead to misleading results.
81
+
82
+ When projects are finished, the processed data is often discarded, leading to a loss of valuable information.
83
+ In better cases, the processed data is also published along with the model results, but usually in a
84
+ non-standardised and non-machine-readable format and without information about the data provenance and processing steps.
85
+
86
+ It sounds abstract, but if someone used cost assumptions for the US in 2015 USD, then there are many wrong ways
87
+ and a few right ways to convert these to e.g. EUR and adjust it for inflation to 2023.
88
+
89
+ ## Install
90
+
91
+ The package is currently under development. A pre-release is published to `PyPI`. The package is not yet available on `conda-forge`.
92
+
93
+ To install the package
94
+
95
+ ```bash
96
+ pip install technologydata
97
+ ```
98
+
99
+ Alternatively, to install the package locally from GitHub, first clone the package and then use `uv` to install it in editable mode:
100
+
101
+ ```bash
102
+ git clone https://github.com/open-energy-transition/technology-data/tree/prototype-2
103
+ cd technology-data
104
+ git checkout prototype-2
105
+ uv sync --group dev --group docs
106
+ ```
107
+
108
+ ## Usage
109
+
110
+ Detailed usage instructions and examples can be found in the available documentation. To build the documentation locally, follow the steps outlined [here](docs/contributing/instructions.md#building-the-documentation-locally).
111
+
112
+ ## Maintainers
113
+
114
+ This repository is currently maintained by [Open Energy Transition](https://openenergytransition.org/) with the maintainers and developers being:
115
+
116
+ - [euronion](https://github.com/euronion)
117
+ - [finozzifa](https://github.com/finozzifa)
118
+
119
+ ## Thanks
120
+
121
+ Development of this prototype package would not have been possible without the funding from [Breakthrough Energy](https://www.breakthroughenergy.org/).
122
+
123
+ ## Contributing
124
+
125
+ For contributing instructions, guidelines and our code of conduct, please refer to the [contributing section](docs/contributing) in the documentation.
126
+
127
+ ## License
128
+
129
+ This project is licensed under the [MIT License](LICENSES/MIT.txt).
130
+
131
+ Primary data included in the repository may be licensed under specific terms.
132
+ Processed data included in the project is licensed under [Creative Commons Attribution 4.0 International (CC BY 4.0)](LICENSES/CC-BY-4.0.txt).
133
+
134
+ To make it easier to identify which data is licensed under which terms, this repository follows the [REUSE](https://reuse.software/) specification.
135
+ This means you can find the license information for each file either located in its header or in [REUSE.toml](REUSE.toml).
@@ -0,0 +1,25 @@
1
+ technologydata/__init__.py,sha256=pPDx9m_E8UIHrjN2NyWNwFUhX6gmr4z0TwEoncXmTk0,1141
2
+ technologydata/datapackage.py,sha256=Ds7rOn1RQyMTmXCW0-gfV7nwVXMNx_7HzdGJxARvJEg,4573
3
+ technologydata/parameter.py,sha256=-8RNs7mgJJtUw607Qvm2xRYOUNN-newiBP1YH7ds_jE,25356
4
+ technologydata/source.py,sha256=ylPTclsoDUwoMmZeYYJe5BtXC_ANdTnpshGrX1ZorJE,15534
5
+ technologydata/source_collection.py,sha256=ikZ6gzYqLYEa38l02h8MlbZPwdjCk7IwJxcqO35NNOM,7408
6
+ technologydata/technology.py,sha256=oKQFGGb-KBWXywW4ezxW7oDG4c9DuiYSXS6nMrbt-mU,5471
7
+ technologydata/technology_collection.py,sha256=_pxgYpPiwAl-hPrWJtWI_OXKzqw2aViSc74FDFhSaNY,16795
8
+ technologydata/constants/__init__.py,sha256=vzOKfJftmW4_vexAVweruWUHUPcQod2kYtrtxiRy09Y,337
9
+ technologydata/constants/energy_density.py,sha256=nKQpYuX0a1pQxPLB_yNZbCsN9_Z5vFArjbjRaA5Dkl8,11343
10
+ technologydata/package_data/dea_energy_storage/dea_energy_storage.py,sha256=J3ZwG6krQb0L5tHuccfOa4SAFy1-9G1dmGMhOPYgxao,21289
11
+ technologydata/package_data/dea_energy_storage/sources.json,sha256=w-Or7idq7OZ3TwaBBBSWyhe94rGgWmgVv8IzlNE2j2w,430
12
+ technologydata/package_data/dea_energy_storage/technologies.json,sha256=tekO_moWQ6BVQ5CKba-ZwhDJXQEw0uVl1Ox0LY-8YQo,847127
13
+ technologydata/package_data/raw/Technology_datasheet_for_energy_storage.xlsx,sha256=w5zl4Vqj_laG1tdhIKutajVEhTNmGEUe4-BhnjV7TzI,329612
14
+ technologydata/technologies/__init__.py,sha256=2gqJCJ_vk84WI-Jx1d0aph-0cykQS1fTPE3C8G1e4Y4,161
15
+ technologydata/technologies/growth_models.py,sha256=qrqwB0abyn_ylUMQpAs4Ena8vu22KyI69HuOWmoCyV4,16400
16
+ technologydata/utils/__init__.py,sha256=gjxOGJZEXABJCJb7kbt_EA5X5Yf02jsm_3fFOYbCt-s,348
17
+ technologydata/utils/carriers.txt,sha256=xGRHyCFTAlrmdqEU8nCTE0ITX_VjsoNL0xmivbTA9Tw,1016
18
+ technologydata/utils/commons.py,sha256=U768ySx7fHkW_4u-fPDAYQ2aINnp3Xd9w4tZqnz2dZI,11276
19
+ technologydata/utils/heating_values.txt,sha256=zkjImePSMf1JO0DdGx2pGanQH-ce05gj8Lva5-qFzeA,668
20
+ technologydata/utils/units.py,sha256=VqxLXOV2YUjwMUO8pFm4XvE6AQXRladCYdC4hyStCMc,18509
21
+ technologydata-0.1.0.dist-info/licenses/LICENSE,sha256=sFeF-fGOZxa6tjQksRRUUTuZQ6IiWVtwQRAJIC_FkrU,1078
22
+ technologydata-0.1.0.dist-info/METADATA,sha256=-MdTVX74zWSbXrrmpC_RWkWM0yQwTURLxxwxlYE2ko0,6000
23
+ technologydata-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ technologydata-0.1.0.dist-info/top_level.txt,sha256=CUb44X9b9SGiX4eT20c8B6Fh9N8iF-bRFXXeApkymRA,15
25
+ technologydata-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+