Compare commits

...

524 Commits

Author SHA1 Message Date
3db3acd3c6 update version number 2024-10-29 22:00:57 +01:00
15aaf2a337 Merge pull request 'feat: Palma catalogues and importers are added' (#73) from feature/palma into main
Reviewed-on: #73
2024-10-29 16:46:17 -04:00
7ad16ba640 feat: Palma catalogues and importers are added 2024-10-29 11:36:47 +01:00
f42bb64b85 revert 3dd64143ab
revert Merge pull request 'feat: all the catalogues, importers, data and tests of palma are added' (#72) from feature/palma into main

Reviewed-on: #72
2024-10-22 14:27:35 -04:00
3dd64143ab Merge pull request 'feat: all the catalogues, importers, data and tests of palma are added' (#72) from feature/palma into main
Reviewed-on: #72
2024-10-21 17:32:20 -04:00
164ffbf9c8 feat: all the catalogues, importers, data and tests of palma are added 2024-10-21 23:30:10 +02:00
bf4018a649 Update version number 2024-10-20 17:39:54 +02:00
6c7f652390 Merge pull request 'fix: the small bug in test units is resolved, the construction and usage factories can be loaded without any order' (#71) from small_bugs_in_user_tests into main
Reviewed-on: #71
2024-10-20 11:15:40 -04:00
4e46b6bc0d fix: the small bug in test units is resolved, the construction and usage factories can be loaded without any order 2024-10-18 12:28:38 +02:00
4ac9ccda81 Update hub/version.py 2024-10-17 08:05:13 -04:00
df2d7a3054 Geojson class modified for the geojson format Guille suggested 2024-10-17 07:40:13 -04:00
47810737fa geojson, construction and usage importers are all modified for mixed use buildings 2024-10-17 07:40:13 -04:00
99535a979c Update hub/version.py 2024-10-17 00:19:27 -04:00
9986552ec1 Merge remote-tracking branch 'origin/main' 2024-10-17 06:17:48 +02:00
d1719b50ed Merge branch 'infilt_availability_oriol' 2024-10-17 06:15:04 +02:00
5e01f4eb7f Merge pull request 'Remove build from setup.py and add it to requirements.txt' (#69) from fix/fix-build into main
Reviewed-on: #69
2024-10-03 14:08:37 -04:00
d38150ac2d Remove build from setup.py and add it to requirements.txt 2024-10-03 13:05:56 -05:00
5e5129ecd7 Merge pull request 'Add build package to setup.py and remove texttest' (#68) from fix/update-setup.py into main
Reviewed-on: #68
2024-10-02 12:07:55 -04:00
3905f228dc correct unittests 2024-10-02 06:30:43 +02:00
33049441f0 Add build package to setup.py and remove texttest 2024-10-01 14:35:08 -05:00
70dd9f7c6a Merge pull request 'fix: change building names in ep_multiple_buildings.py to upper case' (#66) from fix/ep-result-factory-naming-bug into main
Reviewed-on: #66
2024-10-01 10:15:40 -04:00
0ce392ea06 fix: change building names in ep_multiple_buildings.py to upper case 2024-10-01 09:43:57 -04:00
ogavalda
4b6a942324 Small change. test 2024-09-30 15:58:21 -04:00
2495046c44 correct typo 2024-09-30 19:46:07 +02:00
4738de0d8c Merge branch 'infilt_availability_oriol' 2024-09-30 15:17:05 +02:00
e220bf2c0d Merge remote-tracking branch 'origin/main' 2024-09-23 18:26:33 +02:00
a45cf02b28 Update hub/version.py 2024-09-23 11:57:33 -04:00
ef62e2531f add weather file to the EnergyBuildingsExportsFactory 2024-09-23 17:56:58 +02:00
ogavalda
cd34435a9f Including air temperature output 2024-09-16 15:49:18 -04:00
ogavalda
15b96fe154 Changing the full structure to incorporate a second way of entering infiltration (infiltration per outdoor area) 2024-09-16 15:41:27 -04:00
ogavalda
54a6e6b2db Adding infiltration per m2 2024-09-15 10:22:07 -04:00
ogavalda
a7375f0b53 Adding infiltration per m2 2024-09-15 10:15:41 -04:00
ogavalda
725746fbcb Changes in infiltration profiles and the source of values for infiltration 2024-09-15 09:44:07 -04:00
ec320a2e1c Merge pull request 'fix: small bugs in the irradiance units were fixed.' (#65) from irradiance_unit_fixing into main
Reviewed-on: #65
2024-09-09 13:27:05 -04:00
8a68118503 fix: small bugs in the irradiance units were fixed. 2024-09-09 11:11:33 -04:00
a3ec3c7e19 Merge pull request 'Add version number to numpy' (#64) from fix/downgrade-numpy into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/64

Hi Connor, I have accepted the change, but some additional review will be needed, it creates a hard dependency from a specific version of numpy with almost sure will create issues with other libraries, will be nice if you could keep an eye on the issue resolution from sqlalchemy
2024-09-02 12:35:30 -04:00
connor
ace666553a Add version number to numpy 2024-08-30 18:01:26 -04:00
c4636c2c3c Merge remote-tracking branch 'origin/main' 2024-07-25 18:11:10 +02:00
4a01ac51d8 Remove the building from the targets if needed 2024-07-25 18:10:51 +02:00
ee846a6225 Update hub/version.py 2024-07-16 01:25:01 -04:00
a4f3b48617 Merge remote-tracking branch 'origin/main' 2024-07-16 07:24:41 +02:00
abc3fc48dd Add type to constructions to avoid collisions in constructions names 2024-07-16 07:24:22 +02:00
db371aecf3 Update hub/version.py 2024-07-10 05:57:01 -04:00
998df80b63 Correct shading object selection and idd file 2024-07-10 11:55:13 +02:00
446c7fa4ce Correct correct shadding object selection and idd file 2024-07-10 07:39:53 +02:00
3c5f1b7357 Update hub/version.py 2024-07-04 03:55:45 -04:00
6d2bb98470 Correct bug in shadding object creation when no archetype available 2024-07-04 09:53:45 +02:00
26b2af1e2b Merge remote-tracking branch 'origin/modifying_energy_system_catalogs_and_importers' into modifying_energy_system_catalogs_and_importers 2024-07-04 08:17:59 +02:00
dc3372ff5e fix: final changes to catalogue, importer, and parameter importer 2024-07-03 10:35:31 -04:00
a81c0cfee3 Correct bug in shadding object creation when no archetype available 2024-07-01 18:48:37 +02:00
1279dffff8 fix: small issue in the city creator was resolved 2024-07-01 12:16:26 -04:00
1cc7b2cb8c IDFL Add objects without archetype as shadow objects instead 2024-07-01 07:04:50 +02:00
2d11bf6640 Reapply "fix: energy system catalogue and importers are modified"
This reverts commit 9adacadc2a.
2024-06-27 17:21:43 -04:00
9adacadc2a Revert "fix: energy system catalogue and importers are modified"
This reverts commit 15e43a0f35.
2024-06-27 15:33:04 -04:00
15e43a0f35 fix: energy system catalogue and importers are modified 2024-06-27 15:18:26 -04:00
6dbff12ff9 Update hub/version.py 2024-06-27 10:35:47 -04:00
982bcab045 IDFL Add objects without archetype as shadow objects instead 2024-06-27 16:35:24 +02:00
83d308e03b Merge branch 'main' of https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub 2024-06-27 16:25:02 +02:00
915170e60f Bugfix: now surfaces has associated thermal boundaries after construction factory 2024-06-27 16:22:57 +02:00
6afc076402 Merge pull request 'Delete all __pycache__ files' (#62) from fix/remove-pycaches into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/62
2024-06-18 10:50:46 -04:00
6e79541816 Delete all __pycache__ files 2024-06-17 19:08:52 -04:00
c36ce1b83a Correct future warning 2024-05-27 18:09:40 +02:00
3de2e00a54 Update hub/version.py 2024-05-24 05:24:16 -04:00
5fb31fc5ca Merge remote-tracking branch 'origin/main' 2024-05-24 11:23:53 +02:00
7851968bdc Add get city to the control 2024-05-24 11:23:47 +02:00
6684e7ef1b Update hub/version.py 2024-05-24 05:20:25 -04:00
64449aee1d Merge remote-tracking branch 'origin/main' 2024-05-24 11:19:49 +02:00
a0f7ca0e17 Update idf 2024-05-24 11:19:43 +02:00
58d84021cf Update hub/version.py 2024-05-15 10:10:45 -04:00
84d80d5d2a Merge remote-tracking branch 'origin/main' 2024-05-15 16:04:15 +02:00
6b3110d1c0 Update idf 2024-05-15 16:04:08 +02:00
f095483b36 Update hub/version.py 2024-05-13 01:41:22 -04:00
841098b615 fix: energy storage bug is fixed. 2024-05-10 13:27:29 -04:00
73e6dcdaf7 Update hub/version.py 2024-04-29 00:08:21 -04:00
8fe002a22c Merge remote-tracking branch 'origin/main' 2024-04-29 06:02:49 +02:00
ffd0367d50 Bugfix in sra export 2024-04-29 06:02:43 +02:00
f15401c148 Update hub/version.py 2024-04-18 01:43:18 -04:00
9a99f69bdf Merge remote-tracking branch 'origin/main' 2024-04-18 07:42:27 +02:00
fe99059e5a Bugfix in montreal custom catalog 2024-04-18 07:42:19 +02:00
d9d9a628b4 Update hub/version.py 2024-04-16 01:02:37 -04:00
d9a941a97a Correct unittests 2024-04-16 07:01:51 +02:00
b52e66c263 bugfix in heating peak load units 2024-04-15 07:13:53 +02:00
039a0f30ba Update hub/version.py 2024-04-03 01:18:06 -04:00
ebeafddc74 Update hub/version.py 2024-03-28 02:00:40 -04:00
355cc6ecdf bugfix in idf exporter 2024-03-28 06:59:57 +01:00
cbe720f0de Update hub/version.py 2024-03-27 12:08:52 -04:00
52382566d3 bugfix: correct minimal idf 2024-03-27 17:08:08 +01:00
4056a2ab8a Update hub/version.py 2024-03-26 01:05:46 -04:00
1f7b4924fe Merge remote-tracking branch 'origin/main' 2024-03-26 06:04:34 +01:00
20200f0efe Update hub/version.py 2024-03-25 12:47:13 -04:00
919ab60bd5 bugfix in emission systems 2024-03-25 17:27:02 +01:00
aa85473a81 Update hub/version.py 2024-03-25 10:40:46 -04:00
aac006f63a Merge remote-tracking branch 'origin/main' 2024-03-25 15:35:42 +01:00
199d7dc4f9 bugfix in emission systems 2024-03-25 15:35:00 +01:00
3a6a712cad Update hub/version.py 2024-03-25 10:10:56 -04:00
27291ad1b8 bugfix in emission systems 2024-03-25 15:10:03 +01:00
5fdfee9265 update version.py 2024-03-14 14:19:09 +01:00
0f0a923329 Merge pull request 'final_energy_system_model' (#60) from final_energy_system_model into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/60
2024-03-14 09:13:21 -04:00
bf23730281 correct the unit tests 2024-03-12 16:21:43 +01:00
11d493ba0e remove north_america catalog for energy systems 2024-03-12 14:56:22 +01:00
1266f50e29 Correct small error in unittest 2024-02-16 06:01:54 +01:00
674393970c Merge remote-tracking branch 'origin/main' into final_energy_system_model
# Conflicts:
#	.gitignore
#	hub/city_model_structure/building.py
#	hub/exports/building_energy/idf.py
#	hub/imports/geometry_factory.py
2024-02-16 05:50:45 +01:00
8d75fe1f3a remove pychache 2024-02-16 05:38:07 +01:00
57a9d24f4c Title: Improvement of Energy System Parameters Importer
>> To make life easier for people who want to work on energy systems in future, I tried to make my code as generic as possible.
2024-02-13 20:05:03 -05:00
087fead489 Title: Finalizing the energy system data model and system factory
A new XML file named montreal_future_systems.xml is created where the elements of the file are the same as attributes of various classes. Therefore, the catalogue importer and energy system importer should have been updated accordingly. The catalog importer is organized in a general method so whenever someone wants to create a new catalogue they can use the created code as the blueprint.
2024-02-07 20:51:10 -05:00
ebaec9bb7c Title: Finalizing the energy system data model and system factory
A new XML file named montreal_future_systems.xml is created where the elements of the file are the same as attributes of various classes. Therefore, the catalogue importer and energy system importer should have been updated accordingly. The catalog importer is organized in a general method so whenever someone wants to create a new catalogue they can use the created code as the blueprint.
2024-02-07 18:54:09 -05:00
3e193f04a6 The dual_supply_capability is added to the city model structure 2024-02-01 15:41:05 -05:00
ac4742d2c4 A new attribute "dual_supply_capability" is added to NonPVGeneration class in non_pv_generation_system.py and the catalogue importer is adjusted 2024-02-01 12:24:15 -05:00
f976040f45 A new attribute "dual_supply_capability" is added to NonPVGeneration class in non_pv_generation_system.py and the catalogue importer is adjusted 2024-02-01 12:23:58 -05:00
54b62ec2d0 update version 2024-01-25 06:57:33 +01:00
9357cef641 remove wrong indentation 2024-01-25 06:57:00 +01:00
002d5a659d Update hub/version.py 2024-01-22 12:48:42 -05:00
f9815dfa49 Update hub/version.py 2024-01-22 08:36:05 -05:00
eeda66f410 Merge pull request 'feature/upgrade_ep_to_23.2.0' (#59) from feature/upgrade_ep_to_23.2.0 into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/59
2024-01-22 08:34:47 -05:00
aa0f790d8e remove commented code and debug prints 2024-01-22 14:33:44 +01:00
e0e7daf64b Merge remote-tracking branch 'origin/main' into feature/upgrade_ep_to_23.2.0
# Conflicts:
#	hub/exports/building_energy/idf_files/Minimal.idf
2024-01-22 14:32:24 +01:00
5181ddcabb Update hub/version.py 2024-01-18 00:33:39 -05:00
68457b9d26 add nrcan data to the pip package 2024-01-18 06:32:44 +01:00
673cbac77a added storage_medium attribute to thermal_storage_system.py to enable us use water thermodynamic characteristics in the modelling 2024-01-17 13:05:10 -05:00
11f78ccb89 added storage_medium attribute to thermal_storage_system.py to enable us use water thermodynamic characteristics in the modelling 2024-01-10 16:10:42 -05:00
3891c866cf Merge branch 'bug/units' 2024-01-09 14:01:29 +01:00
5230da861c correct units according to oriol's instruction 2024-01-09 14:00:33 +01:00
87b2319fda Update hub/version.py 2024-01-09 07:48:37 -05:00
1916d21c4e restore unittests after debug is completed 2024-01-09 10:32:47 +01:00
57388a9dc4 replace github nrel with cerc offline nrcan_data 2024-01-09 10:31:59 +01:00
89d0d0eae8 replace github nrel with cerc nrcan_data, this may also be local instead 2024-01-09 10:15:43 +01:00
e025510c93 Merge pull request 'ep_23.2.0' (#58) from ep_23.2.0 into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/58
2023-12-07 13:22:23 -05:00
d592de6759 Surfaces have now a more human-readable name 2023-12-07 13:20:21 -05:00
eeac9a24a8 Update energy plus
Shadow object surfaces are now building_name_{index}
Now version 23.2 is supported
Minimal.idf set the proper simulation method
2023-12-07 12:05:59 -05:00
7328118861 Add debugging code to idf.py 2023-12-06 16:20:35 -05:00
9444de6bae Update add minimal.idf and energy+.idd for energyplus 23.2.0 2023-12-06 16:15:35 -05:00
27ceed01fe persistence changes 2023-12-06 15:15:36 -05:00
57c7a76529 The ep results importer for multiple buildings is completed 2023-12-06 13:53:31 -05:00
020190829f The energy plus result importer for multiple buildings is created and tested. Currently, buildings are enriched with hourly values but monthly and yearly values are not working. 2023-12-05 19:38:11 -05:00
c3f2cfb02a updated the idf.py 2023-12-05 13:13:19 -05:00
e3f1d52a21 Add .gitignore to exclude Python bytecode files 2023-12-05 12:47:44 -05:00
716465f1eb Update hub/version.py 2023-11-27 01:01:59 -05:00
81702e0008 Merge remote-tracking branch 'origin/main' 2023-11-27 07:01:30 +01:00
953b6796e9 correct persistence json read 2023-11-27 07:00:39 +01:00
898f364e28 Add additional idfobjects, keys, and cleaned up syntax 2023-11-24 14:45:27 -05:00
03da727d29 Update hub/version.py 2023-11-24 03:03:20 -05:00
b51d49d598 correct persistence json read 2023-11-24 09:01:49 +01:00
802641b4a3 Update hub/version.py 2023-11-24 02:04:37 -05:00
914375171a remove useless files 2023-11-24 08:03:55 +01:00
72b6cddea4 change result structure (concept proof) 2023-11-24 08:02:01 +01:00
7f16449edb Update hub/version.py 2023-11-20 09:59:58 -05:00
c8545835d9 Update hub/version.py 2023-11-20 09:39:57 -05:00
4bc8df8e9d Merge remote-tracking branch 'origin/main' 2023-11-20 15:38:56 +01:00
fca74eeb08 persistence performance improvements 2023-11-20 15:38:49 +01:00
227685695a All working, missing Sizing:Zone and DesignSpecification:OutdoorAir 2023-11-17 15:08:57 -05:00
ac6b023080 Air terminal working, air distribution not yet 2023-11-17 08:55:37 -05:00
383a480f65 Nodelist working 2023-11-17 08:13:53 -05:00
67525e50fa Update hub/version.py 2023-11-17 02:16:59 -05:00
601058fc5e Bug fix in persistence CityObject 2023-11-17 08:16:19 +01:00
cfd2586f80 Tests to incorporate HVAC systems to EP workflow 2023-11-16 15:52:00 -05:00
a3c5219ccd Changed idf to start producing detailed systems 2023-11-16 14:26:41 -05:00
5fa55ea79f Update hub/version.py 2023-11-16 01:42:01 -05:00
68bb9223e3 persistence performance improvements 2023-11-16 07:39:46 +01:00
a1ca764057 Merge remote-tracking branch 'origin/main' 2023-11-15 04:50:22 +01:00
8c27e69a79 remove unused parameters and handlers 2023-11-15 04:50:15 +01:00
557d77661e Update hub/version.py 2023-11-06 04:15:14 -05:00
38d8013356 remove unused parameter 2023-11-06 10:14:27 +01:00
43fa1e0d92 Update hub/version.py 2023-11-03 04:31:21 -04:00
812c9541f4 Merge remote-tracking branch 'origin/main' 2023-11-03 09:30:54 +01:00
c1e392d828 rollback useless change 2023-11-03 09:30:49 +01:00
fe315bc6bb Update hub/version.py 2023-11-03 02:22:45 -04:00
087379b010 Merge remote-tracking branch 'origin/main' 2023-11-03 07:22:03 +01:00
452bfdf780 correct unit tests in ci/cd 2023-11-03 07:21:50 +01:00
63803efccb Update hub/version.py 2023-11-03 01:38:48 -04:00
0f0368c074 Merge remote-tracking branch 'origin/main' 2023-11-03 06:38:17 +01:00
08e701a972 Remove deprecated warning 2023-11-03 06:38:06 +01:00
914d521e81 Update hub/version.py 2023-11-02 11:00:48 -04:00
7f8e0d48ee Correct missing reflectance values 2023-11-02 15:59:30 +01:00
26c0ef7864 rollback change 2023-11-02 09:46:45 +01:00
5ce3a24e55 Update hub/version.py 2023-11-02 02:40:21 -04:00
0894c5dac6 Update hub/version.py 2023-11-02 02:22:19 -04:00
ee06d45ba9 Merge pull request 'bug correction' (#55) from IDF_bug into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/55
2023-11-02 02:21:52 -04:00
3dd928574a bug correction 2023-11-02 07:20:55 +01:00
9591db3bf8 Update hub/version.py 2023-10-31 10:21:01 -04:00
69f3b7f814 Merge pull request 'bug correction' (#54) from correct_layer_name into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/54
2023-10-31 10:20:21 -04:00
6e6c78b6f4 Update hub/version.py 2023-10-31 09:19:24 -04:00
75bb316848 bug correction 2023-10-31 14:17:59 +01:00
ed7d6d13f6 Update hub/version.py 2023-10-31 08:15:20 -04:00
42de9cea97 Merge pull request 'Bug correction in persistence' (#53) from correct_layer_name into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/53
2023-10-31 06:38:45 -04:00
255f9f861a Bug correction in persistence 2023-10-31 11:37:23 +01:00
4449d0b92d Update hub/version.py 2023-10-27 04:29:24 -04:00
89452dcf31 Merge remote-tracking branch 'origin/main' 2023-10-27 10:28:30 +02:00
6aafda7f42 Bug correction in persistence 2023-10-27 10:28:20 +02:00
6da8fde949 Update hub/version.py 2023-10-26 02:18:54 -04:00
6332519e12 Update hub/version.py 2023-10-26 01:04:17 -04:00
9ce97edc13 Merge pull request 'Bug correction in citygml factory' (#52) from geojson_exporter into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/52
2023-10-26 01:03:17 -04:00
98676e81e0 Bug correction in citygml factory 2023-10-26 07:02:26 +02:00
5cba38cf65 Update hub/version.py 2023-10-24 10:09:24 -04:00
3512d4b1e4 Merge pull request 'Add crs to the geometry importer as a parameter' (#51) from geojson_exporter into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/51
2023-10-24 10:03:49 -04:00
b0fabf3b7f Add crs to the geometry importer as a parameter 2023-10-24 15:46:40 +02:00
6d987f43f7 Update hub/version.py 2023-10-24 02:04:05 -04:00
f62ab95cf0 Merge pull request 'geojson_exporter' (#50) from geojson_exporter into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/50
2023-10-24 02:03:06 -04:00
b711bceb7b Add geojson exporter 2023-10-24 08:01:57 +02:00
6ee1466b8e Add geojson exporter 2023-10-24 08:00:48 +02:00
a31281cae7 Code quality improvements and basic creation og geojson from gml 2023-10-20 14:19:46 +02:00
8d5010218b Update hub/version.py 2023-10-19 02:23:18 -04:00
a102a201e6 Merge pull request 'cesiumjs_tileset' (#49) from cesiumjs_tileset into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/49
2023-10-19 02:22:40 -04:00
2afce4acce Corrected obj and glb exporters, building now exposes lower and upper corner 2023-10-19 07:39:35 +02:00
9c1e05939a The city energy system importer for the north america is modified and tested. 2023-10-17 17:25:13 -04:00
978cc9d1ea added system schematics to the data folder and to the catalog importer 2023-10-16 17:48:42 -04:00
0240e89170 modified a small bug in north_america_energy_system_catalog.py 2023-10-16 17:24:39 -04:00
c6d4feec89 Modified the classes and the north amercia catalog importer based on the changes Pilar made in the data model structure 2023-10-16 16:44:53 -04:00
3f649fd020 finished central data model new structure for energy systems, importer and test 2023-10-13 13:31:46 -04:00
d94bce4174 half made changes in central data model for systems 2023-10-13 08:23:40 -04:00
f865490ff9 Corrected bug in distribution_systems_electrical_consumption, but need to be confirmed with pilar 2023-10-13 09:11:03 +02:00
31a4893ed8 Corrections in persistence, distribution_systems_electrical_consumption property seems to be bugged 2023-10-13 08:57:42 +02:00
1a7ad20135 new catalog model and factory finished and test passed 2023-10-12 11:59:52 -04:00
fe76d2fe3d Correct the positions for the boxes 2023-10-11 17:16:18 +02:00
57ee3e734b finished generic system and system parts 2023-10-11 09:14:38 -04:00
7cb4600c15 Correct the positions for the boxes 2023-10-11 14:32:31 +02:00
3578d2faae add temporary indentation to tilesets 2023-10-11 06:01:15 +02:00
d6bfe730eb add non-standard render position to tileset 2023-10-11 06:00:34 +02:00
84f5ebe4a0 checking a problem with lists assignation where a single value should be assigned 2023-10-10 16:22:45 -04:00
6b24a59178 Add exports for cesium tileset and glb format 2023-10-10 11:16:26 +02:00
11dc02d0e8 starting point, not working 2023-10-10 05:03:31 -04:00
d29161b05e rolled back and error introduced in a previous commit 2023-10-06 04:54:20 -04:00
a331dce966 fixed error in logic in _create_generic_systems 2023-10-06 04:14:52 -04:00
3f91da9f45 Merge remote-tracking branch 'origin/energy_system_central_data_model' into energy_system_central_data_model
# Conflicts:
#	hub/imports/energy_systems/montreal_custom_energy_system_parameters.py
2023-10-06 04:13:32 -04:00
f13d7fd860 fixed error in logic in _associate_energy_systems 2023-10-06 04:11:56 -04:00
ef89da8552 created a loop for generation systems in _associate_energy_systems 2023-10-05 16:18:08 -04:00
9654bee978 modified the names and changed all singulars to plurals 2023-10-05 14:45:33 -04:00
dae117fb44 Merge remote-tracking branch 'origin/energy_plus_result_factory' into energy_plus_result_factory 2023-10-05 14:20:24 -04:00
91fa9d5ca1 modified the names and changed all singulars to plurals 2023-10-05 14:20:02 -04:00
8e145eefa1 Merge remote-tracking branch 'origin/energy_system_central_data_model' into energy_system_central_data_model 2023-10-05 13:22:42 -04:00
6f40a15609 Merge remote-tracking branch 'origin/energy_system_central_data_model' into energy_system_central_data_model 2023-10-05 13:22:32 -04:00
4a14deece1 Merge remote-tracking branch 'origin/energy_system_central_data_model' into energy_system_central_data_model 2023-10-05 13:22:19 -04:00
ca48d6448b changed all the singular names to plural and turned distribution and emission systems into lists 2023-10-05 13:18:57 -04:00
f2bfc297b3 Basic style correction and missing docstrings 2023-10-05 11:30:20 +02:00
d5c443a57b Merge remote-tracking branch 'origin/main' into energy_system_central_data_model 2023-10-05 11:02:13 +02:00
568317ebf1 Merge pull request 'fixing_working_hours_problem' (#47) from fixing_working_hours_problem into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/47
2023-10-05 04:40:58 -04:00
af32decf96 Merge pull request 'added_new_functions_to_dictionaries' (#48) from added_new_functions_to_dictionaries into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/48
2023-10-05 04:40:19 -04:00
bb05b01312 Merge pull request 'feature/load_compressed_city' (#46) from feature/load_compressed_city into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/46
2023-10-05 04:38:59 -04:00
cbda6ce13d Tried to modify the test_systems_factory.py but received an error saying GenerationSystem object is not iterable 2023-10-04 17:07:19 -04:00
2b696f9251 Merge branch 'main' into feature/load_compressed_city 2023-10-04 15:12:42 -04:00
a97c068513 Add removal of decompressed pickle file 2023-10-04 15:11:35 -04:00
f0ad1462d0 finished the first part of the test. 2023-10-03 17:50:17 -04:00
23a8fbe86d continued working on the importer 2023-10-03 13:07:20 -04:00
087384475f changing from generation system to generation systemS in system importer north america 2023-10-03 09:40:32 -04:00
fac6844da1 started working on the test 2023-10-02 13:19:58 -04:00
b914c9e7bf made final changes to north_america_custom_energy_system_parameters.py 2023-10-02 12:39:30 -04:00
cf783d8998 add max height to the city in geojson when the high field is given 2023-09-29 07:54:46 +02:00
8bd75c790f Bug correction for building upper_corner it's no longer set to -inf 2023-09-29 05:55:36 +02:00
eaa3064a3f continued working on energy system importer 2023-09-28 19:19:32 -04:00
207058a16f obj export improvements 2023-09-28 15:20:28 +02:00
8dbbfb9460 finished creating generic generation system in the _create_generic_systems method in north_america_custom_energy_system_parameters.py 2023-09-27 17:48:23 -04:00
3809cf2885 continued the work on importer 2023-09-27 13:00:04 -04:00
8b66e20e9a some refactoring 2023-09-27 05:35:28 -04:00
6d34e41e90 Started working on north america energy system importer. 2023-09-26 18:54:58 -04:00
f495c1d27a Merge remote-tracking branch 'origin/solved_bugs_found_in_idf' 2023-09-26 10:33:41 +02:00
a43c971bbb modified working hours to give hours a month, not only total 2023-09-25 05:19:02 -04:00
4ce9f99e4d cleaning mix-branches 2023-09-25 03:41:52 -04:00
e3c9c64e9f Merge remote-tracking branch 'origin/Stochastic_occupancy_model' into energy_plus_result_factory
# Conflicts:
#	.gitignore
2023-09-25 03:35:51 -04:00
8099037225 prints to check error 2023-09-25 03:34:57 -04:00
sanam
12d20f2d7a Stochastic schedule folder created 2023-09-22 09:42:10 -04:00
sanam
bf004f9e18 Stochastic schedule folder created 2023-09-21 14:33:13 -04:00
sanam
482c06e4c2 new class created 2023-09-20 11:30:41 -04:00
cb3e100fd4 I completed the generation_system.py
Created generic_storage_system.py, thermal_storage_system.py, and electrical_storage_system.py and added them to energy_system.py and generic_energy_system.py
2023-09-19 20:35:08 -04:00
41cf280aab work started on generation_system.py and generic_generation_system.py 2023-09-18 15:55:50 -04:00
ee875859e8 work started on generation_system.py and generic_generation_system.py 2023-09-18 15:44:52 -04:00
989d89df80 Merge remote-tracking branch 'origin/main' into main 2023-09-18 12:57:34 -04:00
73dffdc367 Merge branch 'systems_catalog_improvement' into energy_plus_result_factory
# Conflicts:
#	.gitignore
2023-09-14 12:34:50 -04:00
b24a616d00 modified energy plus results importer test 2023-09-14 12:03:50 -04:00
aa6b3c9e74 add two todos to change name into material_name in the layer class 2023-09-14 06:42:28 +02:00
707b42f2a9 modified the test checking if it only returns the hourly, monthly and yearly demands for building 12 2023-09-13 16:36:27 -04:00
cd7104206a changed name of test file 2023-09-13 04:40:11 -04:00
29ba43f1d9 Update function comment and remove empty function 2023-09-12 14:59:44 -04:00
70cc659c14 Add load_compressed function to city.py 2023-09-12 14:57:13 -04:00
f0ffc26348 tried to create a test 2023-09-12 13:16:00 -04:00
cde3bfb4ae tried to get monthly values from the method in helpers/monthly values. 2023-09-08 11:50:20 -04:00
8f8a6dbf71 small changes in importer 2023-09-08 05:05:58 -04:00
1392c05489 results_factory.py is modified
The enrich method is added to energy_plus_workflow.py
The Levis_out.csv is added to test_data
2023-09-07 18:16:07 -04:00
a0d2f7e5bc chnages were made to energy_plus_workflow.py 2023-09-06 14:55:14 -04:00
2a5c552f17 added new functions to dictionaries 2023-09-06 04:40:56 -04:00
86b7378a54 Merge remote-tracking branch 'origin/main' into main 2023-09-06 04:33:49 -04:00
cf6542cb0c Update hub/version.py 2023-09-04 10:49:09 -04:00
4004ff3c84 Merge remote-tracking branch 'origin/update_documentation'
# Conflicts:
#	setup.py
2023-09-04 16:46:38 +02:00
4ae09915b1 erased not needed prints 2023-09-01 13:02:01 -04:00
9624be2cf9 solved bugs introduced with changes in thermal_zone_from_internal_zones new structure 2023-09-01 12:53:01 -04:00
c072527dc6 small changes to pv_generation_system.py 2023-08-29 14:03:30 -04:00
56c84ad399 solved bugs in adapting montreal_custom_catalog.py to new catalog data 2023-08-29 13:29:51 -04:00
a5f70f4720 erased forgotten not needed print 2023-08-29 13:12:56 -04:00
b7c2bbae96 removed last two values in generationSystems as they are not used anymore 2023-08-29 13:02:28 -04:00
8763523d3b added S to some lists, updated montreal_custom_catalog.py to new catalog classes and removed __future__ from those classes that didn't need it 2023-08-29 12:50:49 -04:00
7810f8f91d extracted storage type from abstract class 2023-08-29 12:34:04 -04:00
d4b424357d added to_dictionary to abstract class 2023-08-29 12:31:06 -04:00
8dbeeaafa1 changed some units in electrical storage 2023-08-29 12:29:11 -04:00
c77b2254f1 separated storage class in three, one abstract 2023-08-29 12:26:19 -04:00
b1d42ee00b Merge remote-tracking branch 'origin/systems_catalog_improvement' into systems_catalog_improvement
# Conflicts:
#	hub/catalog_factories/data_models/energy_systems/electrical_storage_system.py
#	hub/catalog_factories/data_models/energy_systems/energy_storage_system.py
#	hub/catalog_factories/data_models/energy_systems/thermal_storage_system.py
2023-08-29 12:25:04 -04:00
6ac63adaaf separated storage class in three, one abstract 2023-08-29 12:22:06 -04:00
644b1c9346 energy_storage_system.py is modified and inherited by thermal_storage_system.py and electrical_storage_system.py 2023-08-29 11:45:02 -04:00
8f1f344769 Update hub/version.py 2023-08-29 01:41:57 -04:00
6551eb6402 partial correction for persistence
IMPORTANT: This version neec top be checked for meb import as seems that the cooling and heating demand aren't imported properly.
2023-08-29 05:55:40 +02:00
24a670755a performance_curves.py is fixed
material_name is replaced with material_id in north_america_energy_system_catalog.py
The unused variables are fixed.
The code is modified to accommodate the reorganization Pilar did in generation_systems
The system.py is modified to be able to accommodate more than one distribution or emission system
2023-08-28 18:37:55 -04:00
cfb76a9ebd partial correction for persistence
IMPORTANT: This version neec top be checked for meb import as seems that the cooling and heating demand aren't imported properly.
2023-08-28 18:34:10 +02:00
5d4813f2f6 some changes in code to be reviewed by Saeed 2023-08-28 10:18:18 -04:00
2f62ce3fdb Update hub/version.py 2023-08-25 13:24:34 -04:00
58e7ce7574 Bug correction in meb
Now it skip the building when the archetype it's not available
2023-08-25 13:21:09 -04:00
43e5bb7310 nothing is changed 2023-08-25 10:22:46 -04:00
fe0a2d3003 north_america_energy_system_catalog.py is completed and all the bugs have been resolved. 2023-08-24 15:09:17 -04:00
3deb63b14a north_america_energy_system_catalog.py is completed
montreal_custom_catalog.py is fixed
2023-08-23 17:09:55 -04:00
9b0bdf2b7e load_generation and load_storage in north_america_energy_system_catalog.py is completed 2023-08-22 19:09:35 -04:00
ade5ef94bb Change INSEL setup for Linux to TBD 2023-08-22 15:59:03 -04:00
40bc0154dd Small grammatical changes to setup.py 2023-08-22 15:57:37 -04:00
ff1bb80a6c Merge remote-tracking branch 'origin/main' 2023-08-22 10:26:29 -04:00
76d3ba8495 Update hub/version.py 2023-08-22 10:26:22 -04:00
491b976fd9 Correct error in meb (temporary workaround) 2023-08-22 10:25:40 -04:00
072d2a6d9d modifications done to the xml file and north_america_energy_system_catalog.py 2023-08-21 17:41:12 -04:00
72b5ff1040 north_america_components.xml is modified
The montreal_custom_catalog.py is modified
2023-08-18 18:22:10 -04:00
e20a700deb emission_system.py is rolled back to original
performance_curves.py is imported in generation_system.py
The order of arguments in pv_generation_system.py is fixed
Modulation range is replaced with maximum and minimum heat output in generation_system.py
maximum,minimum, and nominal cooling output are also added to generation_system.py
2023-08-17 18:48:48 -04:00
0528aa0718 started to work on systems 2023-08-16 20:07:24 -04:00
923b1fe267 generation components are all added to north_america_energy_system_catalog.py
pv_generation_system.py is modified
Tools4CitiesESMF.xml is modified
2023-08-16 19:33:18 -04:00
32a40f17a6 fixing pv problem 2023-08-16 16:28:20 -04:00
e91e61741e add generation and config 2023-08-16 15:58:49 -04:00
16618fb54a started north america energy system catalog importer. NOT WORKING 2023-08-15 16:27:05 -04:00
21ca1e1e28 modified energy_storage_system.py and energy_system_catalog.py generation_system.py
The xml data is modified
2023-08-15 14:49:17 -04:00
ccdc7a454d small changes to performance_curves.py and distribution_system.py
created the montreal_custom_catalog_new.py
uploaded my xml file
2023-08-14 20:26:37 -04:00
3feecac15d cogeneration ratio was removed from generation_system.py
pv_generation_system.py is completed by adding an inheritance from the GenerationSystem class
parameter_function.py is renamed to performance_curves.py and an attribute to get the list of target parameters is added to it
system.py is modified by adding energy storage, performance curves, pv to it
2023-08-14 20:00:17 -04:00
acd7ad2266 distribution_system.py is modified 2023-08-13 22:53:43 -04:00
6ce82c52de Class parameter_function.py is created 2023-08-13 21:17:03 -04:00
d6ee3d6e6b All the attributes related to the material and insulation of the tank are substituted with one attribute called "layers" 2023-08-13 20:44:14 -04:00
231e4a17f2 All the attributes related to the material and insulation of the tank are substituted with one attribute called "layers" 2023-08-13 20:43:05 -04:00
012076492f Merge remote-tracking branch 'origin/systems_catalog_improvement' into systems_catalog_improvement 2023-08-13 10:27:00 -04:00
61d4c012dc Renamed the electricity_generation_system.py to pv_generation_system.py and modified the heating_generation_system to generation_system.py to account for all the generation systems. 2023-08-13 10:26:36 -04:00
fa518126a5 Merge pull request 'modifications_in_location' (#42) from modifications_in_location into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/42
2023-08-11 12:01:36 -04:00
e0037e788f moved gitignore from ./hub/hub to ./hub 2023-08-11 10:25:59 -04:00
e08080c601 added .gitignore to always ignore .idea 2023-08-11 10:21:08 -04:00
1b3a128f33 added .gitignore to always ignore .idea 2023-08-11 10:18:33 -04:00
a3028221e3 added .gitignore to always ignore .idea 2023-08-11 10:17:02 -04:00
19d05940fa added reference-city latitude and longitude 2023-08-11 10:10:41 -04:00
fde6f0f751 Few modifications were mafe to heat_generation_system.py and electricity_generation_system.py classes.
A new class named energy_storage_system.py is created to represent different possible energy storage systems in energy systems.
2023-08-10 20:55:35 -04:00
fdead81f26 Update hub/version.py 2023-08-10 12:26:29 -04:00
db0e400343 Merge remote-tracking branch 'origin/main' 2023-08-10 12:25:30 -04:00
ec288f8db1 rollback unwanted changes for unit test 2023-08-10 12:25:23 -04:00
b746387133 Update hub/version.py 2023-08-10 11:32:17 -04:00
480acc6552 rollback unwanted changes for unit test 2023-08-10 11:31:48 -04:00
7c29405f21 Add missing values to reference city to climate zone and to montreal functions 2023-08-10 11:30:59 -04:00
sanam
e1ca39950f new directory created 2023-08-10 11:25:40 -04:00
sanam
02a03b8239 new directory created 2023-08-10 11:23:23 -04:00
d564218531 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	hub/helpers/data/montreal_function_to_hub_function.py
#	hub/imports/construction/helpers/construction_helper.py
2023-08-10 11:03:09 -04:00
ebb712ebaf Add missing values to reference city to climate zone and to montreal functions 2023-08-10 11:01:37 -04:00
sanam
9094c286c4 new branch created 2023-08-10 10:07:14 -04:00
9034c3375e The generation_system.py is renamed to heat_generation_system.py and the attributes and decorators are modified to encapsulate all the needed attributes for boilers and heat pumps.
Another class named electricity_generation_system.py is created to add the attributes and decorators of PV, inverter, and maybe wind turbines in future. The system.py is also modified accordingly
2023-08-09 19:32:01 -04:00
e2c8ef0c56 Update hub/version.py 2023-08-09 16:12:41 -04:00
30908eda6d Add retrieve result test and correct lock in sqlalchemy 2023-08-09 16:12:10 -04:00
deb04cbe2d Add retrieve result test and correct lock in sqlalchemy 2023-08-09 13:52:55 -04:00
2b95173dfc starting updates in system catalog 2023-08-09 12:02:16 -04:00
7f5cde8175 starting updates in system catalog 2023-08-09 11:25:56 -04:00
bb76d05f17 Update hub/version.py 2023-08-08 13:06:34 -04:00
ca81ce3020 correct unit tests 2023-08-08 13:05:55 -04:00
3f4038286e correct setup.py 2023-08-08 12:45:19 -04:00
c4930b940f correct setup.py 2023-08-08 12:42:48 -04:00
527bf4448b overall quality improvement 2023-08-08 12:39:37 -04:00
1b3053b37f Merge remote-tracking branch 'origin/main' 2023-08-08 12:28:30 -04:00
cf3f85c7f4 overall quality improvement 2023-08-08 12:26:05 -04:00
db7caad218 Update hub/version.py 2023-08-08 12:04:32 -04:00
7fb39171f4 correct unittest 2023-08-08 12:01:21 -04:00
b7d7c9aadb Merge remote-tracking branch 'origin/main' 2023-08-08 12:01:06 -04:00
4012408e2f correct unittest 2023-08-08 12:01:00 -04:00
8502efc710 Merge pull request 'reviewing_units' (#41) from reviewing_units into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/41
2023-08-08 11:35:26 -04:00
19011d3fc1 all test passed 2023-08-08 11:25:06 -04:00
bdf0deb3b3 all test passed 2023-08-08 11:20:44 -04:00
ab81edc33d changed in data ACH to 1/s all Wh in J 2023-08-08 10:19:35 -04:00
a3bf594071 correct unittest 2023-08-08 10:10:04 -04:00
04ef636423 Merge branch 'main' into reviewing_units
# Conflicts:
#	hub/city_model_structure/building_demand/internal_zone.py
#	hub/city_model_structure/building_demand/thermal_zone.py
#	hub/exports/building_energy/insel/insel_monthly_energy_balance.py
2023-08-07 17:01:55 -04:00
d7e041b686 erased traffic folder: was not used at all
changed in data ACH to 1/s
2023-08-07 16:56:01 -04:00
90a196465a Merge pull request 'reviewing_hub' (#40) from reviewing_hub into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/40
2023-08-07 15:48:25 -04:00
7cf926bc5b Merge pull request 'dataframes_to_lists' (#39) from dataframes_to_lists into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/39
2023-08-07 15:39:58 -04:00
83fc351fc7 Merge branch 'main' into reviewing_hub 2023-08-07 15:12:17 -04:00
8a41c4f656 erased not needed prints 2023-08-07 14:54:48 -04:00
589f3e7f11 tested with old versions and got same results 2023-08-07 14:46:19 -04:00
67cd435fd5 all tests passed 2023-08-07 12:32:33 -04:00
1e9afa28a8 all tests passed 2023-08-03 17:00:59 -04:00
212b4b0621 solving bugs from adding thermal_archetypes to the data model 2023-08-03 14:51:54 -04:00
fe8c79d7c2 solving bugs from adding thermal_archetypes to the data model 2023-08-03 13:49:57 -04:00
e138a24555 Update hub/version.py 2023-08-03 12:22:57 -04:00
86845dd05f Merge remote-tracking branch 'origin/main' 2023-08-03 12:21:45 -04:00
1691ada01c Add retrieve result test and correct lock in sqlalchemy 2023-08-03 12:21:40 -04:00
4bbd65bb41 Update hub/version.py 2023-08-03 12:07:37 -04:00
9365b47dc2 Add retrieve result test and correct lock in sqlalchemy 2023-08-03 12:06:45 -04:00
7143913144 Merge remote-tracking branch 'origin/main' 2023-08-02 15:55:17 -04:00
75848ef250 Correct bd test, add dictionary for custom fuel to hub fuel 2023-08-02 15:55:11 -04:00
9f24ed8fae Update hub/version.py 2023-08-02 15:49:54 -04:00
4b5c037ffa small bug 2023-08-02 14:46:17 -04:00
6b89008698 Merge branch 'main' into reviewing_hub
# Conflicts:
#	hub/imports/construction/eilat_physics_parameters.py
#	hub/imports/construction/nrcan_physics_parameters.py
2023-08-02 14:35:31 -04:00
0f982df32e Merge pull request 'retrofit_db_changes' (#38) from retrofit_db_changes into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/38
2023-08-02 14:26:14 -04:00
c38b025c2e Correct bd test, add dictionary for custom fuel to hub fuel 2023-08-02 12:43:51 -04:00
4d0f247a83 cost completed 2023-08-01 16:41:37 -04:00
a147afe76f modified thermal zones to be created in different ways depending on the user needs 2023-07-31 17:01:35 -04:00
e44abc2f3a Update hub/version.py 2023-07-31 16:12:03 -04:00
421c69e97e add missing file to the setup 2023-07-31 16:11:19 -04:00
2292bff1b8 building info can now be retrieved via application, user and scenario 2023-07-31 16:08:10 -04:00
1abf76125f building info can now be retrieved via application, user and scenario 2023-07-31 12:42:02 -04:00
ce995c375d Merge remote-tracking branch 'origin/main' into retrofit_db_changes 2023-07-31 12:40:29 -04:00
2cc938e416 Update hub/version.py 2023-07-31 12:22:44 -04:00
eb246a18e6 Additional changes, the db_control building info need to be changed 2023-07-28 14:55:06 -04:00
57322e1b19 Merge pull request 'system_catalog_fix' (#37) from system_catalog_fix into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/37
2023-07-28 14:06:43 -04:00
24324988b6 added dictionary from montreal fuel type to hub 2023-07-28 13:18:23 -04:00
ee419b0849 added dictionary from montreal fuel type to hub 2023-07-28 12:26:17 -04:00
f95c45660e persistence changes 2023-07-28 08:25:47 -04:00
4add8b2cff initial changes 2023-07-26 15:03:31 -04:00
5afb60e7c4 Partially correct unit-test 2023-07-25 16:14:52 -04:00
35e50cf551 Partially correct unit-test 2023-07-25 16:00:14 -04:00
066a25bfee Partially correct unit-test 2023-07-25 15:36:49 -04:00
0d63a6c4e9 Update hub/version.py 2023-07-25 12:04:41 -04:00
e82d20bfd7 Merge pull request 'added surface type to construction names' (#36) from bug_in_construction_importers into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/36
2023-07-25 12:03:42 -04:00
a3d4390a19 added surface type to construction names 2023-07-25 12:02:20 -04:00
f3208f1735 starting the process of changing thermal_zones generation, NOT working version 2023-07-25 11:40:47 -04:00
c5c298d708 Update hub/version.py 2023-07-25 09:31:24 -04:00
1ae07cc14f Merge remote-tracking branch 'origin/main' 2023-07-25 09:29:46 -04:00
37c09df932 IDF export will not longer blindly assign the Montreal as weather file 2023-07-25 09:29:39 -04:00
92f0c81109 Update hub/version.py 2023-07-24 12:33:55 -04:00
a8c961f7bc Correct trimesh dependency 2023-07-24 12:33:24 -04:00
2c7fc66423 Update hub/version.py 2023-07-21 16:12:37 -04:00
1a8dbe7f87 Merge pull request 'erased not needed print' (#35) from erased_print_in_meb into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/35
2023-07-18 12:29:58 -04:00
89bfa33d1e erased not needed print 2023-07-18 11:56:18 -04:00
6dd9fc8328 Update 'hub/version.py' 2023-07-18 11:53:01 -04:00
d27134677d Merge pull request 'bug None/1000 solved' (#34) from bug_in_costs_catalog into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/34
2023-07-18 11:52:37 -04:00
f8ccdf7eeb bug None/1000 solved 2023-07-18 11:49:38 -04:00
883db8840c Update 'hub/version.py' 2023-07-17 18:55:24 -04:00
06ddbd0f98 Merge pull request 'errors_in_meb' (#33) from errors_in_meb into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/33
2023-07-17 18:53:32 -04:00
34d2f5e8ab Merge branch 'fixing_bugs_from_eilat_case' into errors_in_meb 2023-07-17 17:27:02 -04:00
285c819d31 rewritten thermal_openings creation to allow change in window ratio (doing it wrong previously) 2023-07-17 17:26:47 -04:00
2744f86c66 rewritten thermal_openings creation to allow change in window ratio (doing it wrong previously) 2023-07-17 17:26:28 -04:00
91f77ee477 updated RECOGNIZED_FUNTIONS_AND_USAGES.md 2023-07-17 12:36:16 -04:00
caaa51774c changed window uvalue in eilat archetypes 2023-07-17 10:56:48 -04:00
dede8d4929 Update 'hub/version.py' 2023-07-14 12:54:55 -04:00
df2e7a9370 Merge pull request 'updating_catalogs_with_to_string' (#31) from updating_catalogs_with_to_string into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/31
2023-07-14 12:48:24 -04:00
048f79e1ae added two new values in dictionaries 2023-07-13 17:47:04 -04:00
2af993630a changed units to new item in dictionaries in to_dictionary methods 2023-07-13 17:06:42 -04:00
24620d485e added also to_dictionary to greenery catalog (Greenery catalog must be reviewed!!) 2023-07-13 17:00:33 -04:00
6ab3ee3dc3 solved some issues on units in costs catalog 2023-07-13 15:53:20 -04:00
639e5a952d upgraded catalogs with to_dictionary method and __str__ 2023-07-13 13:05:23 -04:00
c69f071958 Merge pull request 'add_data_for_quebec_and_israel' (#30) from add_data_for_quebec_and_israel into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/30
2023-07-12 16:29:55 -04:00
8434bdef04 solved bug in idf 2023-07-12 16:28:14 -04:00
8be6e3b3e8 small update to eilat_archetypes.json 2023-07-12 15:55:26 -04:00
ae157a9243 added eilat construction to catalog and imports and erased unused city_to_climate_reference_city in construction_helper.py 2023-07-12 11:36:35 -04:00
5247eda305 added eilat usage to catalog and imports 2023-07-11 16:34:11 -04:00
f146cf3281 added 2 more regions to the _epw_file dictionary 2023-07-10 16:32:22 -04:00
f2201c9575 remove useless class 2023-07-10 16:11:44 -04:00
7da26947d7 Merge remote-tracking branch 'origin/main' into main 2023-07-10 15:42:28 -04:00
3818bf5595 correct typo 2023-07-10 15:05:34 -04:00
8839cf07c2 improve unit tests and bug fix 2023-07-10 15:03:04 -04:00
af93dbb3a1 improve unit tests and bug fix 2023-07-10 14:55:36 -04:00
46ed69ef6e remove bag fix 2023-07-10 14:28:31 -04:00
e646f97890 correct exccluded file 2023-07-10 14:13:58 -04:00
778aa5e187 Consistence improvements and name fixing 2023-07-10 14:13:06 -04:00
06de614588 Merge remote-tracking branch 'origin/main' 2023-07-10 14:05:43 -04:00
408a32f781 Consistence improvements and name fixing 2023-07-10 14:05:38 -04:00
461d8d4ca0 Merge pull request 'Fix bug with opening pickle files from a pickle created in another operating system (for Windows and Linux only)' (#29) from fix_multi_os_pickle_load_bug into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/29
2023-07-10 14:00:14 -04:00
66739bc306 Fix bug with opening pickle files from a pickle created in another operating system (for Windows and Linux only) 2023-07-07 22:16:55 -04:00
bca68f33ff solved a bug assigning the energy systems to the buildings 2023-07-07 15:31:32 -04:00
68ca47d644 Merge remote-tracking branch 'origin/main' into main 2023-07-07 14:36:38 -04:00
f39c1deff4 Update 'hub/version.py' 2023-07-07 14:36:02 -04:00
c8379eeb38 Merge remote-tracking branch 'origin/main' into main 2023-07-07 14:35:24 -04:00
1acdebd6e7 Merge remote-tracking branch 'origin/main' 2023-07-07 14:33:41 -04:00
1ee5fd9dc5 Bug in geojson import 2023-07-07 14:33:34 -04:00
dfd41250e5 Merge pull request 'modified ground_temperature name due to an overwriting of year in month' (#28) from bug_in_ground_temperature into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/28
2023-07-07 14:00:05 -04:00
db2687f75f changed name for a more clear understanding 2023-07-07 13:55:19 -04:00
2612d63414 Merge remote-tracking branch 'origin/main' into main 2023-07-07 12:59:38 -04:00
722b41aa10 Remove deprecated waring in setup.py 2023-07-07 12:36:34 -04:00
93c867558a modified ground_temperature name due to an overwriting of year in month 2023-07-07 11:57:24 -04:00
0d05960102 no comments 2023-06-13 16:19:51 -04:00
1d3293a55f Update 'hub/version.py' 2023-06-13 12:50:56 -04:00
fbdd4e369e Bug fixing in associate energy systems 2023-06-13 12:48:46 -04:00
d11c67615c Update 'hub/version.py' 2023-06-09 13:42:23 -04:00
99ba0d430b Merge pull request 'solved_bugs_ep' (#27) from solved_bugs_ep into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/27
2023-06-09 13:41:48 -04:00
5dcead4e5d bug in function reading, expecting str and getting int 2023-06-09 13:36:21 -04:00
e07156ac30 Merge remote-tracking branch 'origin/main' into main 2023-06-09 13:12:12 -04:00
db1d2362c8 bug in idf.py and added uuid to building_name in geojson importer 2023-06-09 13:12:00 -04:00
0989aadbf4 Update 'hub/version.py' 2023-06-09 12:31:33 -04:00
23cb327e1b Bug fixing in associate energy systems 2023-06-09 12:31:01 -04:00
7eafd33b33 Update 'hub/version.py' 2023-06-09 11:28:41 -04:00
0c7d428aa6 Merge remote-tracking branch 'origin/some_improvements_in_city_model' 2023-06-09 11:22:18 -04:00
08df06dc2e Bug fixing in remove_city_object method 2023-06-09 11:21:45 -04:00
65f9e3e43e added thermal_zones to the building 2023-06-08 14:18:29 -04:00
e14bc5c2bd Update 'hub/version.py' 2023-06-08 11:20:17 -04:00
4e22bfec4a Merge remote-tracking branch 'origin/main' 2023-06-08 11:16:10 -04:00
745c8f9080 add weather region_codes 2023-06-08 11:16:04 -04:00
650966c195 Merge pull request 'systems_caltalog_xml' (#26) from systems_caltalog_xml into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/26
2023-06-08 10:30:53 -04:00
4e382f0633 updated systems catalog test 2023-06-08 09:58:57 -04:00
e0f11b7c02 Merge branch 'main' into retrofit_project 2023-06-08 09:30:25 -04:00
38bda1920d Merge remote-tracking branch 'origin/retrofit_project' into retrofit_project 2023-06-08 09:30:04 -04:00
56fae48277 Changed systems xml file to include new systems without cooling 2023-06-07 16:17:46 -04:00
357e9f4a7e add weather region_codes 2023-06-07 15:27:38 -04:00
ea00266626 Merge pull request 'retrofit_project' (#25) from retrofit_project into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/25
2023-06-07 14:50:00 -04:00
8e20e747a5 Merge remote-tracking branch 'origin/retrofit_project' into retrofit_project 2023-06-07 14:40:54 -04:00
d67c5c438b solved bug in _calculate_working_hours 2023-06-07 14:40:44 -04:00
00a1313235 Update 'hub/version.py' 2023-06-07 13:19:48 -04:00
5cc62ccabd Complete the merge cities 2023-06-07 12:55:03 -04:00
c6852e5af6 Merge remote-tracking branch 'origin/retrofit_project' into retrofit_project 2023-06-07 09:50:50 -04:00
502e1e8279 added system for refurbishment to catalog 2023-06-07 09:48:45 -04:00
4d8e41b4a3 Add canadian weather file as default 2023-06-07 07:58:18 -04:00
eae26f6388 Partial correction of merge function 2023-06-06 17:44:07 -04:00
bd23185ca3 Merge remote-tracking branch 'origin/retrofit_project' into retrofit_project 2023-06-06 16:09:44 -04:00
4ccf16411c solved feature in idf generator 2023-06-06 16:09:33 -04:00
d6e6d06d39 Code quality check 2023-06-06 14:31:25 -04:00
ff642a2c0c Correct the weather factory and remove useless files 2023-06-06 13:32:37 -04:00
8d3d1ac427 Homogenize unit tests and remove useless files 2023-06-05 13:42:15 -04:00
1819541a21 Merge remote-tracking branch 'origin/retrofit_project' into retrofit_project 2023-06-05 11:53:54 -04:00
5e969986fb Refactor in building.
heating and cooling are now heating_demand and cooling_demand
2023-06-05 11:53:47 -04:00
c0a2a8a2f9 solved bug in onsite electrical production 2023-06-05 11:52:29 -04:00
c6b986ad1b Add custom_insel_block parameter to insel MEB and add unit test 2023-06-02 18:48:55 -04:00
1e68e3d40a Partial completion cost needs. 2023-06-02 15:05:29 -04:00
21b1eef3d3 Merge branch 'main' into retrofit_project 2023-06-02 13:18:41 -04:00
dab931cadb Partial correction of hub 2023-06-02 10:25:34 -04:00
1d8a1dc275 solved bug in enumerate 2023-06-01 14:10:03 -04:00
665bf8159b Merge branch 'systems_catalog' into meb_bugs_fixing
# Conflicts:
#	hub/city_model_structure/building.py
#	hub/imports/energy_systems/montreal_custom_energy_system_parameters.py
2023-06-01 13:55:27 -04:00
03a1744f87 Merge remote-tracking branch 'origin/systems_catalog' into systems_catalog 2023-06-01 13:39:49 -04:00
11b38eaa4c modified the place where the energy systems are created 2023-06-01 13:39:29 -04:00
22c4f362fe solved a bug in income.py and added creation of energy systems in buildings in importer 2023-06-01 11:06:16 -04:00
0956f40e42 Bug correction and code quality improvements 2023-05-31 16:55:44 -04:00
3e9060c6a7 Update 'hub/version.py' 2023-05-31 13:56:22 -04:00
909b453036 Merge pull request 'retrofit_project' (#24) from retrofit_project into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/24
2023-05-31 13:55:49 -04:00
11ba482e73 correct bug 2023-05-31 13:53:22 -04:00
6809a4fca3 update pylintrc and correct setup.py 2023-05-31 13:51:35 -04:00
eeb75c101f update pylintrc and correct setup.py 2023-05-31 11:04:41 -04:00
891b660824 Refactor and code quality improvements 2023-05-30 17:13:49 -04:00
b60be9c39e Create Linux documentation and add INSEL to Windows documentation 2023-04-24 15:42:07 -04:00
ac96b0467b Update Windows documentation for new version of PyCharm and gitea 2023-04-24 14:14:14 -04:00
363 changed files with 160999 additions and 553846 deletions

View File

@ -9,4 +9,4 @@
**/hub/logs/ **/hub/logs/
**/__pycache__/ **/__pycache__/
**/.idea/ **/.idea/
cerc_hub.egg-info

50
hub/LINUX_INSTALL.md Normal file
View File

@ -0,0 +1,50 @@
# LINUX_INSTALL
## Prepare your environment
### Install Miniconda
1. Get the link for the latest version of Miniconda from https://docs.conda.io/en/latest/miniconda.html
2. Download the installer using wget
````
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
````
3. Make the installer executable
````
chmod +x ./Miniconda3-latest-Linux-x86_64.sh
````
4. Run the installer
````
./Miniconda3-latest-Linux-x86_64.sh
````
5. Holder enter until you are prompted to accept the license terms. Enter yes.
6. Initialize the conda environment
````
conda init bash
````
7. Source .bashrc
````
source ~/.bashrc
````
8. Create a conda environment for the hub
````
conda create --name hub python=3.9.16
````
### Setup SRA
1. Get the sra binary and libshortwave.so library from Guille or Koa
2. Place the binary and the library into your directory of choice
3. Make a symlink for the binary and place it into /usr/local/bin/sra
````
sudo ln -s ~/sra /usr/local/bin/sra
````
4. Make a symlink for the library and place it into /usr/local/lib/libshortwave.so
````
sudo ln -s ~/libshortwave.so /usr/local/lib/libshortwave.so
````
### Setup INSEL
1. TBD
### Get a Python editor
You are welcome to use the Python editor of your preference. The CERC team generally uses PyCharm to develop the hub.
The latest version of PyCharm can be downloaded from [JetBrains website](https://www.jetbrains.com/pycharm/promo/?source=google&medium=cpc&campaign=14127625109&term=pycharm&content=536947779504&gad=1&gclid=CjwKCAjw0ZiiBhBKEiwA4PT9z2AxPfy39x_RcBqlYxJ6sm_s55T9qvA_sZ8ZfkhIVX6FOD-ySbmzARoCcpQQAvD_BwE).
For setup and installation instructions, please view the "Get a Python Editor"
from the [WINDOWS_INSTALL](https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/src/branch/main/hub/WINDOWS_INSTALL.md)
documentation.

View File

@ -1,20 +1,16 @@
# Functions and usages internally recognized within the hub # Functions and usages internally recognized within the hub
The hub uses a list of building functions a building usages that are the only ones recognized. All new categories should be added to the dictionaries that translate from the input formats to the libs functions. From the libs functions to the libs usages and from the libs usages and libs functions to the output formats. The hub uses a list of building functions that are the only ones recognized. All new categories should be added to the dictionaries that translate from the input formats to the hub functions and from the hub functions to the output formats.
Input formats accepted:
* Function:
* pluto
* hft
Output formats accepted: Output formats accepted:
* Function: * Function:
* nrel * nrel
* nrcan * nrcan
* eilat
* Usage: * Usage:
* ca * nrcan
* hft
* comnet * comnet
* eilat
Libs_functions: Libs_functions:
* single family house * single family house

View File

@ -2,16 +2,16 @@
This is an installation guide for Windows, covering all the steps needed to begin developing code for the Urban This is an installation guide for Windows, covering all the steps needed to begin developing code for the Urban
Simulation Platform 'Hub'. At the end of this process you will have installed and configured all the necessary applications, Simulation Platform 'Hub'. At the end of this process you will have installed and configured all the necessary applications,
set up your own project on CERC's Gitlab and created your first python file. set up your own project on CERC's Gitea and created your first python file.
## Prepare your environment ## Prepare your environment
g
To develop any new code for the Urban Simulation Platform you must have the right software applications installed and configured. To develop any new code for the Urban Simulation Platform you must have the right software applications installed and configured.
The Platform is written in python and so the applications you need are: The Platform is written in python and so the applications you need are:
* Miniconda * Miniconda
* SRA Files
* Python Editor * Python Editor
You also need to register a user account with the CERC's code repository on Gitlab and have the necessary permissions for You also need to register a user account with the CERC's code repository on Gitea and have the necessary permissions for
creating new code. For that purpose, please, contact Guillermo (guillermo.gutierrezmorote@concordia.ca) or creating new code. For that purpose, please, contact Guillermo (guillermo.gutierrezmorote@concordia.ca) or
Koa (kekoa.wells@concordia.ca) as soon as possible. Koa (kekoa.wells@concordia.ca) as soon as possible.
@ -47,6 +47,29 @@ _The term '...' is not recognized as the name of a cmdlet, function,..._
To solve it, type 'Set-ExecutionPolicy Unrestricted' as shown in the image. To solve it, type 'Set-ExecutionPolicy Unrestricted' as shown in the image.
### Setup SRA
1. Get the SRA executable and dll files from Guille or Koa
2. Create a folder in "C:\Program Files\" called "sra"
![create_sra](docs/img_windows_install/img_34.png)
3. Copy shortwave_integer.exe and pthreadGC2.dll into the sra folder.
![create_sra](docs/img_windows_install/img_35.png)
4. Add the newly created sra folder to the Path, similar to step 2 from the Miniconda setup above.
![create_sra](docs/img_windows_install/img_36.png)
### Install and setup INSEL
1. Get the INSEL installer from Guille or Koa
2. Run the installer to completion using the default installation path
3. Add the INSEL installation folder to the Path
![create_sra](docs/img_windows_install/img_41.png)
### Get a Python editor ### Get a Python editor
1. You will need a python editor in order to import the existing Hub source code and to write your own python code. 1. You will need a python editor in order to import the existing Hub source code and to write your own python code.
@ -55,7 +78,7 @@ an excellent open-source python editor.
2. Run the installer, and follow the installation instructions for PyCharm, you may change a few options, 2. Run the installer, and follow the installation instructions for PyCharm, you may change a few options,
but the default ones should be fine. but the default ones should be fine.
**NOTE:** If Pycharm asks you to create a Virtual Environment, click **Cancel**. You will do it later using Conda instead. **NOTE:** If PyCharm asks you to create a Virtual Environment, click **Cancel**. You will do it later using Conda instead.
![creating_virtual_environment](docs/img_windows_install/img_31.png) ![creating_virtual_environment](docs/img_windows_install/img_31.png)
@ -70,14 +93,12 @@ You can find it also at **Git->Clone...**
![pycharm get from version control](docs/img_windows_install/img_6.png) ![pycharm get from version control](docs/img_windows_install/img_6.png)
3. Select **Git** as the **Version control**. For the URL use the link to the Hub repository, as seen below. 3. Select **Git** as the **Version control**. Open the [hub repository](https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub)
on Gitea and copy the URL from your browser to use as the URL inside PyCharm.
![pycharm get from version control screen](docs/img_windows_install/img_1.png) ![pycharm get from version control screen](docs/img_windows_install/img_1.png)
(You can also copy this URL by going to the Hub repository in [Gitlab](https://rs-loy-gitlab.concordia.ca/Guille/hub.git) ![gitea get https](docs/img_windows_install/img_39.png)
and clicking on the **Copy URL** button, next to **Clone with HTTPS**)
![gitlab get https](docs/img_windows_install/img_17.png)
The Directory to store the Hub source code locally is automatically created for you. Edit this if you prefer it to be stored somewhere else. The Directory to store the Hub source code locally is automatically created for you. Edit this if you prefer it to be stored somewhere else.
@ -152,7 +173,7 @@ _lca_classes_,... And, click on the **Create** button.
3. Click on the **Git** button in the bottom-left corner to pop-up the window showing the Git information. 3. Click on the **Git** button in the bottom-left corner to pop-up the window showing the Git information.
See your new branch has been created under _Local_. See your new branch has been created under _Local_.
4. Now we need to let the CERC Gitlab repository know about this new branch. You do this by right-clicking on 4. Now we need to let the CERC Gitea repository know about this new branch. You do this by right-clicking on
your branch and selecting **Push...** from the drop-down menu. your branch and selecting **Push...** from the drop-down menu.
5. Then click on the **Push** button at the bottom-right of the **Push Commits** window. 5. Then click on the **Push** button at the bottom-right of the **Push Commits** window.
@ -180,33 +201,35 @@ See the picture below.
![pycharm configuration screen](docs/img_windows_install/img_5.png) ![pycharm configuration screen](docs/img_windows_install/img_5.png)
## Set up a new project on Gitlab ## Set up a new project on Gitea
You will need an account before you can access the Gitea. Please contact Guillermo (guillermo.gutierrezmorote@concordia.ca) or
Koa (kekoa.wells@concordia.ca) to request an account.
1. Open a browser and to the [CERC Git](https://rs-loy-gitlab.concordia.ca/). Click on the blue **New project** button. 1. Open a browser and go to the [CERC Gitea](https://nextgenerations-cities.encs.concordia.ca/). Click on the **+** in the top right
and select "New Repository" or press the **+** below the Organization tab.
![git new project screen](docs/img_windows_install/img_14.png) ![git new project screen](docs/img_windows_install/img_37.png)
2. Choose the **Create blank project** option from the three options seen below. 2. Choose the **Create blank project** option from the three options seen below.
3. Type in a name that describes your project: _hp_workflow_, _bus_system_optimization_... 3. Type in a name that describes your project: _hp_workflow_, _bus_system_optimization_...
(remember to follow the CERC naming conventions described in the [Coding Style](PYGUIDE.md)). (remember to follow the CERC naming conventions described in the [Coding Style](PYGUIDE.md)).
Check the option **Initialize repository with a README**, and ideally, check the **Visibility Level** to be **Public**. Ideally, uncheck the option **Make Repository Private**, and check the **Initialize Repository**
Then click on the **Create project** button. Then click on the **Create project** button.
![git give a name](docs/img_windows_install/img_15.png) ![git give a name](docs/img_windows_install/img_38.png)
You should then see a confirmation screen with all the information about your new project. You should then see a confirmation screen with all the information about your new project.
## Get your project into Pycharm ## Get your project into Pycharm
1. Now you can make a clone of this project, within PyCharm. First, copy the URL by clicking on the blue **Clone** button 1. Now you can make a clone of this project, within PyCharm. First, go to the page of your repository on the Gitea and copy the URL.
and then click on the **Copy URL** button, next to the **Clone with HTTPS** link.
2. Switch back to PyCharm and close the Hub project by choosing **File->Close Project**. You will then see the 2. Switch back to PyCharm and close the Hub project by choosing **File->Close Project**. You will then see the
**Welcome To PyCharm** window again. **Welcome To PyCharm** window again.
3. Clone a copy of your Project into PyCharm, following the steps 2-6 of the _GET THE CERC HUB SOURCE CODE_ 3. Clone a copy of your Project into PyCharm, following the steps 2-6 of the _GET THE CERC HUB SOURCE CODE_
section above, but using the URL link that you just copied for your gitlab project. section above, but using the URL link that you just copied for your Gitea project.
4. Select **File->Settings** to open the **Settings** window. From the panel on the left click on 4. Select **File->Settings** to open the **Settings** window. From the panel on the left click on
**Project:<project name> -> Project Structure**. **Project:<project name> -> Project Structure**.
@ -242,5 +265,5 @@ city = GeometryFactory('citygml', path='myfile.gml').city
9. Always remember to push your own project changes as the last thing you do before ending your working day! 9. Always remember to push your own project changes as the last thing you do before ending your working day!
First, commit your changes by clicking on the green check in the top-right corner of Pycharm. Add a comment that explains briefly your changes. First, commit your changes by clicking on the green check in the top-right corner of Pycharm. Add a comment that explains briefly your changes.
Then, pull by clicking on the blue arrow to be sure that there are no conflicts between your version (local) and the remote one (gitlab). Then, pull by clicking on the blue arrow to be sure that there are no conflicts between your version (local) and the remote one (Gitea).
Once the conflicts are solved and the merge in local is done, push the changes by clicking on the green arrow. Once the conflicts are solved and the merge in local is done, push the changes by clicking on the green arrow.

View File

View File

@ -8,7 +8,7 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
class Catalog: class Catalog:
""" """
Catalogs base class not implemented instance of the Catalog base class, Catalogs base class
catalog_factories will inherit from this class. catalog_factories will inherit from this class.
""" """

View File

@ -1,3 +1,9 @@
"""
Construction helper module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
"""
from hub.helpers import constants as cte from hub.helpers import constants as cte
@ -34,12 +40,24 @@ class ConstructionHelper:
@property @property
def reference_standard_to_construction_period(self): def reference_standard_to_construction_period(self):
"""
Get reference standard to construction period dictionary
:return: {}
"""
return self._reference_standard_to_construction_period return self._reference_standard_to_construction_period
@property @property
def nrel_surfaces_types_to_hub_types(self): def nrel_surfaces_types_to_hub_types(self):
"""
Get reference nrel surface type to hub type dictionary
:return: {}
"""
return self._nrel_surfaces_types_to_hub_types return self._nrel_surfaces_types_to_hub_types
@property @property
def nrcan_surfaces_types_to_hub_types(self): def nrcan_surfaces_types_to_hub_types(self):
"""
Get reference nrcan surface type to hub type dictionary
:return: {}
"""
return self._nrcan_surfaces_types_to_hub_types return self._nrcan_surfaces_types_to_hub_types

View File

@ -0,0 +1,238 @@
"""
Eilat construction catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import json
from pathlib import Path
from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.construction.content import Content
from hub.catalog_factories.construction.construction_helper import ConstructionHelper
from hub.catalog_factories.data_models.construction.construction import Construction
from hub.catalog_factories.data_models.construction.archetype import Archetype
from hub.catalog_factories.data_models.construction.window import Window
from hub.catalog_factories.data_models.construction.material import Material
from hub.catalog_factories.data_models.construction.layer import Layer
import hub.helpers.constants as cte
class EilatCatalog(Catalog):
"""
Eilat catalog class
"""
def __init__(self, path):
_path_archetypes = Path(path / 'eilat_archetypes.json').resolve()
_path_constructions = (path / 'eilat_constructions.json').resolve()
with open(_path_archetypes, 'r', encoding='utf-8') as file:
self._archetypes = json.load(file)
with open(_path_constructions, 'r', encoding='utf-8') as file:
self._constructions = json.load(file)
self._catalog_windows = self._load_windows()
self._catalog_materials = self._load_materials()
self._catalog_constructions = self._load_constructions()
self._catalog_archetypes = self._load_archetypes()
# store the full catalog data model in self._content
self._content = Content(self._catalog_archetypes,
self._catalog_constructions,
self._catalog_materials,
self._catalog_windows)
def _load_windows(self):
_catalog_windows = []
windows = self._constructions['transparent_surfaces']
for window in windows:
name = list(window.keys())[0]
window_id = name
g_value = window[name]['shgc']
window_type = window[name]['type']
frame_ratio = window[name]['frame_ratio']
overall_u_value = window[name]['u_value']
_catalog_windows.append(Window(window_id, frame_ratio, g_value, overall_u_value, name, window_type))
return _catalog_windows
def _load_materials(self):
_catalog_materials = []
materials = self._constructions['materials']
for material in materials:
name = list(material.keys())[0]
material_id = name
no_mass = material[name]['no_mass']
thermal_resistance = None
conductivity = None
density = None
specific_heat = None
solar_absorptance = None
thermal_absorptance = None
visible_absorptance = None
if no_mass:
thermal_resistance = material[name]['thermal_resistance']
else:
solar_absorptance = material[name]['solar_absorptance']
thermal_absorptance = str(1 - float(material[name]['thermal_emittance']))
visible_absorptance = material[name]['visible_absorptance']
conductivity = material[name]['conductivity']
density = material[name]['density']
specific_heat = material[name]['specific_heat']
_material = Material(material_id,
name,
solar_absorptance,
thermal_absorptance,
visible_absorptance,
no_mass,
thermal_resistance,
conductivity,
density,
specific_heat)
_catalog_materials.append(_material)
return _catalog_materials
def _load_constructions(self):
_catalog_constructions = []
constructions = self._constructions['opaque_surfaces']
for construction in constructions:
name = list(construction.keys())[0]
construction_id = name
construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[construction[name]['type']]
layers = []
for layer in construction[name]['layers']:
layer_id = layer
layer_name = layer
material_id = layer
thickness = construction[name]['layers'][layer]
for material in self._catalog_materials:
if str(material_id) == str(material.id):
layers.append(Layer(layer_id, layer_name, material, thickness))
break
_catalog_constructions.append(Construction(construction_id, construction_type, name, layers))
return _catalog_constructions
def _load_archetypes(self):
_catalog_archetypes = []
archetypes = self._archetypes['archetypes']
for archetype in archetypes:
archetype_id = f'{archetype["function"]}_{archetype["period_of_construction"]}_{archetype["climate_zone"]}'
function = archetype['function']
name = archetype_id
climate_zone = archetype['climate_zone']
construction_period = archetype['period_of_construction']
average_storey_height = archetype['average_storey_height']
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
infiltration_rate_for_ventilation_system_off = archetype[
'infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
infiltration_rate_for_ventilation_system_on = archetype[
'infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
archetype_constructions = []
for archetype_construction in archetype['constructions']:
archetype_construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[archetype_construction]
archetype_construction_name = archetype['constructions'][archetype_construction]['opaque_surface_name']
for construction in self._catalog_constructions:
if archetype_construction_type == construction.type and construction.name == archetype_construction_name:
_construction = None
_window = None
_window_ratio = None
if 'transparent_surface_name' in archetype['constructions'][archetype_construction].keys():
_window_ratio = archetype['constructions'][archetype_construction]['transparent_ratio']
_window_id = archetype['constructions'][archetype_construction]['transparent_surface_name']
for window in self._catalog_windows:
if _window_id == window.id:
_window = window
break
_construction = Construction(construction.id,
construction.type,
construction.name,
construction.layers,
_window_ratio,
_window)
archetype_constructions.append(_construction)
break
_catalog_archetypes.append(Archetype(archetype_id,
name,
function,
climate_zone,
construction_period,
archetype_constructions,
average_storey_height,
None,
extra_loses_due_to_thermal_bridges,
None,
infiltration_rate_for_ventilation_system_off,
infiltration_rate_for_ventilation_system_on,
0,
0))
return _catalog_archetypes
def names(self, category=None):
"""
Get the catalog elements names
:parm: optional category filter
"""
if category is None:
_names = {'archetypes': [], 'constructions': [], 'materials': [], 'windows': []}
for archetype in self._content.archetypes:
_names['archetypes'].append(archetype.name)
for construction in self._content.constructions:
_names['constructions'].append(construction.name)
for material in self._content.materials:
_names['materials'].append(material.name)
for window in self._content.windows:
_names['windows'].append(window.name)
else:
_names = {category: []}
if category.lower() == 'archetypes':
for archetype in self._content.archetypes:
_names[category].append(archetype.name)
elif category.lower() == 'constructions':
for construction in self._content.constructions:
_names[category].append(construction.name)
elif category.lower() == 'materials':
for material in self._content.materials:
_names[category].append(material.name)
elif category.lower() == 'windows':
for window in self._content.windows:
_names[category].append(window.name)
else:
raise ValueError(f'Unknown category [{category}]')
return _names
def entries(self, category=None):
"""
Get the catalog elements
:parm: optional category filter
"""
if category is None:
return self._content
if category.lower() == 'archetypes':
return self._content.archetypes
if category.lower() == 'constructions':
return self._content.constructions
if category.lower() == 'materials':
return self._content.materials
if category.lower() == 'windows':
return self._content.windows
raise ValueError(f'Unknown category [{category}]')
def get_entry(self, name):
"""
Get one catalog element by names
:parm: entry name
"""
for entry in self._content.archetypes:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.constructions:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.materials:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.windows:
if entry.name.lower() == name.lower():
return entry
raise IndexError(f"{name} doesn't exists in the catalog")

View File

@ -15,15 +15,19 @@ from hub.catalog_factories.data_models.construction.archetype import Archetype
from hub.catalog_factories.data_models.construction.window import Window from hub.catalog_factories.data_models.construction.window import Window
from hub.catalog_factories.data_models.construction.material import Material from hub.catalog_factories.data_models.construction.material import Material
from hub.catalog_factories.data_models.construction.layer import Layer from hub.catalog_factories.data_models.construction.layer import Layer
import hub.helpers.constants as cte
class NrcanCatalog(Catalog): class NrcanCatalog(Catalog):
"""
Nrcan catalog class
"""
def __init__(self, path): def __init__(self, path):
_path_archetypes = Path(path / 'nrcan_archetypes.json').resolve() _path_archetypes = Path(path / 'nrcan_archetypes.json').resolve()
_path_constructions = (path / 'nrcan_constructions.json') _path_constructions = (path / 'nrcan_constructions.json').resolve()
with open(_path_archetypes, 'r') as file: with open(_path_archetypes, 'r', encoding='utf-8') as file:
self._archetypes = json.load(file) self._archetypes = json.load(file)
with open(_path_constructions, 'r') as file: with open(_path_constructions, 'r', encoding='utf-8') as file:
self._constructions = json.load(file) self._constructions = json.load(file)
self._catalog_windows = self._load_windows() self._catalog_windows = self._load_windows()
@ -118,8 +122,18 @@ class NrcanCatalog(Catalog):
average_storey_height = archetype['average_storey_height'] average_storey_height = archetype['average_storey_height']
thermal_capacity = float(archetype['thermal_capacity']) * 1000 thermal_capacity = float(archetype['thermal_capacity']) * 1000
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges'] extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
infiltration_rate_for_ventilation_system_off = archetype['infiltration_rate_for_ventilation_system_off'] infiltration_rate_for_ventilation_system_off = (
infiltration_rate_for_ventilation_system_on = archetype['infiltration_rate_for_ventilation_system_on'] archetype['infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
)
infiltration_rate_for_ventilation_system_on = (
archetype['infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
)
infiltration_rate_area_for_ventilation_system_off = (
archetype['infiltration_rate_area_for_ventilation_system_off'] * 1
)
infiltration_rate_area_for_ventilation_system_on = (
archetype['infiltration_rate_area_for_ventilation_system_on'] * 1
)
archetype_constructions = [] archetype_constructions = []
for archetype_construction in archetype['constructions']: for archetype_construction in archetype['constructions']:
@ -145,7 +159,6 @@ class NrcanCatalog(Catalog):
_window) _window)
archetype_constructions.append(_construction) archetype_constructions.append(_construction)
break break
_catalog_archetypes.append(Archetype(archetype_id, _catalog_archetypes.append(Archetype(archetype_id,
name, name,
function, function,
@ -157,7 +170,10 @@ class NrcanCatalog(Catalog):
extra_loses_due_to_thermal_bridges, extra_loses_due_to_thermal_bridges,
None, None,
infiltration_rate_for_ventilation_system_off, infiltration_rate_for_ventilation_system_off,
infiltration_rate_for_ventilation_system_on)) infiltration_rate_for_ventilation_system_on,
infiltration_rate_area_for_ventilation_system_off,
infiltration_rate_area_for_ventilation_system_on
))
return _catalog_archetypes return _catalog_archetypes
def names(self, category=None): def names(self, category=None):
@ -200,17 +216,15 @@ class NrcanCatalog(Catalog):
""" """
if category is None: if category is None:
return self._content return self._content
else: if category.lower() == 'archetypes':
if category.lower() == 'archetypes': return self._content.archetypes
return self._content.archetypes if category.lower() == 'constructions':
elif category.lower() == 'constructions': return self._content.constructions
return self._content.constructions if category.lower() == 'materials':
elif category.lower() == 'materials': return self._content.materials
return self._content.materials if category.lower() == 'windows':
elif category.lower() == 'windows': return self._content.windows
return self._content.windows raise ValueError(f'Unknown category [{category}]')
else:
raise ValueError(f'Unknown category [{category}]')
def get_entry(self, name): def get_entry(self, name):
""" """

View File

@ -5,8 +5,8 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import xmltodict
from pathlib import Path from pathlib import Path
import xmltodict
from hub.catalog_factories.catalog import Catalog from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.construction.window import Window from hub.catalog_factories.data_models.construction.window import Window
from hub.catalog_factories.data_models.construction.material import Material from hub.catalog_factories.data_models.construction.material import Material
@ -15,15 +15,19 @@ from hub.catalog_factories.data_models.construction.construction import Construc
from hub.catalog_factories.data_models.construction.content import Content from hub.catalog_factories.data_models.construction.content import Content
from hub.catalog_factories.data_models.construction.archetype import Archetype from hub.catalog_factories.data_models.construction.archetype import Archetype
from hub.catalog_factories.construction.construction_helper import ConstructionHelper from hub.catalog_factories.construction.construction_helper import ConstructionHelper
import hub.helpers.constants as cte
class NrelCatalog(Catalog): class NrelCatalog(Catalog):
"""
Nrel catalog class
"""
def __init__(self, path): def __init__(self, path):
archetypes_path = str(Path(path / 'us_archetypes.xml').resolve()) archetypes_path = str(Path(path / 'us_archetypes.xml').resolve())
constructions_path = str(Path(path / 'us_constructions.xml').resolve()) constructions_path = str(Path(path / 'us_constructions.xml').resolve())
with open(constructions_path) as xml: with open(constructions_path, 'r', encoding='utf-8') as xml:
self._constructions = xmltodict.parse(xml.read(), force_list=('material', 'window', 'construction', 'layer')) self._constructions = xmltodict.parse(xml.read(), force_list=('material', 'window', 'construction', 'layer'))
with open(archetypes_path) as xml: with open(archetypes_path, 'r', encoding='utf-8') as xml:
self._archetypes = xmltodict.parse(xml.read(), force_list=('archetype', 'construction')) self._archetypes = xmltodict.parse(xml.read(), force_list=('archetype', 'construction'))
self._catalog_windows = self._load_windows() self._catalog_windows = self._load_windows()
self._catalog_materials = self._load_materials() self._catalog_materials = self._load_materials()
@ -58,9 +62,9 @@ class NrelCatalog(Catalog):
thermal_absorptance = float(material['thermal_absorptance']['#text']) thermal_absorptance = float(material['thermal_absorptance']['#text'])
visible_absorptance = float(material['visible_absorptance']['#text']) visible_absorptance = float(material['visible_absorptance']['#text'])
no_mass = False no_mass = False
thermal_resistance = None, thermal_resistance = None
conductivity = None, conductivity = None
density = None, density = None
specific_heat = None specific_heat = None
if 'no_mass' in material and material['no_mass'] == 'true': if 'no_mass' in material and material['no_mass'] == 'true':
no_mass = True no_mass = True
@ -121,10 +125,10 @@ class NrelCatalog(Catalog):
indirect_heated_ratio = float(archetype['indirect_heated_ratio']['#text']) indirect_heated_ratio = float(archetype['indirect_heated_ratio']['#text'])
infiltration_rate_for_ventilation_system_off = float( infiltration_rate_for_ventilation_system_off = float(
archetype['infiltration_rate_for_ventilation_system_off']['#text'] archetype['infiltration_rate_for_ventilation_system_off']['#text']
) ) / cte.HOUR_TO_SECONDS
infiltration_rate_for_ventilation_system_on = float( infiltration_rate_for_ventilation_system_on = float(
archetype['infiltration_rate_for_ventilation_system_on']['#text'] archetype['infiltration_rate_for_ventilation_system_on']['#text']
) ) / cte.HOUR_TO_SECONDS
archetype_constructions = [] archetype_constructions = []
for archetype_construction in archetype['constructions']['construction']: for archetype_construction in archetype['constructions']['construction']:
@ -158,7 +162,9 @@ class NrelCatalog(Catalog):
extra_loses_due_to_thermal_bridges, extra_loses_due_to_thermal_bridges,
indirect_heated_ratio, indirect_heated_ratio,
infiltration_rate_for_ventilation_system_off, infiltration_rate_for_ventilation_system_off,
infiltration_rate_for_ventilation_system_on)) infiltration_rate_for_ventilation_system_on,
0,
0))
return _catalog_archetypes return _catalog_archetypes
def names(self, category=None): def names(self, category=None):
@ -201,17 +207,15 @@ class NrelCatalog(Catalog):
""" """
if category is None: if category is None:
return self._content return self._content
else: if category.lower() == 'archetypes':
if category.lower() == 'archetypes': return self._content.archetypes
return self._content.archetypes if category.lower() == 'constructions':
elif category.lower() == 'constructions': return self._content.constructions
return self._content.constructions if category.lower() == 'materials':
elif category.lower() == 'materials': return self._content.materials
return self._content.materials if category.lower() == 'windows':
elif category.lower() == 'windows': return self._content.windows
return self._content.windows raise ValueError(f'Unknown category [{category}]')
else:
raise ValueError(f'Unknown category [{category}]')
def get_entry(self, name): def get_entry(self, name):
""" """

View File

@ -0,0 +1,242 @@
"""
Palma construction catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Cecilia Pérez Pérez cperez@irec.cat
"""
import json
from pathlib import Path
from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.construction.content import Content
from hub.catalog_factories.construction.construction_helper import ConstructionHelper
from hub.catalog_factories.data_models.construction.construction import Construction
from hub.catalog_factories.data_models.construction.archetype import Archetype
from hub.catalog_factories.data_models.construction.window import Window
from hub.catalog_factories.data_models.construction.material import Material
from hub.catalog_factories.data_models.construction.layer import Layer
import hub.helpers.constants as cte
class PalmaCatalog(Catalog):
"""
Palma catalog class
"""
def __init__(self, path):
_path_archetypes = Path(path / 'palma_archetypes.json').resolve()
_path_constructions = (path / 'palma_constructions.json').resolve()
with open(_path_archetypes, 'r', encoding='utf-8') as file:
self._archetypes = json.load(file)
with open(_path_constructions, 'r', encoding='utf-8') as file:
self._constructions = json.load(file)
self._catalog_windows = self._load_windows()
self._catalog_materials = self._load_materials()
self._catalog_constructions = self._load_constructions()
self._catalog_archetypes = self._load_archetypes()
# store the full catalog data model in self._content
self._content = Content(self._catalog_archetypes,
self._catalog_constructions,
self._catalog_materials,
self._catalog_windows)
def _load_windows(self):
_catalog_windows = []
windows = self._constructions['transparent_surfaces']
for window in windows:
name = list(window.keys())[0]
window_id = name
g_value = window[name]['shgc']
window_type = window[name]['type']
frame_ratio = window[name]['frame_ratio']
overall_u_value = window[name]['u_value']
_catalog_windows.append(Window(window_id, frame_ratio, g_value, overall_u_value, name, window_type))
return _catalog_windows
def _load_materials(self):
_catalog_materials = []
materials = self._constructions['materials']
for material in materials:
name = list(material.keys())[0]
material_id = name
no_mass = material[name]['no_mass']
thermal_resistance = None
conductivity = None
density = None
specific_heat = None
solar_absorptance = None
thermal_absorptance = None
visible_absorptance = None
if no_mass:
thermal_resistance = material[name]['thermal_resistance']
else:
solar_absorptance = material[name]['solar_absorptance']
thermal_absorptance = str(1 - float(material[name]['thermal_emittance']))
visible_absorptance = material[name]['visible_absorptance']
conductivity = material[name]['conductivity']
density = material[name]['density']
specific_heat = material[name]['specific_heat']
_material = Material(material_id,
name,
solar_absorptance,
thermal_absorptance,
visible_absorptance,
no_mass,
thermal_resistance,
conductivity,
density,
specific_heat)
_catalog_materials.append(_material)
return _catalog_materials
def _load_constructions(self):
_catalog_constructions = []
constructions = self._constructions['opaque_surfaces']
for construction in constructions:
name = list(construction.keys())[0]
construction_id = name
construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[construction[name]['type']]
layers = []
for layer in construction[name]['layers']:
layer_id = layer
layer_name = layer
material_id = layer
thickness = construction[name]['layers'][layer]
for material in self._catalog_materials:
if str(material_id) == str(material.id):
layers.append(Layer(layer_id, layer_name, material, thickness))
break
_catalog_constructions.append(Construction(construction_id, construction_type, name, layers))
return _catalog_constructions
def _load_archetypes(self):
_catalog_archetypes = []
archetypes = self._archetypes['archetypes']
for archetype in archetypes:
archetype_id = f'{archetype["function"]}_{archetype["period_of_construction"]}_{archetype["climate_zone"]}'
function = archetype['function']
name = archetype_id
climate_zone = archetype['climate_zone']
construction_period = archetype['period_of_construction']
average_storey_height = archetype['average_storey_height']
thermal_capacity = float(archetype['thermal_capacity']) * 1000
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
infiltration_rate_for_ventilation_system_off = archetype['infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
infiltration_rate_for_ventilation_system_on = archetype['infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
infiltration_rate_area_for_ventilation_system_off = (
archetype['infiltration_rate_area_for_ventilation_system_off'] * 1
)
infiltration_rate_area_for_ventilation_system_on = (
archetype['infiltration_rate_area_for_ventilation_system_on'] * 1
)
archetype_constructions = []
for archetype_construction in archetype['constructions']:
archetype_construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[archetype_construction]
archetype_construction_name = archetype['constructions'][archetype_construction]['opaque_surface_name']
for construction in self._catalog_constructions:
if archetype_construction_type == construction.type and construction.name == archetype_construction_name:
_construction = None
_window = None
_window_ratio = None
if 'transparent_surface_name' in archetype['constructions'][archetype_construction].keys():
_window_ratio = archetype['constructions'][archetype_construction]['transparent_ratio']
_window_id = archetype['constructions'][archetype_construction]['transparent_surface_name']
for window in self._catalog_windows:
if _window_id == window.id:
_window = window
break
_construction = Construction(construction.id,
construction.type,
construction.name,
construction.layers,
_window_ratio,
_window)
archetype_constructions.append(_construction)
break
_catalog_archetypes.append(Archetype(archetype_id,
name,
function,
climate_zone,
construction_period,
archetype_constructions,
average_storey_height,
thermal_capacity,
extra_loses_due_to_thermal_bridges,
None,
infiltration_rate_for_ventilation_system_off,
infiltration_rate_for_ventilation_system_on,
infiltration_rate_area_for_ventilation_system_off,
infiltration_rate_area_for_ventilation_system_on))
return _catalog_archetypes
def names(self, category=None):
"""
Get the catalog elements names
:parm: optional category filter
"""
if category is None:
_names = {'archetypes': [], 'constructions': [], 'materials': [], 'windows': []}
for archetype in self._content.archetypes:
_names['archetypes'].append(archetype.name)
for construction in self._content.constructions:
_names['constructions'].append(construction.name)
for material in self._content.materials:
_names['materials'].append(material.name)
for window in self._content.windows:
_names['windows'].append(window.name)
else:
_names = {category: []}
if category.lower() == 'archetypes':
for archetype in self._content.archetypes:
_names[category].append(archetype.name)
elif category.lower() == 'constructions':
for construction in self._content.constructions:
_names[category].append(construction.name)
elif category.lower() == 'materials':
for material in self._content.materials:
_names[category].append(material.name)
elif category.lower() == 'windows':
for window in self._content.windows:
_names[category].append(window.name)
else:
raise ValueError(f'Unknown category [{category}]')
return _names
def entries(self, category=None):
"""
Get the catalog elements
:parm: optional category filter
"""
if category is None:
return self._content
if category.lower() == 'archetypes':
return self._content.archetypes
if category.lower() == 'constructions':
return self._content.constructions
if category.lower() == 'materials':
return self._content.materials
if category.lower() == 'windows':
return self._content.windows
raise ValueError(f'Unknown category [{category}]')
def get_entry(self, name):
"""
Get one catalog element by names
:parm: entry name
"""
for entry in self._content.archetypes:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.constructions:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.materials:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.windows:
if entry.name.lower() == name.lower():
return entry
raise IndexError(f"{name} doesn't exists in the catalog")

View File

@ -4,17 +4,23 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import logging
from pathlib import Path from pathlib import Path
from typing import TypeVar from typing import TypeVar
from hub.catalog_factories.construction.nrel_catalog import NrelCatalog
from hub.helpers.utils import validate_import_export_type
from hub.catalog_factories.construction.nrcan_catalog import NrcanCatalog from hub.catalog_factories.construction.nrcan_catalog import NrcanCatalog
from hub.catalog_factories.construction.nrel_catalog import NrelCatalog
from hub.catalog_factories.construction.eilat_catalog import EilatCatalog
from hub.catalog_factories.construction.palma_catalog import PalmaCatalog
from hub.helpers.utils import validate_import_export_type
Catalog = TypeVar('Catalog') Catalog = TypeVar('Catalog')
class ConstructionCatalogFactory: class ConstructionCatalogFactory:
"""
Construction catalog factory class
"""
def __init__(self, handler, base_path=None): def __init__(self, handler, base_path=None):
if base_path is None: if base_path is None:
base_path = Path(Path(__file__).parent.parent / 'data/construction') base_path = Path(Path(__file__).parent.parent / 'data/construction')
@ -32,10 +38,24 @@ class ConstructionCatalogFactory:
@property @property
def _nrcan(self): def _nrcan(self):
""" """
Retrieve NREL catalog Retrieve NRCAN catalog
""" """
return NrcanCatalog(self._path) return NrcanCatalog(self._path)
@property
def _eilat(self):
"""
Retrieve Eilat catalog
"""
return EilatCatalog(self._path)
@property
def _palma(self):
"""
Retrieve Palma catalog
"""
return PalmaCatalog(self._path)
@property @property
def catalog(self) -> Catalog: def catalog(self) -> Catalog:
""" """

View File

View File

@ -18,9 +18,12 @@ from hub.catalog_factories.data_models.cost.income import Income
class MontrealCustomCatalog(Catalog): class MontrealCustomCatalog(Catalog):
"""
Montreal custom catalog class
"""
def __init__(self, path): def __init__(self, path):
path = (path / 'montreal_costs.xml') path = (path / 'montreal_costs.xml').resolve()
with open(path) as xml: with open(path, 'r', encoding='utf-8') as xml:
self._archetypes = xmltodict.parse(xml.read(), force_list='archetype') self._archetypes = xmltodict.parse(xml.read(), force_list='archetype')
# store the full catalog data model in self._content # store the full catalog data model in self._content

View File

@ -13,7 +13,7 @@ from hub.catalog_factories.cost.montreal_custom_catalog import MontrealCustomCat
Catalog = TypeVar('Catalog') Catalog = TypeVar('Catalog')
class CostCatalogFactory: class CostsCatalogFactory:
""" """
CostsCatalogFactory class CostsCatalogFactory class
""" """

View File

@ -9,6 +9,9 @@ from hub.catalog_factories.data_models.construction.construction import Construc
class Archetype: class Archetype:
"""
Archetype class
"""
def __init__(self, archetype_id, def __init__(self, archetype_id,
name, name,
function, function,
@ -20,7 +23,10 @@ class Archetype:
extra_loses_due_to_thermal_bridges, extra_loses_due_to_thermal_bridges,
indirect_heated_ratio, indirect_heated_ratio,
infiltration_rate_for_ventilation_system_off, infiltration_rate_for_ventilation_system_off,
infiltration_rate_for_ventilation_system_on): infiltration_rate_for_ventilation_system_on,
infiltration_rate_area_for_ventilation_system_off,
infiltration_rate_area_for_ventilation_system_on
):
self._id = archetype_id self._id = archetype_id
self._name = name self._name = name
self._function = function self._function = function
@ -33,6 +39,8 @@ class Archetype:
self._indirect_heated_ratio = indirect_heated_ratio self._indirect_heated_ratio = indirect_heated_ratio
self._infiltration_rate_for_ventilation_system_off = infiltration_rate_for_ventilation_system_off self._infiltration_rate_for_ventilation_system_off = infiltration_rate_for_ventilation_system_off
self._infiltration_rate_for_ventilation_system_on = infiltration_rate_for_ventilation_system_on self._infiltration_rate_for_ventilation_system_on = infiltration_rate_for_ventilation_system_on
self._infiltration_rate_area_for_ventilation_system_off = infiltration_rate_area_for_ventilation_system_off
self._infiltration_rate_area_for_ventilation_system_on = infiltration_rate_area_for_ventilation_system_on
@property @property
def id(self): def id(self):
@ -117,7 +125,7 @@ class Archetype:
@property @property
def infiltration_rate_for_ventilation_system_off(self): def infiltration_rate_for_ventilation_system_off(self):
""" """
Get archetype infiltration rate for ventilation system off in ACH Get archetype infiltration rate for ventilation system off in 1/s
:return: float :return: float
""" """
return self._infiltration_rate_for_ventilation_system_off return self._infiltration_rate_for_ventilation_system_off
@ -125,7 +133,46 @@ class Archetype:
@property @property
def infiltration_rate_for_ventilation_system_on(self): def infiltration_rate_for_ventilation_system_on(self):
""" """
Get archetype infiltration rate for ventilation system on in ACH Get archetype infiltration rate for ventilation system on in 1/s
:return: float :return: float
""" """
return self._infiltration_rate_for_ventilation_system_on return self._infiltration_rate_for_ventilation_system_on
@property
def infiltration_rate_area_for_ventilation_system_off(self):
"""
Get archetype infiltration rate for ventilation system off in m3/sm2
:return: float
"""
return self._infiltration_rate_area_for_ventilation_system_off
@property
def infiltration_rate_area_for_ventilation_system_on(self):
"""
Get archetype infiltration rate for ventilation system on in m3/sm2
:return: float
"""
return self._infiltration_rate_for_ventilation_system_on
def to_dictionary(self):
"""Class content to dictionary"""
_constructions = []
for _construction in self.constructions:
_constructions.append(_construction.to_dictionary())
content = {'Archetype': {'id': self.id,
'name': self.name,
'function': self.function,
'climate zone': self.climate_zone,
'period of construction': self.construction_period,
'average storey height [m]': self.average_storey_height,
'thermal capacity [J/m3K]': self.thermal_capacity,
'extra loses due to thermal bridges [W/m2K]': self.extra_loses_due_to_thermal_bridges,
'indirect heated ratio': self.indirect_heated_ratio,
'infiltration rate for ventilation off [1/s]': self.infiltration_rate_for_ventilation_system_off,
'infiltration rate for ventilation on [1/s]': self.infiltration_rate_for_ventilation_system_on,
'infiltration rate area for ventilation off [m3/sm2]': self.infiltration_rate_area_for_ventilation_system_off,
'infiltration rate area for ventilation on [m3/sm2]': self.infiltration_rate_area_for_ventilation_system_on,
'constructions': _constructions
}
}
return content

View File

@ -4,11 +4,15 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
from hub.catalog_factories.data_models.construction.layer import Layer from hub.catalog_factories.data_models.construction.layer import Layer
from hub.catalog_factories.data_models.construction.window import Window from hub.catalog_factories.data_models.construction.window import Window
class Construction: class Construction:
"""
Construction class
"""
def __init__(self, construction_id, construction_type, name, layers, window_ratio=None, window=None): def __init__(self, construction_id, construction_type, name, layers, window_ratio=None, window=None):
self._id = construction_id self._id = construction_id
self._type = construction_type self._type = construction_type
@ -64,3 +68,21 @@ class Construction:
:return: Window :return: Window
""" """
return self._window return self._window
def to_dictionary(self):
"""Class content to dictionary"""
_layers = []
for _layer in self.layers:
_layers.append(_layer.to_dictionary())
_window = None
if self.window is not None:
_window = self.window.to_dictionary()
content = {'Construction': {'id': self.id,
'name': self.name,
'type': self.type,
'window ratio': self.window_ratio,
'window': _window,
'layers': _layers
}
}
return content

View File

@ -7,6 +7,9 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
class Content: class Content:
"""
Content class
"""
def __init__(self, archetypes, constructions, materials, windows): def __init__(self, archetypes, constructions, materials, windows):
self._archetypes = archetypes self._archetypes = archetypes
self._constructions = constructions self._constructions = constructions
@ -40,3 +43,21 @@ class Content:
All windows in the catalog All windows in the catalog
""" """
return self._windows return self._windows
def to_dictionary(self):
"""Class content to dictionary"""
_archetypes = []
for _archetype in self.archetypes:
_archetypes.append(_archetype.to_dictionary())
content = {'Archetypes': _archetypes}
return content
def __str__(self):
"""Print content"""
_archetypes = []
for _archetype in self.archetypes:
_archetypes.append(_archetype.to_dictionary())
content = {'Archetypes': _archetypes}
return str(content)

View File

@ -5,8 +5,13 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
from hub.catalog_factories.data_models.construction.material import Material
class Layer: class Layer:
"""
Layer class
"""
def __init__(self, layer_id, name, material, thickness): def __init__(self, layer_id, name, material, thickness):
self._id = layer_id self._id = layer_id
self._name = name self._name = name
@ -30,7 +35,7 @@ class Layer:
return self._name return self._name
@property @property
def material(self): def material(self) -> Material:
""" """
Get layer material Get layer material
:return: Material :return: Material
@ -44,3 +49,13 @@ class Layer:
:return: None or float :return: None or float
""" """
return self._thickness return self._thickness
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Layer': {'id': self.id,
'name': self.name,
'thickness [m]': self.thickness,
'material': self.material.to_dictionary()
}
}
return content

View File

@ -7,6 +7,9 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
class Material: class Material:
"""
Material class
"""
def __init__(self, material_id, def __init__(self, material_id,
name, name,
solar_absorptance, solar_absorptance,
@ -107,3 +110,19 @@ class Material:
:return: None or float :return: None or float
""" """
return self._thermal_resistance return self._thermal_resistance
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Material': {'id': self.id,
'name': self.name,
'is no-mass': self.no_mass,
'density [kg/m3]': self.density,
'specific heat [J/kgK]': self.specific_heat,
'conductivity [W/mK]': self.conductivity,
'thermal resistance [m2K/W]': self.thermal_resistance,
'solar absorptance': self.solar_absorptance,
'thermal absorptance': self.thermal_absorptance,
'visible absorptance': self.visible_absorptance
}
}
return content

View File

@ -7,6 +7,9 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
class Window: class Window:
"""
Window class
"""
def __init__(self, window_id, frame_ratio, g_value, overall_u_value, name, window_type=None): def __init__(self, window_id, frame_ratio, g_value, overall_u_value, name, window_type=None):
self._id = window_id self._id = window_id
self._frame_ratio = frame_ratio self._frame_ratio = frame_ratio
@ -61,4 +64,16 @@ class Window:
Get transparent surface type, 'window' or 'skylight' Get transparent surface type, 'window' or 'skylight'
:return: str :return: str
""" """
return self.type return self._type
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Window': {'id': self.id,
'name': self.name,
'type': self.type,
'frame ratio': self.frame_ratio,
'g-value': self.g_value,
'overall U value [W/m2K]': self.overall_u_value
}
}
return content

View File

@ -11,6 +11,9 @@ from hub.catalog_factories.data_models.cost.income import Income
class Archetype: class Archetype:
"""
Archetype class
"""
def __init__(self, def __init__(self,
lod, lod,
function, function,
@ -99,7 +102,7 @@ class Archetype:
@property @property
def end_of_life_cost(self): def end_of_life_cost(self):
""" """
Get end of life cost in given currency Get end of life cost in given currency per m2
:return: float :return: float
""" """
return self._end_of_life_cost return self._end_of_life_cost
@ -111,3 +114,19 @@ class Archetype:
:return: Income :return: Income
""" """
return self._income return self._income
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Archetype': {'name': self.name,
'level of detail': self.lod,
'municipality': self.municipality,
'country': self.country,
'currency': self.currency,
'function': self.function,
'capital cost': self.capital_cost.to_dictionary(),
'operational cost': self.operational_cost.to_dictionary(),
'end of life cost [currency/m2]': self.end_of_life_cost,
'income': self.income.to_dictionary()
}
}
return content

View File

@ -10,6 +10,9 @@ from hub.catalog_factories.data_models.cost.chapter import Chapter
class CapitalCost: class CapitalCost:
"""
Capital cost class
"""
def __init__(self, general_chapters, design_allowance, overhead_and_profit): def __init__(self, general_chapters, design_allowance, overhead_and_profit):
self._general_chapters = general_chapters self._general_chapters = general_chapters
self._design_allowance = design_allowance self._design_allowance = design_allowance
@ -48,3 +51,16 @@ class CapitalCost:
if chapter.chapter_type == name: if chapter.chapter_type == name:
return chapter return chapter
raise KeyError(f'Chapter name {name} not found') raise KeyError(f'Chapter name {name} not found')
def to_dictionary(self):
"""Class content to dictionary"""
_chapters = []
for _chapter in self.general_chapters:
_chapters.append(_chapter.to_dictionary())
content = {'Capital cost': {'design allowance': self.design_allowance,
'overhead and profit': self.overhead_and_profit,
'chapters': _chapters
}
}
return content

View File

@ -10,6 +10,9 @@ from hub.catalog_factories.data_models.cost.item_description import ItemDescript
class Chapter: class Chapter:
"""
Chapter class
"""
def __init__(self, chapter_type, items): def __init__(self, chapter_type, items):
self._chapter_type = chapter_type self._chapter_type = chapter_type
@ -40,3 +43,15 @@ class Chapter:
if item.type == name: if item.type == name:
return item return item
raise KeyError(f'Item name {name} not found') raise KeyError(f'Item name {name} not found')
def to_dictionary(self):
"""Class content to dictionary"""
_items = []
for _item in self.items:
_items.append(_item.to_dictionary())
content = {'Chapter': {'chapter type': self.chapter_type,
'items': _items
}
}
return content

View File

@ -8,6 +8,9 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
class Content: class Content:
"""
Content class
"""
def __init__(self, archetypes): def __init__(self, archetypes):
self._archetypes = archetypes self._archetypes = archetypes
@ -17,3 +20,21 @@ class Content:
All archetypes in the catalog All archetypes in the catalog
""" """
return self._archetypes return self._archetypes
def to_dictionary(self):
"""Class content to dictionary"""
_archetypes = []
for _archetype in self.archetypes:
_archetypes.append(_archetype.to_dictionary())
content = {'Archetypes': _archetypes}
return content
def __str__(self):
"""Print content"""
_archetypes = []
for _archetype in self.archetypes:
_archetypes.append(_archetype.to_dictionary())
content = {'Archetypes': _archetypes}
return str(content)

View File

@ -9,6 +9,9 @@ from typing import Union
class Fuel: class Fuel:
"""
Fuel class
"""
def __init__(self, fuel_type, def __init__(self, fuel_type,
fixed_monthly=None, fixed_monthly=None,
fixed_power=None, fixed_power=None,
@ -40,10 +43,12 @@ class Fuel:
@property @property
def fixed_power(self) -> Union[None, float]: def fixed_power(self) -> Union[None, float]:
""" """
Get fixed operational costs depending on the peak power consumed in currency per month per kW Get fixed operational costs depending on the peak power consumed in currency per month per W
:return: None or float :return: None or float
""" """
return self._fixed_power if self._fixed_power is not None:
return self._fixed_power/1000
return None
@property @property
def variable(self) -> Union[tuple[None, None], tuple[float, str]]: def variable(self) -> Union[tuple[None, None], tuple[float, str]]:
@ -52,3 +57,15 @@ class Fuel:
:return: None, None or float, str :return: None, None or float, str
""" """
return self._variable, self._variable_units return self._variable, self._variable_units
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Fuel': {'fuel type': self.type,
'fixed operational costs [currency/month]': self.fixed_monthly,
'fixed operational costs depending on the peak power consumed [currency/month W]': self.fixed_power,
'variable operational costs': self.variable[0],
'units': self.variable[1]
}
}
return content

View File

@ -9,6 +9,9 @@ from typing import Union
class Income: class Income:
"""
Income class
"""
def __init__(self, construction_subsidy=None, def __init__(self, construction_subsidy=None,
hvac_subsidy=None, hvac_subsidy=None,
photovoltaic_subsidy=None, photovoltaic_subsidy=None,
@ -24,7 +27,7 @@ class Income:
@property @property
def construction_subsidy(self) -> Union[None, float]: def construction_subsidy(self) -> Union[None, float]:
""" """
Get subsidy for construction in percentage Get subsidy for construction in percentage %
:return: None or float :return: None or float
""" """
return self._construction_subsidy return self._construction_subsidy
@ -32,7 +35,7 @@ class Income:
@property @property
def hvac_subsidy(self) -> Union[None, float]: def hvac_subsidy(self) -> Union[None, float]:
""" """
Get subsidy for HVAC system in percentage Get subsidy for HVAC system in percentage %
:return: None or float :return: None or float
""" """
return self._hvac_subsidy return self._hvac_subsidy
@ -51,7 +54,7 @@ class Income:
Get electricity export incomes in currency per J Get electricity export incomes in currency per J
:return: None or float :return: None or float
""" """
return self._construction_subsidy return self._electricity_export
@property @property
def reductions_tax(self) -> Union[None, float]: def reductions_tax(self) -> Union[None, float]:
@ -60,3 +63,15 @@ class Income:
:return: None or float :return: None or float
""" """
return self._reductions_tax return self._reductions_tax
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Income': {'construction subsidy [%]': self.construction_subsidy,
'hvac subsidy [%]': self.hvac_subsidy,
'photovoltaic subsidy [%]': self.photovoltaic_subsidy,
'electricity export [currency/J]': self.electricity_export,
'reductions tax': self.reductions_tax
}
}
return content

View File

@ -9,6 +9,9 @@ from typing import Union
class ItemDescription: class ItemDescription:
"""
Item description class
"""
def __init__(self, item_type, def __init__(self, item_type,
initial_investment=None, initial_investment=None,
initial_investment_unit=None, initial_investment_unit=None,
@ -66,3 +69,18 @@ class ItemDescription:
:return: None or float :return: None or float
""" """
return self._lifetime return self._lifetime
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Item': {'type': self.type,
'initial investment': self.initial_investment[0],
'initial investment units': self.initial_investment[1],
'refurbishment': self.refurbishment[0],
'refurbishment units': self.refurbishment[1],
'reposition': self.reposition[0],
'reposition units': self.reposition[1],
'life time [years]': self.lifetime
}
}
return content

View File

@ -10,6 +10,9 @@ from hub.catalog_factories.data_models.cost.fuel import Fuel
class OperationalCost: class OperationalCost:
"""
Operational cost class
"""
def __init__(self, fuels, maintenance_heating, maintenance_cooling, maintenance_pv, co2): def __init__(self, fuels, maintenance_heating, maintenance_cooling, maintenance_pv, co2):
self._fuels = fuels self._fuels = fuels
self._maintenance_heating = maintenance_heating self._maintenance_heating = maintenance_heating
@ -21,7 +24,7 @@ class OperationalCost:
def fuels(self) -> List[Fuel]: def fuels(self) -> List[Fuel]:
""" """
Get fuels listed in capital costs Get fuels listed in capital costs
:return: [FUEL] :return: [Fuel]
""" """
return self._fuels return self._fuels
@ -56,3 +59,18 @@ class OperationalCost:
:return: float :return: float
""" """
return self._co2 return self._co2
def to_dictionary(self):
"""Class content to dictionary"""
_fuels = []
for _fuel in self.fuels:
_fuels.append(_fuel.to_dictionary())
content = {'Maintenance': {'fuels': _fuels,
'cost of maintaining the heating system [currency/W]': self.maintenance_heating,
'cost of maintaining the cooling system [currency/W]': self.maintenance_cooling,
'cost of maintaining the PV system [currency/W]': self.maintenance_pv,
'cost of CO2 emissions [currency/kgCO2]': self.co2
}
}
return content

View File

@ -1,8 +1,9 @@
""" """
Energy System catalog archetype Energy System catalog archetype, understood as a cluster of energy systems
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
""" """
from typing import List from typing import List
@ -11,27 +12,21 @@ from hub.catalog_factories.data_models.energy_systems.system import System
class Archetype: class Archetype:
def __init__(self, lod, name, systems): """
Archetype class
"""
def __init__(self, name, systems):
self._lod = lod
self._name = name self._name = name
self._systems = systems self._systems = systems
@property
def lod(self):
"""
Get level of detail of the catalog
:return: string
"""
return self._lod
@property @property
def name(self): def name(self):
""" """
Get name Get name
:return: string :return: string
""" """
return f'{self._name}_lod{self._lod}' return self._name
@property @property
def systems(self) -> List[System]: def systems(self) -> List[System]:
@ -40,3 +35,16 @@ class Archetype:
:return: [Equipment] :return: [Equipment]
""" """
return self._systems return self._systems
def to_dictionary(self):
"""Class content to dictionary"""
_systems = []
for _system in self.systems:
_systems.append(_system.to_dictionary())
content = {
'Archetype': {
'name': self.name,
'systems': _systems
}
}
return content

View File

@ -7,12 +7,14 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
class Content: class Content:
def __init__(self, archetypes, systems, generations, distributions, emissions): """
Content class
"""
def __init__(self, archetypes, systems, generations=None, distributions=None):
self._archetypes = archetypes self._archetypes = archetypes
self._systems = systems self._systems = systems
self._generations = generations self._generations = generations
self._distributions = distributions self._distributions = distributions
self._emissions = emissions
@property @property
def archetypes(self): def archetypes(self):
@ -42,9 +44,20 @@ class Content:
""" """
return self._distributions return self._distributions
@property def to_dictionary(self):
def emission_equipments(self): """Class content to dictionary"""
""" _archetypes = []
All emission equipments in the catalog for _archetype in self.archetypes:
""" _archetypes.append(_archetype.to_dictionary())
return self._emissions content = {'Archetypes': _archetypes}
return content
def __str__(self):
"""Print content"""
_archetypes = []
for _archetype in self.archetypes:
_archetypes.append(_archetype.to_dictionary())
content = {'Archetypes': _archetypes}
return str(content)

View File

@ -3,20 +3,35 @@ Energy System catalog distribution system
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
""" """
from typing import Union, List, TypeVar
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
GenerationSystem = TypeVar('GenerationSystem')
class DistributionSystem: class DistributionSystem:
def __init__(self, system_id, name, system_type, supply_temperature, distribution_consumption_fix_flow, """
distribution_consumption_variable_flow, heat_losses): Distribution system class
"""
def __init__(self, system_id, model_name=None, system_type=None, supply_temperature=None,
distribution_consumption_fix_flow=None, distribution_consumption_variable_flow=None, heat_losses=None,
generation_systems=None, energy_storage_systems=None, emission_systems=None):
self._system_id = system_id self._system_id = system_id
self._name = name self._model_name = model_name
self._type = system_type self._type = system_type
self._supply_temperature = supply_temperature self._supply_temperature = supply_temperature
self._distribution_consumption_fix_flow = distribution_consumption_fix_flow self._distribution_consumption_fix_flow = distribution_consumption_fix_flow
self._distribution_consumption_variable_flow = distribution_consumption_variable_flow self._distribution_consumption_variable_flow = distribution_consumption_variable_flow
self._heat_losses = heat_losses self._heat_losses = heat_losses
self._generation_systems = generation_systems
self._energy_storage_systems = energy_storage_systems
self._emission_systems = emission_systems
@property @property
def id(self): def id(self):
@ -26,29 +41,13 @@ class DistributionSystem:
""" """
return self._system_id return self._system_id
@id.setter
def id(self, value):
"""
Set system id
:param value: float
"""
self._system_id = value
@property @property
def name(self): def model_name(self):
""" """
Get name Get model name
:return: string :return: string
""" """
return self._name return self._model_name
@name.setter
def name(self, value):
"""
Set name
:param value: string
"""
self._name = value
@property @property
def type(self): def type(self):
@ -78,7 +77,7 @@ class DistributionSystem:
def distribution_consumption_variable_flow(self): def distribution_consumption_variable_flow(self):
""" """
Get distribution_consumption if the pump or fan work at variable mass or volume flow in ratio Get distribution_consumption if the pump or fan work at variable mass or volume flow in ratio
over energy produced (Wh/Wh) over energy produced (J/J)
:return: float :return: float
""" """
return self._distribution_consumption_variable_flow return self._distribution_consumption_variable_flow
@ -86,7 +85,56 @@ class DistributionSystem:
@property @property
def heat_losses(self): def heat_losses(self):
""" """
Get heat_losses in ratio over energy produced Get heat_losses in ratio over energy produced in J/J
:return: float :return: float
""" """
return self._heat_losses return self._heat_losses
@property
def generation_systems(self) -> Union[None, List[GenerationSystem]]:
"""
Get generation systems connected to the distribution system
:return: [GenerationSystem]
"""
return self._generation_systems
@property
def energy_storage_systems(self) -> Union[None, List[EnergyStorageSystem]]:
"""
Get energy storage systems connected to this distribution system
:return: [EnergyStorageSystem]
"""
return self._energy_storage_systems
@property
def emission_systems(self) -> Union[None, List[EmissionSystem]]:
"""
Get energy emission systems connected to this distribution system
:return: [EmissionSystem]
"""
return self._emission_systems
def to_dictionary(self):
"""Class content to dictionary"""
_generation_systems = [_generation_system.to_dictionary() for _generation_system in
self.generation_systems] if self.generation_systems is not None else None
_energy_storage_systems = [_energy_storage_system.to_dictionary() for _energy_storage_system in
self.energy_storage_systems] if self.energy_storage_systems is not None else None
_emission_systems = [_emission_system.to_dictionary() for _emission_system in
self.emission_systems] if self.emission_systems is not None else None
content = {
'Layer': {
'id': self.id,
'model name': self.model_name,
'type': self.type,
'supply temperature [Celsius]': self.supply_temperature,
'distribution consumption if fix flow over peak power [W/W]': self.distribution_consumption_fix_flow,
'distribution consumption if variable flow over peak power [J/J]': self.distribution_consumption_variable_flow,
'heat losses per energy produced [J/J]': self.heat_losses,
'generation systems connected': _generation_systems,
'energy storage systems connected': _energy_storage_systems,
'emission systems connected': _emission_systems
}
}
return content

View File

@ -0,0 +1,103 @@
"""
Energy System catalog electrical storage system
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
"""
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
class ElectricalStorageSystem(EnergyStorageSystem):
""""
Energy Storage System Class
"""
def __init__(self, storage_id, type_energy_stored=None, model_name=None, manufacturer=None, storage_type=None,
nominal_capacity=None, losses_ratio=None, rated_output_power=None, nominal_efficiency=None,
battery_voltage=None, depth_of_discharge=None, self_discharge_rate=None):
super().__init__(storage_id, model_name, manufacturer, nominal_capacity, losses_ratio)
self._type_energy_stored = type_energy_stored
self._storage_type = storage_type
self._rated_output_power = rated_output_power
self._nominal_efficiency = nominal_efficiency
self._battery_voltage = battery_voltage
self._depth_of_discharge = depth_of_discharge
self._self_discharge_rate = self_discharge_rate
@property
def type_energy_stored(self):
"""
Get type of energy stored from ['electrical', 'thermal']
:return: string
"""
return self._type_energy_stored
@property
def storage_type(self):
"""
Get storage type from ['lithium_ion', 'lead_acid', 'NiCd']
:return: string
"""
return self._storage_type
@property
def rated_output_power(self):
"""
Get the rated output power of storage system in Watts
:return: float
"""
return self._rated_output_power
@property
def nominal_efficiency(self):
"""
Get the nominal efficiency of the storage system
:return: float
"""
return self._nominal_efficiency
@property
def battery_voltage(self):
"""
Get the battery voltage in Volts
:return: float
"""
return self._battery_voltage
@property
def depth_of_discharge(self):
"""
Get the depth of discharge as a percentage
:return: float
"""
return self._depth_of_discharge
@property
def self_discharge_rate(self):
"""
Get the self discharge rate of battery as a percentage
:return: float
"""
return self._self_discharge_rate
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Storage component': {
'storage id': self.id,
'type of energy stored': self.type_energy_stored,
'model name': self.model_name,
'manufacturer': self.manufacturer,
'storage type': self.storage_type,
'nominal capacity [J]': self.nominal_capacity,
'losses-ratio [J/J]': self.losses_ratio,
'rated power [W]': self.rated_output_power,
'nominal efficiency': self.nominal_efficiency,
'battery voltage [V]': self.battery_voltage,
'depth of discharge [%]': self.depth_of_discharge,
'self discharge rate': self.self_discharge_rate
}
}
return content

View File

@ -7,10 +7,13 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
class EmissionSystem: class EmissionSystem:
def __init__(self, system_id, name, system_type, parasitic_energy_consumption): """
Emission system class
"""
def __init__(self, system_id, model_name=None, system_type=None, parasitic_energy_consumption=0):
self._system_id = system_id self._system_id = system_id
self._name = name self._model_name = model_name
self._type = system_type self._type = system_type
self._parasitic_energy_consumption = parasitic_energy_consumption self._parasitic_energy_consumption = parasitic_energy_consumption
@ -22,29 +25,13 @@ class EmissionSystem:
""" """
return self._system_id return self._system_id
@id.setter
def id(self, value):
"""
Set system id
:param value: float
"""
self._system_id = value
@property @property
def name(self): def model_name(self):
""" """
Get name Get model name
:return: string :return: string
""" """
return self._name return self._model_name
@name.setter
def name(self, value):
"""
Set name
:param value: string
"""
self._name = value
@property @property
def type(self): def type(self):
@ -57,7 +44,17 @@ class EmissionSystem:
@property @property
def parasitic_energy_consumption(self): def parasitic_energy_consumption(self):
""" """
Get parasitic_energy_consumption in ratio (Wh/Wh) Get parasitic_energy_consumption in ratio (J/J)
:return: float :return: float
""" """
return self._parasitic_energy_consumption return self._parasitic_energy_consumption
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Layer': {'id': self.id,
'model name': self.model_name,
'type': self.type,
'parasitic energy consumption per energy produced [J/J]': self.parasitic_energy_consumption
}
}
return content

View File

@ -0,0 +1,75 @@
"""
Energy System catalog heat generation system
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from abc import ABC
class EnergyStorageSystem(ABC):
""""
Energy Storage System Abstract Class
"""
def __init__(self, storage_id, model_name=None, manufacturer=None,
nominal_capacity=None, losses_ratio=None):
self._storage_id = storage_id
self._model_name = model_name
self._manufacturer = manufacturer
self._nominal_capacity = nominal_capacity
self._losses_ratio = losses_ratio
@property
def id(self):
"""
Get storage id
:return: string
"""
return self._storage_id
@property
def type_energy_stored(self):
"""
Get type of energy stored from ['electrical', 'thermal']
:return: string
"""
raise NotImplementedError
@property
def model_name(self):
"""
Get system model
:return: string
"""
return self._model_name
@property
def manufacturer(self):
"""
Get name of manufacturer
:return: string
"""
return self._manufacturer
@property
def nominal_capacity(self):
"""
Get the nominal capacity of the storage system in Jules
:return: float
"""
return self._nominal_capacity
@property
def losses_ratio(self):
"""
Get the losses-ratio of storage system in Jules lost / Jules stored
:return: float
"""
return self._losses_ratio
def to_dictionary(self):
"""Class content to dictionary"""
raise NotImplementedError

View File

@ -1,30 +1,33 @@
""" """
Energy System catalog generation system Energy System catalog heat generation system
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
""" """
from __future__ import annotations from __future__ import annotations
from typing import Union from abc import ABC
from typing import List, Union
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
class GenerationSystem: class GenerationSystem(ABC):
def __init__(self, system_id, name, system_type, fuel_type, source_types, heat_efficiency, cooling_efficiency, """
electricity_efficiency, source_temperature, source_mass_flow, storage, auxiliary_equipment): Heat Generation system class
"""
def __init__(self, system_id, name, model_name=None, manufacturer=None, fuel_type=None,
distribution_systems=None, energy_storage_systems=None):
self._system_id = system_id self._system_id = system_id
self._name = name self._name = name
self._type = system_type self._model_name = model_name
self._manufacturer = manufacturer
self._fuel_type = fuel_type self._fuel_type = fuel_type
self._source_types = source_types self._distribution_systems = distribution_systems
self._heat_efficiency = heat_efficiency self._energy_storage_systems = energy_storage_systems
self._cooling_efficiency = cooling_efficiency
self._electricity_efficiency = electricity_efficiency
self._source_temperature = source_temperature
self._source_mass_flow = source_mass_flow
self._storage = storage
self._auxiliary_equipment = auxiliary_equipment
@property @property
def id(self): def id(self):
@ -34,106 +37,62 @@ class GenerationSystem:
""" """
return self._system_id return self._system_id
@id.setter
def id(self, value):
"""
Set system id
:param value: float
"""
self._system_id = value
@property @property
def name(self): def name(self):
""" """
Get name Get system name
:return: string :return: string
""" """
return self._name return self._name
@name.setter
def name(self, value):
"""
Set name
:param value: string
"""
self._name = value
@property @property
def type(self): def system_type(self):
""" """
Get type Get type
:return: string :return: string
""" """
return self._type raise NotImplementedError
@property
def model_name(self):
"""
Get system id
:return: float
"""
return self._model_name
@property
def manufacturer(self):
"""
Get name
:return: string
"""
return self._manufacturer
@property @property
def fuel_type(self): def fuel_type(self):
""" """
Get fuel_type from [renewable, gas, diesel, electricity, wood, coal] Get fuel_type from [renewable, gas, diesel, electricity, wood, coal, biogas]
:return: string :return: string
""" """
return self._fuel_type return self._fuel_type
@property @property
def source_types(self): def distribution_systems(self) -> Union[None, List[DistributionSystem]]:
""" """
Get source_type from [air, water, geothermal, district_heating, grid, on_site_electricity] Get distributions systems connected to this generation system
:return: [string] :return: [DistributionSystem]
""" """
return self._source_types return self._distribution_systems
@property @property
def heat_efficiency(self): def energy_storage_systems(self) -> Union[None, List[EnergyStorageSystem]]:
""" """
Get heat_efficiency Get energy storage systems connected to this generation system
:return: float :return: [EnergyStorageSystem]
""" """
return self._heat_efficiency return self._energy_storage_systems
@property def to_dictionary(self):
def cooling_efficiency(self): """Class content to dictionary"""
""" raise NotImplementedError
Get cooling_efficiency
:return: float
"""
return self._cooling_efficiency
@property
def electricity_efficiency(self):
"""
Get electricity_efficiency
:return: float
"""
return self._electricity_efficiency
@property
def source_temperature(self):
"""
Get source_temperature in degree Celsius
:return: float
"""
return self._source_temperature
@property
def source_mass_flow(self):
"""
Get source_mass_flow in kg/s
:return: float
"""
return self._source_mass_flow
@property
def storage(self):
"""
Get boolean storage exists
:return: bool
"""
return self._storage
@property
def auxiliary_equipment(self) -> Union[None, GenerationSystem]:
"""
Get auxiliary_equipment
:return: GenerationSystem
"""
return self._auxiliary_equipment

View File

@ -0,0 +1,344 @@
"""
Energy System catalog non PV generation system
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
"""
from typing import Union
from hub.catalog_factories.data_models.energy_systems.performance_curves import PerformanceCurves
from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem
class NonPvGenerationSystem(GenerationSystem):
"""
Non PV Generation system class
"""
def __init__(self, system_id, name, system_type, model_name=None, manufacturer=None, fuel_type=None,
nominal_heat_output=None, maximum_heat_output=None, minimum_heat_output=None, source_medium=None,
supply_medium=None, heat_efficiency=None, nominal_cooling_output=None, maximum_cooling_output=None,
minimum_cooling_output=None, cooling_efficiency=None, electricity_efficiency=None,
source_temperature=None, source_mass_flow=None, nominal_electricity_output=None,
maximum_heat_supply_temperature=None, minimum_heat_supply_temperature=None,
maximum_cooling_supply_temperature=None, minimum_cooling_supply_temperature=None, heat_output_curve=None,
heat_fuel_consumption_curve=None, heat_efficiency_curve=None, cooling_output_curve=None,
cooling_fuel_consumption_curve=None, cooling_efficiency_curve=None,
distribution_systems=None, energy_storage_systems=None, domestic_hot_water=False,
reversible=None, simultaneous_heat_cold=None):
super().__init__(system_id=system_id, name=name, model_name=model_name, manufacturer=manufacturer,
fuel_type=fuel_type, distribution_systems=distribution_systems,
energy_storage_systems=energy_storage_systems)
self._system_type = system_type
self._nominal_heat_output = nominal_heat_output
self._maximum_heat_output = maximum_heat_output
self._minimum_heat_output = minimum_heat_output
self._heat_efficiency = heat_efficiency
self._nominal_cooling_output = nominal_cooling_output
self._maximum_cooling_output = maximum_cooling_output
self._minimum_cooling_output = minimum_cooling_output
self._cooling_efficiency = cooling_efficiency
self._electricity_efficiency = electricity_efficiency
self._nominal_electricity_output = nominal_electricity_output
self._source_medium = source_medium
self._source_temperature = source_temperature
self._source_mass_flow = source_mass_flow
self._supply_medium = supply_medium
self._maximum_heat_supply_temperature = maximum_heat_supply_temperature
self._minimum_heat_supply_temperature = minimum_heat_supply_temperature
self._maximum_cooling_supply_temperature = maximum_cooling_supply_temperature
self._minimum_cooling_supply_temperature = minimum_cooling_supply_temperature
self._heat_output_curve = heat_output_curve
self._heat_fuel_consumption_curve = heat_fuel_consumption_curve
self._heat_efficiency_curve = heat_efficiency_curve
self._cooling_output_curve = cooling_output_curve
self._cooling_fuel_consumption_curve = cooling_fuel_consumption_curve
self._cooling_efficiency_curve = cooling_efficiency_curve
self._domestic_hot_water = domestic_hot_water
self._reversible = reversible
self._simultaneous_heat_cold = simultaneous_heat_cold
@property
def system_type(self):
"""
Get type
:return: string
"""
return self._system_type
@property
def nominal_heat_output(self):
"""
Get nominal heat output of heat generation devices in W
:return: float
"""
return self._nominal_heat_output
@property
def maximum_heat_output(self):
"""
Get maximum heat output of heat generation devices in W
:return: float
"""
return self._maximum_heat_output
@property
def minimum_heat_output(self):
"""
Get minimum heat output of heat generation devices in W
:return: float
"""
return self._minimum_heat_output
@property
def source_medium(self):
"""
Get source_type from [air, water, ground, district_heating, grid, on_site_electricity]
:return: string
"""
return self._source_medium
@property
def supply_medium(self):
"""
Get the supply medium from ['air', 'water']
:return: string
"""
return self._supply_medium
@property
def heat_efficiency(self):
"""
Get heat_efficiency
:return: float
"""
return self._heat_efficiency
@property
def nominal_cooling_output(self):
"""
Get nominal cooling output of heat generation devices in W
:return: float
"""
return self._nominal_cooling_output
@property
def maximum_cooling_output(self):
"""
Get maximum heat output of heat generation devices in W
:return: float
"""
return self._maximum_cooling_output
@property
def minimum_cooling_output(self):
"""
Get minimum heat output of heat generation devices in W
:return: float
"""
return self._minimum_cooling_output
@property
def cooling_efficiency(self):
"""
Get cooling_efficiency
:return: float
"""
return self._cooling_efficiency
@property
def electricity_efficiency(self):
"""
Get electricity_efficiency
:return: float
"""
return self._electricity_efficiency
@property
def source_temperature(self):
"""
Get source_temperature in degree Celsius
:return: float
"""
return self._source_temperature
@property
def source_mass_flow(self):
"""
Get source_mass_flow in kg/s
:return: float
"""
return self._source_mass_flow
@property
def nominal_electricity_output(self):
"""
Get nominal_power_output of electricity generation devices or inverters in W
:return: float
"""
return self._nominal_electricity_output
@property
def maximum_heat_supply_temperature(self):
"""
Get the maximum heat supply temperature in degree Celsius
:return: float
"""
return self._minimum_heat_supply_temperature
@property
def minimum_heat_supply_temperature(self):
"""
Get the minimum heat supply temperature in degree Celsius
:return: float
"""
return self._minimum_heat_supply_temperature
@property
def maximum_cooling_supply_temperature(self):
"""
Get the maximum cooling supply temperature in degree Celsius
:return: float
"""
return self._maximum_cooling_supply_temperature
@property
def minimum_cooling_supply_temperature(self):
"""
Get the minimum cooling supply temperature in degree Celsius
:return: float
"""
return self._minimum_cooling_supply_temperature
@property
def heat_output_curve(self) -> Union[None, PerformanceCurves]:
"""
Get the heat output curve of the heat generation device
:return: PerformanceCurve
"""
return self._heat_output_curve
@property
def heat_fuel_consumption_curve(self) -> Union[None, PerformanceCurves]:
"""
Get the heating fuel consumption curve of the heat generation device
:return: PerformanceCurve
"""
return self._heat_fuel_consumption_curve
@property
def heat_efficiency_curve(self) -> Union[None, PerformanceCurves]:
"""
Get the heating efficiency curve of the heat generation device
:return: PerformanceCurve
"""
return self._heat_efficiency_curve
@property
def cooling_output_curve(self) -> Union[None, PerformanceCurves]:
"""
Get the heat output curve of the heat generation device
:return: PerformanceCurve
"""
return self._cooling_output_curve
@property
def cooling_fuel_consumption_curve(self) -> Union[None, PerformanceCurves]:
"""
Get the heating fuel consumption curve of the heat generation device
:return: PerformanceCurve
"""
return self._cooling_fuel_consumption_curve
@property
def cooling_efficiency_curve(self) -> Union[None, PerformanceCurves]:
"""
Get the heating efficiency curve of the heat generation device
:return: PerformanceCurve
"""
return self._cooling_efficiency_curve
@property
def domestic_hot_water(self):
"""
Get the ability to produce domestic hot water
:return: bool
"""
return self._domestic_hot_water
@property
def reversibility(self):
"""
Get the ability to produce heating and cooling
:return: bool
"""
return self._reversible
@property
def simultaneous_heat_cold(self):
"""
Get the ability to produce heating and cooling at the same time
:return: bool
"""
return self._simultaneous_heat_cold
def to_dictionary(self):
"""Class content to dictionary"""
_distribution_systems = [_distribution_system.to_dictionary() for _distribution_system in
self.distribution_systems] if self.distribution_systems is not None else None
_energy_storage_systems = [_energy_storage_system.to_dictionary() for _energy_storage_system in
self.energy_storage_systems] if self.energy_storage_systems is not None else None
_heat_output_curve = self.heat_output_curve.to_dictionary() if (
self.heat_output_curve is not None) else None
_heat_fuel_consumption_curve = self.heat_fuel_consumption_curve.to_dictionary() if (
self.heat_fuel_consumption_curve is not None) else None
_heat_efficiency_curve = self.heat_efficiency_curve.to_dictionary() if (
self.heat_efficiency_curve is not None) else None
_cooling_output_curve = self.cooling_output_curve.to_dictionary() if (
self.cooling_output_curve is not None) else None
_cooling_fuel_consumption_curve = self.cooling_fuel_consumption_curve.to_dictionary() if (
self.cooling_fuel_consumption_curve is not None) else None
_cooling_efficiency_curve = self.cooling_efficiency_curve.to_dictionary() if (
self.cooling_efficiency_curve is not None) else None
content = {
'Energy Generation component':
{
'id': self.id,
'model name': self.model_name,
'manufacturer': self.manufacturer,
'type': self.system_type,
'fuel type': self.fuel_type,
'nominal heat output [W]': self.nominal_heat_output,
'maximum heat output [W]': self.maximum_heat_output,
'minimum heat output [W]': self.minimum_heat_output,
'source medium': self.source_medium,
'supply medium': self.supply_medium,
'source temperature [Celsius]': self.source_temperature,
'source mass flow [kg/s]': self.source_mass_flow,
'heat efficiency': self.heat_efficiency,
'nominal cooling output [W]': self.nominal_cooling_output,
'maximum cooling output [W]': self.maximum_cooling_output,
'minimum cooling output [W]': self.minimum_cooling_output,
'cooling efficiency': self.cooling_efficiency,
'electricity efficiency': self.electricity_efficiency,
'nominal power output [W]': self.nominal_electricity_output,
'maximum heating supply temperature [Celsius]': self.maximum_heat_supply_temperature,
'minimum heating supply temperature [Celsius]': self.minimum_heat_supply_temperature,
'maximum cooling supply temperature [Celsius]': self.maximum_cooling_supply_temperature,
'minimum cooling supply temperature [Celsius]': self.minimum_cooling_supply_temperature,
'heat output curve': self.heat_output_curve,
'heat fuel consumption curve': self.heat_fuel_consumption_curve,
'heat efficiency curve': _heat_efficiency_curve,
'cooling output curve': self.cooling_output_curve,
'cooling fuel consumption curve': self.cooling_fuel_consumption_curve,
'cooling efficiency curve': self.cooling_efficiency_curve,
'distribution systems connected': _distribution_systems,
'storage systems connected': _energy_storage_systems,
'domestic hot water production capability': self.domestic_hot_water,
'reversible cycle': self.reversibility,
'simultaneous heat and cooling production': self.simultaneous_heat_cold
}
}
return content

View File

@ -0,0 +1,72 @@
"""
Energy System catalog heat generation system
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from __future__ import annotations
class PerformanceCurves:
"""
Parameter function class
"""
def __init__(self, curve_type, dependant_variable, parameters, coefficients):
self._curve_type = curve_type
self._dependant_variable = dependant_variable
self._parameters = parameters
self._coefficients = coefficients
@property
def curve_type(self):
"""
The type of the fit function from the following
Linear =>>> y = a + b*x
Exponential =>>> y = a*(b**x)
Second degree polynomial =>>> y = a + b*x + c*(x**2)
Power =>>> y = a*(x**b)
Bi-Quadratic =>>> y = a + b*x + c*(x**2) + d*z + e*(z**2) + f*x*z
Get the type of function from ['linear', 'exponential', 'second degree polynomial', 'power', 'bi-quadratic']
:return: string
"""
return self._curve_type
@property
def dependant_variable(self):
"""
y (e.g. COP in COP = a*source temperature**2 + b*source temperature + c*source temperature*supply temperature +
d*supply temperature + e*supply temperature**2 + f)
"""
return self._dependant_variable
@property
def parameters(self):
"""
Get the list of parameters involved in fitting process as ['x', 'z'] (e.g. [source temperature, supply temperature]
in COP=)
:return: string
"""
return self._parameters
@property
def coefficients(self):
"""
Get the coefficients of the functions as list of ['a', 'b', 'c', 'd', 'e', 'f']
:return: [coefficients]
"""
return self._coefficients
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Parameter Function': {
'curve type': self.curve_type,
'dependant variable': self.dependant_variable,
'parameter(s)': self.parameters,
'coefficients': self.coefficients,
}
}
return content

View File

@ -0,0 +1,165 @@
"""
Energy System catalog heat generation system
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem
class PvGenerationSystem(GenerationSystem):
"""
Electricity Generation system class
"""
def __init__(self, system_id, name, system_type, model_name=None, manufacturer=None, electricity_efficiency=None,
nominal_electricity_output=None, nominal_ambient_temperature=None, nominal_cell_temperature=None,
nominal_radiation=None, standard_test_condition_cell_temperature=None,
standard_test_condition_maximum_power=None, standard_test_condition_radiation=None,
cell_temperature_coefficient=None, width=None, height=None, distribution_systems=None,
energy_storage_systems=None):
super().__init__(system_id=system_id, name=name, model_name=model_name,
manufacturer=manufacturer, fuel_type='renewable', distribution_systems=distribution_systems,
energy_storage_systems=energy_storage_systems)
self._system_type = system_type
self._electricity_efficiency = electricity_efficiency
self._nominal_electricity_output = nominal_electricity_output
self._nominal_ambient_temperature = nominal_ambient_temperature
self._nominal_cell_temperature = nominal_cell_temperature
self._nominal_radiation = nominal_radiation
self._standard_test_condition_cell_temperature = standard_test_condition_cell_temperature
self._standard_test_condition_maximum_power = standard_test_condition_maximum_power
self._standard_test_condition_radiation = standard_test_condition_radiation
self._cell_temperature_coefficient = cell_temperature_coefficient
self._width = width
self._height = height
@property
def system_type(self):
"""
Get type
:return: string
"""
return self._system_type
@property
def nominal_electricity_output(self):
"""
Get nominal_power_output of electricity generation devices or inverters in W
:return: float
"""
return self._nominal_electricity_output
@property
def electricity_efficiency(self):
"""
Get electricity_efficiency
:return: float
"""
return self._electricity_efficiency
@property
def nominal_ambient_temperature(self):
"""
Get nominal ambient temperature of PV panels in degree Celsius
:return: float
"""
return self._nominal_ambient_temperature
@property
def nominal_cell_temperature(self):
"""
Get nominal cell temperature of PV panels in degree Celsius
:return: float
"""
return self._nominal_cell_temperature
@property
def nominal_radiation(self):
"""
Get nominal radiation of PV panels
:return: float
"""
return self._nominal_radiation
@property
def standard_test_condition_cell_temperature(self):
"""
Get standard test condition cell temperature of PV panels in degree Celsius
:return: float
"""
return self._standard_test_condition_cell_temperature
@property
def standard_test_condition_maximum_power(self):
"""
Get standard test condition maximum power of PV panels in W
:return: float
"""
return self._standard_test_condition_maximum_power
@property
def standard_test_condition_radiation(self):
"""
Get standard test condition cell temperature of PV panels in W/m2
:return: float
"""
return self._standard_test_condition_radiation
@property
def cell_temperature_coefficient(self):
"""
Get cell temperature coefficient of PV module
:return: float
"""
return self._cell_temperature_coefficient
@property
def width(self):
"""
Get PV module width in m
:return: float
"""
return self._width
@property
def height(self):
"""
Get PV module height in m
:return: float
"""
return self._height
def to_dictionary(self):
"""Class content to dictionary"""
_distribution_systems = [_distribution_system.to_dictionary() for _distribution_system in
self.distribution_systems] if self.distribution_systems is not None else None
_energy_storage_systems = [_energy_storage_system.to_dictionary() for _energy_storage_system in
self.energy_storage_systems] if self.energy_storage_systems is not None else None
content = {
'Energy Generation component':
{
'id': self.id,
'model name': self.model_name,
'manufacturer': self.manufacturer,
'type': self.system_type,
'fuel type': self.fuel_type,
'electricity efficiency': self.electricity_efficiency,
'nominal power output [W]': self.nominal_electricity_output,
'nominal ambient temperature [Celsius]': self.nominal_ambient_temperature,
'nominal cell temperature [Celsius]': self.nominal_cell_temperature,
'nominal radiation [W/m2]': self.nominal_radiation,
'standard test condition cell temperature [Celsius]': self.standard_test_condition_cell_temperature,
'standard test condition maximum power [W]': self.standard_test_condition_maximum_power,
'standard test condition radiation [W/m2]': self.standard_test_condition_radiation,
'cell temperature coefficient': self.cell_temperature_coefficient,
'width': self.width,
'height': self.height,
'distribution systems connected': _distribution_systems,
'storage systems connected': _energy_storage_systems
}
}
return content

View File

@ -1,42 +1,36 @@
""" """
Energy System catalog equipment Energy Systems catalog System
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
""" """
from typing import Union from typing import Union, List
from pathlib import Path
from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
class System: class System:
def __init__(self, """
lod, System class
system_id, """
name,
demand_types,
generation_system,
distribution_system,
emission_system):
self._lod = lod def __init__(self,
system_id,
demand_types,
name=None,
generation_systems=None,
distribution_systems=None,
configuration_schema=None):
self._system_id = system_id self._system_id = system_id
self._name = name self._name = name
self._demand_types = demand_types self._demand_types = demand_types
self._generation_system = generation_system self._generation_systems = generation_systems
self._distribution_system = distribution_system self._distribution_systems = distribution_systems
self._emission_system = emission_system self._configuration_schema = configuration_schema
@property
def lod(self):
"""
Get level of detail of the catalog
:return: string
"""
return self._lod
@property @property
def id(self): def id(self):
@ -49,39 +43,57 @@ class System:
@property @property
def name(self): def name(self):
""" """
Get name Get the system name
:return: string :return: string
""" """
return f'{self._name}_lod{self._lod}' return self._name
@property @property
def demand_types(self): def demand_types(self):
""" """
Get demand able to cover from [heating, cooling, domestic_hot_water, electricity] Get demand able to cover from ['heating', 'cooling', 'domestic_hot_water', 'electricity']
:return: [string] :return: [string]
""" """
return self._demand_types return self._demand_types
@property @property
def generation_system(self) -> GenerationSystem: def generation_systems(self) -> Union[None, List[GenerationSystem]]:
""" """
Get generation system Get generation systems
:return: GenerationSystem :return: [GenerationSystem]
""" """
return self._generation_system return self._generation_systems
@property @property
def distribution_system(self) -> Union[None, DistributionSystem]: def distribution_systems(self) -> Union[None, List[DistributionSystem]]:
""" """
Get distribution system Get distribution systems
:return: DistributionSystem :return: [DistributionSystem]
""" """
return self._distribution_system return self._distribution_systems
@property @property
def emission_system(self) -> Union[None, EmissionSystem]: def configuration_schema(self) -> Path:
""" """
Get emission system Get system configuration schema
:return: EmissionSystem :return: Path
""" """
return self._emission_system return self._configuration_schema
def to_dictionary(self):
"""Class content to dictionary"""
_generation_systems = []
for _generation in self.generation_systems:
_generation_systems.append(_generation.to_dictionary())
_distribution_systems = [_distribution.to_dictionary() for _distribution in
self.distribution_systems] if self.distribution_systems is not None else None
content = {'system': {'id': self.id,
'name': self.name,
'demand types': self.demand_types,
'generation system(s)': _generation_systems,
'distribution system(s)': _distribution_systems,
'configuration schema path': self.configuration_schema
}
}
return content

View File

@ -0,0 +1,126 @@
"""
Energy System catalog thermal storage system
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
"""
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
from hub.catalog_factories.data_models.construction.layer import Layer
from hub.catalog_factories.data_models.construction.material import Material
class ThermalStorageSystem(EnergyStorageSystem):
""""
Energy Storage System Class
"""
def __init__(self, storage_id, type_energy_stored=None, model_name=None, manufacturer=None, storage_type=None,
nominal_capacity=None, losses_ratio=None, volume=None, height=None, layers=None,
maximum_operating_temperature=None, storage_medium=None, heating_coil_capacity=None):
super().__init__(storage_id, model_name, manufacturer, nominal_capacity, losses_ratio)
self._type_energy_stored = type_energy_stored
self._storage_type = storage_type
self._volume = volume
self._height = height
self._layers = layers
self._maximum_operating_temperature = maximum_operating_temperature
self._storage_medium = storage_medium
self._heating_coil_capacity = heating_coil_capacity
@property
def type_energy_stored(self):
"""
Get type of energy stored from ['electrical', 'thermal']
:return: string
"""
return self._type_energy_stored
@property
def storage_type(self):
"""
Get storage type from ['thermal', 'sensible', 'latent']
:return: string
"""
return self._storage_type
@property
def volume(self):
"""
Get the physical volume of the storage system in cubic meters
:return: float
"""
return self._volume
@property
def height(self):
"""
Get the diameter of the storage system in meters
:return: float
"""
return self._height
@property
def layers(self) -> [Layer]:
"""
Get construction layers
:return: [layer]
"""
return self._layers
@property
def maximum_operating_temperature(self):
"""
Get maximum operating temperature of the storage system in degree Celsius
:return: float
"""
return self._maximum_operating_temperature
@property
def storage_medium(self) -> Material:
"""
Get thermodynamic characteristics of the storage medium
:return: [material
"""
return self._storage_medium
@property
def heating_coil_capacity(self):
"""
Get heating coil capacity in Watts
:return: [material
"""
return self._heating_coil_capacity
def to_dictionary(self):
"""Class content to dictionary"""
_layers = None
_medias = None
if self.layers is not None:
_layers = []
for _layer in self.layers:
_layers.append(_layer.to_dictionary())
if self.storage_medium is not None:
_medias = self.storage_medium.to_dictionary()
content = {
'Storage component':
{
'storage id': self.id,
'type of energy stored': self.type_energy_stored,
'model name': self.model_name,
'manufacturer': self.manufacturer,
'storage type': self.storage_type,
'nominal capacity [J]': self.nominal_capacity,
'losses-ratio [J/J]': self.losses_ratio,
'volume [m3]': self.volume,
'height [m]': self.height,
'layers': _layers,
'maximum operating temperature [Celsius]': self.maximum_operating_temperature,
'storage_medium': self.storage_medium.to_dictionary(),
'heating coil capacity [W]': self.heating_coil_capacity
}
}
return content

View File

@ -7,6 +7,9 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
class Content: class Content:
"""
Content class
"""
def __init__(self, vegetations, plants, soils): def __init__(self, vegetations, plants, soils):
self._vegetations = vegetations self._vegetations = vegetations
self._plants = plants self._plants = plants
@ -32,3 +35,21 @@ class Content:
All soils in the catalog All soils in the catalog
""" """
return self._soils return self._soils
def to_dictionary(self):
"""Class content to dictionary"""
_archetypes = []
for _archetype in self.vegetations:
_archetypes.append(_archetype.to_dictionary())
content = {'Archetypes': _archetypes}
return content
def __str__(self):
"""Print content"""
_archetypes = []
for _archetype in self.vegetations:
_archetypes.append(_archetype.to_dictionary())
content = {'Archetypes': _archetypes}
return str(content)

View File

@ -9,6 +9,9 @@ from hub.catalog_factories.data_models.greenery.soil import Soil as hub_soil
class Plant: class Plant:
"""
Plant class
"""
def __init__(self, category, plant): def __init__(self, category, plant):
self._name = plant.name self._name = plant.name
self._category = category self._category = category
@ -93,3 +96,22 @@ class Plant:
:return: [Soil] :return: [Soil]
""" """
return self._grows_on return self._grows_on
def to_dictionary(self):
"""Class content to dictionary"""
_soils = []
for _soil in self.grows_on:
_soils.append(_soil.to_dictionary())
content = {'Plant': {'name': self.name,
'category': self.category,
'height [m]': self.height,
'leaf area index': self.leaf_area_index,
'leaf reflectivity': self.leaf_reflectivity,
'leaf emissivity': self.leaf_emissivity,
'minimal stomatal resistance [s/m]': self.minimal_stomatal_resistance,
'co2 sequestration [kg????]': self.co2_sequestration,
'soils where it grows on': _soils
}
}
return content

View File

@ -5,10 +5,13 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
from hub.catalog_factories.data_models.greenery.plant import Plant as hub_plant from hub.catalog_factories.data_models.greenery.plant import Plant as HubPlant
class PlantPercentage(hub_plant): class PlantPercentage(HubPlant):
"""
Plant percentage class
"""
def __init__(self, percentage, plant_category, plant): def __init__(self, percentage, plant_category, plant):
super().__init__(plant_category, plant) super().__init__(plant_category, plant)
@ -21,3 +24,23 @@ class PlantPercentage(hub_plant):
:return: float :return: float
""" """
return self._percentage return self._percentage
def to_dictionary(self):
"""Class content to dictionary"""
_soils = []
for _soil in self.grows_on:
_soils.append(_soil.to_dictionary())
content = {'Plant': {'name': self.name,
'percentage': self.percentage,
'category': self.category,
'height [m]': self.height,
'leaf area index': self.leaf_area_index,
'leaf reflectivity': self.leaf_reflectivity,
'leaf emissivity': self.leaf_emissivity,
'minimal stomatal resistance [s/m]': self.minimal_stomatal_resistance,
'co2 sequestration [kg????]': self.co2_sequestration,
'soils where it grows on': _soils
}
}
return content

View File

@ -7,6 +7,9 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
class Soil: class Soil:
"""
Soil class
"""
def __init__(self, soil): def __init__(self, soil):
self._name = soil.name self._name = soil.name
self._roughness = soil.roughness self._roughness = soil.roughness
@ -19,7 +22,7 @@ class Soil:
self._saturation_volumetric_moisture_content = soil.saturationVolumetricMoistureContent self._saturation_volumetric_moisture_content = soil.saturationVolumetricMoistureContent
self._residual_volumetric_moisture_content = soil.residualVolumetricMoistureContent self._residual_volumetric_moisture_content = soil.residualVolumetricMoistureContent
self._initial_volumetric_moisture_content = soil.initialVolumetricMoistureContent self._initial_volumetric_moisture_content = soil.initialVolumetricMoistureContent
@property @property
def name(self): def name(self):
""" """
@ -107,3 +110,20 @@ class Soil:
:return: float :return: float
""" """
return self._initial_volumetric_moisture_content return self._initial_volumetric_moisture_content
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Soil': {'name': self.name,
# 'roughness': self.roughness, # todo: this line prints value=2????
'dry conductivity [W/m2K]': self.dry_conductivity,
'dry density [kg/m3]': self.dry_density,
'dry specific heat [J/kgK]': self.dry_specific_heat,
'thermal absorptance': self.thermal_absorptance,
'solar absorptance': self.solar_absorptance,
'visible absorptance': self.visible_absorptance,
'saturation volumetric moisture content [units??]': self.saturation_volumetric_moisture_content,
'residual volumetric moisture content [units??]': self.residual_volumetric_moisture_content
}
}
return content

View File

@ -9,6 +9,9 @@ from hub.catalog_factories.data_models.greenery.plant_percentage import PlantPer
class Vegetation: class Vegetation:
"""
Vegetation class
"""
def __init__(self, category, vegetation, plant_percentages): def __init__(self, category, vegetation, plant_percentages):
self._name = vegetation.name self._name = vegetation.name
self._category = category self._category = category
@ -168,3 +171,28 @@ class Vegetation:
:return: float :return: float
""" """
return self._soil_initial_volumetric_moisture_content return self._soil_initial_volumetric_moisture_content
def to_dictionary(self):
"""Class content to dictionary"""
_plants = []
for _plant in self.plant_percentages:
_plants.append(_plant.to_dictionary())
content = {'Archetype': {'name': self.name,
'category': self.category,
'air gap thickness [m]': self.air_gap,
'soil thickness [m]': self.soil_thickness,
'soil name': self.soil_name,
# 'soil roughness': self.soil_roughness, # todo: this line prints value=2????
'dry soil conductivity [W/m2K]': self.dry_soil_conductivity,
'dry soil density [kg/m3]': self.dry_soil_density,
'dry soil specific heat [J/kgK]': self.dry_soil_specific_heat,
'soil thermal absorptance': self.soil_thermal_absorptance,
'soil solar absorptance': self.soil_solar_absorptance,
'soil visible absorptance': self.soil_visible_absorptance,
'soil saturation volumetric moisture content [units??]': self.soil_saturation_volumetric_moisture_content,
'soil residual volumetric moisture content [units??]': self.soil_residual_volumetric_moisture_content,
'plants': _plants
}
}
return content

View File

@ -24,7 +24,7 @@ class Appliances:
@property @property
def density(self) -> Union[None, float]: def density(self) -> Union[None, float]:
""" """
Get appliances density in Watts per m2 Get appliances density in W/m2
:return: None or float :return: None or float
""" """
return self._density return self._density
@ -61,3 +61,16 @@ class Appliances:
:return: None or [Schedule] :return: None or [Schedule]
""" """
return self._schedules return self._schedules
def to_dictionary(self):
"""Class content to dictionary"""
_schedules = []
for _schedule in self.schedules:
_schedules.append(_schedule.to_dictionary())
content = {'Appliances': {'density [W/m2]': self.density,
'convective fraction': self.convective_fraction,
'radiative fraction': self.radiative_fraction,
'latent fraction': self.latent_fraction,
'schedules': _schedules}
}
return content

View File

@ -8,6 +8,9 @@ from hub.catalog_factories.data_models.usages.usage import Usage
class Content: class Content:
"""
Content class
"""
def __init__(self, usages): def __init__(self, usages):
self._usages = usages self._usages = usages
@ -18,3 +21,20 @@ class Content:
""" """
return self._usages return self._usages
def to_dictionary(self):
"""Class content to dictionary"""
_usages = []
for _usage in self.usages:
_usages.append(_usage.to_dictionary())
content = {'Usages': _usages}
return content
def __str__(self):
"""Print content"""
_usages = []
for _usage in self.usages:
_usages.append(_usage.to_dictionary())
content = {'Usages': _usages}
return str(content)

View File

@ -52,3 +52,15 @@ class DomesticHotWater:
:return: None or [Schedule] :return: None or [Schedule]
""" """
return self._schedules return self._schedules
def to_dictionary(self):
"""Class content to dictionary"""
_schedules = []
for _schedule in self.schedules:
_schedules.append(_schedule.to_dictionary())
content = {'Domestic hot water': {'density [W/m2]': self.density,
'peak flow [m3/sm2]': self.peak_flow,
'service temperature [Celsius]': self.service_temperature,
'schedules': _schedules}
}
return content

View File

@ -1,22 +0,0 @@
"""
Usage catalog internal gain
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
class InternalGain:
"""
InternalGain class
"""
def __init__(self, internal_gain_type, average_internal_gain, convective_fraction, radiative_fraction, latent_fraction, schedules):
self._type = internal_gain_type
self._average_internal_gain = average_internal_gain
self._convective_fraction = convective_fraction
self._radiative_fraction = radiative_fraction
self._latent_fraction = latent_fraction
self._schedules = schedules

View File

@ -11,6 +11,9 @@ from hub.catalog_factories.data_models.usages.schedule import Schedule
class Lighting: class Lighting:
"""
Lighting class
"""
def __init__(self, density, convective_fraction, radiative_fraction, latent_fraction, schedules): def __init__(self, density, convective_fraction, radiative_fraction, latent_fraction, schedules):
self._density = density self._density = density
self._convective_fraction = convective_fraction self._convective_fraction = convective_fraction
@ -58,3 +61,16 @@ class Lighting:
:return: None or [Schedule] :return: None or [Schedule]
""" """
return self._schedules return self._schedules
def to_dictionary(self):
"""Class content to dictionary"""
_schedules = []
for _schedule in self.schedules:
_schedules.append(_schedule.to_dictionary())
content = {'Lighting': {'density [W/m2]': self.density,
'convective fraction': self.convective_fraction,
'radiative fraction': self.radiative_fraction,
'latent fraction': self.latent_fraction,
'schedules': _schedules}
}
return content

View File

@ -65,3 +65,16 @@ class Occupancy:
:return: None or [Schedule] :return: None or [Schedule]
""" """
return self._schedules return self._schedules
def to_dictionary(self):
"""Class content to dictionary"""
_schedules = []
for _schedule in self.schedules:
_schedules.append(_schedule.to_dictionary())
content = {'Occupancy': {'occupancy density [persons/m2]': self.occupancy_density,
'sensible convective internal gain [W/m2]': self.sensible_convective_internal_gain,
'sensible radiative internal gain [W/m2]': self.sensible_radiative_internal_gain,
'latent internal gain [W/m2]': self.latent_internal_gain,
'schedules': _schedules}
}
return content

View File

@ -73,3 +73,14 @@ class Schedule:
:return: None or [str] :return: None or [str]
""" """
return self._day_types return self._day_types
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Schedule': {'type': self.type,
'time range': self.time_range,
'time step': self.time_step,
'data type': self.data_type,
'day types': self.day_types,
'values': self.values}
}
return content

View File

@ -76,3 +76,23 @@ class ThermalControl:
:return: None or [Schedule] :return: None or [Schedule]
""" """
return self._cooling_set_point_schedules return self._cooling_set_point_schedules
def to_dictionary(self):
"""Class content to dictionary"""
_hvac_schedules = []
for _schedule in self.hvac_availability_schedules:
_hvac_schedules.append(_schedule.to_dictionary())
_heating_set_point_schedules = []
for _schedule in self.heating_set_point_schedules:
_heating_set_point_schedules.append(_schedule.to_dictionary())
_cooling_set_point_schedules = []
for _schedule in self.cooling_set_point_schedules:
_cooling_set_point_schedules.append(_schedule.to_dictionary())
content = {'Thermal control': {'mean heating set point [Celsius]': self.mean_heating_set_point,
'heating set back [Celsius]': self.heating_set_back,
'mean cooling set point [Celsius]': self.mean_cooling_set_point,
'hvac availability schedules': _hvac_schedules,
'heating set point schedules': _heating_set_point_schedules,
'cooling set point schedules': _cooling_set_point_schedules}
}
return content

View File

@ -8,12 +8,15 @@ from typing import Union
from hub.catalog_factories.data_models.usages.appliances import Appliances from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.lighting import Lighting from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.ocupancy import Occupancy from hub.catalog_factories.data_models.usages.occupancy import Occupancy
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
class Usage: class Usage:
"""
Usage class
"""
def __init__(self, name, def __init__(self, name,
hours_day, hours_day,
days_year, days_year,
@ -62,7 +65,7 @@ class Usage:
@property @property
def mechanical_air_change(self) -> Union[None, float]: def mechanical_air_change(self) -> Union[None, float]:
""" """
Get usage zone mechanical air change in air change per hour (ACH) Get usage zone mechanical air change in air change per second (1/s)
:return: None or float :return: None or float
""" """
return self._mechanical_air_change return self._mechanical_air_change
@ -122,3 +125,19 @@ class Usage:
:return: None or DomesticHotWater :return: None or DomesticHotWater
""" """
return self._domestic_hot_water return self._domestic_hot_water
def to_dictionary(self):
"""Class content to dictionary"""
content = {'Usage': {'name': self.name,
'hours a day': self.hours_day,
'days a year': self.days_year,
'mechanical air change [ACH]': self.mechanical_air_change,
'ventilation rate [m3/sm2]': self.ventilation_rate,
'occupancy': self.occupancy.to_dictionary(),
'lighting': self.lighting.to_dictionary(),
'appliances': self.appliances.to_dictionary(),
'thermal control': self.thermal_control.to_dictionary(),
'domestic hot water': self.domestic_hot_water.to_dictionary()
}
}
return content

View File

@ -1,51 +1,55 @@
""" """
Montreal custom energy systems catalog Montreal custom energy systems catalog module
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
from ast import literal_eval
import xmltodict import xmltodict
from hub.catalog_factories.catalog import Catalog from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.energy_systems.system import System from hub.catalog_factories.data_models.energy_systems.system import System
from hub.catalog_factories.data_models.energy_systems.content import Content from hub.catalog_factories.data_models.energy_systems.content import Content
from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem from hub.catalog_factories.data_models.energy_systems.non_pv_generation_system import NonPvGenerationSystem
from hub.catalog_factories.data_models.energy_systems.pv_generation_system import PvGenerationSystem
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
from hub.catalog_factories.data_models.energy_systems.archetype import Archetype from hub.catalog_factories.data_models.energy_systems.archetype import Archetype
from hub.catalog_factories.data_models.energy_systems.thermal_storage_system import ThermalStorageSystem
from hub.catalog_factories.data_models.energy_systems.electrical_storage_system import ElectricalStorageSystem
class MontrealCustomCatalog(Catalog): class MontrealCustomCatalog(Catalog):
"""
Montreal custom energy systems catalog class
"""
def __init__(self, path): def __init__(self, path):
path = str(path / 'montreal_custom_systems.xml') path = str(path / 'montreal_custom_systems.xml')
with open(path) as xml: with open(path, 'r', encoding='utf-8') as xml:
self._archetypes = xmltodict.parse(xml.read(), force_list=('system', 'system_cluster', 'equipment', self._archetypes = xmltodict.parse(xml.read(), force_list=('system', 'system_cluster', 'equipment',
'demand', 'system_id')) 'demand', 'system_id'))
self._lod = float(self._archetypes['catalog']['@lod'])
self._catalog_generation_equipments = self._load_generation_equipments() self._catalog_generation_equipments = self._load_generation_equipments()
self._catalog_distribution_equipments = self._load_distribution_equipments()
self._catalog_emission_equipments = self._load_emission_equipments() self._catalog_emission_equipments = self._load_emission_equipments()
self._catalog_distribution_equipments = self._load_distribution_equipments()
self._catalog_systems = self._load_systems() self._catalog_systems = self._load_systems()
self._catalog_archetypes = self._load_archetypes() self._catalog_archetypes = self._load_archetypes()
# store the full catalog data model in self._content # store the full catalog data model in self._content
self._content = Content(self._catalog_archetypes, self._content = Content(self._catalog_archetypes,
self._catalog_systems, self._catalog_systems,
self._catalog_generation_equipments, self._catalog_generation_equipments,
self._catalog_distribution_equipments, self._catalog_distribution_equipments)
self._catalog_emission_equipments)
def _load_generation_equipments(self): def _load_generation_equipments(self):
_equipments = [] _equipments = []
_storages = []
equipments = self._archetypes['catalog']['generation_equipments']['equipment'] equipments = self._archetypes['catalog']['generation_equipments']['equipment']
for equipment in equipments: for equipment in equipments:
equipment_id = float(equipment['@id']) equipment_id = float(equipment['@id'])
equipment_type = equipment['@type'] equipment_type = equipment['@type']
fuel_type = equipment['@fuel_type'] fuel_type = equipment['@fuel_type']
name = equipment['name'] model_name = equipment['name']
heating_efficiency = None heating_efficiency = None
if 'heating_efficiency' in equipment: if 'heating_efficiency' in equipment:
heating_efficiency = float(equipment['heating_efficiency']) heating_efficiency = float(equipment['heating_efficiency'])
@ -55,21 +59,38 @@ class MontrealCustomCatalog(Catalog):
electricity_efficiency = None electricity_efficiency = None
if 'electrical_efficiency' in equipment: if 'electrical_efficiency' in equipment:
electricity_efficiency = float(equipment['electrical_efficiency']) electricity_efficiency = float(equipment['electrical_efficiency'])
storage = eval(equipment['storage'].capitalize())
generation_system = GenerationSystem(equipment_id,
name,
equipment_type,
fuel_type,
None,
heating_efficiency,
cooling_efficiency,
electricity_efficiency,
None,
None,
storage,
None)
storage_systems = None
storage = literal_eval(equipment['storage'].capitalize())
if storage:
if equipment_type == 'electricity generator':
storage_system = ElectricalStorageSystem(equipment_id)
else:
storage_system = ThermalStorageSystem(equipment_id)
storage_systems = [storage_system]
if model_name == 'PV system':
system_type = 'Photovoltaic'
generation_system = PvGenerationSystem(equipment_id,
name=None,
system_type= system_type,
model_name=model_name,
electricity_efficiency=electricity_efficiency,
energy_storage_systems=storage_systems
)
else:
generation_system = NonPvGenerationSystem(equipment_id,
name=None,
model_name=model_name,
system_type=equipment_type,
fuel_type=fuel_type,
heat_efficiency=heating_efficiency,
cooling_efficiency=cooling_efficiency,
electricity_efficiency=electricity_efficiency,
energy_storage_systems=storage_systems,
domestic_hot_water=False
)
_equipments.append(generation_system) _equipments.append(generation_system)
return _equipments return _equipments
def _load_distribution_equipments(self): def _load_distribution_equipments(self):
@ -78,7 +99,7 @@ class MontrealCustomCatalog(Catalog):
for equipment in equipments: for equipment in equipments:
equipment_id = float(equipment['@id']) equipment_id = float(equipment['@id'])
equipment_type = equipment['@type'] equipment_type = equipment['@type']
name = equipment['name'] model_name = equipment['name']
distribution_heat_losses = None distribution_heat_losses = None
if 'distribution_heat_losses' in equipment: if 'distribution_heat_losses' in equipment:
distribution_heat_losses = float(equipment['distribution_heat_losses']['#text']) / 100 distribution_heat_losses = float(equipment['distribution_heat_losses']['#text']) / 100
@ -87,15 +108,22 @@ class MontrealCustomCatalog(Catalog):
distribution_consumption_fix_flow = float(equipment['distribution_consumption_fix_flow']['#text']) / 100 distribution_consumption_fix_flow = float(equipment['distribution_consumption_fix_flow']['#text']) / 100
distribution_consumption_variable_flow = None distribution_consumption_variable_flow = None
if 'distribution_consumption_variable_flow' in equipment: if 'distribution_consumption_variable_flow' in equipment:
distribution_consumption_variable_flow = float(equipment['distribution_consumption_variable_flow']['#text']) / 100 distribution_consumption_variable_flow = float(
equipment['distribution_consumption_variable_flow']['#text']) / 100
emission_equipment = equipment['dissipation_id']
_emission_equipments = None
for equipment_archetype in self._catalog_emission_equipments:
if int(equipment_archetype.id) == int(emission_equipment):
_emission_equipments = [equipment_archetype]
distribution_system = DistributionSystem(equipment_id, distribution_system = DistributionSystem(equipment_id,
name, model_name=model_name,
equipment_type, system_type=equipment_type,
None, distribution_consumption_fix_flow=distribution_consumption_fix_flow,
distribution_consumption_fix_flow, distribution_consumption_variable_flow=distribution_consumption_variable_flow,
distribution_consumption_variable_flow, heat_losses=distribution_heat_losses,
distribution_heat_losses) emission_systems=_emission_equipments)
_equipments.append(distribution_system) _equipments.append(distribution_system)
return _equipments return _equipments
@ -106,15 +134,15 @@ class MontrealCustomCatalog(Catalog):
for equipment in equipments: for equipment in equipments:
equipment_id = float(equipment['@id']) equipment_id = float(equipment['@id'])
equipment_type = equipment['@type'] equipment_type = equipment['@type']
name = equipment['name'] model_name = equipment['name']
parasitic_consumption = None parasitic_consumption = 0
if 'parasitic_consumption' in equipment: if 'parasitic_consumption' in equipment:
parasitic_consumption = float(equipment['parasitic_consumption']['#text']) / 100 parasitic_consumption = float(equipment['parasitic_consumption']['#text']) / 100
emission_system = EmissionSystem(equipment_id, emission_system = EmissionSystem(equipment_id,
name, model_name=model_name,
equipment_type, system_type=equipment_type,
parasitic_consumption) parasitic_energy_consumption=parasitic_consumption)
_equipments.append(emission_system) _equipments.append(emission_system)
return _equipments return _equipments
@ -127,28 +155,21 @@ class MontrealCustomCatalog(Catalog):
name = system['name'] name = system['name']
demands = system['demands']['demand'] demands = system['demands']['demand']
generation_equipment = system['equipments']['generation_id'] generation_equipment = system['equipments']['generation_id']
_generation_equipment = None _generation_equipments = None
for equipment_archetype in self._catalog_generation_equipments: for equipment_archetype in self._catalog_generation_equipments:
if int(equipment_archetype.id) == int(generation_equipment): if int(equipment_archetype.id) == int(generation_equipment):
_generation_equipment = equipment_archetype _generation_equipments = [equipment_archetype]
distribution_equipment = system['equipments']['distribution_id'] distribution_equipment = system['equipments']['distribution_id']
_distribution_equipment = None _distribution_equipments = None
for equipment_archetype in self._catalog_distribution_equipments: for equipment_archetype in self._catalog_distribution_equipments:
if int(equipment_archetype.id) == int(distribution_equipment): if int(equipment_archetype.id) == int(distribution_equipment):
_distribution_equipment = equipment_archetype _distribution_equipments = [equipment_archetype]
emission_equipment = system['equipments']['dissipation_id']
_emission_equipment = None
for equipment_archetype in self._catalog_emission_equipments:
if int(equipment_archetype.id) == int(emission_equipment):
_emission_equipment = equipment_archetype
_catalog_systems.append(System(self._lod, _catalog_systems.append(System(system_id,
system_id,
name,
demands, demands,
_generation_equipment, name=name,
_distribution_equipment, generation_systems=_generation_equipments,
_emission_equipment)) distribution_systems=_distribution_equipments))
return _catalog_systems return _catalog_systems
def _load_archetypes(self): def _load_archetypes(self):
@ -162,7 +183,7 @@ class MontrealCustomCatalog(Catalog):
for system_archetype in self._catalog_systems: for system_archetype in self._catalog_systems:
if int(system_archetype.id) == int(system): if int(system_archetype.id) == int(system):
_systems.append(system_archetype) _systems.append(system_archetype)
_catalog_archetypes.append(Archetype(self._lod, name, _systems)) _catalog_archetypes.append(Archetype(name, _systems))
return _catalog_archetypes return _catalog_archetypes
def names(self, category=None): def names(self, category=None):
@ -172,17 +193,15 @@ class MontrealCustomCatalog(Catalog):
""" """
if category is None: if category is None:
_names = {'archetypes': [], 'systems': [], 'generation_equipments': [], 'distribution_equipments': [], _names = {'archetypes': [], 'systems': [], 'generation_equipments': [], 'distribution_equipments': [],
'emission_equipments':[]} 'emission_equipments': []}
for archetype in self._content.archetypes: for archetype in self._content.archetypes:
_names['archetypes'].append(archetype.name) _names['archetypes'].append(archetype.name)
for system in self._content.systems: for system in self._content.systems:
_names['systems'].append(system.name) _names['systems'].append(system.name)
for equipment in self._content.generation_equipments: for equipment in self._content.generation_equipments:
_names['generation_equipments'].append(equipment.name) _names['generation_equipments'].append(equipment.model_name)
for equipment in self._content.distribution_equipments: for equipment in self._content.distribution_equipments:
_names['distribution_equipments'].append(equipment.name) _names['distribution_equipments'].append(equipment.model_name)
for equipment in self._content.emission_equipments:
_names['emission_equipments'].append(equipment.name)
else: else:
_names = {category: []} _names = {category: []}
if category.lower() == 'archetypes': if category.lower() == 'archetypes':
@ -193,13 +212,10 @@ class MontrealCustomCatalog(Catalog):
_names[category].append(system.name) _names[category].append(system.name)
elif category.lower() == 'generation_equipments': elif category.lower() == 'generation_equipments':
for system in self._content.generation_equipments: for system in self._content.generation_equipments:
_names[category].append(system.name) _names[category].append(system.model_name)
elif category.lower() == 'distribution_equipments': elif category.lower() == 'distribution_equipments':
for system in self._content.distribution_equipments: for system in self._content.distribution_equipments:
_names[category].append(system.name) _names[category].append(system.model_name)
elif category.lower() == 'emission_equipments':
for system in self._content.emission_equipments:
_names[category].append(system.name)
else: else:
raise ValueError(f'Unknown category [{category}]') raise ValueError(f'Unknown category [{category}]')
return _names return _names
@ -211,19 +227,14 @@ class MontrealCustomCatalog(Catalog):
""" """
if category is None: if category is None:
return self._content return self._content
else: if category.lower() == 'archetypes':
if category.lower() == 'archetypes': return self._content.archetypes
return self._content.archetypes if category.lower() == 'systems':
elif category.lower() == 'systems': return self._content.systems
return self._content.systems if category.lower() == 'generation_equipments':
elif category.lower() == 'generation_equipments': return self._content.generation_equipments
return self._content.generation_equipments if category.lower() == 'distribution_equipments':
elif category.lower() == 'distribution_equipments': return self._content.distribution_equipments
return self._content.distribution_equipments
elif category.lower() == 'emission_equipments':
return self._content.emission_equipments
else:
raise ValueError(f'Unknown category [{category}]')
def get_entry(self, name): def get_entry(self, name):
""" """
@ -237,12 +248,9 @@ class MontrealCustomCatalog(Catalog):
if entry.name.lower() == name.lower(): if entry.name.lower() == name.lower():
return entry return entry
for entry in self._content.generation_equipments: for entry in self._content.generation_equipments:
if entry.name.lower() == name.lower(): if entry.model_name.lower() == name.lower():
return entry return entry
for entry in self._content.distribution_equipments: for entry in self._content.distribution_equipments:
if entry.name.lower() == name.lower(): if entry.model_name.lower() == name.lower():
return entry
for entry in self._content.emission_equipments:
if entry.name.lower() == name.lower():
return entry return entry
raise IndexError(f"{name} doesn't exists in the catalog") raise IndexError(f"{name} doesn't exists in the catalog")

View File

@ -0,0 +1,559 @@
"""
Montreal future energy system catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
"""
import xmltodict
from pathlib import Path
from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
from hub.catalog_factories.data_models.energy_systems.system import System
from hub.catalog_factories.data_models.energy_systems.content import Content
from hub.catalog_factories.data_models.energy_systems.non_pv_generation_system import NonPvGenerationSystem
from hub.catalog_factories.data_models.energy_systems.pv_generation_system import PvGenerationSystem
from hub.catalog_factories.data_models.energy_systems.thermal_storage_system import ThermalStorageSystem
from hub.catalog_factories.data_models.energy_systems.performance_curves import PerformanceCurves
from hub.catalog_factories.data_models.energy_systems.archetype import Archetype
from hub.catalog_factories.data_models.construction.material import Material
from hub.catalog_factories.data_models.construction.layer import Layer
class MontrealFutureSystemCatalogue(Catalog):
"""
North america energy system catalog class
"""
def __init__(self, path):
path = str(path / 'montreal_future_systems.xml')
with open(path, 'r', encoding='utf-8') as xml:
self._archetypes = xmltodict.parse(xml.read(),
force_list=['pv_generation_component', 'templateStorages', 'demand'])
self._storage_components = self._load_storage_components()
self._generation_components = self._load_generation_components()
self._energy_emission_components = self._load_emission_equipments()
self._distribution_components = self._load_distribution_equipments()
self._systems = self._load_systems()
self._system_archetypes = self._load_archetypes()
self._content = Content(self._system_archetypes,
self._systems,
generations=self._generation_components,
distributions=self._distribution_components)
def _load_generation_components(self):
generation_components = []
non_pv_generation_components = self._archetypes['EnergySystemCatalog']['energy_generation_components'][
'non_pv_generation_component']
if non_pv_generation_components is not None:
for non_pv in non_pv_generation_components:
system_id = non_pv['system_id']
name = non_pv['name']
system_type = non_pv['system_type']
model_name = non_pv['model_name']
manufacturer = non_pv['manufacturer']
fuel_type = non_pv['fuel_type']
distribution_systems = non_pv['distribution_systems']
energy_storage_systems = None
if non_pv['energy_storage_systems'] is not None:
storage_component = non_pv['energy_storage_systems']['storage_id']
storage_systems = self._search_storage_equipment(self._load_storage_components(), storage_component)
energy_storage_systems = storage_systems
nominal_heat_output = non_pv['nominal_heat_output']
maximum_heat_output = non_pv['maximum_heat_output']
minimum_heat_output = non_pv['minimum_heat_output']
source_medium = non_pv['source_medium']
supply_medium = non_pv['supply_medium']
heat_efficiency = non_pv['heat_efficiency']
nominal_cooling_output = non_pv['nominal_cooling_output']
maximum_cooling_output = non_pv['maximum_cooling_output']
minimum_cooling_output = non_pv['minimum_cooling_output']
cooling_efficiency = non_pv['cooling_efficiency']
electricity_efficiency = non_pv['electricity_efficiency']
source_temperature = non_pv['source_temperature']
source_mass_flow = non_pv['source_mass_flow']
nominal_electricity_output = non_pv['nominal_electricity_output']
maximum_heat_supply_temperature = non_pv['maximum_heat_supply_temperature']
minimum_heat_supply_temperature = non_pv['minimum_heat_supply_temperature']
maximum_cooling_supply_temperature = non_pv['maximum_cooling_supply_temperature']
minimum_cooling_supply_temperature = non_pv['minimum_cooling_supply_temperature']
heat_output_curve = None
heat_fuel_consumption_curve = None
heat_efficiency_curve = None
cooling_output_curve = None
cooling_fuel_consumption_curve = None
cooling_efficiency_curve = None
if non_pv['heat_output_curve'] is not None:
curve_type = non_pv['heat_output_curve']['curve_type']
dependant_variable = non_pv['heat_output_curve']['dependant_variable']
parameters = non_pv['heat_output_curve']['parameters']
coefficients = list(non_pv['heat_output_curve']['coefficients'].values())
heat_output_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['heat_fuel_consumption_curve'] is not None:
curve_type = non_pv['heat_fuel_consumption_curve']['curve_type']
dependant_variable = non_pv['heat_fuel_consumption_curve']['dependant_variable']
parameters = non_pv['heat_fuel_consumption_curve']['parameters']
coefficients = list(non_pv['heat_fuel_consumption_curve']['coefficients'].values())
heat_fuel_consumption_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['heat_efficiency_curve'] is not None:
curve_type = non_pv['heat_efficiency_curve']['curve_type']
dependant_variable = non_pv['heat_efficiency_curve']['dependant_variable']
parameters = non_pv['heat_efficiency_curve']['parameters']
coefficients = list(non_pv['heat_efficiency_curve']['coefficients'].values())
heat_efficiency_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['cooling_output_curve'] is not None:
curve_type = non_pv['cooling_output_curve']['curve_type']
dependant_variable = non_pv['cooling_output_curve']['dependant_variable']
parameters = non_pv['cooling_output_curve']['parameters']
coefficients = list(non_pv['cooling_output_curve']['coefficients'].values())
cooling_output_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['cooling_fuel_consumption_curve'] is not None:
curve_type = non_pv['cooling_fuel_consumption_curve']['curve_type']
dependant_variable = non_pv['cooling_fuel_consumption_curve']['dependant_variable']
parameters = non_pv['cooling_fuel_consumption_curve']['parameters']
coefficients = list(non_pv['cooling_fuel_consumption_curve']['coefficients'].values())
cooling_fuel_consumption_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['cooling_efficiency_curve'] is not None:
curve_type = non_pv['cooling_efficiency_curve']['curve_type']
dependant_variable = non_pv['cooling_efficiency_curve']['dependant_variable']
parameters = non_pv['cooling_efficiency_curve']['parameters']
coefficients = list(non_pv['cooling_efficiency_curve']['coefficients'].values())
cooling_efficiency_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
dhw = None
if non_pv['domestic_hot_water'] is not None:
if non_pv['domestic_hot_water'] == 'True':
dhw = True
else:
dhw = False
reversible = None
if non_pv['reversible'] is not None:
if non_pv['reversible'] == 'True':
reversible = True
else:
reversible = False
dual_supply = None
if non_pv['simultaneous_heat_cold'] is not None:
if non_pv['simultaneous_heat_cold'] == 'True':
dual_supply = True
else:
dual_supply = False
non_pv_component = NonPvGenerationSystem(system_id=system_id,
name=name,
system_type=system_type,
model_name=model_name,
manufacturer=manufacturer,
fuel_type=fuel_type,
nominal_heat_output=nominal_heat_output,
maximum_heat_output=maximum_heat_output,
minimum_heat_output=minimum_heat_output,
source_medium=source_medium,
supply_medium=supply_medium,
heat_efficiency=heat_efficiency,
nominal_cooling_output=nominal_cooling_output,
maximum_cooling_output=maximum_cooling_output,
minimum_cooling_output=minimum_cooling_output,
cooling_efficiency=cooling_efficiency,
electricity_efficiency=electricity_efficiency,
source_temperature=source_temperature,
source_mass_flow=source_mass_flow,
nominal_electricity_output=nominal_electricity_output,
maximum_heat_supply_temperature=maximum_heat_supply_temperature,
minimum_heat_supply_temperature=minimum_heat_supply_temperature,
maximum_cooling_supply_temperature=maximum_cooling_supply_temperature,
minimum_cooling_supply_temperature=minimum_cooling_supply_temperature,
heat_output_curve=heat_output_curve,
heat_fuel_consumption_curve=heat_fuel_consumption_curve,
heat_efficiency_curve=heat_efficiency_curve,
cooling_output_curve=cooling_output_curve,
cooling_fuel_consumption_curve=cooling_fuel_consumption_curve,
cooling_efficiency_curve=cooling_efficiency_curve,
distribution_systems=distribution_systems,
energy_storage_systems=energy_storage_systems,
domestic_hot_water=dhw,
reversible=reversible,
simultaneous_heat_cold=dual_supply)
generation_components.append(non_pv_component)
pv_generation_components = self._archetypes['EnergySystemCatalog']['energy_generation_components'][
'pv_generation_component']
if pv_generation_components is not None:
for pv in pv_generation_components:
system_id = pv['system_id']
name = pv['name']
system_type = pv['system_type']
model_name = pv['model_name']
manufacturer = pv['manufacturer']
electricity_efficiency = pv['electricity_efficiency']
nominal_electricity_output = pv['nominal_electricity_output']
nominal_ambient_temperature = pv['nominal_ambient_temperature']
nominal_cell_temperature = pv['nominal_cell_temperature']
nominal_radiation = pv['nominal_radiation']
standard_test_condition_cell_temperature = pv['standard_test_condition_cell_temperature']
standard_test_condition_maximum_power = pv['standard_test_condition_maximum_power']
standard_test_condition_radiation = pv['standard_test_condition_radiation']
cell_temperature_coefficient = pv['cell_temperature_coefficient']
width = pv['width']
height = pv['height']
distribution_systems = pv['distribution_systems']
energy_storage_systems = None
if pv['energy_storage_systems'] is not None:
storage_component = pv['energy_storage_systems']['storage_id']
storage_systems = self._search_storage_equipment(self._load_storage_components(), storage_component)
energy_storage_systems = storage_systems
pv_component = PvGenerationSystem(system_id=system_id,
name=name,
system_type=system_type,
model_name=model_name,
manufacturer=manufacturer,
electricity_efficiency=electricity_efficiency,
nominal_electricity_output=nominal_electricity_output,
nominal_ambient_temperature=nominal_ambient_temperature,
nominal_cell_temperature=nominal_cell_temperature,
nominal_radiation=nominal_radiation,
standard_test_condition_cell_temperature=
standard_test_condition_cell_temperature,
standard_test_condition_maximum_power=standard_test_condition_maximum_power,
standard_test_condition_radiation=standard_test_condition_radiation,
cell_temperature_coefficient=cell_temperature_coefficient,
width=width,
height=height,
distribution_systems=distribution_systems,
energy_storage_systems=energy_storage_systems)
generation_components.append(pv_component)
return generation_components
def _load_distribution_equipments(self):
_equipments = []
distribution_systems = self._archetypes['EnergySystemCatalog']['distribution_systems']['distribution_system']
if distribution_systems is not None:
for distribution_system in distribution_systems:
system_id = None
model_name = None
system_type = None
supply_temperature = None
distribution_consumption_fix_flow = None
distribution_consumption_variable_flow = None
heat_losses = None
generation_systems = None
energy_storage_systems = None
emission_systems = None
distribution_equipment = DistributionSystem(system_id=system_id,
model_name=model_name,
system_type=system_type,
supply_temperature=supply_temperature,
distribution_consumption_fix_flow=distribution_consumption_fix_flow,
distribution_consumption_variable_flow=
distribution_consumption_variable_flow,
heat_losses=heat_losses,
generation_systems=generation_systems,
energy_storage_systems=energy_storage_systems,
emission_systems=emission_systems
)
_equipments.append(distribution_equipment)
return _equipments
def _load_emission_equipments(self):
_equipments = []
dissipation_systems = self._archetypes['EnergySystemCatalog']['dissipation_systems']['dissipation_system']
if dissipation_systems is not None:
for dissipation_system in dissipation_systems:
system_id = None
model_name = None
system_type = None
parasitic_energy_consumption = 0
emission_system = EmissionSystem(system_id=system_id,
model_name=model_name,
system_type=system_type,
parasitic_energy_consumption=parasitic_energy_consumption)
_equipments.append(emission_system)
return _equipments
def _load_storage_components(self):
storage_components = []
thermal_storages = self._archetypes['EnergySystemCatalog']['energy_storage_components']['thermalStorages']
template_storages = self._archetypes['EnergySystemCatalog']['energy_storage_components']['templateStorages']
for tes in thermal_storages:
storage_id = tes['storage_id']
type_energy_stored = tes['type_energy_stored']
model_name = tes['model_name']
manufacturer = tes['manufacturer']
storage_type = tes['storage_type']
volume = tes['physical_characteristics']['volume']
height = tes['physical_characteristics']['height']
maximum_operating_temperature = tes['maximum_operating_temperature']
materials = self._load_materials()
insulation_material_id = tes['insulation']['material_id']
insulation_material = self._search_material(materials, insulation_material_id)
material_id = tes['physical_characteristics']['material_id']
tank_material = self._search_material(materials, material_id)
thickness = float(tes['insulation']['insulationThickness']) / 100 # from cm to m
insulation_layer = Layer(None, 'insulation', insulation_material, thickness)
thickness = float(tes['physical_characteristics']['tankThickness']) / 100 # from cm to m
tank_layer = Layer(None, 'tank', tank_material, thickness)
media = self._load_media()
media_id = tes['storage_medium']['medium_id']
medium = self._search_media(media, media_id)
layers = [insulation_layer, tank_layer]
nominal_capacity = tes['nominal_capacity']
losses_ratio = tes['losses_ratio']
heating_coil_capacity = tes['heating_coil_capacity']
storage_component = ThermalStorageSystem(storage_id=storage_id,
model_name=model_name,
type_energy_stored=type_energy_stored,
manufacturer=manufacturer,
storage_type=storage_type,
nominal_capacity=nominal_capacity,
losses_ratio=losses_ratio,
volume=volume,
height=height,
layers=layers,
maximum_operating_temperature=maximum_operating_temperature,
storage_medium=medium,
heating_coil_capacity=heating_coil_capacity)
storage_components.append(storage_component)
for template in template_storages:
storage_id = template['storage_id']
storage_type = template['storage_type']
type_energy_stored = template['type_energy_stored']
maximum_operating_temperature = template['maximum_operating_temperature']
height = float(template['physical_characteristics']['height'])
materials = self._load_materials()
insulation_material_id = template['insulation']['material_id']
insulation_material = self._search_material(materials, insulation_material_id)
material_id = template['physical_characteristics']['material_id']
tank_material = self._search_material(materials, material_id)
thickness = float(template['insulation']['insulationThickness']) / 100 # from cm to m
insulation_layer = Layer(None, 'insulation', insulation_material, thickness)
thickness = float(template['physical_characteristics']['tankThickness']) / 100 # from cm to m
tank_layer = Layer(None, 'tank', tank_material, thickness)
layers = [insulation_layer, tank_layer]
media = self._load_media()
media_id = template['storage_medium']['medium_id']
medium = self._search_media(media, media_id)
model_name = template['model_name']
manufacturer = template['manufacturer']
nominal_capacity = template['nominal_capacity']
losses_ratio = template['losses_ratio']
volume = template['physical_characteristics']['volume']
heating_coil_capacity = template['heating_coil_capacity']
storage_component = ThermalStorageSystem(storage_id=storage_id,
model_name=model_name,
type_energy_stored=type_energy_stored,
manufacturer=manufacturer,
storage_type=storage_type,
nominal_capacity=nominal_capacity,
losses_ratio=losses_ratio,
volume=volume,
height=height,
layers=layers,
maximum_operating_temperature=maximum_operating_temperature,
storage_medium=medium,
heating_coil_capacity=heating_coil_capacity)
storage_components.append(storage_component)
return storage_components
def _load_systems(self):
base_path = Path(Path(__file__).parent.parent.parent / 'data/energy_systems')
_catalog_systems = []
systems = self._archetypes['EnergySystemCatalog']['systems']['system']
for system in systems:
system_id = system['id']
name = system['name']
demands = system['demands']['demand']
generation_components = system['components']['generation_id']
generation_systems = self._search_generation_equipment(self._load_generation_components(), generation_components)
configuration_schema = Path(base_path / system['schema'])
energy_system = System(system_id=system_id,
name=name,
demand_types=demands,
generation_systems=generation_systems,
distribution_systems=None,
configuration_schema=configuration_schema)
_catalog_systems.append(energy_system)
return _catalog_systems
def _load_archetypes(self):
_system_archetypes = []
system_clusters = self._archetypes['EnergySystemCatalog']['system_archetypes']['system_archetype']
for system_cluster in system_clusters:
name = system_cluster['name']
systems = system_cluster['systems']['system_id']
integer_system_ids = [int(item) for item in systems]
_systems = []
for system_archetype in self._systems:
if int(system_archetype.id) in integer_system_ids:
_systems.append(system_archetype)
_system_archetypes.append(Archetype(name=name, systems=_systems))
return _system_archetypes
def _load_materials(self):
materials = []
_materials = self._archetypes['EnergySystemCatalog']['materials']['material']
for _material in _materials:
material_id = _material['material_id']
name = _material['name']
conductivity = _material['conductivity']
solar_absorptance = _material['solar_absorptance']
thermal_absorptance = _material['thermal_absorptance']
density = _material['density']
specific_heat = _material['specific_heat']
no_mass = _material['no_mass']
visible_absorptance = _material['visible_absorptance']
thermal_resistance = _material['thermal_resistance']
material = Material(material_id,
name,
solar_absorptance=solar_absorptance,
thermal_absorptance=thermal_absorptance,
density=density,
conductivity=conductivity,
thermal_resistance=thermal_resistance,
visible_absorptance=visible_absorptance,
no_mass=no_mass,
specific_heat=specific_heat)
materials.append(material)
return materials
@staticmethod
def _search_material(materials, material_id):
_material = None
for material in materials:
if int(material.id) == int(material_id):
_material = material
break
if _material is None:
raise ValueError(f'Material with the id = [{material_id}] not found in catalog ')
return _material
def _load_media(self):
media = []
_media = [self._archetypes['EnergySystemCatalog']['media']['medium']]
for _medium in _media:
medium_id = _medium['medium_id']
density = _medium['density']
name = _medium['name']
conductivity = _medium['conductivity']
solar_absorptance = _medium['solar_absorptance']
thermal_absorptance = _medium['thermal_absorptance']
specific_heat = _medium['specific_heat']
no_mass = _medium['no_mass']
visible_absorptance = _medium['visible_absorptance']
thermal_resistance = _medium['thermal_resistance']
medium = Material(material_id=medium_id,
name=name,
solar_absorptance=solar_absorptance,
thermal_absorptance=thermal_absorptance,
visible_absorptance=visible_absorptance,
no_mass=no_mass,
thermal_resistance=thermal_resistance,
conductivity=conductivity,
density=density,
specific_heat=specific_heat)
media.append(medium)
return media
@staticmethod
def _search_media(media, medium_id):
_medium = None
for medium in media:
if int(medium.id) == int(medium_id):
_medium = medium
break
if _medium is None:
raise ValueError(f'media with the id = [{medium_id}] not found in catalog ')
return _medium
@staticmethod
def _search_generation_equipment(generation_systems, generation_id):
_generation_systems = []
if isinstance(generation_id, list):
integer_ids = [int(item) for item in generation_id]
for generation in generation_systems:
if int(generation.id) in integer_ids:
_generation_systems.append(generation)
else:
integer_id = int(generation_id)
for generation in generation_systems:
if int(generation.id) == integer_id:
_generation_systems.append(generation)
if len(_generation_systems) == 0:
_generation_systems = None
raise ValueError(f'The system with the following id is not found in catalog [{generation_id}]')
return _generation_systems
@staticmethod
def _search_storage_equipment(storage_systems, storage_id):
_storage_systems = []
for storage in storage_systems:
if storage.id in storage_id:
_storage_systems.append(storage)
if len(_storage_systems) == 0:
_storage_systems = None
raise ValueError(f'The system with the following id is not found in catalog [{storage_id}]')
return _storage_systems
def names(self, category=None):
"""
Get the catalog elements names
:parm: optional category filter
"""
if category is None:
_names = {'archetypes': [], 'systems': [], 'generation_equipments': [], 'storage_equipments': []}
for archetype in self._content.archetypes:
_names['archetypes'].append(archetype.name)
for system in self._content.systems:
_names['systems'].append(system.name)
for equipment in self._content.generation_equipments:
_names['generation_equipments'].append(equipment.name)
else:
_names = {category: []}
if category.lower() == 'archetypes':
for archetype in self._content.archetypes:
_names[category].append(archetype.name)
elif category.lower() == 'systems':
for system in self._content.systems:
_names[category].append(system.name)
elif category.lower() == 'generation_equipments':
for system in self._content.generation_equipments:
_names[category].append(system.name)
else:
raise ValueError(f'Unknown category [{category}]')
return _names
def entries(self, category=None):
"""
Get the catalog elements
:parm: optional category filter
"""
if category is None:
return self._content
if category.lower() == 'archetypes':
return self._content.archetypes
if category.lower() == 'systems':
return self._content.systems
if category.lower() == 'generation_equipments':
return self._content.generation_equipments
raise ValueError(f'Unknown category [{category}]')
def get_entry(self, name):
"""
Get one catalog element by names
:parm: entry name
"""
for entry in self._content.archetypes:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.systems:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.generation_equipments:
if entry.name.lower() == name.lower():
return entry
raise IndexError(f"{name} doesn't exists in the catalog")

View File

@ -0,0 +1,520 @@
"""
Palma energy system catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
"""
import xmltodict
from pathlib import Path
from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
from hub.catalog_factories.data_models.energy_systems.system import System
from hub.catalog_factories.data_models.energy_systems.content import Content
from hub.catalog_factories.data_models.energy_systems.non_pv_generation_system import NonPvGenerationSystem
from hub.catalog_factories.data_models.energy_systems.pv_generation_system import PvGenerationSystem
from hub.catalog_factories.data_models.energy_systems.thermal_storage_system import ThermalStorageSystem
from hub.catalog_factories.data_models.energy_systems.performance_curves import PerformanceCurves
from hub.catalog_factories.data_models.energy_systems.archetype import Archetype
from hub.catalog_factories.data_models.construction.material import Material
from hub.catalog_factories.data_models.construction.layer import Layer
class PalmaSystemCatalogue(Catalog):
"""
North america energy system catalog class
"""
def __init__(self, path):
path = str(path / 'palma_systems.xml')
with open(path, 'r', encoding='utf-8') as xml:
self._archetypes = xmltodict.parse(xml.read(),
force_list=['pv_generation_component', 'demand'])
self._storage_components = self._load_storage_components()
self._generation_components = self._load_generation_components()
self._energy_emission_components = self._load_emission_equipments()
self._distribution_components = self._load_distribution_equipments()
self._systems = self._load_systems()
self._system_archetypes = self._load_archetypes()
self._content = Content(self._system_archetypes,
self._systems,
generations=self._generation_components,
distributions=self._distribution_components)
def _load_generation_components(self):
generation_components = []
non_pv_generation_components = self._archetypes['EnergySystemCatalog']['energy_generation_components'][
'non_pv_generation_component']
if non_pv_generation_components is not None:
for non_pv in non_pv_generation_components:
system_id = non_pv['system_id']
name = non_pv['name']
system_type = non_pv['system_type']
model_name = non_pv['model_name']
manufacturer = non_pv['manufacturer']
fuel_type = non_pv['fuel_type']
distribution_systems = non_pv['distribution_systems']
energy_storage_systems = None
if non_pv['energy_storage_systems'] is not None:
storage_component = non_pv['energy_storage_systems']['storage_id']
storage_systems = self._search_storage_equipment(self._load_storage_components(), storage_component)
energy_storage_systems = storage_systems
nominal_heat_output = non_pv['nominal_heat_output']
maximum_heat_output = non_pv['maximum_heat_output']
minimum_heat_output = non_pv['minimum_heat_output']
source_medium = non_pv['source_medium']
supply_medium = non_pv['supply_medium']
heat_efficiency = non_pv['heat_efficiency']
nominal_cooling_output = non_pv['nominal_cooling_output']
maximum_cooling_output = non_pv['maximum_cooling_output']
minimum_cooling_output = non_pv['minimum_cooling_output']
cooling_efficiency = non_pv['cooling_efficiency']
electricity_efficiency = non_pv['electricity_efficiency']
source_temperature = non_pv['source_temperature']
source_mass_flow = non_pv['source_mass_flow']
nominal_electricity_output = non_pv['nominal_electricity_output']
maximum_heat_supply_temperature = non_pv['maximum_heat_supply_temperature']
minimum_heat_supply_temperature = non_pv['minimum_heat_supply_temperature']
maximum_cooling_supply_temperature = non_pv['maximum_cooling_supply_temperature']
minimum_cooling_supply_temperature = non_pv['minimum_cooling_supply_temperature']
heat_output_curve = None
heat_fuel_consumption_curve = None
heat_efficiency_curve = None
cooling_output_curve = None
cooling_fuel_consumption_curve = None
cooling_efficiency_curve = None
if non_pv['heat_output_curve'] is not None:
curve_type = non_pv['heat_output_curve']['curve_type']
dependant_variable = non_pv['heat_output_curve']['dependant_variable']
parameters = non_pv['heat_output_curve']['parameters']
coefficients = list(non_pv['heat_output_curve']['coefficients'].values())
heat_output_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['heat_fuel_consumption_curve'] is not None:
curve_type = non_pv['heat_fuel_consumption_curve']['curve_type']
dependant_variable = non_pv['heat_fuel_consumption_curve']['dependant_variable']
parameters = non_pv['heat_fuel_consumption_curve']['parameters']
coefficients = list(non_pv['heat_fuel_consumption_curve']['coefficients'].values())
heat_fuel_consumption_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['heat_efficiency_curve'] is not None:
curve_type = non_pv['heat_efficiency_curve']['curve_type']
dependant_variable = non_pv['heat_efficiency_curve']['dependant_variable']
parameters = non_pv['heat_efficiency_curve']['parameters']
coefficients = list(non_pv['heat_efficiency_curve']['coefficients'].values())
heat_efficiency_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['cooling_output_curve'] is not None:
curve_type = non_pv['cooling_output_curve']['curve_type']
dependant_variable = non_pv['cooling_output_curve']['dependant_variable']
parameters = non_pv['cooling_output_curve']['parameters']
coefficients = list(non_pv['cooling_output_curve']['coefficients'].values())
cooling_output_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['cooling_fuel_consumption_curve'] is not None:
curve_type = non_pv['cooling_fuel_consumption_curve']['curve_type']
dependant_variable = non_pv['cooling_fuel_consumption_curve']['dependant_variable']
parameters = non_pv['cooling_fuel_consumption_curve']['parameters']
coefficients = list(non_pv['cooling_fuel_consumption_curve']['coefficients'].values())
cooling_fuel_consumption_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
if non_pv['cooling_efficiency_curve'] is not None:
curve_type = non_pv['cooling_efficiency_curve']['curve_type']
dependant_variable = non_pv['cooling_efficiency_curve']['dependant_variable']
parameters = non_pv['cooling_efficiency_curve']['parameters']
coefficients = list(non_pv['cooling_efficiency_curve']['coefficients'].values())
cooling_efficiency_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
dhw = None
if non_pv['domestic_hot_water'] is not None:
if non_pv['domestic_hot_water'] == 'True':
dhw = True
else:
dhw = False
reversible = None
if non_pv['reversible'] is not None:
if non_pv['reversible'] == 'True':
reversible = True
else:
reversible = False
dual_supply = None
if non_pv['simultaneous_heat_cold'] is not None:
if non_pv['simultaneous_heat_cold'] == 'True':
dual_supply = True
else:
dual_supply = False
non_pv_component = NonPvGenerationSystem(system_id=system_id,
name=name,
system_type=system_type,
model_name=model_name,
manufacturer=manufacturer,
fuel_type=fuel_type,
nominal_heat_output=nominal_heat_output,
maximum_heat_output=maximum_heat_output,
minimum_heat_output=minimum_heat_output,
source_medium=source_medium,
supply_medium=supply_medium,
heat_efficiency=heat_efficiency,
nominal_cooling_output=nominal_cooling_output,
maximum_cooling_output=maximum_cooling_output,
minimum_cooling_output=minimum_cooling_output,
cooling_efficiency=cooling_efficiency,
electricity_efficiency=electricity_efficiency,
source_temperature=source_temperature,
source_mass_flow=source_mass_flow,
nominal_electricity_output=nominal_electricity_output,
maximum_heat_supply_temperature=maximum_heat_supply_temperature,
minimum_heat_supply_temperature=minimum_heat_supply_temperature,
maximum_cooling_supply_temperature=maximum_cooling_supply_temperature,
minimum_cooling_supply_temperature=minimum_cooling_supply_temperature,
heat_output_curve=heat_output_curve,
heat_fuel_consumption_curve=heat_fuel_consumption_curve,
heat_efficiency_curve=heat_efficiency_curve,
cooling_output_curve=cooling_output_curve,
cooling_fuel_consumption_curve=cooling_fuel_consumption_curve,
cooling_efficiency_curve=cooling_efficiency_curve,
distribution_systems=distribution_systems,
energy_storage_systems=energy_storage_systems,
domestic_hot_water=dhw,
reversible=reversible,
simultaneous_heat_cold=dual_supply)
generation_components.append(non_pv_component)
pv_generation_components = self._archetypes['EnergySystemCatalog']['energy_generation_components'][
'pv_generation_component']
if pv_generation_components is not None:
for pv in pv_generation_components:
system_id = pv['system_id']
name = pv['name']
system_type = pv['system_type']
model_name = pv['model_name']
manufacturer = pv['manufacturer']
electricity_efficiency = pv['electricity_efficiency']
nominal_electricity_output = pv['nominal_electricity_output']
nominal_ambient_temperature = pv['nominal_ambient_temperature']
nominal_cell_temperature = pv['nominal_cell_temperature']
nominal_radiation = pv['nominal_radiation']
standard_test_condition_cell_temperature = pv['standard_test_condition_cell_temperature']
standard_test_condition_maximum_power = pv['standard_test_condition_maximum_power']
standard_test_condition_radiation = pv['standard_test_condition_radiation']
cell_temperature_coefficient = pv['cell_temperature_coefficient']
width = pv['width']
height = pv['height']
distribution_systems = pv['distribution_systems']
energy_storage_systems = None
if pv['energy_storage_systems'] is not None:
storage_component = pv['energy_storage_systems']['storage_id']
storage_systems = self._search_storage_equipment(self._load_storage_components(), storage_component)
energy_storage_systems = storage_systems
pv_component = PvGenerationSystem(system_id=system_id,
name=name,
system_type=system_type,
model_name=model_name,
manufacturer=manufacturer,
electricity_efficiency=electricity_efficiency,
nominal_electricity_output=nominal_electricity_output,
nominal_ambient_temperature=nominal_ambient_temperature,
nominal_cell_temperature=nominal_cell_temperature,
nominal_radiation=nominal_radiation,
standard_test_condition_cell_temperature=
standard_test_condition_cell_temperature,
standard_test_condition_maximum_power=standard_test_condition_maximum_power,
standard_test_condition_radiation=standard_test_condition_radiation,
cell_temperature_coefficient=cell_temperature_coefficient,
width=width,
height=height,
distribution_systems=distribution_systems,
energy_storage_systems=energy_storage_systems)
generation_components.append(pv_component)
return generation_components
def _load_distribution_equipments(self):
_equipments = []
distribution_systems = self._archetypes['EnergySystemCatalog']['distribution_systems']['distribution_system']
if distribution_systems is not None:
for distribution_system in distribution_systems:
system_id = None
model_name = None
system_type = None
supply_temperature = None
distribution_consumption_fix_flow = None
distribution_consumption_variable_flow = None
heat_losses = None
generation_systems = None
energy_storage_systems = None
emission_systems = None
distribution_equipment = DistributionSystem(system_id=system_id,
model_name=model_name,
system_type=system_type,
supply_temperature=supply_temperature,
distribution_consumption_fix_flow=distribution_consumption_fix_flow,
distribution_consumption_variable_flow=
distribution_consumption_variable_flow,
heat_losses=heat_losses,
generation_systems=generation_systems,
energy_storage_systems=energy_storage_systems,
emission_systems=emission_systems
)
_equipments.append(distribution_equipment)
return _equipments
def _load_emission_equipments(self):
_equipments = []
dissipation_systems = self._archetypes['EnergySystemCatalog']['dissipation_systems']['dissipation_system']
if dissipation_systems is not None:
for dissipation_system in dissipation_systems:
system_id = None
model_name = None
system_type = None
parasitic_energy_consumption = 0
emission_system = EmissionSystem(system_id=system_id,
model_name=model_name,
system_type=system_type,
parasitic_energy_consumption=parasitic_energy_consumption)
_equipments.append(emission_system)
return _equipments
def _load_storage_components(self):
storage_components = []
thermal_storages = self._archetypes['EnergySystemCatalog']['energy_storage_components']['thermalStorages']
for tes in thermal_storages:
storage_id = tes['storage_id']
type_energy_stored = tes['type_energy_stored']
model_name = tes['model_name']
manufacturer = tes['manufacturer']
storage_type = tes['storage_type']
volume = tes['physical_characteristics']['volume']
height = tes['physical_characteristics']['height']
maximum_operating_temperature = tes['maximum_operating_temperature']
materials = self._load_materials()
insulation_material_id = tes['insulation']['material_id']
insulation_material = self._search_material(materials, insulation_material_id)
material_id = tes['physical_characteristics']['material_id']
tank_material = self._search_material(materials, material_id)
thickness = float(tes['insulation']['insulationThickness']) / 100 # from cm to m
insulation_layer = Layer(None, 'insulation', insulation_material, thickness)
thickness = float(tes['physical_characteristics']['tankThickness']) / 100 # from cm to m
tank_layer = Layer(None, 'tank', tank_material, thickness)
media = self._load_media()
media_id = tes['storage_medium']['medium_id']
medium = self._search_media(media, media_id)
layers = [insulation_layer, tank_layer]
nominal_capacity = tes['nominal_capacity']
losses_ratio = tes['losses_ratio']
heating_coil_capacity = tes['heating_coil_capacity']
storage_component = ThermalStorageSystem(storage_id=storage_id,
model_name=model_name,
type_energy_stored=type_energy_stored,
manufacturer=manufacturer,
storage_type=storage_type,
nominal_capacity=nominal_capacity,
losses_ratio=losses_ratio,
volume=volume,
height=height,
layers=layers,
maximum_operating_temperature=maximum_operating_temperature,
storage_medium=medium,
heating_coil_capacity=heating_coil_capacity)
storage_components.append(storage_component)
return storage_components
def _load_systems(self):
base_path = Path(Path(__file__).parent.parent.parent / 'data/energy_systems')
_catalog_systems = []
systems = self._archetypes['EnergySystemCatalog']['systems']['system']
for system in systems:
system_id = system['id']
name = system['name']
demands = system['demands']['demand']
generation_components = system['components']['generation_id']
generation_systems = self._search_generation_equipment(self._load_generation_components(), generation_components)
configuration_schema = None
if system['schema'] is not None:
configuration_schema = Path(base_path / system['schema'])
energy_system = System(system_id=system_id,
name=name,
demand_types=demands,
generation_systems=generation_systems,
distribution_systems=None,
configuration_schema=configuration_schema)
_catalog_systems.append(energy_system)
return _catalog_systems
def _load_archetypes(self):
_system_archetypes = []
system_clusters = self._archetypes['EnergySystemCatalog']['system_archetypes']['system_archetype']
for system_cluster in system_clusters:
name = system_cluster['name']
systems = system_cluster['systems']['system_id']
integer_system_ids = [int(item) for item in systems]
_systems = []
for system_archetype in self._systems:
if int(system_archetype.id) in integer_system_ids:
_systems.append(system_archetype)
_system_archetypes.append(Archetype(name=name, systems=_systems))
return _system_archetypes
def _load_materials(self):
materials = []
_materials = self._archetypes['EnergySystemCatalog']['materials']['material']
for _material in _materials:
material_id = _material['material_id']
name = _material['name']
conductivity = _material['conductivity']
solar_absorptance = _material['solar_absorptance']
thermal_absorptance = _material['thermal_absorptance']
density = _material['density']
specific_heat = _material['specific_heat']
no_mass = _material['no_mass']
visible_absorptance = _material['visible_absorptance']
thermal_resistance = _material['thermal_resistance']
material = Material(material_id,
name,
solar_absorptance=solar_absorptance,
thermal_absorptance=thermal_absorptance,
density=density,
conductivity=conductivity,
thermal_resistance=thermal_resistance,
visible_absorptance=visible_absorptance,
no_mass=no_mass,
specific_heat=specific_heat)
materials.append(material)
return materials
@staticmethod
def _search_material(materials, material_id):
_material = None
for material in materials:
if int(material.id) == int(material_id):
_material = material
break
if _material is None:
raise ValueError(f'Material with the id = [{material_id}] not found in catalog ')
return _material
def _load_media(self):
media = []
_media = [self._archetypes['EnergySystemCatalog']['media']['medium']]
for _medium in _media:
medium_id = _medium['medium_id']
density = _medium['density']
name = _medium['name']
conductivity = _medium['conductivity']
solar_absorptance = _medium['solar_absorptance']
thermal_absorptance = _medium['thermal_absorptance']
specific_heat = _medium['specific_heat']
no_mass = _medium['no_mass']
visible_absorptance = _medium['visible_absorptance']
thermal_resistance = _medium['thermal_resistance']
medium = Material(material_id=medium_id,
name=name,
solar_absorptance=solar_absorptance,
thermal_absorptance=thermal_absorptance,
visible_absorptance=visible_absorptance,
no_mass=no_mass,
thermal_resistance=thermal_resistance,
conductivity=conductivity,
density=density,
specific_heat=specific_heat)
media.append(medium)
return media
@staticmethod
def _search_media(media, medium_id):
_medium = None
for medium in media:
if int(medium.id) == int(medium_id):
_medium = medium
break
if _medium is None:
raise ValueError(f'media with the id = [{medium_id}] not found in catalog ')
return _medium
@staticmethod
def _search_generation_equipment(generation_systems, generation_id):
_generation_systems = []
if isinstance(generation_id, list):
integer_ids = [int(item) for item in generation_id]
for generation in generation_systems:
if int(generation.id) in integer_ids:
_generation_systems.append(generation)
else:
integer_id = int(generation_id)
for generation in generation_systems:
if int(generation.id) == integer_id:
_generation_systems.append(generation)
if len(_generation_systems) == 0:
_generation_systems = None
raise ValueError(f'The system with the following id is not found in catalog [{generation_id}]')
return _generation_systems
@staticmethod
def _search_storage_equipment(storage_systems, storage_id):
_storage_systems = []
for storage in storage_systems:
if storage.id in storage_id:
_storage_systems.append(storage)
if len(_storage_systems) == 0:
_storage_systems = None
raise ValueError(f'The system with the following id is not found in catalog [{storage_id}]')
return _storage_systems
def names(self, category=None):
"""
Get the catalog elements names
:parm: optional category filter
"""
if category is None:
_names = {'archetypes': [], 'systems': [], 'generation_equipments': [], 'storage_equipments': []}
for archetype in self._content.archetypes:
_names['archetypes'].append(archetype.name)
for system in self._content.systems:
_names['systems'].append(system.name)
for equipment in self._content.generation_equipments:
_names['generation_equipments'].append(equipment.name)
else:
_names = {category: []}
if category.lower() == 'archetypes':
for archetype in self._content.archetypes:
_names[category].append(archetype.name)
elif category.lower() == 'systems':
for system in self._content.systems:
_names[category].append(system.name)
elif category.lower() == 'generation_equipments':
for system in self._content.generation_equipments:
_names[category].append(system.name)
else:
raise ValueError(f'Unknown category [{category}]')
return _names
def entries(self, category=None):
"""
Get the catalog elements
:parm: optional category filter
"""
if category is None:
return self._content
if category.lower() == 'archetypes':
return self._content.archetypes
if category.lower() == 'systems':
return self._content.systems
if category.lower() == 'generation_equipments':
return self._content.generation_equipments
raise ValueError(f'Unknown category [{category}]')
def get_entry(self, name):
"""
Get one catalog element by names
:parm: entry name
"""
for entry in self._content.archetypes:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.systems:
if entry.name.lower() == name.lower():
return entry
for entry in self._content.generation_equipments:
if entry.name.lower() == name.lower():
return entry
raise IndexError(f"{name} doesn't exists in the catalog")

View File

@ -4,16 +4,22 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
import logging
from pathlib import Path from pathlib import Path
from typing import TypeVar from typing import TypeVar
from hub.catalog_factories.energy_systems.montreal_custom_catalog import MontrealCustomCatalog from hub.catalog_factories.energy_systems.montreal_custom_catalog import MontrealCustomCatalog
from hub.catalog_factories.energy_systems.montreal_future_system_catalogue import MontrealFutureSystemCatalogue
from hub.catalog_factories.energy_systems.palma_system_catalgue import PalmaSystemCatalogue
from hub.helpers.utils import validate_import_export_type from hub.helpers.utils import validate_import_export_type
Catalog = TypeVar('Catalog') Catalog = TypeVar('Catalog')
class EnergySystemsCatalogFactory: class EnergySystemsCatalogFactory:
"""
Energy system catalog factory class
"""
def __init__(self, handler, base_path=None): def __init__(self, handler, base_path=None):
if base_path is None: if base_path is None:
base_path = Path(Path(__file__).parent.parent / 'data/energy_systems') base_path = Path(Path(__file__).parent.parent / 'data/energy_systems')
@ -28,6 +34,20 @@ class EnergySystemsCatalogFactory:
""" """
return MontrealCustomCatalog(self._path) return MontrealCustomCatalog(self._path)
@property
def _montreal_future(self):
"""
Retrieve North American catalog
"""
return MontrealFutureSystemCatalogue(self._path)
@property
def _palma(self):
"""
Retrieve Palma catalog
"""
return PalmaSystemCatalogue(self._path)
@property @property
def catalog(self) -> Catalog: def catalog(self) -> Catalog:
""" """

View File

@ -15,304 +15,304 @@ getEClassifier = partial(Ecore.getEClassifier, searchspace=eClassifiers)
Management = EEnum('Management', literals=['Intensive', 'Extensive', 'SemiIntensive', 'NA']) Management = EEnum('Management', literals=['Intensive', 'Extensive', 'SemiIntensive', 'NA'])
Roughness = EEnum('Roughness', literals=['VeryRough', 'Rough', Roughness = EEnum('Roughness', literals=['VeryRough', 'Rough',
'MediumRough', 'MediumSmooth', 'Smooth', 'VerySmooth']) 'MediumRough', 'MediumSmooth', 'Smooth', 'VerySmooth'])
class Soil(EObject, metaclass=MetaEClass): class Soil(EObject, metaclass=MetaEClass):
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True) name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
roughness = EAttribute(eType=Roughness, unique=True, derived=False, roughness = EAttribute(eType=Roughness, unique=True, derived=False,
changeable=True, default_value=Roughness.MediumRough) changeable=True, default_value=Roughness.MediumRough)
conductivityOfDrySoil = EAttribute( conductivityOfDrySoil = EAttribute(
eType=EString, unique=True, derived=False, changeable=True, default_value='1.0 W/(m*K)') eType=EString, unique=True, derived=False, changeable=True, default_value='1.0 W/(m*K)')
densityOfDrySoil = EAttribute(eType=EString, unique=True, derived=False, densityOfDrySoil = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='1100 kg/m³') changeable=True, default_value='1100 kg/m³')
specificHeatOfDrySoil = EAttribute( specificHeatOfDrySoil = EAttribute(
eType=EString, unique=True, derived=False, changeable=True, default_value='1200 J/(kg*K)') eType=EString, unique=True, derived=False, changeable=True, default_value='1200 J/(kg*K)')
thermalAbsorptance = EAttribute(eType=EString, unique=True, thermalAbsorptance = EAttribute(eType=EString, unique=True,
derived=False, changeable=True, default_value='0.9') derived=False, changeable=True, default_value='0.9')
solarAbsorptance = EAttribute(eType=EString, unique=True, solarAbsorptance = EAttribute(eType=EString, unique=True,
derived=False, changeable=True, default_value='0.7') derived=False, changeable=True, default_value='0.7')
visibleAbsorptance = EAttribute(eType=EString, unique=True, visibleAbsorptance = EAttribute(eType=EString, unique=True,
derived=False, changeable=True, default_value='0.75') derived=False, changeable=True, default_value='0.75')
saturationVolumetricMoistureContent = EAttribute( saturationVolumetricMoistureContent = EAttribute(
eType=EString, unique=True, derived=False, changeable=True, default_value='0.0') eType=EString, unique=True, derived=False, changeable=True, default_value='0.0')
residualVolumetricMoistureContent = EAttribute( residualVolumetricMoistureContent = EAttribute(
eType=EString, unique=True, derived=False, changeable=True, default_value='0.05') eType=EString, unique=True, derived=False, changeable=True, default_value='0.05')
initialVolumetricMoistureContent = EAttribute( initialVolumetricMoistureContent = EAttribute(
eType=EString, unique=True, derived=False, changeable=True, default_value='0.1') eType=EString, unique=True, derived=False, changeable=True, default_value='0.1')
def __init__(self, *, name=None, roughness=None, conductivityOfDrySoil=None, densityOfDrySoil=None, specificHeatOfDrySoil=None, thermalAbsorptance=None, solarAbsorptance=None, visibleAbsorptance=None, saturationVolumetricMoistureContent=None, residualVolumetricMoistureContent=None, initialVolumetricMoistureContent=None): def __init__(self, *, name=None, roughness=None, conductivityOfDrySoil=None, densityOfDrySoil=None, specificHeatOfDrySoil=None, thermalAbsorptance=None, solarAbsorptance=None, visibleAbsorptance=None, saturationVolumetricMoistureContent=None, residualVolumetricMoistureContent=None, initialVolumetricMoistureContent=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if name is not None: if name is not None:
self.name = name self.name = name
if roughness is not None: if roughness is not None:
self.roughness = roughness self.roughness = roughness
if conductivityOfDrySoil is not None: if conductivityOfDrySoil is not None:
self.conductivityOfDrySoil = conductivityOfDrySoil self.conductivityOfDrySoil = conductivityOfDrySoil
if densityOfDrySoil is not None: if densityOfDrySoil is not None:
self.densityOfDrySoil = densityOfDrySoil self.densityOfDrySoil = densityOfDrySoil
if specificHeatOfDrySoil is not None: if specificHeatOfDrySoil is not None:
self.specificHeatOfDrySoil = specificHeatOfDrySoil self.specificHeatOfDrySoil = specificHeatOfDrySoil
if thermalAbsorptance is not None: if thermalAbsorptance is not None:
self.thermalAbsorptance = thermalAbsorptance self.thermalAbsorptance = thermalAbsorptance
if solarAbsorptance is not None: if solarAbsorptance is not None:
self.solarAbsorptance = solarAbsorptance self.solarAbsorptance = solarAbsorptance
if visibleAbsorptance is not None: if visibleAbsorptance is not None:
self.visibleAbsorptance = visibleAbsorptance self.visibleAbsorptance = visibleAbsorptance
if saturationVolumetricMoistureContent is not None: if saturationVolumetricMoistureContent is not None:
self.saturationVolumetricMoistureContent = saturationVolumetricMoistureContent self.saturationVolumetricMoistureContent = saturationVolumetricMoistureContent
if residualVolumetricMoistureContent is not None: if residualVolumetricMoistureContent is not None:
self.residualVolumetricMoistureContent = residualVolumetricMoistureContent self.residualVolumetricMoistureContent = residualVolumetricMoistureContent
if initialVolumetricMoistureContent is not None: if initialVolumetricMoistureContent is not None:
self.initialVolumetricMoistureContent = initialVolumetricMoistureContent self.initialVolumetricMoistureContent = initialVolumetricMoistureContent
class Plant(EObject, metaclass=MetaEClass): class Plant(EObject, metaclass=MetaEClass):
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True) name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
height = EAttribute(eType=EString, unique=True, derived=False, height = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='0.1 m') changeable=True, default_value='0.1 m')
leafAreaIndex = EAttribute(eType=EString, unique=True, derived=False, leafAreaIndex = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='2.5') changeable=True, default_value='2.5')
leafReflectivity = EAttribute(eType=EString, unique=True, leafReflectivity = EAttribute(eType=EString, unique=True,
derived=False, changeable=True, default_value='0.1') derived=False, changeable=True, default_value='0.1')
leafEmissivity = EAttribute(eType=EString, unique=True, derived=False, leafEmissivity = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='0.9') changeable=True, default_value='0.9')
minimalStomatalResistance = EAttribute( minimalStomatalResistance = EAttribute(
eType=EString, unique=True, derived=False, changeable=True, default_value='100.0 s/m') eType=EString, unique=True, derived=False, changeable=True, default_value='100.0 s/m')
co2Sequestration = EAttribute(eType=EString, unique=True, derived=False, co2Sequestration = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='kgCO₂eq') changeable=True, default_value='kgCO₂eq')
growsOn = EReference(ordered=True, unique=True, containment=False, derived=False, upper=-1) growsOn = EReference(ordered=True, unique=True, containment=False, derived=False, upper=-1)
def __init__(self, *, name=None, height=None, leafAreaIndex=None, leafReflectivity=None, leafEmissivity=None, minimalStomatalResistance=None, growsOn=None, co2Sequestration=None): def __init__(self, *, name=None, height=None, leafAreaIndex=None, leafReflectivity=None, leafEmissivity=None, minimalStomatalResistance=None, growsOn=None, co2Sequestration=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if name is not None: if name is not None:
self.name = name self.name = name
if height is not None: if height is not None:
self.height = height self.height = height
if leafAreaIndex is not None: if leafAreaIndex is not None:
self.leafAreaIndex = leafAreaIndex self.leafAreaIndex = leafAreaIndex
if leafReflectivity is not None: if leafReflectivity is not None:
self.leafReflectivity = leafReflectivity self.leafReflectivity = leafReflectivity
if leafEmissivity is not None: if leafEmissivity is not None:
self.leafEmissivity = leafEmissivity self.leafEmissivity = leafEmissivity
if minimalStomatalResistance is not None: if minimalStomatalResistance is not None:
self.minimalStomatalResistance = minimalStomatalResistance self.minimalStomatalResistance = minimalStomatalResistance
if co2Sequestration is not None: if co2Sequestration is not None:
self.co2Sequestration = co2Sequestration self.co2Sequestration = co2Sequestration
if growsOn: if growsOn:
self.growsOn.extend(growsOn) self.growsOn.extend(growsOn)
class SupportEnvelope(EObject, metaclass=MetaEClass): class SupportEnvelope(EObject, metaclass=MetaEClass):
roughness = EAttribute(eType=Roughness, unique=True, derived=False, roughness = EAttribute(eType=Roughness, unique=True, derived=False,
changeable=True, default_value=Roughness.MediumRough) changeable=True, default_value=Roughness.MediumRough)
solarAbsorptance = EAttribute(eType=EDouble, unique=True, solarAbsorptance = EAttribute(eType=EDouble, unique=True,
derived=False, changeable=True, default_value=0.0)
conductivity = EAttribute(eType=EDouble, unique=True, derived=False,
changeable=True, default_value=0.0)
visibleAbsorptance = EAttribute(eType=EDouble, unique=True,
derived=False, changeable=True, default_value=0.0)
specificHeat = EAttribute(eType=EDouble, unique=True, derived=False,
changeable=True, default_value=0.0)
density = EAttribute(eType=EDouble, unique=True, derived=False,
changeable=True, default_value=0.0)
thermalAbsorptance = EAttribute(eType=EDouble, unique=True,
derived=False, changeable=True, default_value=0.0) derived=False, changeable=True, default_value=0.0)
conductivity = EAttribute(eType=EDouble, unique=True, derived=False,
changeable=True, default_value=0.0)
visibleAbsorptance = EAttribute(eType=EDouble, unique=True,
derived=False, changeable=True, default_value=0.0)
specificHeat = EAttribute(eType=EDouble, unique=True, derived=False,
changeable=True, default_value=0.0)
density = EAttribute(eType=EDouble, unique=True, derived=False,
changeable=True, default_value=0.0)
thermalAbsorptance = EAttribute(eType=EDouble, unique=True,
derived=False, changeable=True, default_value=0.0)
def __init__(self, *, roughness=None, solarAbsorptance=None, conductivity=None, visibleAbsorptance=None, specificHeat=None, density=None, thermalAbsorptance=None): def __init__(self, *, roughness=None, solarAbsorptance=None, conductivity=None, visibleAbsorptance=None, specificHeat=None, density=None, thermalAbsorptance=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if roughness is not None: if roughness is not None:
self.roughness = roughness self.roughness = roughness
if solarAbsorptance is not None: if solarAbsorptance is not None:
self.solarAbsorptance = solarAbsorptance self.solarAbsorptance = solarAbsorptance
if conductivity is not None: if conductivity is not None:
self.conductivity = conductivity self.conductivity = conductivity
if visibleAbsorptance is not None: if visibleAbsorptance is not None:
self.visibleAbsorptance = visibleAbsorptance self.visibleAbsorptance = visibleAbsorptance
if specificHeat is not None: if specificHeat is not None:
self.specificHeat = specificHeat self.specificHeat = specificHeat
if density is not None: if density is not None:
self.density = density self.density = density
if thermalAbsorptance is not None: if thermalAbsorptance is not None:
self.thermalAbsorptance = thermalAbsorptance self.thermalAbsorptance = thermalAbsorptance
class GreeneryCatalog(EObject, metaclass=MetaEClass): class GreeneryCatalog(EObject, metaclass=MetaEClass):
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True) name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
description = EAttribute(eType=EString, unique=True, derived=False, changeable=True) description = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
source = EAttribute(eType=EString, unique=True, derived=False, changeable=True) source = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
plantCategories = EReference(ordered=True, unique=True, plantCategories = EReference(ordered=True, unique=True,
containment=True, derived=False, upper=-1) containment=True, derived=False, upper=-1)
vegetationCategories = EReference(ordered=True, unique=True, vegetationCategories = EReference(ordered=True, unique=True,
containment=True, derived=False, upper=-1) containment=True, derived=False, upper=-1)
soils = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1) soils = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
def __init__(self, *, name=None, description=None, source=None, plantCategories=None, vegetationCategories=None, soils=None): def __init__(self, *, name=None, description=None, source=None, plantCategories=None, vegetationCategories=None, soils=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if name is not None: if name is not None:
self.name = name self.name = name
if description is not None: if description is not None:
self.description = description self.description = description
if source is not None: if source is not None:
self.source = source self.source = source
if plantCategories: if plantCategories:
self.plantCategories.extend(plantCategories) self.plantCategories.extend(plantCategories)
if vegetationCategories: if vegetationCategories:
self.vegetationCategories.extend(vegetationCategories) self.vegetationCategories.extend(vegetationCategories)
if soils: if soils:
self.soils.extend(soils) self.soils.extend(soils)
class PlantCategory(EObject, metaclass=MetaEClass): class PlantCategory(EObject, metaclass=MetaEClass):
"""Excluding (that is non-overlapping) categories like Trees, Hedeges, Grasses that help users finding a specific biol. plant species.""" """Excluding (that is non-overlapping) categories like Trees, Hedeges, Grasses that help users finding a specific biol. plant species."""
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True) name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1) plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
def __init__(self, *, name=None, plants=None): def __init__(self, *, name=None, plants=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if name is not None: if name is not None:
self.name = name self.name = name
if plants: if plants:
self.plants.extend(plants) self.plants.extend(plants)
class IrrigationSchedule(EObject, metaclass=MetaEClass): class IrrigationSchedule(EObject, metaclass=MetaEClass):
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True) name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
def __init__(self, *, name=None): def __init__(self, *, name=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if name is not None: if name is not None:
self.name = name self.name = name
class Vegetation(EObject, metaclass=MetaEClass): class Vegetation(EObject, metaclass=MetaEClass):
"""Plant life or total plant cover (as of an area)""" """Plant life or total plant cover (as of an area)"""
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True) name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
thicknessOfSoil = EAttribute(eType=EString, unique=True, derived=False, thicknessOfSoil = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='20 cm') changeable=True, default_value='20 cm')
management = EAttribute(eType=Management, unique=True, derived=False, management = EAttribute(eType=Management, unique=True, derived=False,
changeable=True, default_value=Management.NA) changeable=True, default_value=Management.NA)
airGap = EAttribute(eType=EString, unique=True, derived=False, airGap = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='0.0 cm') changeable=True, default_value='0.0 cm')
soil = EReference(ordered=True, unique=True, containment=False, derived=False) soil = EReference(ordered=True, unique=True, containment=False, derived=False)
plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1) plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
def __init__(self, *, name=None, thicknessOfSoil=None, soil=None, plants=None, management=None, airGap=None): def __init__(self, *, name=None, thicknessOfSoil=None, soil=None, plants=None, management=None, airGap=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if name is not None: if name is not None:
self.name = name self.name = name
if thicknessOfSoil is not None: if thicknessOfSoil is not None:
self.thicknessOfSoil = thicknessOfSoil self.thicknessOfSoil = thicknessOfSoil
if management is not None: if management is not None:
self.management = management self.management = management
if airGap is not None: if airGap is not None:
self.airGap = airGap self.airGap = airGap
if soil is not None: if soil is not None:
self.soil = soil self.soil = soil
if plants: if plants:
self.plants.extend(plants) self.plants.extend(plants)
class VegetationCategory(EObject, metaclass=MetaEClass): class VegetationCategory(EObject, metaclass=MetaEClass):
"""Excluding (that is non-overlapping) categories to help users finding a specific vegetation template.""" """Excluding (that is non-overlapping) categories to help users finding a specific vegetation template."""
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True) name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
vegetationTemplates = EReference(ordered=True, unique=True, vegetationTemplates = EReference(ordered=True, unique=True,
containment=True, derived=False, upper=-1) containment=True, derived=False, upper=-1)
def __init__(self, *, vegetationTemplates=None, name=None): def __init__(self, *, vegetationTemplates=None, name=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if name is not None: if name is not None:
self.name = name self.name = name
if vegetationTemplates: if vegetationTemplates:
self.vegetationTemplates.extend(vegetationTemplates) self.vegetationTemplates.extend(vegetationTemplates)
class PlantPercentage(EObject, metaclass=MetaEClass): class PlantPercentage(EObject, metaclass=MetaEClass):
percentage = EAttribute(eType=EString, unique=True, derived=False, percentage = EAttribute(eType=EString, unique=True, derived=False,
changeable=True, default_value='100') changeable=True, default_value='100')
plant = EReference(ordered=True, unique=True, containment=False, derived=False) plant = EReference(ordered=True, unique=True, containment=False, derived=False)
def __init__(self, *, percentage=None, plant=None): def __init__(self, *, percentage=None, plant=None):
# if kwargs: # if kwargs:
# raise AttributeError('unexpected arguments: {}'.format(kwargs)) # raise AttributeError('unexpected arguments: {}'.format(kwargs))
super().__init__() super().__init__()
if percentage is not None: if percentage is not None:
self.percentage = percentage self.percentage = percentage
if plant is not None: if plant is not None:
self.plant = plant self.plant = plant

View File

@ -5,33 +5,36 @@ Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
from pyecore.resources import ResourceSet, URI
from hub.catalog_factories.greenery.ecore_greenery.greenerycatalog import GreeneryCatalog as gc
from hub.catalog_factories.catalog import Catalog
from pathlib import Path from pathlib import Path
from hub.catalog_factories.data_models.greenery.vegetation import Vegetation as libs_vegetation from pyecore.resources import ResourceSet, URI
from hub.catalog_factories.data_models.greenery.plant import Plant as libs_plant from hub.catalog_factories.greenery.ecore_greenery.greenerycatalog import GreeneryCatalog as Gc
from hub.catalog_factories.data_models.greenery.soil import Soil as libs_soil from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.greenery.plant_percentage import PlantPercentage as libs_pp from hub.catalog_factories.data_models.greenery.vegetation import Vegetation as HubVegetation
from hub.catalog_factories.data_models.greenery.plant import Plant as HubPlant
from hub.catalog_factories.data_models.greenery.soil import Soil as HubSoil
from hub.catalog_factories.data_models.greenery.plant_percentage import PlantPercentage as HubPlantPercentage
from hub.catalog_factories.data_models.greenery.content import Content as GreeneryContent from hub.catalog_factories.data_models.greenery.content import Content as GreeneryContent
class GreeneryCatalog(Catalog): class GreeneryCatalog(Catalog):
"""
Greenery catalog class
"""
def __init__(self, path): def __init__(self, path):
base_path = Path(Path(__file__).parent / 'ecore_greenery' / 'greenerycatalog_no_quantities.ecore') base_path = Path(Path(__file__).parent / 'ecore_greenery/greenerycatalog_no_quantities.ecore').resolve()
resource_set = ResourceSet() resource_set = ResourceSet()
data_model = resource_set.get_resource(URI(str(base_path))) data_model = resource_set.get_resource(URI(str(base_path)))
data_model_root = data_model.contents[0] data_model_root = data_model.contents[0]
resource_set.metamodel_registry[data_model_root.nsURI] = data_model_root resource_set.metamodel_registry[data_model_root.nsURI] = data_model_root
resource = resource_set.get_resource(URI(str(path))) resource = resource_set.get_resource(URI(str(path)))
catalog_data: gc = resource.contents[0] catalog_data: Gc = resource.contents[0]
plants = [] plants = []
for plant_category in catalog_data.plantCategories: for plant_category in catalog_data.plantCategories:
name = plant_category.name name = plant_category.name
for plant in plant_category.plants: for plant in plant_category.plants:
plants.append(libs_plant(name, plant)) plants.append(HubPlant(name, plant))
vegetations = [] vegetations = []
for vegetation_category in catalog_data.vegetationCategories: for vegetation_category in catalog_data.vegetationCategories:
@ -45,17 +48,19 @@ class GreeneryCatalog(Catalog):
if plant.name == plant_percentage.plant.name: if plant.name == plant_percentage.plant.name:
plant_category = plant.category plant_category = plant.category
break break
plant_percentages.append(libs_pp(plant_percentage.percentage, plant_category, plant_percentage.plant)) plant_percentages.append(
vegetations.append(libs_vegetation(name, vegetation, plant_percentages)) HubPlantPercentage(plant_percentage.percentage, plant_category, plant_percentage.plant)
)
vegetations.append(HubVegetation(name, vegetation, plant_percentages))
plants = [] plants = []
for plant_category in catalog_data.plantCategories: for plant_category in catalog_data.plantCategories:
name = plant_category.name name = plant_category.name
for plant in plant_category.plants: for plant in plant_category.plants:
plants.append(libs_plant(name, plant)) plants.append(HubPlant(name, plant))
soils = [] soils = []
for soil in catalog_data.soils: for soil in catalog_data.soils:
soils.append(libs_soil(soil)) soils.append(HubSoil(soil))
self._content = GreeneryContent(vegetations, plants, soils) self._content = GreeneryContent(vegetations, plants, soils)
@ -103,14 +108,15 @@ class GreeneryCatalog(Catalog):
raise IndexError(f"{name} doesn't exists in the catalog") raise IndexError(f"{name} doesn't exists in the catalog")
def entries(self, category=None): def entries(self, category=None):
"""
Get all entries from the greenery catalog optionally filtered by category
"""
if category is None: if category is None:
return self._content return self._content
else: if category.lower() == 'vegetations':
if category.lower() == 'vegetations': return self._content.vegetations
return self._content.vegetations if category.lower() == 'plants':
elif category.lower() == 'plants': return self._content.plants
return self._content.plants if category.lower() == 'soils':
elif category.lower() == 'soils': return self._content.soils
return self._content.soils raise ValueError(f'Unknown category [{category}]')
else:
raise ValueError(f'Unknown category [{category}]')

View File

@ -4,12 +4,12 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import logging
from pathlib import Path from pathlib import Path
from typing import TypeVar from typing import TypeVar
from hub.catalog_factories.greenery.greenery_catalog import GreeneryCatalog from hub.catalog_factories.greenery.greenery_catalog import GreeneryCatalog
from hub.helpers.utils import validate_import_export_type
Catalog = TypeVar('Catalog') Catalog = TypeVar('Catalog')
@ -19,9 +19,8 @@ class GreeneryCatalogFactory:
""" """
def __init__(self, handler, base_path=None): def __init__(self, handler, base_path=None):
if base_path is None: if base_path is None:
base_path = Path(Path(__file__).parent.parent / 'data/greenery') base_path = (Path(__file__).parent.parent / 'data/greenery').resolve()
self._handler = '_' + handler.lower() self._handler = '_' + handler.lower()
class_funcs = validate_import_export_type(GreeneryCatalogFactory, handler)
self._path = base_path self._path = base_path
@property @property

View File

View File

@ -14,7 +14,7 @@ from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.usages.appliances import Appliances from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.content import Content from hub.catalog_factories.data_models.usages.content import Content
from hub.catalog_factories.data_models.usages.lighting import Lighting from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.ocupancy import Occupancy from hub.catalog_factories.data_models.usages.occupancy import Occupancy
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.data_models.usages.schedule import Schedule from hub.catalog_factories.data_models.usages.schedule import Schedule
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
@ -24,13 +24,15 @@ from hub.helpers.configuration_helper import ConfigurationHelper as ch
class ComnetCatalog(Catalog): class ComnetCatalog(Catalog):
"""
Comnet catalog class
"""
def __init__(self, path): def __init__(self, path):
self._comnet_archetypes_path = str(path / 'comnet_archetypes.xlsx') self._comnet_archetypes_path = str(path / 'comnet_archetypes.xlsx')
self._comnet_schedules_path = str(path / 'comnet_schedules_archetypes.xlsx') self._comnet_schedules_path = str(path / 'comnet_schedules_archetypes.xlsx')
self._archetypes = self._read_archetype_file() self._archetypes = self._read_archetype_file()
self._schedules = self._read_schedules_file() self._schedules = self._read_schedules_file()
# todo: comment with @Guille, this hypotheses should go in the import factory?
sensible_convective = ch().comnet_occupancy_sensible_convective sensible_convective = ch().comnet_occupancy_sensible_convective
sensible_radiative = ch().comnet_occupancy_sensible_radiant sensible_radiative = ch().comnet_occupancy_sensible_radiant
lighting_convective = ch().comnet_lighting_convective lighting_convective = ch().comnet_lighting_convective
@ -148,13 +150,13 @@ class ComnetCatalog(Catalog):
if day == cte.SATURDAY: if day == cte.SATURDAY:
start = start + 1 start = start + 1
end = end + 1 end = end + 1
elif day == cte.SUNDAY or day == cte.HOLIDAY: elif day in (cte.SUNDAY, cte.HOLIDAY):
start = start + 2 start = start + 2
end = end + 2 end = end + 2
_schedule_values[day] = _extracted_data.iloc[start:end, 3:27].to_numpy().tolist()[0] _schedule_values[day] = _extracted_data.iloc[start:end, 3:27].to_numpy().tolist()[0]
_schedule = [] _schedule = []
for day in _schedule_values: for day in _schedule_values:
if schedule_name == 'ClgSetPt' or schedule_name == 'HtgSetPt' or schedule_name == 'WtrHtrSetPt': if schedule_name in ('ClgSetPt', 'HtgSetPt', 'WtrHtrSetPt'):
# to celsius # to celsius
if 'n.a.' in _schedule_values[day]: if 'n.a.' in _schedule_values[day]:
_schedule_values[day] = None _schedule_values[day] = None
@ -188,14 +190,14 @@ class ComnetCatalog(Catalog):
schedules_key = {} schedules_key = {}
for j in range(0, number_usage_types-1): for j in range(0, number_usage_types-1):
usage_parameters = _extracted_data.iloc[j] usage_parameters = _extracted_data.iloc[j]
usage_type = usage_parameters[0] usage_type = usage_parameters.iloc[0]
lighting_data[usage_type] = usage_parameters[1:6].values.tolist() lighting_data[usage_type] = usage_parameters.iloc[1:6].values.tolist()
plug_loads_data[usage_type] = usage_parameters[8:13].values.tolist() plug_loads_data[usage_type] = usage_parameters.iloc[8:13].values.tolist()
occupancy_data[usage_type] = usage_parameters[17:20].values.tolist() occupancy_data[usage_type] = usage_parameters.iloc[17:20].values.tolist()
ventilation_rate[usage_type] = usage_parameters[20:21].item() ventilation_rate[usage_type] = usage_parameters.iloc[20:21].item()
water_heating[usage_type] = usage_parameters[23:24].item() water_heating[usage_type] = usage_parameters.iloc[23:24].item()
process_data[usage_type] = usage_parameters[24:26].values.tolist() process_data[usage_type] = usage_parameters.iloc[24:26].values.tolist()
schedules_key[usage_type] = usage_parameters[27:28].item() schedules_key[usage_type] = usage_parameters.iloc[27:28].item()
return {'lighting': lighting_data, return {'lighting': lighting_data,
'plug loads': plug_loads_data, 'plug loads': plug_loads_data,

View File

@ -0,0 +1,234 @@
"""
Eilat usage catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
import io
from typing import Dict
import pandas as pd
import hub.helpers.constants as cte
from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.content import Content
from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.occupancy import Occupancy
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.data_models.usages.schedule import Schedule
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
from hub.catalog_factories.data_models.usages.usage import Usage
from hub.catalog_factories.usage.usage_helper import UsageHelper
from hub.helpers.configuration_helper import ConfigurationHelper as ch
class EilatCatalog(Catalog):
"""
Eilat catalog class
"""
def __init__(self, path):
self._eilat_archetypes_path = str(path / 'eilat_archetypes.xlsx')
self._eilat_schedules_path = str(path / 'eilat_schedules_archetypes.xlsx')
self._archetypes = self._read_archetype_file()
self._schedules = self._read_schedules_file()
sensible_convective = ch().comnet_occupancy_sensible_convective
sensible_radiative = ch().comnet_occupancy_sensible_radiant
lighting_convective = ch().comnet_lighting_convective
lighting_radiative = ch().comnet_lighting_radiant
lighting_latent = ch().comnet_lighting_latent
appliances_convective = ch().comnet_plugs_convective
appliances_radiative = ch().comnet_plugs_radiant
appliances_latent = ch().comnet_plugs_latent
usages = []
for schedule_key in self._archetypes['schedules_key']:
eilat_usage = schedule_key
schedule_name = self._archetypes['schedules_key'][schedule_key]
hours_day = None
days_year = None
occupancy_archetype = self._archetypes['occupancy'][eilat_usage]
lighting_archetype = self._archetypes['lighting'][eilat_usage]
appliances_archetype = self._archetypes['plug loads'][eilat_usage]
mechanical_air_change = None # eilat provides ventilation rate only
ventilation_rate = self._archetypes['ventilation rate'][eilat_usage]
# convert cfm/ft2 to m3/m2.s
ventilation_rate = ventilation_rate / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
domestic_hot_water_archetype = self._archetypes['water heating'][eilat_usage]
# get occupancy
occupancy_density = occupancy_archetype[0] / pow(cte.METERS_TO_FEET, 2)
sensible_heat_gain = occupancy_archetype[1] * cte.BTU_H_TO_WATTS
latent_heat_gain = occupancy_archetype[1] * cte.BTU_H_TO_WATTS
if occupancy_density != 0:
occupancy_density = 1 / occupancy_density
sensible_convective_internal_gain = occupancy_density * sensible_heat_gain * sensible_convective
sensible_radiative_internal_gain = occupancy_density * sensible_heat_gain * sensible_radiative
latent_internal_gain = occupancy_density * latent_heat_gain
occupancy = Occupancy(occupancy_density,
sensible_convective_internal_gain,
sensible_radiative_internal_gain,
latent_internal_gain,
self._schedules[schedule_name]['Occupancy'])
# get lighting
density = lighting_archetype[4] * pow(cte.METERS_TO_FEET, 2)
lighting = Lighting(density,
lighting_convective,
lighting_radiative,
lighting_latent,
self._schedules[schedule_name]['Lights'])
# get appliances
density = appliances_archetype[0]
if density == 'n.a.':
density = 0
# convert W/ft2 to W/m2
density = float(density) * pow(cte.METERS_TO_FEET, 2)
appliances = Appliances(density,
appliances_convective,
appliances_radiative,
appliances_latent,
self._schedules[schedule_name]['Receptacle'])
# get thermal control
thermal_control = ThermalControl(None,
None,
None,
self._schedules[schedule_name]['HVAC Avail'],
self._schedules[schedule_name]['HtgSetPt'],
self._schedules[schedule_name]['ClgSetPt']
)
# get domestic hot water
density = domestic_hot_water_archetype
# convert Btu/h/occ to W/m2
density = float(density) * cte.BTU_H_TO_WATTS * occupancy_density
domestic_hot_water_service_temperature = self._schedules[schedule_name]['WtrHtrSetPt'][0].values[0]
domestic_hot_water = DomesticHotWater(density,
None,
domestic_hot_water_service_temperature,
self._schedules[schedule_name]['Service Hot Water']
)
usages.append(Usage(eilat_usage,
hours_day,
days_year,
mechanical_air_change,
ventilation_rate,
occupancy,
lighting,
appliances,
thermal_control,
domestic_hot_water))
self._content = Content(usages)
def _read_schedules_file(self) -> Dict:
dictionary = {}
eilat_usages = UsageHelper().eilat_schedules_key_to_eilat_schedules
eilat_days = UsageHelper().comnet_days
eilat_data_types = UsageHelper().comnet_data_type_to_hub_data_type
for usage_name in eilat_usages:
with open(self._eilat_schedules_path, 'rb') as xls:
_extracted_data = pd.read_excel(
io.BytesIO(xls.read()),
sheet_name=eilat_usages[usage_name],
skiprows=[0, 1, 2, 3], nrows=39, usecols="A:AA"
)
_schedules = {}
for row in range(0, 39, 3):
_schedule_values = {}
schedule_name = _extracted_data.loc[row:row, 'Description'].item()
schedule_data_type = eilat_data_types[_extracted_data.loc[row:row, 'Type'].item()]
for day in eilat_days:
# Monday to Friday
start = row
end = row + 1
if day == cte.FRIDAY:
start = start + 1
end = end + 1
elif day in (cte.SATURDAY, cte.HOLIDAY):
start = start + 2
end = end + 2
_schedule_values[day] = _extracted_data.iloc[start:end, 3:27].to_numpy().tolist()[0]
_schedule = []
for day in _schedule_values:
if schedule_name in ('ClgSetPt', 'HtgSetPt', 'WtrHtrSetPt'):
# to celsius
if 'n.a.' in _schedule_values[day]:
_schedule_values[day] = None
else:
_schedule_values[day] = [(float(value)-32)*5/9 for value in _schedule_values[day]]
_schedule.append(Schedule(schedule_name, _schedule_values[day], schedule_data_type, cte.HOUR, cte.DAY, [day]))
_schedules[schedule_name] = _schedule
dictionary[usage_name] = _schedules
return dictionary
def _read_archetype_file(self) -> Dict:
"""
reads xlsx files containing usage information into a dictionary
:return : Dict
"""
number_usage_types = 3
with open(self._eilat_archetypes_path, 'rb') as xls:
_extracted_data = pd.read_excel(
io.BytesIO(xls.read()),
sheet_name="Modeling Data",
skiprows=[0, 1, 2],
nrows=number_usage_types + 1, usecols="A:AB"
)
lighting_data = {}
plug_loads_data = {}
occupancy_data = {}
ventilation_rate = {}
water_heating = {}
process_data = {}
schedules_key = {}
for j in range(0, number_usage_types):
usage_parameters = _extracted_data.iloc[j]
usage_type = usage_parameters.iloc[0]
lighting_data[usage_type] = usage_parameters[1:6].values.tolist()
plug_loads_data[usage_type] = usage_parameters[8:13].values.tolist()
occupancy_data[usage_type] = usage_parameters[17:20].values.tolist()
ventilation_rate[usage_type] = usage_parameters[20:21].item()
water_heating[usage_type] = usage_parameters[23:24].item()
process_data[usage_type] = usage_parameters[24:26].values.tolist()
schedules_key[usage_type] = usage_parameters[27:28].item()
return {'lighting': lighting_data,
'plug loads': plug_loads_data,
'occupancy': occupancy_data,
'ventilation rate': ventilation_rate,
'water heating': water_heating,
'process': process_data,
'schedules_key': schedules_key
}
def names(self, category=None):
"""
Get the catalog elements names
:parm: for usage catalog category filter does nothing as there is only one category (usages)
"""
_names = {'usages': []}
for usage in self._content.usages:
_names['usages'].append(usage.name)
return _names
def entries(self, category=None):
"""
Get the catalog elements
:parm: for usage catalog category filter does nothing as there is only one category (usages)
"""
return self._content
def get_entry(self, name):
"""
Get one catalog element by names
:parm: entry name
"""
for usage in self._content.usages:
if usage.name.lower() == name.lower():
return usage
raise IndexError(f"{name} doesn't exists in the catalog")

View File

@ -8,6 +8,8 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
import json import json
import urllib.request import urllib.request
from pathlib import Path
import xmltodict import xmltodict
import hub.helpers.constants as cte import hub.helpers.constants as cte
@ -15,7 +17,7 @@ from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.usages.appliances import Appliances from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.content import Content from hub.catalog_factories.data_models.usages.content import Content
from hub.catalog_factories.data_models.usages.lighting import Lighting from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.ocupancy import Occupancy from hub.catalog_factories.data_models.usages.occupancy import Occupancy
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.data_models.usages.schedule import Schedule from hub.catalog_factories.data_models.usages.schedule import Schedule
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
@ -24,13 +26,15 @@ from hub.catalog_factories.usage.usage_helper import UsageHelper
class NrcanCatalog(Catalog): class NrcanCatalog(Catalog):
"""
Nrcan catalog class
"""
def __init__(self, path): def __init__(self, path):
path = str(path / 'nrcan.xml') self._schedules_path = Path(path / 'nrcan_schedules.json').resolve()
self._space_types_path = Path(path / 'nrcan_space_types.json').resolve()
self._space_compliance_path = Path(path / 'nrcan_space_compliance_2015.json').resolve()
self._content = None self._content = None
self._schedules = {} self._schedules = {}
with open(path) as xml:
self._metadata = xmltodict.parse(xml.read())
self._base_url = self._metadata['nrcan']['@base_url']
self._load_schedules() self._load_schedules()
self._content = Content(self._load_archetypes()) self._content = Content(self._load_archetypes())
@ -52,11 +56,9 @@ class NrcanCatalog(Catalog):
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types) return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
def _load_schedules(self): def _load_schedules(self):
usage = self._metadata['nrcan']
url = f'{self._base_url}{usage["schedules"]}'
_schedule_types = [] _schedule_types = []
with urllib.request.urlopen(url) as json_file: with open(self._schedules_path, 'r') as f:
schedules_type = json.load(json_file) schedules_type = json.load(f)
for schedule_type in schedules_type['tables']['schedules']['table']: for schedule_type in schedules_type['tables']['schedules']['table']:
schedule = NrcanCatalog._extract_schedule(schedule_type) schedule = NrcanCatalog._extract_schedule(schedule_type)
if schedule_type['name'] not in _schedule_types: if schedule_type['name'] not in _schedule_types:
@ -70,19 +72,18 @@ class NrcanCatalog(Catalog):
self._schedules[schedule_type['name']] = _schedules self._schedules[schedule_type['name']] = _schedules
def _get_schedules(self, name): def _get_schedules(self, name):
schedule = None
if name in self._schedules: if name in self._schedules:
return self._schedules[name] schedule = self._schedules[name]
return schedule
def _load_archetypes(self): def _load_archetypes(self):
usages = [] usages = []
name = self._metadata['nrcan'] with open(self._space_types_path, 'r') as f:
url_1 = f'{self._base_url}{name["space_types"]}' space_types = json.load(f)['tables']['space_types']['table']
url_2 = f'{self._base_url}{name["space_types_compliance"]}'
with urllib.request.urlopen(url_1) as json_file:
space_types = json.load(json_file)['tables']['space_types']['table']
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding'] space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
with urllib.request.urlopen(url_2) as json_file: with open(self._space_compliance_path, 'r') as f:
space_types_compliance = json.load(json_file)['tables']['space_compliance']['table'] space_types_compliance = json.load(f)['tables']['space_compliance']['table']
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding'] space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
space_types_dictionary = {} space_types_dictionary = {}
for space_type in space_types_compliance: for space_type in space_types_compliance:
@ -94,7 +95,10 @@ class NrcanCatalog(Catalog):
# W/m2 # W/m2
appliances_density = space_type['electric_equipment_per_area_w_per_m2'] appliances_density = space_type['electric_equipment_per_area_w_per_m2']
# peak flow in gallons/h/m2 # peak flow in gallons/h/m2
domestic_hot_water_peak_flow = space_type['service_water_heating_peak_flow_per_area'] * cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS domestic_hot_water_peak_flow = (
space_type['service_water_heating_peak_flow_per_area'] *
cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS
)
space_types_dictionary[usage_type] = {'occupancy_per_area': occupancy_density, space_types_dictionary[usage_type] = {'occupancy_per_area': occupancy_density,
'lighting_per_area': lighting_density, 'lighting_per_area': lighting_density,
'electric_equipment_per_area': appliances_density, 'electric_equipment_per_area': appliances_density,
@ -126,8 +130,8 @@ class NrcanCatalog(Catalog):
hvac_availability = self._get_schedules(hvac_schedule_name) hvac_availability = self._get_schedules(hvac_schedule_name)
domestic_hot_water_load_schedule = self._get_schedules(domestic_hot_water_schedule_name) domestic_hot_water_load_schedule = self._get_schedules(domestic_hot_water_schedule_name)
# ACH # ACH -> 1/s
mechanical_air_change = space_type['ventilation_air_changes'] mechanical_air_change = space_type['ventilation_air_changes'] / cte.HOUR_TO_SECONDS
# cfm/ft2 to m3/m2.s # cfm/ft2 to m3/m2.s
ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS) ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
# cfm/person to m3/m2.s # cfm/person to m3/m2.s

View File

@ -0,0 +1,227 @@
"""
Palma usage catalog
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Cecilia Pérez cperez@irec.cat
"""
import json
import urllib.request
from pathlib import Path
import xmltodict
import hub.helpers.constants as cte
from hub.catalog_factories.catalog import Catalog
from hub.catalog_factories.data_models.usages.appliances import Appliances
from hub.catalog_factories.data_models.usages.content import Content
from hub.catalog_factories.data_models.usages.lighting import Lighting
from hub.catalog_factories.data_models.usages.occupancy import Occupancy
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
from hub.catalog_factories.data_models.usages.schedule import Schedule
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
from hub.catalog_factories.data_models.usages.usage import Usage
from hub.catalog_factories.usage.usage_helper import UsageHelper
class PalmaCatalog(Catalog):
"""
Palma catalog class
"""
def __init__(self, path):
self._schedules_path = Path(path / 'palma_schedules.json').resolve()
self._space_types_path = Path(path / 'palma_space_types.json').resolve()
self._space_compliance_path = Path(path / 'palma_space_compliance.json').resolve()
self._content = None
self._schedules = {}
self._load_schedules()
self._content = Content(self._load_archetypes())
@staticmethod
def _extract_schedule(raw):
nrcan_schedule_type = raw['category']
if 'Heating' in raw['name'] and 'Water' not in raw['name']:
nrcan_schedule_type = f'{nrcan_schedule_type} Heating'
elif 'Cooling' in raw['name']:
nrcan_schedule_type = f'{nrcan_schedule_type} Cooling'
if nrcan_schedule_type not in UsageHelper().nrcan_schedule_type_to_hub_schedule_type:
return None
hub_type = UsageHelper().nrcan_schedule_type_to_hub_schedule_type[nrcan_schedule_type]
data_type = UsageHelper().nrcan_data_type_to_hub_data_type[raw['units']]
time_step = UsageHelper().nrcan_time_to_hub_time[raw['type']]
# nrcan only uses daily range for the schedules
time_range = cte.DAY
day_types = UsageHelper().nrcan_day_type_to_hub_days[raw['day_types']]
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
def _load_schedules(self):
_schedule_types = []
with open(self._schedules_path, 'r') as f:
schedules_type = json.load(f)
for schedule_type in schedules_type['tables']['schedules']['table']:
schedule = PalmaCatalog._extract_schedule(schedule_type)
if schedule_type['name'] not in _schedule_types:
_schedule_types.append(schedule_type['name'])
if schedule is not None:
self._schedules[schedule_type['name']] = [schedule]
else:
if schedule is not None:
_schedules = self._schedules[schedule_type['name']]
_schedules.append(schedule)
self._schedules[schedule_type['name']] = _schedules
def _get_schedules(self, name):
schedule = None
if name in self._schedules:
schedule = self._schedules[name]
return schedule
def _load_archetypes(self):
usages = []
with open(self._space_types_path, 'r') as f:
space_types = json.load(f)['tables']['space_types']['table']
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
with open(self._space_compliance_path, 'r') as f:
space_types_compliance = json.load(f)['tables']['space_compliance']['table']
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
space_types_dictionary = {}
for space_type in space_types_compliance:
usage_type = space_type['building_type']
# people/m2
occupancy_density = space_type['occupancy_per_area_people_per_m2']
# W/m2
lighting_density = space_type['lighting_per_area_w_per_m2']
# W/m2
appliances_density = space_type['electric_equipment_per_area_w_per_m2']
# peak flow in gallons/h/m2
domestic_hot_water_peak_flow = (
space_type['service_water_heating_peak_flow_per_area'] *
cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS
)
space_types_dictionary[usage_type] = {'occupancy_per_area': occupancy_density,
'lighting_per_area': lighting_density,
'electric_equipment_per_area': appliances_density,
'service_water_heating_peak_flow_per_area': domestic_hot_water_peak_flow
}
for space_type in space_types:
usage_type = space_type['building_type']
space_type_compliance = space_types_dictionary[usage_type]
occupancy_density = space_type_compliance['occupancy_per_area']
sensible_convective_internal_gain = space_type['sensible_convective_internal_gain']
sensible_radiative_internal_gain = space_type['sensible_radiative_internal_gain']
latent_internal_gain = space_type['latent_internal_gain']
lighting_density = space_type_compliance['lighting_per_area']
appliances_density = space_type_compliance['electric_equipment_per_area']
domestic_hot_water_peak_flow = space_type_compliance['service_water_heating_peak_flow_per_area']
occupancy_schedule_name = space_type['occupancy_schedule']
lighting_schedule_name = space_type['lighting_schedule']
appliance_schedule_name = space_type['electric_equipment_schedule']
hvac_schedule_name = space_type['exhaust_schedule']
if hvac_schedule_name and 'FAN' in hvac_schedule_name:
hvac_schedule_name = hvac_schedule_name.replace('FAN', 'Fan')
if not hvac_schedule_name:
hvac_schedule_name = 'default_HVAC_schedule'
heating_setpoint_schedule_name = space_type['heating_setpoint_schedule']
cooling_setpoint_schedule_name = space_type['cooling_setpoint_schedule']
domestic_hot_water_schedule_name = space_type['service_water_heating_schedule']
occupancy_schedule = self._get_schedules(occupancy_schedule_name)
lighting_schedule = self._get_schedules(lighting_schedule_name)
appliance_schedule = self._get_schedules(appliance_schedule_name)
heating_schedule = self._get_schedules(heating_setpoint_schedule_name)
cooling_schedule = self._get_schedules(cooling_setpoint_schedule_name)
hvac_availability = self._get_schedules(hvac_schedule_name)
domestic_hot_water_load_schedule = self._get_schedules(domestic_hot_water_schedule_name)
# ACH -> 1/s
mechanical_air_change = space_type['ventilation_air_changes'] / cte.HOUR_TO_SECONDS
# cfm/ft2 to m3/m2.s
ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
# cfm/person to m3/m2.s
ventilation_rate += space_type['ventilation_per_person'] / (
pow(cte.METERS_TO_FEET, 3) * cte.MINUTES_TO_SECONDS
) * occupancy_density
lighting_radiative_fraction = space_type['lighting_fraction_radiant']
lighting_convective_fraction = 0
if lighting_radiative_fraction is not None:
lighting_convective_fraction = 1 - lighting_radiative_fraction
lighting_latent_fraction = 0
appliances_radiative_fraction = space_type['electric_equipment_fraction_radiant']
appliances_latent_fraction = space_type['electric_equipment_fraction_latent']
appliances_convective_fraction = 0
if appliances_radiative_fraction is not None and appliances_latent_fraction is not None:
appliances_convective_fraction = 1 - appliances_radiative_fraction - appliances_latent_fraction
domestic_hot_water_service_temperature = space_type['service_water_heating_target_temperature']
occupancy = Occupancy(occupancy_density,
sensible_convective_internal_gain,
sensible_radiative_internal_gain,
latent_internal_gain,
occupancy_schedule)
lighting = Lighting(lighting_density,
lighting_convective_fraction,
lighting_radiative_fraction,
lighting_latent_fraction,
lighting_schedule)
appliances = Appliances(appliances_density,
appliances_convective_fraction,
appliances_radiative_fraction,
appliances_latent_fraction,
appliance_schedule)
thermal_control = ThermalControl(None,
None,
None,
hvac_availability,
heating_schedule,
cooling_schedule)
domestic_hot_water = DomesticHotWater(None,
domestic_hot_water_peak_flow,
domestic_hot_water_service_temperature,
domestic_hot_water_load_schedule)
hours_day = None
days_year = None
usages.append(Usage(usage_type,
hours_day,
days_year,
mechanical_air_change,
ventilation_rate,
occupancy,
lighting,
appliances,
thermal_control,
domestic_hot_water))
return usages
def names(self, category=None):
"""
Get the catalog elements names
:parm: for usage catalog category filter does nothing as there is only one category (usages)
"""
_names = {'usages': []}
for usage in self._content.usages:
_names['usages'].append(usage.name)
return _names
def entries(self, category=None):
"""
Get the catalog elements
:parm: for usage catalog category filter does nothing as there is only one category (usages)
"""
return self._content
def get_entry(self, name):
"""
Get one catalog element by names
:parm: entry name
"""
for usage in self._content.usages:
if usage.name.lower() == name.lower():
return usage
raise IndexError(f"{name} doesn't exists in the catalog")

View File

@ -4,10 +4,10 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
import sys
import hub.helpers.constants as cte
from typing import Dict from typing import Dict
import hub.helpers.constants as cte
class UsageHelper: class UsageHelper:
""" """
@ -90,43 +90,64 @@ class UsageHelper:
'C-14 Gymnasium': 'C-14 Gymnasium' 'C-14 Gymnasium': 'C-14 Gymnasium'
} }
_eilat_schedules_key_to_eilat_schedules = {
'C-12 Residential': 'C-12 Residential',
'C-15 Dormitory': 'C-15 Dormitory',
'C-16 Hotel employees': 'C-16 Hotel employees'
}
@property @property
def nrcan_day_type_to_hub_days(self): def nrcan_day_type_to_hub_days(self):
"""
Get a dictionary to convert nrcan day types to hub day types
"""
return self._nrcan_day_type_to_hub_days return self._nrcan_day_type_to_hub_days
@property @property
def nrcan_schedule_type_to_hub_schedule_type(self): def nrcan_schedule_type_to_hub_schedule_type(self):
"""
Get a dictionary to convert nrcan schedule types to hub schedule types
"""
return self._nrcan_schedule_type_to_hub_schedule_type return self._nrcan_schedule_type_to_hub_schedule_type
@property @property
def nrcan_data_type_to_hub_data_type(self): def nrcan_data_type_to_hub_data_type(self):
"""
Get a dictionary to convert nrcan data types to hub data types
"""
return self._nrcan_data_type_to_hub_data_type return self._nrcan_data_type_to_hub_data_type
@property @property
def nrcan_time_to_hub_time(self): def nrcan_time_to_hub_time(self):
"""
Get a dictionary to convert nrcan time to hub time
"""
return self._nrcan_time_to_hub_time return self._nrcan_time_to_hub_time
@property @property
def comnet_data_type_to_hub_data_type(self): def comnet_data_type_to_hub_data_type(self) -> Dict:
"""
Get a dictionary to convert comnet data types to hub data types
"""
return self._comnet_data_type_to_hub_data_type return self._comnet_data_type_to_hub_data_type
@property @property
def comnet_schedules_key_to_comnet_schedules(self) -> Dict: def comnet_schedules_key_to_comnet_schedules(self) -> Dict:
"""
Get a dictionary to convert hub schedules to comnet schedules
"""
return self._comnet_schedules_key_to_comnet_schedules return self._comnet_schedules_key_to_comnet_schedules
@property @property
def comnet_days(self): def comnet_days(self) -> [str]:
"""
Get the list of days used in comnet
"""
return self._comnet_days return self._comnet_days
@staticmethod @property
def schedules_key(usage): def eilat_schedules_key_to_eilat_schedules(self) -> [str]:
""" """
Get Comnet schedules key from the list found in the Comnet usage file Get a dictionary to convert hub schedules to eilat schedules
:param usage: str
:return: str
""" """
try: return self._eilat_schedules_key_to_eilat_schedules
return UsageHelper._comnet_schedules_key_to_comnet_schedules[usage]
except KeyError:
sys.stderr.write('Error: Comnet keyword not found. An update of the Comnet files might have been '
'done changing the keywords.\n')

View File

@ -4,17 +4,23 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import logging
from pathlib import Path from pathlib import Path
from typing import TypeVar from typing import TypeVar
from hub.catalog_factories.usage.comnet_catalog import ComnetCatalog from hub.catalog_factories.usage.comnet_catalog import ComnetCatalog
from hub.catalog_factories.usage.nrcan_catalog import NrcanCatalog from hub.catalog_factories.usage.nrcan_catalog import NrcanCatalog
from hub.catalog_factories.usage.eilat_catalog import EilatCatalog
from hub.catalog_factories.usage.palma_catalog import PalmaCatalog
from hub.helpers.utils import validate_import_export_type from hub.helpers.utils import validate_import_export_type
Catalog = TypeVar('Catalog') Catalog = TypeVar('Catalog')
class UsageCatalogFactory: class UsageCatalogFactory:
"""
Usage catalog factory class
"""
def __init__(self, handler, base_path=None): def __init__(self, handler, base_path=None):
if base_path is None: if base_path is None:
base_path = Path(Path(__file__).parent.parent / 'data/usage') base_path = Path(Path(__file__).parent.parent / 'data/usage')
@ -37,6 +43,20 @@ class UsageCatalogFactory:
# nrcan retrieves the data directly from github # nrcan retrieves the data directly from github
return NrcanCatalog(self._path) return NrcanCatalog(self._path)
@property
def _palma(self):
"""
Retrieve Palma catalog
"""
return PalmaCatalog(self._path)
@property
def _eilat(self):
"""
Retrieve Eilat catalog
"""
return EilatCatalog(self._path)
@property @property
def catalog(self) -> Catalog: def catalog(self) -> Catalog:
""" """

View File

View File

@ -45,14 +45,13 @@ class Plane:
:return: (A, B, C, D) :return: (A, B, C, D)
""" """
if self._equation is None: if self._equation is None:
a = self.normal[0] a = self.normal[0]
b = self.normal[1] b = self.normal[1]
c = self.normal[2] c = self.normal[2]
d = ((-1 * self.origin.coordinates[0]) * self.normal[0]) d = -1 * self.origin.coordinates[0] * self.normal[0]
d += ((-1 * self.origin.coordinates[1]) * self.normal[1]) d += -1 * self.origin.coordinates[1] * self.normal[1]
d += ((-1 * self.origin.coordinates[2]) * self.normal[2]) d += -1 * self.origin.coordinates[2] * self.normal[2]
self._equation = (a, b, c, d) self._equation = a, b, c, d
return self._equation return self._equation
def distance_to_point(self, point): def distance_to_point(self, point):

View File

@ -31,7 +31,7 @@ class Point:
:return: float :return: float
""" """
power = 0 power = 0
for dimension in range(0, len(self.coordinates)): for dimension in enumerate(self.coordinates):
power += math.pow(other_point.coordinates[dimension]-self.coordinates[dimension], 2) power += math.pow(other_point.coordinates[dimension]-self.coordinates[dimension], 2)
distance = math.sqrt(power) distance = math.sqrt(power)
return distance return distance

View File

@ -6,20 +6,21 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
from __future__ import annotations from __future__ import annotations
import logging import logging
import math import math
import sys import sys
from typing import List from typing import List
import numpy as np import numpy as np
from trimesh import Trimesh
import trimesh.intersections
import trimesh.creation import trimesh.creation
import trimesh.geometry import trimesh.geometry
import trimesh.intersections
from shapely.geometry.polygon import Polygon as shapley_polygon from shapely.geometry.polygon import Polygon as shapley_polygon
from trimesh import Trimesh
from hub.city_model_structure.attributes.plane import Plane from hub.city_model_structure.attributes.plane import Plane
from hub.city_model_structure.attributes.point import Point from hub.city_model_structure.attributes.point import Point
import hub.helpers.constants as cte
class Polygon: class Polygon:
@ -69,44 +70,6 @@ class Polygon:
""" """
return self._coordinates return self._coordinates
def contains_point(self, point):
"""
Determines if the given point is contained by the current polygon
:return: boolean
"""
# fixme: This method doesn't seems to work.
n = len(self.vertices)
angle_sum = 0
for i in range(0, n):
vector_0 = self.vertices[i]
vector_1 = self.vertices[(i+1) % n]
# set to origin
vector_0[0] = vector_0[0] - point.coordinates[0]
vector_0[1] = vector_0[1] - point.coordinates[1]
vector_0[2] = vector_0[2] - point.coordinates[2]
vector_1[0] = vector_1[0] - point.coordinates[0]
vector_1[1] = vector_1[1] - point.coordinates[1]
vector_1[2] = vector_1[2] - point.coordinates[2]
module = np.linalg.norm(vector_0) * np.linalg.norm(vector_1)
scalar_product = np.dot(vector_0, vector_1)
angle = np.pi/2
if module != 0:
angle = abs(np.arcsin(scalar_product / module))
angle_sum += angle
return abs(angle_sum - math.pi*2) < cte.EPSILON
def contains_polygon(self, polygon):
"""
Determines if the given polygon is contained by the current polygon
:return: boolean
"""
for point in polygon.points:
if not self.contains_point(point):
return False
return True
@property @property
def points_list(self) -> np.ndarray: def points_list(self) -> np.ndarray:
""" """
@ -142,12 +105,12 @@ class Polygon:
if self._area is None: if self._area is None:
self._area = 0 self._area = 0
for triangle in self.triangles: for triangle in self.triangles:
ab = np.zeros(3) a_b = np.zeros(3)
ac = np.zeros(3) a_c = np.zeros(3)
for i in range(0, 3): for i in range(0, 3):
ab[i] = triangle.coordinates[1][i] - triangle.coordinates[0][i] a_b[i] = triangle.coordinates[1][i] - triangle.coordinates[0][i]
ac[i] = triangle.coordinates[2][i] - triangle.coordinates[0][i] a_c[i] = triangle.coordinates[2][i] - triangle.coordinates[0][i]
self._area += np.linalg.norm(np.cross(ab, ac)) / 2 self._area += np.linalg.norm(np.cross(a_b, a_c)) / 2
return self._area return self._area
@area.setter @area.setter
@ -217,7 +180,11 @@ class Polygon:
return -alpha return -alpha
@staticmethod @staticmethod
def triangle_mesh(vertices, normal): def triangle_mesh(vertices, normal) -> Trimesh:
"""
Get the triangulated mesh for the polygon
:return: Trimesh
"""
min_x = 1e16 min_x = 1e16
min_y = 1e16 min_y = 1e16
min_z = 1e16 min_z = 1e16
@ -246,8 +213,7 @@ class Polygon:
polygon = shapley_polygon(coordinates) polygon = shapley_polygon(coordinates)
try: try:
_, faces = trimesh.creation.triangulate_polygon(polygon, engine='triangle')
vertices_2d, faces = trimesh.creation.triangulate_polygon(polygon, engine='triangle')
mesh = Trimesh(vertices=vertices, faces=faces) mesh = Trimesh(vertices=vertices, faces=faces)
@ -267,13 +233,17 @@ class Polygon:
return mesh return mesh
except ValueError: except ValueError:
logging.error(f'Not able to triangulate polygon\n') logging.error('Not able to triangulate polygon\n')
_vertices = [[0, 0, 0], [0, 0, 1], [0, 1, 0]] _vertices = [[0, 0, 0], [0, 0, 1], [0, 1, 0]]
_faces = [[0, 1, 2]] _faces = [[0, 1, 2]]
return Trimesh(vertices=_vertices, faces=_faces) return Trimesh(vertices=_vertices, faces=_faces)
@property @property
def triangles(self) -> List[Polygon]: def triangles(self) -> List[Polygon]:
"""
Triangulate the polygon and return a list of triangular polygons
:return: [Polygon]
"""
if self._triangles is None: if self._triangles is None:
self._triangles = [] self._triangles = []
_mesh = self.triangle_mesh(self.coordinates, self.normal) _mesh = self.triangle_mesh(self.coordinates, self.normal)
@ -336,7 +306,7 @@ class Polygon:
def _reshape(self, triangles) -> Polygon: def _reshape(self, triangles) -> Polygon:
edges_list = [] edges_list = []
for i in range(0, len(triangles)): for i in enumerate(triangles):
for edge in triangles[i].edges: for edge in triangles[i].edges:
if not self._edge_in_edges_list(edge, edges_list): if not self._edge_in_edges_list(edge, edges_list):
edges_list.append(edge) edges_list.append(edge)
@ -421,13 +391,14 @@ class Polygon:
if len(points) != 3: if len(points) != 3:
sub_polygons = polygon.triangles sub_polygons = polygon.triangles
# todo: I modified this! To be checked @Guille # todo: I modified this! To be checked @Guille
if len(sub_polygons) >= 1: if len(sub_polygons) < 1:
for sub_polygon in sub_polygons: continue
face = [] for sub_polygon in sub_polygons:
points = sub_polygon.coordinates face = []
for point in points: points = sub_polygon.coordinates
face.append(self._position_of(point, face)) for point in points:
self._faces.append(face) face.append(self._position_of(point, face))
self._faces.append(face)
else: else:
for point in points: for point in points:
face.append(self._position_of(point, face)) face.append(self._position_of(point, face))
@ -440,7 +411,7 @@ class Polygon:
:return: int :return: int
""" """
vertices = self.vertices vertices = self.vertices
for i in range(len(vertices)): for i in enumerate(vertices):
# ensure not duplicated vertex # ensure not duplicated vertex
power = 0 power = 0
vertex2 = vertices[i] vertex2 = vertices[i]

View File

@ -41,10 +41,10 @@ class Polyhedron:
:return: int :return: int
""" """
vertices = self.vertices vertices = self.vertices
for i in range(len(vertices)): for i, vertex in enumerate(vertices):
# ensure not duplicated vertex # ensure not duplicated vertex
power = 0 power = 0
vertex2 = vertices[i] vertex2 = vertex
for dimension in range(0, 3): for dimension in range(0, 3):
power += math.pow(vertex2[dimension] - point[dimension], 2) power += math.pow(vertex2[dimension] - point[dimension], 2)
distance = math.sqrt(power) distance = math.sqrt(power)
@ -92,13 +92,14 @@ class Polyhedron:
points = polygon.coordinates points = polygon.coordinates
if len(points) != 3: if len(points) != 3:
sub_polygons = polygon.triangles sub_polygons = polygon.triangles
if len(sub_polygons) >= 1: if len(sub_polygons) < 1:
for sub_polygon in sub_polygons: continue
face = [] for sub_polygon in sub_polygons:
points = sub_polygon.coordinates face = []
for point in points: points = sub_polygon.coordinates
face.append(self._position_of(point, face)) for point in points:
self._faces.append(face) face.append(self._position_of(point, face))
self._faces.append(face)
else: else:
for point in points: for point in points:
face.append(self._position_of(point, face)) face.append(self._position_of(point, face))

View File

@ -10,12 +10,11 @@ import logging
from typing import List, Union, TypeVar from typing import List, Union, TypeVar
import numpy as np import numpy as np
import pandas as pd
import hub.helpers.constants as cte import hub.helpers.constants as cte
from hub.city_model_structure.attributes.polyhedron import Polyhedron from hub.city_model_structure.attributes.polyhedron import Polyhedron
from hub.city_model_structure.building_demand.household import Household from hub.city_model_structure.building_demand.household import Household
from hub.city_model_structure.building_demand.internal_zone import InternalZone from hub.city_model_structure.building_demand.internal_zone import InternalZone
from hub.city_model_structure.building_demand.thermal_zone import ThermalZone
from hub.city_model_structure.building_demand.surface import Surface from hub.city_model_structure.building_demand.surface import Surface
from hub.city_model_structure.city_object import CityObject from hub.city_model_structure.city_object import CityObject
from hub.city_model_structure.energy_systems.energy_system import EnergySystem from hub.city_model_structure.energy_systems.energy_system import EnergySystem
@ -42,20 +41,21 @@ class Building(CityObject):
self._floor_area = None self._floor_area = None
self._roof_type = None self._roof_type = None
self._internal_zones = None self._internal_zones = None
self._thermal_zones_from_internal_zones = None
self._shell = None self._shell = None
self._aliases = None self._aliases = []
self._type = 'building' self._type = 'building'
self._cold_water_temperature = dict() self._cold_water_temperature = {}
self._heating = dict() self._heating_demand = {}
self._cooling = dict() self._cooling_demand = {}
self._lighting_electrical_demand = dict() self._lighting_electrical_demand = {}
self._appliances_electrical_demand = dict() self._appliances_electrical_demand = {}
self._domestic_hot_water_heat_demand = dict() self._domestic_hot_water_heat_demand = {}
self._heating_consumption = dict() self._heating_consumption = {}
self._cooling_consumption = dict() self._cooling_consumption = {}
self._domestic_hot_water_consumption = dict() self._domestic_hot_water_consumption = {}
self._distribution_systems_electrical_consumption = dict() self._distribution_systems_electrical_consumption = {}
self._onsite_electrical_production = dict() self._onsite_electrical_production = {}
self._eave_height = None self._eave_height = None
self._energy_systems = None self._energy_systems = None
self._systems_archetype_name = None self._systems_archetype_name = None
@ -70,6 +70,9 @@ class Building(CityObject):
self._min_x = min(self._min_x, surface.lower_corner[0]) self._min_x = min(self._min_x, surface.lower_corner[0])
self._min_y = min(self._min_y, surface.lower_corner[1]) self._min_y = min(self._min_y, surface.lower_corner[1])
self._min_z = min(self._min_z, surface.lower_corner[2]) self._min_z = min(self._min_z, surface.lower_corner[2])
self._max_x = max(self._max_x, surface.upper_corner[0])
self._max_y = max(self._max_y, surface.upper_corner[1])
self._max_z = max(self._max_z, surface.upper_corner[2])
surface.id = surface_id surface.id = surface_id
if surface.type == cte.GROUND: if surface.type == cte.GROUND:
self._grounds.append(surface) self._grounds.append(surface)
@ -86,7 +89,10 @@ class Building(CityObject):
elif surface.type == cte.INTERIOR_SLAB: elif surface.type == cte.INTERIOR_SLAB:
self._interior_slabs.append(surface) self._interior_slabs.append(surface)
else: else:
logging.error(f'Building {self.name} [{self.aliases}] has an unexpected surface type {surface.type}.\n') logging.error('Building %s [%s] has an unexpected surface type %s.', self.name, self.aliases, surface.type)
self._domestic_hot_water_peak_load = None
self._fuel_consumption_breakdown = {}
self._pv_generation = {}
@property @property
def shell(self) -> Polyhedron: def shell(self) -> Polyhedron:
@ -114,9 +120,24 @@ class Building(CityObject):
:return: [InternalZone] :return: [InternalZone]
""" """
if self._internal_zones is None: if self._internal_zones is None:
self._internal_zones = [InternalZone(self.surfaces, self.floor_area)] self._internal_zones = [InternalZone(self.surfaces, self.floor_area, self.volume)]
return self._internal_zones return self._internal_zones
@property
def thermal_zones_from_internal_zones(self) -> Union[None, List[ThermalZone]]:
"""
Get building thermal zones
:return: [ThermalZone]
"""
if self._thermal_zones_from_internal_zones is None:
self._thermal_zones_from_internal_zones = []
for internal_zone in self.internal_zones:
if internal_zone.thermal_zones_from_internal_zones is None:
self._thermal_zones_from_internal_zones = None
return self._thermal_zones_from_internal_zones
self._thermal_zones_from_internal_zones.append(internal_zone.thermal_zones_from_internal_zones[0])
return self._thermal_zones_from_internal_zones
@property @property
def grounds(self) -> List[Surface]: def grounds(self) -> List[Surface]:
""" """
@ -243,6 +264,15 @@ class Building(CityObject):
Get building average storey height in meters Get building average storey height in meters
:return: None or float :return: None or float
""" """
if len(self.internal_zones) > 1:
self._average_storey_height = 0
for internal_zone in self.internal_zones:
self._average_storey_height += internal_zone.mean_height / len(self.internal_zones)
else:
if self.internal_zones[0].thermal_archetype is None:
self._average_storey_height = None
else:
self._average_storey_height = self.internal_zones[0].thermal_archetype.average_storey_height
return self._average_storey_height return self._average_storey_height
@average_storey_height.setter @average_storey_height.setter
@ -262,7 +292,10 @@ class Building(CityObject):
""" """
if self._storeys_above_ground is None: if self._storeys_above_ground is None:
if self.eave_height is not None and self.average_storey_height is not None: if self.eave_height is not None and self.average_storey_height is not None:
self._storeys_above_ground = int(self.eave_height / self.average_storey_height) storeys_above_ground = int(self.eave_height / self.average_storey_height)
if storeys_above_ground == 0:
storeys_above_ground += 1
self._storeys_above_ground = storeys_above_ground
return self._storeys_above_ground return self._storeys_above_ground
@storeys_above_ground.setter @storeys_above_ground.setter
@ -278,7 +311,7 @@ class Building(CityObject):
def cold_water_temperature(self) -> {float}: def cold_water_temperature(self) -> {float}:
""" """
Get cold water temperature in degrees Celsius Get cold water temperature in degrees Celsius
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
return self._cold_water_temperature return self._cold_water_temperature
@ -286,123 +319,176 @@ class Building(CityObject):
def cold_water_temperature(self, value): def cold_water_temperature(self, value):
""" """
Set cold water temperature in degrees Celsius Set cold water temperature in degrees Celsius
:param value: dict{DataFrame(float)} :param value: dict{[float]}
""" """
self._cold_water_temperature = value self._cold_water_temperature = value
@property @property
def heating(self) -> dict: def heating_demand(self) -> dict:
""" """
Get heating demand in Wh Get heating demand in J
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
return self._heating return self._heating_demand
@heating.setter @heating_demand.setter
def heating(self, value): def heating_demand(self, value):
""" """
Set heating demand in Wh Set heating demand in J
:param value: dict{DataFrame(float)} :param value: dict{[float]}
""" """
self._heating = value self._heating_demand = value
@property @property
def cooling(self) -> dict: def cooling_demand(self) -> dict:
""" """
Get cooling demand in Wh Get cooling demand in J
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
return self._cooling return self._cooling_demand
@cooling.setter @cooling_demand.setter
def cooling(self, value): def cooling_demand(self, value):
""" """
Set cooling demand in Wh Set cooling demand in J
:param value: dict{DataFrame(float)} :param value: dict{[float]}
""" """
self._cooling = value self._cooling_demand = value
@property @property
def lighting_electrical_demand(self) -> dict: def lighting_electrical_demand(self) -> dict:
""" """
Get lighting electrical demand in Wh Get lighting electrical demand in J
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
return self._lighting_electrical_demand return self._lighting_electrical_demand
@lighting_electrical_demand.setter @lighting_electrical_demand.setter
def lighting_electrical_demand(self, value): def lighting_electrical_demand(self, value):
""" """
Set lighting electrical demand in Wh Set lighting electrical demand in J
:param value: dict{DataFrame(float)} :param value: dict{[float]}
""" """
self._lighting_electrical_demand = value self._lighting_electrical_demand = value
@property @property
def appliances_electrical_demand(self) -> dict: def appliances_electrical_demand(self) -> dict:
""" """
Get appliances electrical demand in Wh Get appliances electrical demand in J
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
return self._appliances_electrical_demand return self._appliances_electrical_demand
@appliances_electrical_demand.setter @appliances_electrical_demand.setter
def appliances_electrical_demand(self, value): def appliances_electrical_demand(self, value):
""" """
Set appliances electrical demand in Wh Set appliances electrical demand in J
:param value: dict{DataFrame(float)} :param value: dict{[float]}
""" """
self._appliances_electrical_demand = value self._appliances_electrical_demand = value
@property @property
def domestic_hot_water_heat_demand(self) -> dict: def domestic_hot_water_heat_demand(self) -> dict:
""" """
Get domestic hot water heat demand in Wh Get domestic hot water heat demand in J
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
return self._domestic_hot_water_heat_demand return self._domestic_hot_water_heat_demand
@domestic_hot_water_heat_demand.setter @domestic_hot_water_heat_demand.setter
def domestic_hot_water_heat_demand(self, value): def domestic_hot_water_heat_demand(self, value):
""" """
Set domestic hot water heat demand in Wh Set domestic hot water heat demand in J
:param value: dict{DataFrame(float)} :param value: dict{[float]}
""" """
self._domestic_hot_water_heat_demand = value self._domestic_hot_water_heat_demand = value
@property
def lighting_peak_load(self) -> Union[None, dict]:
"""
Get lighting peak load in W
:return: dict{[float]}
"""
results = {}
peak_lighting = 0
peak = 0
for thermal_zone in self.thermal_zones_from_internal_zones:
lighting = thermal_zone.lighting
for schedule in lighting.schedules:
peak = max(schedule.values) * lighting.density * thermal_zone.total_floor_area
if peak > peak_lighting:
peak_lighting = peak
results[cte.MONTH] = [peak for _ in range(0, 12)]
results[cte.YEAR] = [peak]
return results
@property
def appliances_peak_load(self) -> Union[None, dict]:
"""
Get appliances peak load in W
:return: dict{[float]}
"""
results = {}
peak_appliances = 0
peak = 0
for thermal_zone in self.thermal_zones_from_internal_zones:
appliances = thermal_zone.appliances
for schedule in appliances.schedules:
peak = max(schedule.values) * appliances.density * thermal_zone.total_floor_area
if peak > peak_appliances:
peak_appliances = peak
results[cte.MONTH] = [peak for _ in range(0, 12)]
results[cte.YEAR] = [peak]
return results
@property @property
def heating_peak_load(self) -> Union[None, dict]: def heating_peak_load(self) -> Union[None, dict]:
""" """
Get heating peak load in W Get heating peak load in W
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
results = {} results = {}
if cte.HOUR in self.heating: if cte.HOUR in self.heating_demand:
monthly_values = PeakLoads().\ monthly_values = PeakLoads().peak_loads_from_hourly(self.heating_demand[cte.HOUR])
peak_loads_from_hourly(self.heating[cte.HOUR][next(iter(self.heating[cte.HOUR]))])
else: else:
monthly_values = PeakLoads(self).heating_peak_loads_from_methodology monthly_values = PeakLoads(self).heating_peak_loads_from_methodology
if monthly_values is None: if monthly_values is None:
return None return None
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=[cte.HEATING_PEAK_LOAD]) results[cte.MONTH] = [x / cte.WATTS_HOUR_TO_JULES for x in monthly_values]
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=[cte.HEATING_PEAK_LOAD]) results[cte.YEAR] = [max(monthly_values) / cte.WATTS_HOUR_TO_JULES]
return results return results
@property @property
def cooling_peak_load(self) -> Union[None, dict]: def cooling_peak_load(self) -> Union[None, dict]:
""" """
Get cooling peak load in W Get cooling peak load in W
:return: dict{DataFrame(float)} :return: dict{[float]}
""" """
results = {} results = {}
if cte.HOUR in self.cooling: if cte.HOUR in self.cooling_demand:
monthly_values = PeakLoads().peak_loads_from_hourly(self.cooling[cte.HOUR][next(iter(self.cooling[cte.HOUR]))]) monthly_values = PeakLoads().peak_loads_from_hourly(self.cooling_demand[cte.HOUR])
else: else:
monthly_values = PeakLoads(self).cooling_peak_loads_from_methodology monthly_values = PeakLoads(self).cooling_peak_loads_from_methodology
if monthly_values is None: if monthly_values is None:
return None return None
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=[cte.COOLING_PEAK_LOAD]) results[cte.MONTH] = [x / cte.WATTS_HOUR_TO_JULES for x in monthly_values]
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=[cte.COOLING_PEAK_LOAD]) results[cte.YEAR] = [max(monthly_values) / cte.WATTS_HOUR_TO_JULES]
return results
@property
def domestic_hot_water_peak_load(self) -> Union[None, dict]:
"""
Get cooling peak load in W
:return: dict{[float]}
"""
results = {}
monthly_values = None
if cte.HOUR in self.domestic_hot_water_heat_demand:
monthly_values = PeakLoads().peak_loads_from_hourly(self.domestic_hot_water_heat_demand[cte.HOUR])
if monthly_values is None:
return None
results[cte.MONTH] = [x / cte.WATTS_HOUR_TO_JULES for x in monthly_values]
results[cte.YEAR] = [max(monthly_values) / cte.WATTS_HOUR_TO_JULES]
return results return results
@property @property
@ -414,7 +500,7 @@ class Building(CityObject):
if self._eave_height is None: if self._eave_height is None:
self._eave_height = 0 self._eave_height = 0
for wall in self.walls: for wall in self.walls:
self._eave_height = max(self._eave_height, wall.upper_corner[2]) self._eave_height = max(self._eave_height, wall.upper_corner[2]) - self.simplified_polyhedron.min_z
return self._eave_height return self._eave_height
@property @property
@ -488,10 +574,7 @@ class Building(CityObject):
""" """
Add a new alias for the building Add a new alias for the building
""" """
if self._aliases is None: self._aliases.append(value)
self._aliases = [value]
else:
self._aliases.append(value)
if self.city is not None: if self.city is not None:
self.city.add_building_alias(self, value) self.city.add_building_alias(self, value)
@ -558,12 +641,12 @@ class Building(CityObject):
@property @property
def heating_consumption(self): def heating_consumption(self):
""" """
Get energy consumption for heating according to the heating system installed in Wh Get energy consumption for heating according to the heating system installed in J
return: dict return: dict
""" """
if len(self._heating_consumption) == 0: if len(self._heating_consumption) == 0:
for heating_demand_key in self.heating: for heating_demand_key in self.heating_demand:
demand = self.heating[heating_demand_key][cte.INSEL_MEB] demand = self.heating_demand[heating_demand_key]
consumption_type = cte.HEATING consumption_type = cte.HEATING
final_energy_consumed = self._calculate_consumption(consumption_type, demand) final_energy_consumed = self._calculate_consumption(consumption_type, demand)
if final_energy_consumed is None: if final_energy_consumed is None:
@ -574,12 +657,12 @@ class Building(CityObject):
@property @property
def cooling_consumption(self): def cooling_consumption(self):
""" """
Get energy consumption for cooling according to the cooling system installed in Wh Get energy consumption for cooling according to the cooling system installed in J
return: dict return: dict
""" """
if len(self._cooling_consumption) == 0: if len(self._cooling_consumption) == 0:
for cooling_demand_key in self.cooling: for cooling_demand_key in self.cooling_demand:
demand = self.cooling[cooling_demand_key][cte.INSEL_MEB] demand = self.cooling_demand[cooling_demand_key]
consumption_type = cte.COOLING consumption_type = cte.COOLING
final_energy_consumed = self._calculate_consumption(consumption_type, demand) final_energy_consumed = self._calculate_consumption(consumption_type, demand)
if final_energy_consumed is None: if final_energy_consumed is None:
@ -590,12 +673,12 @@ class Building(CityObject):
@property @property
def domestic_hot_water_consumption(self): def domestic_hot_water_consumption(self):
""" """
Get energy consumption for domestic according to the domestic hot water system installed in Wh Get energy consumption for domestic according to the domestic hot water system installed in J
return: dict return: dict
""" """
if len(self._domestic_hot_water_consumption) == 0: if len(self._domestic_hot_water_consumption) == 0:
for domestic_hot_water_demand_key in self.domestic_hot_water_heat_demand: for domestic_hot_water_demand_key in self.domestic_hot_water_heat_demand:
demand = self.domestic_hot_water_heat_demand[domestic_hot_water_demand_key][cte.INSEL_MEB] demand = self.domestic_hot_water_heat_demand[domestic_hot_water_demand_key]
consumption_type = cte.DOMESTIC_HOT_WATER consumption_type = cte.DOMESTIC_HOT_WATER
final_energy_consumed = self._calculate_consumption(consumption_type, demand) final_energy_consumed = self._calculate_consumption(consumption_type, demand)
if final_energy_consumed is None: if final_energy_consumed is None:
@ -606,75 +689,93 @@ class Building(CityObject):
def _calculate_working_hours(self): def _calculate_working_hours(self):
_working_hours = {} _working_hours = {}
for internal_zone in self.internal_zones: for internal_zone in self.internal_zones:
for thermal_zone in internal_zone.thermal_zones: for thermal_zone in internal_zone.thermal_zones_from_internal_zones:
_working_hours_per_thermal_zone = {} _working_hours_per_thermal_zone = {}
for schedule in thermal_zone.thermal_control.hvac_availability_schedules: for schedule in thermal_zone.thermal_control.hvac_availability_schedules:
_working_hours_per_schedule = [0] * len(schedule.values) _working_hours_per_schedule = [0] * len(schedule.values)
for i, value in enumerate(schedule.values): for i, value in enumerate(schedule.values):
if value > 0: if value > 0:
_working_hours_per_schedule[i] = 1 _working_hours_per_schedule[i] = 1
for day_type in schedule.day_types: for day_type in schedule.day_types:
_working_hours_per_thermal_zone[day_type] = _working_hours_per_schedule _working_hours_per_thermal_zone[day_type] = _working_hours_per_schedule
if len(_working_hours) == 0: if len(_working_hours) == 0:
_working_hours = _working_hours_per_thermal_zone _working_hours = _working_hours_per_thermal_zone
else: else:
for key in _working_hours.keys(): for key, item in _working_hours.items():
saved_values = _working_hours_per_thermal_zone[key] saved_values = _working_hours_per_thermal_zone[key]
for i, value in enumerate(_working_hours[key]): for i, value in enumerate(item):
if saved_values[i] == 1: _working_hours[key][i] = max(_working_hours[key][i], saved_values[i])
_working_hours[key][i] = 1
_total_hours = 0 working_hours = {}
for key in _working_hours: values_months = []
_total_hours += _working_hours[key] * cte.DAYS_A_YEAR[key] for month in cte.WEEK_DAYS_A_MONTH.keys():
return _total_hours _total_hours_month = 0
for key in _working_hours:
hours = sum(_working_hours[key])
_total_hours_month += hours * cte.WEEK_DAYS_A_MONTH[month][key]
values_months.append(_total_hours_month)
working_hours[cte.MONTH] = values_months
working_hours[cte.YEAR] = sum(working_hours[cte.MONTH])
return working_hours
@property @property
def distribution_systems_electrical_consumption(self): def distribution_systems_electrical_consumption(self):
""" """
Get total electricity consumption for distribution and emission systems in Wh Get total electricity consumption for distribution and emission systems in J
return: dict return: dict
""" """
if len(self._distribution_systems_electrical_consumption) == 0: _distribution_systems_electrical_consumption = {}
_peak_load = self.heating_peak_load[cte.YEAR][cte.HEATING_PEAK_LOAD][0] if len(self._distribution_systems_electrical_consumption) != 0:
_peak_load_type = cte.HEATING return self._distribution_systems_electrical_consumption
if _peak_load < self.cooling_peak_load[cte.YEAR][cte.COOLING_PEAK_LOAD][0]: _peak_load = self.heating_peak_load[cte.YEAR][0]
_peak_load = self.cooling_peak_load[cte.YEAR][cte.COOLING_PEAK_LOAD][0] _peak_load_type = cte.HEATING
_peak_load_type = cte.COOLING if _peak_load < self.cooling_peak_load[cte.YEAR][0]:
_peak_load = self.cooling_peak_load[cte.YEAR][0]
_peak_load_type = cte.COOLING
_consumption_fix_flow = 0 _working_hours = self._calculate_working_hours()
if self.energy_systems is None: _consumption_fix_flow = 0
return self._distribution_systems_electrical_consumption if self.energy_systems is None:
for energy_system in self.energy_systems: return self._distribution_systems_electrical_consumption
emission_system = energy_system.emission_system.generic_emission_system for energy_system in self.energy_systems:
parasitic_energy_consumption = 0 distribution_systems = energy_system.distribution_systems
if emission_system is not None: if distribution_systems is not None:
parasitic_energy_consumption = emission_system.parasitic_energy_consumption for distribution_system in distribution_systems:
distribution_system = energy_system.distribution_system.generic_distribution_system emission_systems = distribution_system.emission_systems
consumption_variable_flow = distribution_system.distribution_consumption_variable_flow parasitic_energy_consumption = 0
for demand_type in energy_system.demand_types: if emission_systems is not None:
if demand_type.lower() == cte.HEATING: for emission_system in emission_systems:
if _peak_load_type == cte.HEATING: parasitic_energy_consumption += emission_system.parasitic_energy_consumption
_consumption_fix_flow = distribution_system.distribution_consumption_fix_flow consumption_variable_flow = distribution_system.distribution_consumption_variable_flow
for heating_demand_key in self.heating: for demand_type in energy_system.demand_types:
_consumption = [0]*len(self.heating) if demand_type.lower() == cte.HEATING.lower():
_demand = self.heating[heating_demand_key][cte.INSEL_MEB] if _peak_load_type == cte.HEATING.lower():
for i in range(0, len(_consumption)): _consumption_fix_flow = distribution_system.distribution_consumption_fix_flow
_consumption[i] += (parasitic_energy_consumption + consumption_variable_flow) * _demand[i] for heating_demand_key in self.heating_demand:
self._distribution_systems_electrical_consumption[heating_demand_key] = _consumption _consumption = [0]*len(self.heating_demand[heating_demand_key])
if demand_type.lower() == cte.COOLING: _demand = self.heating_demand[heating_demand_key]
if _peak_load_type == cte.COOLING: for i, _ in enumerate(_consumption):
_consumption_fix_flow = distribution_system.distribution_consumption_fix_flow _consumption[i] += (parasitic_energy_consumption + consumption_variable_flow) * _demand[i]
for demand_key in self.cooling: self._distribution_systems_electrical_consumption[heating_demand_key] = _consumption
_consumption = self._distribution_systems_electrical_consumption[demand_key] if demand_type.lower() == cte.COOLING.lower():
_demand = self.cooling[demand_key][cte.INSEL_MEB] if _peak_load_type == cte.COOLING.lower():
for i in range(0, len(_consumption)): _consumption_fix_flow = distribution_system.distribution_consumption_fix_flow
_consumption[i] += (parasitic_energy_consumption + consumption_variable_flow) * _demand[i] for demand_key in self.cooling_demand:
self._distribution_systems_electrical_consumption[demand_key] = _consumption _consumption = self._distribution_systems_electrical_consumption[demand_key]
_demand = self.cooling_demand[demand_key]
for i, _ in enumerate(_consumption):
_consumption[i] += (parasitic_energy_consumption + consumption_variable_flow) * _demand[i]
self._distribution_systems_electrical_consumption[demand_key] = _consumption
for key, item in self._distribution_systems_electrical_consumption.items():
for i in range(0, len(item)):
_working_hours_value = _working_hours[key]
if len(item) == 12:
_working_hours_value = _working_hours[key][i]
self._distribution_systems_electrical_consumption[key][i] += (
_peak_load * _consumption_fix_flow * _working_hours_value * cte.WATTS_HOUR_TO_JULES
)
for key in self._distribution_systems_electrical_consumption:
for i in range(0, len(self._distribution_systems_electrical_consumption[key])):
self._distribution_systems_electrical_consumption[key][i] += _peak_load * _consumption_fix_flow \
* self._calculate_working_hours()
return self._distribution_systems_electrical_consumption return self._distribution_systems_electrical_consumption
def _calculate_consumption(self, consumption_type, demand): def _calculate_consumption(self, consumption_type, demand):
@ -683,15 +784,21 @@ class Building(CityObject):
if self.energy_systems is None: if self.energy_systems is None:
return None return None
for energy_system in self.energy_systems: for energy_system in self.energy_systems:
generation_systems = energy_system.generation_systems
for demand_type in energy_system.demand_types: for demand_type in energy_system.demand_types:
if demand_type.lower() == consumption_type.lower(): if demand_type.lower() == consumption_type.lower():
if consumption_type == cte.HEATING or consumption_type == cte.DOMESTIC_HOT_WATER: if consumption_type in (cte.HEATING, cte.DOMESTIC_HOT_WATER):
coefficient_of_performance = energy_system.generation_system.generic_generation_system.heat_efficiency for generation_system in generation_systems:
if generation_system.heat_efficiency is not None:
coefficient_of_performance = float(generation_system.heat_efficiency)
elif consumption_type == cte.COOLING: elif consumption_type == cte.COOLING:
coefficient_of_performance = energy_system.generation_system.generic_generation_system.cooling_efficiency for generation_system in generation_systems:
if generation_system.cooling_efficiency is not None:
coefficient_of_performance = float(generation_system.cooling_efficiency)
elif consumption_type == cte.ELECTRICITY: elif consumption_type == cte.ELECTRICITY:
coefficient_of_performance = \ for generation_system in generation_systems:
energy_system.generation_system.generic_generation_system.electricity_efficiency if generation_system.electricity_efficiency is not None:
coefficient_of_performance = float(generation_system.electricity_efficiency)
if coefficient_of_performance == 0: if coefficient_of_performance == 0:
values = [0]*len(demand) values = [0]*len(demand)
final_energy_consumed = values final_energy_consumed = values
@ -704,11 +811,9 @@ class Building(CityObject):
@property @property
def onsite_electrical_production(self): def onsite_electrical_production(self):
""" """
Get total electricity produced onsite in Wh Get total electricity produced onsite in J
return: dict return: dict
""" """
# Add other systems whenever new ones appear
orientation_losses_factor = {cte.MONTH: {'north': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], orientation_losses_factor = {cte.MONTH: {'north': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'east': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'east': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'south': [2.137931, 1.645503, 1.320946, 1.107817, 0.993213, 0.945175, 'south': [2.137931, 1.645503, 1.320946, 1.107817, 0.993213, 0.945175,
@ -719,18 +824,96 @@ class Building(CityObject):
'south': [1.212544], 'south': [1.212544],
'west': [0]} 'west': [0]}
} }
# Add other systems whenever new ones appear
if self.energy_systems is None: if self.energy_systems is None:
return self._onsite_electrical_production return self._onsite_electrical_production
for energy_system in self.energy_systems: for energy_system in self.energy_systems:
if energy_system.generation_system.generic_generation_system.type == cte.PHOTOVOLTAIC: for generation_system in energy_system.generation_systems:
_efficiency = energy_system.generation_system.generic_generation_system.electricity_efficiency if generation_system.system_type == cte.PHOTOVOLTAIC:
self._onsite_electrical_production = {} if generation_system.electricity_efficiency is not None:
for _key in self.roofs[0].global_irradiance.keys(): _efficiency = float(generation_system.electricity_efficiency)
_results = [0 for _ in range(0, len(self.roofs[0].global_irradiance[_key][cte.SRA]))] else:
for surface in self.roofs: _efficiency = 0
_results = [x + y * _efficiency * surface.perimeter_area self._onsite_electrical_production = {}
* surface.solar_collectors_area_reduction_factor * z for _key in self.roofs[0].global_irradiance.keys():
for x, y, z in zip(_results, surface.global_irradiance[_key][cte.SRA], _results = [0 for _ in range(0, len(self.roofs[0].global_irradiance[_key]))]
orientation_losses_factor[cte.MONTH]['south'])] for surface in self.roofs:
self._onsite_electrical_production[_key] = _results if _key in orientation_losses_factor:
_results = [x + y * _efficiency * surface.perimeter_area
* surface.solar_collectors_area_reduction_factor * z
for x, y, z in zip(_results, surface.global_irradiance[_key],
orientation_losses_factor[_key]['south'])]
self._onsite_electrical_production[_key] = _results
return self._onsite_electrical_production return self._onsite_electrical_production
@property
def lower_corner(self):
"""
Get building lower corner.
"""
return [self._min_x, self._min_y, self._min_z]
@property
def upper_corner(self):
"""
Get building upper corner.
"""
return [self._max_x, self._max_y, self._max_z]
@property
def energy_consumption_breakdown(self) -> dict:
"""
Get energy consumption of different sectors
return: dict
"""
fuel_breakdown = {cte.ELECTRICITY: {cte.LIGHTING: self.lighting_electrical_demand[cte.YEAR][0],
cte.APPLIANCES: self.appliances_electrical_demand[cte.YEAR][0]}}
energy_systems = self.energy_systems
for energy_system in energy_systems:
demand_types = energy_system.demand_types
generation_systems = energy_system.generation_systems
for demand_type in demand_types:
for generation_system in generation_systems:
if generation_system.system_type != cte.PHOTOVOLTAIC:
if generation_system.fuel_type not in fuel_breakdown:
fuel_breakdown[generation_system.fuel_type] = {}
if demand_type in generation_system.energy_consumption:
fuel_breakdown[f'{generation_system.fuel_type}'][f'{demand_type}'] = (
generation_system.energy_consumption)[f'{demand_type}'][cte.YEAR][0]
storage_systems = generation_system.energy_storage_systems
if storage_systems:
for storage_system in storage_systems:
if storage_system.type_energy_stored == 'thermal' and storage_system.heating_coil_energy_consumption:
fuel_breakdown[cte.ELECTRICITY][f'{demand_type}'] += storage_system.heating_coil_energy_consumption[cte.YEAR][0]
#TODO: When simulation models of all energy system archetypes are created, this part can be removed
heating_fuels = []
dhw_fuels = []
for energy_system in self.energy_systems:
if cte.HEATING in energy_system.demand_types:
for generation_system in energy_system.generation_systems:
heating_fuels.append(generation_system.fuel_type)
if cte.DOMESTIC_HOT_WATER in energy_system.demand_types:
for generation_system in energy_system.generation_systems:
dhw_fuels.append(generation_system.fuel_type)
for key in fuel_breakdown:
if key == cte.ELECTRICITY and cte.COOLING not in fuel_breakdown[key]:
for energy_system in energy_systems:
if cte.COOLING in energy_system.demand_types and cte.COOLING not in fuel_breakdown[key]:
for generation_system in energy_system.generation_systems:
fuel_breakdown[generation_system.fuel_type][cte.COOLING] = self.cooling_consumption[cte.YEAR][0]
for fuel in heating_fuels:
if cte.HEATING not in fuel_breakdown[fuel]:
for energy_system in energy_systems:
if cte.HEATING in energy_system.demand_types:
for generation_system in energy_system.generation_systems:
fuel_breakdown[generation_system.fuel_type][cte.HEATING] = self.heating_consumption[cte.YEAR][0]
for fuel in dhw_fuels:
if cte.DOMESTIC_HOT_WATER not in fuel_breakdown[fuel]:
for energy_system in energy_systems:
if cte.DOMESTIC_HOT_WATER in energy_system.demand_types:
for generation_system in energy_system.generation_systems:
fuel_breakdown[generation_system.fuel_type][cte.DOMESTIC_HOT_WATER] = self.domestic_hot_water_consumption[cte.YEAR][0]
self._fuel_consumption_breakdown = fuel_breakdown
return self._fuel_consumption_breakdown

View File

@ -0,0 +1,151 @@
"""
Construction thermal parameters
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2023 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from hub.city_model_structure.building_demand.layer import Layer
class Construction:
"""
Construction class
"""
def __init__(self):
self._type = None
self._name = None
self._layers = None
self._window_ratio = None
self._window_frame_ratio = None
self._window_g_value = None
self._window_overall_u_value = None
self._window_type = None
@property
def type(self):
"""
Get construction type
:return: str
"""
return self._type
@type.setter
def type(self, value):
"""
Set construction type
:param value: str
"""
self._type = value
@property
def name(self):
"""
Get construction name
:return: str
"""
return self._name
@name.setter
def name(self, value):
"""
Set construction name
:param value: str
"""
self._name = value
@property
def layers(self) -> [Layer]:
"""
Get layers
:return: [layer]
"""
return self._layers
@layers.setter
def layers(self, value):
"""
Set layers
:param value: [layer]
"""
self._layers = value
@property
def window_ratio(self):
"""
Get window ratio
:return: dict
"""
return self._window_ratio
@window_ratio.setter
def window_ratio(self, value):
"""
Set window ratio
:param value: dict
"""
self._window_ratio = value
@property
def window_frame_ratio(self):
"""
Get window frame ratio
:return: float
"""
return self._window_frame_ratio
@window_frame_ratio.setter
def window_frame_ratio(self, value):
"""
Set window frame ratio
:param value: float
"""
self._window_frame_ratio = value
@property
def window_g_value(self):
"""
Get transparent surface g-value
:return: float
"""
return self._window_g_value
@window_g_value.setter
def window_g_value(self, value):
"""
Set transparent surface g-value
:param value: float
"""
self._window_g_value = value
@property
def window_overall_u_value(self):
"""
Get transparent surface overall U-value in W/m2K
:return: float
"""
return self._window_overall_u_value
@window_overall_u_value.setter
def window_overall_u_value(self, value):
"""
Set transparent surface overall U-value in W/m2K
:param value: float
"""
self._window_overall_u_value = value
@property
def window_type(self):
"""
Get transparent surface type, 'window' or 'skylight'
:return: str
"""
return self._window_type
@window_type.setter
def window_type(self, value):
"""
Set transparent surface type, 'window' or 'skylight'
:return: str
"""
self._window_type = value

View File

@ -8,24 +8,25 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
import uuid import uuid
from typing import Union, List from typing import Union, List
from hub.city_model_structure.building_demand.usage import Usage from hub.city_model_structure.building_demand.usage import Usage
from hub.city_model_structure.building_demand.thermal_archetype import ThermalArchetype
from hub.city_model_structure.building_demand.thermal_zone import ThermalZone from hub.city_model_structure.building_demand.thermal_zone import ThermalZone
from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary
from hub.city_model_structure.attributes.polyhedron import Polyhedron from hub.city_model_structure.attributes.polyhedron import Polyhedron
from hub.city_model_structure.energy_systems.hvac_system import HvacSystem
class InternalZone: class InternalZone:
""" """
InternalZone class InternalZone class
""" """
def __init__(self, surfaces, area): def __init__(self, surfaces, area, volume):
self._surfaces = surfaces self._surfaces = surfaces
self._id = None self._id = None
self._geometry = None self._geometry = None
self._volume = None self._volume = volume
self._area = area self._area = area
self._thermal_zones = None self._thermal_zones_from_internal_zones = None
self._usages = None self._usages = None
self._hvac_system = None self._thermal_archetype = None
@property @property
def id(self): def id(self):
@ -64,7 +65,7 @@ class InternalZone:
Get internal zone volume in cubic meters Get internal zone volume in cubic meters
:return: float :return: float
""" """
return self.geometry.volume return self._volume
@property @property
def area(self): def area(self):
@ -74,10 +75,18 @@ class InternalZone:
""" """
return self._area return self._area
@property
def mean_height(self):
"""
Get internal zone mean height in meters
:return: float
"""
return self.volume / self.area
@property @property
def usages(self) -> [Usage]: def usages(self) -> [Usage]:
""" """
Get internal zone usage zones Get usage archetypes
:return: [Usage] :return: [Usage]
""" """
return self._usages return self._usages
@ -85,39 +94,59 @@ class InternalZone:
@usages.setter @usages.setter
def usages(self, value): def usages(self, value):
""" """
Set internal zone usage zones Set usage archetypes
:param value: [Usage] :param value: [Usage]
""" """
self._usages = value self._usages = value
@property @property
def hvac_system(self) -> Union[None, HvacSystem]: def thermal_archetype(self) -> ThermalArchetype:
""" """
Get HVAC system installed for this thermal zone Get thermal archetype parameters
:return: None or HvacSystem :return: ThermalArchetype
""" """
return self._hvac_system return self._thermal_archetype
@hvac_system.setter @thermal_archetype.setter
def hvac_system(self, value): def thermal_archetype(self, value):
""" """
Set HVAC system installed for this thermal zone Set thermal archetype parameters
:param value: HvacSystem :param value: ThermalArchetype
""" """
self._hvac_system = value self._thermal_archetype = value
@property @property
def thermal_zones(self) -> Union[None, List[ThermalZone]]: def thermal_zones_from_internal_zones(self) -> Union[None, List[ThermalZone]]:
""" """
Get building thermal zones Get building thermal zones as one per internal zone
:return: [ThermalZone] :return: [ThermalZone]
""" """
return self._thermal_zones _thermal_boundaries = []
for surface in self.surfaces:
if surface.holes_polygons is None:
windows_areas = None
else:
windows_areas = []
for hole in surface.holes_polygons:
windows_areas.append(hole.area)
_thermal_boundary = ThermalBoundary(surface, surface.solid_polygon.area, windows_areas)
surface.associated_thermal_boundaries = [_thermal_boundary]
_thermal_boundaries.append(_thermal_boundary)
if self.thermal_archetype is None:
return None # there are no archetype
_number_of_storeys = int(self.volume / self.area / self.thermal_archetype.average_storey_height)
if _number_of_storeys == 0:
_number_of_storeys = 1
_thermal_zone = ThermalZone(_thermal_boundaries, self, self.volume, self.area, _number_of_storeys)
for thermal_boundary in _thermal_zone.thermal_boundaries:
thermal_boundary.thermal_zones = [_thermal_zone]
self._thermal_zones_from_internal_zones = [_thermal_zone]
return self._thermal_zones_from_internal_zones
@thermal_zones.setter @thermal_zones_from_internal_zones.setter
def thermal_zones(self, value): def thermal_zones_from_internal_zones(self, value):
""" """
Set city object thermal zones Set city object thermal zones as one per internal zone
:param value: [ThermalZone] :param value: [ThermalZone]
""" """
self._thermal_zones = value self._thermal_zones_from_internal_zones = value

View File

@ -4,9 +4,9 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
""" """
import uuid import uuid
from typing import Union from typing import Union
from hub.city_model_structure.building_demand.material import Material
class Layer: class Layer:
@ -14,9 +14,17 @@ class Layer:
Layer class Layer class
""" """
def __init__(self): def __init__(self):
self._material = None
self._thickness = None self._thickness = None
self._id = None self._id = None
self._material_name = None
self._conductivity = None
self._specific_heat = None
self._density = None
self._solar_absorptance = None
self._thermal_absorptance = None
self._visible_absorptance = None
self._no_mass = False
self._thermal_resistance = None
@property @property
def id(self): def id(self):
@ -28,22 +36,6 @@ class Layer:
self._id = uuid.uuid4() self._id = uuid.uuid4()
return self._id return self._id
@property
def material(self) -> Material:
"""
Get layer material
:return: Material
"""
return self._material
@material.setter
def material(self, value):
"""
Set layer material
:param value: Material
"""
self._material = value
@property @property
def thickness(self) -> Union[None, float]: def thickness(self) -> Union[None, float]:
""" """
@ -60,3 +52,155 @@ class Layer:
""" """
if value is not None: if value is not None:
self._thickness = float(value) self._thickness = float(value)
@property
def material_name(self):
"""
Get material name
:return: str
"""
return self._material_name
@material_name.setter
def material_name(self, value):
"""
Set material name
:param value: string
"""
self._material_name = str(value)
@property
def conductivity(self) -> Union[None, float]:
"""
Get material conductivity in W/mK
:return: None or float
"""
return self._conductivity
@conductivity.setter
def conductivity(self, value):
"""
Set material conductivity in W/mK
:param value: float
"""
if value is not None:
self._conductivity = float(value)
@property
def specific_heat(self) -> Union[None, float]:
"""
Get material conductivity in J/kgK
:return: None or float
"""
return self._specific_heat
@specific_heat.setter
def specific_heat(self, value):
"""
Get material conductivity in J/kgK
:param value: float
"""
if value is not None:
self._specific_heat = float(value)
@property
def density(self) -> Union[None, float]:
"""
Get material density in kg/m3
:return: None or float
"""
return self._density
@density.setter
def density(self, value):
"""
Set material density
:param value: float
"""
if value is not None:
self._density = float(value)
@property
def solar_absorptance(self) -> Union[None, float]:
"""
Get material solar absorptance
:return: None or float
"""
return self._solar_absorptance
@solar_absorptance.setter
def solar_absorptance(self, value):
"""
Set material solar absorptance
:param value: float
"""
if value is not None:
self._solar_absorptance = float(value)
@property
def thermal_absorptance(self) -> Union[None, float]:
"""
Get material thermal absorptance
:return: None or float
"""
return self._thermal_absorptance
@thermal_absorptance.setter
def thermal_absorptance(self, value):
"""
Set material thermal absorptance
:param value: float
"""
if value is not None:
self._thermal_absorptance = float(value)
@property
def visible_absorptance(self) -> Union[None, float]:
"""
Get material visible absorptance
:return: None or float
"""
return self._visible_absorptance
@visible_absorptance.setter
def visible_absorptance(self, value):
"""
Set material visible absorptance
:param value: float
"""
if value is not None:
self._visible_absorptance = float(value)
@property
def no_mass(self) -> Union[None, bool]:
"""
Get material no mass flag
:return: None or Boolean
"""
return self._no_mass
@no_mass.setter
def no_mass(self, value):
"""
Set material no mass flag
:param value: Boolean
"""
if value is not None:
self._no_mass = value
@property
def thermal_resistance(self) -> Union[None, float]:
"""
Get material thermal resistance in m2K/W
:return: None or float
"""
return self._thermal_resistance
@thermal_resistance.setter
def thermal_resistance(self, value):
"""
Set material thermal resistance in m2K/W
:param value: float
"""
if value is not None:
self._thermal_resistance = float(value)

View File

@ -1,193 +0,0 @@
"""
Material module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from typing import Union
class Material:
"""
Material class
"""
def __init__(self):
self._id = None
self._name = None
self._conductivity = None
self._specific_heat = None
self._density = None
self._solar_absorptance = None
self._thermal_absorptance = None
self._visible_absorptance = None
self._no_mass = False
self._thermal_resistance = None
@property
def id(self):
"""
Get material id
:return: str
"""
return self._id
@id.setter
def id(self, value):
"""
Set material id
:param value: str
"""
self._id = value
@property
def name(self):
"""
Get material name
:return: str
"""
return self._name
@name.setter
def name(self, value):
"""
Set material name
:param value: string
"""
self._name = str(value)
@property
def conductivity(self) -> Union[None, float]:
"""
Get material conductivity in W/mK
:return: None or float
"""
return self._conductivity
@conductivity.setter
def conductivity(self, value):
"""
Set material conductivity in W/mK
:param value: float
"""
if value is not None:
self._conductivity = float(value)
@property
def specific_heat(self) -> Union[None, float]:
"""
Get material conductivity in J/kgK
:return: None or float
"""
return self._specific_heat
@specific_heat.setter
def specific_heat(self, value):
"""
Get material conductivity in J/kgK
:param value: float
"""
if value is not None:
self._specific_heat = float(value)
@property
def density(self) -> Union[None, float]:
"""
Get material density in kg/m3
:return: None or float
"""
return self._density
@density.setter
def density(self, value):
"""
Set material density
:param value: float
"""
if value is not None:
self._density = float(value)
@property
def solar_absorptance(self) -> Union[None, float]:
"""
Get material solar absorptance
:return: None or float
"""
return self._solar_absorptance
@solar_absorptance.setter
def solar_absorptance(self, value):
"""
Set material solar absorptance
:param value: float
"""
if value is not None:
self._solar_absorptance = float(value)
@property
def thermal_absorptance(self) -> Union[None, float]:
"""
Get material thermal absorptance
:return: None or float
"""
return self._thermal_absorptance
@thermal_absorptance.setter
def thermal_absorptance(self, value):
"""
Set material thermal absorptance
:param value: float
"""
if value is not None:
self._thermal_absorptance = float(value)
@property
def visible_absorptance(self) -> Union[None, float]:
"""
Get material visible absorptance
:return: None or float
"""
return self._visible_absorptance
@visible_absorptance.setter
def visible_absorptance(self, value):
"""
Set material visible absorptance
:param value: float
"""
if value is not None:
self._visible_absorptance = float(value)
@property
def no_mass(self) -> Union[None, bool]:
"""
Get material no mass flag
:return: None or Boolean
"""
return self._no_mass
@no_mass.setter
def no_mass(self, value):
"""
Set material no mass flag
:param value: Boolean
"""
if value is not None:
self._no_mass = value
@property
def thermal_resistance(self) -> Union[None, float]:
"""
Get material thermal resistance in m2K/W
:return: None or float
"""
return self._thermal_resistance
@thermal_resistance.setter
def thermal_resistance(self, value):
"""
Set material thermal resistance in m2K/W
:param value: float
"""
if value is not None:
self._thermal_resistance = float(value)

View File

@ -90,7 +90,9 @@ class Storey:
:return: ThermalZone :return: ThermalZone
""" """
if self._thermal_zone is None: if self._thermal_zone is None:
self._thermal_zone = ThermalZone(self.thermal_boundaries, self._internal_zone, self.volume, self.floor_area) _number_of_storeys = 1
self._thermal_zone = ThermalZone(self.thermal_boundaries, self._internal_zone,
self.volume, self.floor_area, _number_of_storeys)
return self._thermal_zone return self._thermal_zone
@property @property

View File

@ -10,14 +10,15 @@ from __future__ import annotations
import math import math
import uuid import uuid
import numpy as np
from typing import List, Union from typing import List, Union
import numpy as np
from hub.city_model_structure.attributes.polygon import Polygon from hub.city_model_structure.attributes.polygon import Polygon
from hub.city_model_structure.attributes.plane import Plane from hub.city_model_structure.attributes.plane import Plane
from hub.city_model_structure.attributes.point import Point from hub.city_model_structure.attributes.point import Point
from hub.city_model_structure.greenery.vegetation import Vegetation from hub.city_model_structure.greenery.vegetation import Vegetation
from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary from hub.city_model_structure.building_demand.thermal_boundary import ThermalBoundary
import hub.helpers.constants as cte import hub.helpers.constants as cte
from hub.helpers.configuration_helper import ConfigurationHelper
class Surface: class Surface:
@ -34,17 +35,19 @@ class Surface:
self._area = None self._area = None
self._lower_corner = None self._lower_corner = None
self._upper_corner = None self._upper_corner = None
self._global_irradiance = dict() self._global_irradiance = {}
self._perimeter_polygon = perimeter_polygon self._perimeter_polygon = perimeter_polygon
self._holes_polygons = holes_polygons self._holes_polygons = holes_polygons
self._solid_polygon = solid_polygon self._solid_polygon = solid_polygon
self._short_wave_reflectance = None self._short_wave_reflectance = None
self._long_wave_emittance = None self._long_wave_emittance = None
self._inverse = None self._inverse = None
self._associated_thermal_boundaries = [] self._associated_thermal_boundaries = None
self._vegetation = None self._vegetation = None
self._percentage_shared = None self._percentage_shared = None
self._solar_collectors_area_reduction_factor = None self._solar_collectors_area_reduction_factor = None
self._global_irradiance_tilted = {}
self._installed_solar_collector_area = None
@property @property
def name(self): def name(self):
@ -154,7 +157,6 @@ class Surface:
if self._inclination is None: if self._inclination is None:
self._inclination = np.arccos(self.perimeter_polygon.normal[2]) self._inclination = np.arccos(self.perimeter_polygon.normal[2])
return self._inclination return self._inclination
@property @property
def type(self): def type(self):
""" """
@ -178,16 +180,16 @@ class Surface:
@property @property
def global_irradiance(self) -> dict: def global_irradiance(self) -> dict:
""" """
Get global irradiance on surface in Wh/m2 Get global irradiance on surface in W/m2
:return: dict{DataFrame(float)} :return: dict
""" """
return self._global_irradiance return self._global_irradiance
@global_irradiance.setter @global_irradiance.setter
def global_irradiance(self, value): def global_irradiance(self, value):
""" """
Set global irradiance on surface in Wh/m2 Set global irradiance on surface in W/m2
:param value: dict{DataFrame(float)} :param value: dict
""" """
self._global_irradiance = value self._global_irradiance = value
@ -384,3 +386,35 @@ class Surface:
:param value: float :param value: float
""" """
self._solar_collectors_area_reduction_factor = value self._solar_collectors_area_reduction_factor = value
@property
def global_irradiance_tilted(self) -> dict:
"""
Get global irradiance on a tilted surface in W/m2
:return: dict
"""
return self._global_irradiance_tilted
@global_irradiance_tilted.setter
def global_irradiance_tilted(self, value):
"""
Set global irradiance on a tilted surface in W/m2
:param value: dict
"""
self._global_irradiance_tilted = value
@property
def installed_solar_collector_area(self):
"""
Get installed solar collector area in m2
:return: dict
"""
return self._installed_solar_collector_area
@installed_solar_collector_area.setter
def installed_solar_collector_area(self, value):
"""
Set installed solar collector area in m2
:return: dict
"""
self._installed_solar_collector_area = value

View File

@ -0,0 +1,168 @@
"""
Thermal archetype module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from hub.city_model_structure.building_demand.construction import Construction
class ThermalArchetype:
"""
ThermalArchetype class
"""
def __init__(self):
self._constructions = None
self._average_storey_height = None
self._thermal_capacity = None
self._extra_loses_due_to_thermal_bridges = None
self._indirect_heated_ratio = None
self._infiltration_rate_for_ventilation_system_off = None
self._infiltration_rate_for_ventilation_system_on = None
self._infiltration_rate_area_for_ventilation_system_off=None
self._infiltration_rate_area_for_ventilation_system_on=None
@property
def constructions(self) -> [Construction]:
"""
Get archetype constructions
:return: [Construction]
"""
return self._constructions
@constructions.setter
def constructions(self, value):
"""
Set archetype constructions
:param value: [Construction]
"""
self._constructions = value
@property
def average_storey_height(self):
"""
Get average storey height in m
:return: float
"""
return self._average_storey_height
@average_storey_height.setter
def average_storey_height(self, value):
"""
Set average storey height in m
:param value: float
"""
self._average_storey_height = value
@property
def thermal_capacity(self):
"""
Get thermal capacity in J/m3K
:return: float
"""
return self._thermal_capacity
@thermal_capacity.setter
def thermal_capacity(self, value):
"""
Set thermal capacity in J/m3K
:param value: float
"""
self._thermal_capacity = value
@property
def extra_loses_due_to_thermal_bridges(self):
"""
Get extra loses due to thermal bridges in W/m2K
:return: float
"""
return self._extra_loses_due_to_thermal_bridges
@extra_loses_due_to_thermal_bridges.setter
def extra_loses_due_to_thermal_bridges(self, value):
"""
Set extra loses due to thermal bridges in W/m2K
:param value: float
"""
self._extra_loses_due_to_thermal_bridges = value
@property
def indirect_heated_ratio(self):
"""
Get indirect heated area ratio
:return: float
"""
return self._indirect_heated_ratio
@indirect_heated_ratio.setter
def indirect_heated_ratio(self, value):
"""
Set indirect heated area ratio
:param value: float
"""
self._indirect_heated_ratio = value
@property
def infiltration_rate_for_ventilation_system_off(self):
"""
Get infiltration rate for ventilation system off in ACH
:return: float
"""
return self._infiltration_rate_for_ventilation_system_off
@infiltration_rate_for_ventilation_system_off.setter
def infiltration_rate_for_ventilation_system_off(self, value):
"""
Set infiltration rate for ventilation system off in ACH
:param value: float
"""
self._infiltration_rate_for_ventilation_system_off = value
@property
def infiltration_rate_for_ventilation_system_on(self):
"""
Get infiltration rate for ventilation system on in ACH
:return: float
"""
return self._infiltration_rate_for_ventilation_system_on
@infiltration_rate_for_ventilation_system_on.setter
def infiltration_rate_for_ventilation_system_on(self, value):
"""
Set infiltration rate for ventilation system on in ACH
:param value: float
"""
self._infiltration_rate_for_ventilation_system_on = value
@property
def infiltration_rate_area_for_ventilation_system_off(self):
"""
Get infiltration rate for ventilation system off in l/s/m2
:return: float
"""
return self._infiltration_rate_for_ventilation_system_off
@infiltration_rate_area_for_ventilation_system_off.setter
def infiltration_rate_area_for_ventilation_system_off(self, value):
"""
Set infiltration rate for ventilation system off in l/s/m2
:param value: float
"""
self._infiltration_rate_for_ventilation_system_off = value
@property
def infiltration_rate_area_for_ventilation_system_on(self):
"""
Get infiltration rate for ventilation system on in l/s/m2
:return: float
"""
return self._infiltration_rate_for_ventilation_system_on
@infiltration_rate_area_for_ventilation_system_on.setter
def infiltration_rate_area_for_ventilation_system_on(self, value):
"""
Set infiltration rate for ventilation system on in l/s/m2
:param value: float
"""
self._infiltration_rate_for_ventilation_system_on = value

View File

@ -7,7 +7,9 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
""" """
import uuid import uuid
import math
from typing import List, Union, TypeVar from typing import List, Union, TypeVar
import logging
from hub.helpers.configuration_helper import ConfigurationHelper as ch from hub.helpers.configuration_helper import ConfigurationHelper as ch
import hub.helpers.constants as cte import hub.helpers.constants as cte
from hub.city_model_structure.building_demand.layer import Layer from hub.city_model_structure.building_demand.layer import Layer
@ -35,8 +37,11 @@ class ThermalBoundary:
self._construction_name = None self._construction_name = None
self._thickness = None self._thickness = None
self._internal_surface = None self._internal_surface = None
self._window_ratio = None self._external_surface = None
self._window_ratio_is_calculated = False self._window_ratio = 0
self._window_ratio_to_be_calculated = False
if self._windows_areas is not None:
self._window_ratio_to_be_calculated = True
@property @property
def id(self): def id(self):
@ -51,7 +56,7 @@ class ThermalBoundary:
@property @property
def parent_surface(self) -> Surface: def parent_surface(self) -> Surface:
""" """
Get the surface that belongs to the thermal boundary Get the surface that belongs to the thermal boundary, considered the external surface of that boundary
:return: Surface :return: Surface
""" """
return self._parent_surface return self._parent_surface
@ -90,7 +95,7 @@ class ThermalBoundary:
self._thickness = 0.0 self._thickness = 0.0
if self.layers is not None: if self.layers is not None:
for layer in self.layers: for layer in self.layers:
if not layer.material.no_mass: if not layer.no_mass:
self._thickness += layer.thickness self._thickness += layer.thickness
return self._thickness return self._thickness
@ -101,18 +106,7 @@ class ThermalBoundary:
:return: None or [ThermalOpening] :return: None or [ThermalOpening]
""" """
if self._thermal_openings is None: if self._thermal_openings is None:
if self.window_ratio is not None: if self.windows_areas is not None:
if self.window_ratio == 0:
self._thermal_openings = []
else:
thermal_opening = ThermalOpening()
if self.window_ratio == 1:
_area = self.opaque_area
else:
_area = self.opaque_area * self.window_ratio / (1-self.window_ratio)
thermal_opening.area = _area
self._thermal_openings = [thermal_opening]
else:
if len(self.windows_areas) > 0: if len(self.windows_areas) > 0:
self._thermal_openings = [] self._thermal_openings = []
for window_area in self.windows_areas: for window_area in self.windows_areas:
@ -121,24 +115,57 @@ class ThermalBoundary:
self._thermal_openings.append(thermal_opening) self._thermal_openings.append(thermal_opening)
else: else:
self._thermal_openings = [] self._thermal_openings = []
else:
if self.window_ratio is not None:
if self.window_ratio == 0:
self._thermal_openings = []
else:
thermal_opening = ThermalOpening()
if self.window_ratio == 1:
_area = self.opaque_area
else:
_area = self.opaque_area * self.window_ratio / (1-self.window_ratio)
thermal_opening.area = _area
self._thermal_openings = [thermal_opening]
else:
self._thermal_openings = []
else:
if self.windows_areas is not None:
return self._thermal_openings
if self.window_ratio is not None:
if self.window_ratio == 0:
self._thermal_openings = []
else:
if len(self._thermal_openings) == 0:
thermal_opening = ThermalOpening()
if self.window_ratio == 1:
_area = self.opaque_area
else:
_area = self.opaque_area * self.window_ratio / (1-self.window_ratio)
thermal_opening.area = _area
self._thermal_openings = [thermal_opening]
else:
for _thermal_opening in self._thermal_openings:
if self.window_ratio == 1:
_area = self.opaque_area
else:
_area = self.opaque_area * self.window_ratio / (1-self.window_ratio)
_thermal_opening.area = _area
self._thermal_openings = [_thermal_opening]
for thermal_opening in self._thermal_openings:
thermal_opening.g_value = self._construction_archetype.window_g_value
thermal_opening.overall_u_value = self._construction_archetype.window_overall_u_value
thermal_opening.frame_ratio = self._construction_archetype.window_frame_ratio
thermal_opening.construction_name = self._construction_archetype.window_type
return self._thermal_openings return self._thermal_openings
@property @property
def construction_name(self) -> Union[None, str]: def _construction_archetype(self):
""" construction_archetypes = self.thermal_zones[0].parent_internal_zone.thermal_archetype.constructions
Get construction name for construction_archetype in construction_archetypes:
:return: None or str if str(self.type) == str(construction_archetype.type):
""" return construction_archetype
return self._construction_name return None
@construction_name.setter
def construction_name(self, value):
"""
Set construction name
:param value: str
"""
if value is not None:
self._construction_name = str(value)
@property @property
def layers(self) -> List[Layer]: def layers(self) -> List[Layer]:
@ -146,16 +173,13 @@ class ThermalBoundary:
Get thermal boundary layers Get thermal boundary layers
:return: [Layers] :return: [Layers]
""" """
if self._construction_archetype is not None:
self._layers = self._construction_archetype.layers
else:
logging.error('Layers not defined\n')
raise ValueError('Layers not defined')
return self._layers return self._layers
@layers.setter
def layers(self, value):
"""
Set thermal boundary layers
:param value: [Layer]
"""
self._layers = value
@property @property
def type(self): def type(self):
""" """
@ -174,28 +198,31 @@ class ThermalBoundary:
If none of those sources are available, it returns None. If none of those sources are available, it returns None.
:return: float :return: float
""" """
if self.windows_areas is not None: if self._window_ratio_to_be_calculated:
if not self._window_ratio_is_calculated: if len(self.windows_areas) == 0:
_calculated = True self._window_ratio = 0
if len(self.windows_areas) == 0: else:
self._window_ratio = 0 total_window_area = 0
for window_area in self.windows_areas:
total_window_area += window_area
self._window_ratio = total_window_area / (self.opaque_area + total_window_area)
else:
if self.type in (cte.WALL, cte.ROOF):
if -math.sqrt(2) / 2 < math.sin(self.parent_surface.azimuth) < math.sqrt(2) / 2:
if 0 < math.cos(self.parent_surface.azimuth):
self._window_ratio = \
float(self._construction_archetype.window_ratio['north']) / 100
else:
self._window_ratio = \
float(self._construction_archetype.window_ratio['south']) / 100
elif math.sqrt(2) / 2 <= math.sin(self._parent_surface.azimuth):
self._window_ratio = \
float(self._construction_archetype.window_ratio['east']) / 100
else: else:
total_window_area = 0 self._window_ratio = \
for window_area in self.windows_areas: float(self._construction_archetype.window_ratio['west']) / 100
total_window_area += window_area
self._window_ratio = total_window_area / (self.opaque_area + total_window_area)
return self._window_ratio return self._window_ratio
@window_ratio.setter
def window_ratio(self, value):
"""
Set thermal boundary window ratio
:param value: str
"""
if self._window_ratio_is_calculated:
raise ValueError('Window ratio cannot be assigned when the windows are defined in the geometry.')
self._window_ratio = float(value)
@property @property
def windows_areas(self) -> [float]: def windows_areas(self) -> [float]:
""" """
@ -220,15 +247,28 @@ class ThermalBoundary:
r_value = 1.0/h_i + 1.0/h_e r_value = 1.0/h_i + 1.0/h_e
try: try:
for layer in self.layers: for layer in self.layers:
if layer.material.no_mass: if layer.no_mass:
r_value += float(layer.material.thermal_resistance) r_value += float(layer.thermal_resistance)
else: else:
r_value += float(layer.thickness) / float(layer.material.conductivity) r_value += float(layer.thickness) / float(layer.conductivity)
self._u_value = 1.0/r_value self._u_value = 1.0/r_value
except TypeError: except TypeError:
raise Exception('Constructions layers are not initialized') from TypeError raise TypeError('Constructions layers are not initialized') from TypeError
return self._u_value return self._u_value
@property
def construction_name(self):
"""
Get construction name
:return: str
"""
if self._construction_archetype is not None:
self._construction_name = self._construction_archetype.name
else:
logging.error('Construction name not defined\n')
raise ValueError('Construction name not defined')
return self._construction_name
@u_value.setter @u_value.setter
def u_value(self, value): def u_value(self, value):
""" """
@ -280,4 +320,18 @@ class ThermalBoundary:
""" """
if self._internal_surface is None: if self._internal_surface is None:
self._internal_surface = self.parent_surface.inverse self._internal_surface = self.parent_surface.inverse
# The agreement is that the layers are defined from outside to inside
internal_layer = self.layers[len(self.layers) - 1]
self._internal_surface.short_wave_reflectance = 1 - internal_layer.solar_absorptance
self._internal_surface.long_wave_emittance = 1 - internal_layer.solar_absorptance
return self._internal_surface return self._internal_surface
@property
def external_surface(self) -> Surface:
if self._external_surface is None:
# The agreement is that the layers are defined from outside to inside
self._external_surface = self.parent_surface
self._external_surface.short_wave_reflectance = 1 - self.layers[0].solar_absorptance
self._external_surface.long_wave_emittance = 1 - self.layers[0].solar_absorptance
return self._external_surface

View File

@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
""" """
from math import inf
from typing import Union, List from typing import Union, List
from hub.city_model_structure.attributes.schedule import Schedule from hub.city_model_structure.attributes.schedule import Schedule
@ -22,20 +23,16 @@ class ThermalControl:
@staticmethod @staticmethod
def _maximum_value(schedules): def _maximum_value(schedules):
maximum = -1000 maximum = -inf
for schedule in schedules: for schedule in schedules:
for value in schedule.values: maximum = max(maximum, max(schedule.values))
if value > maximum:
maximum = value
return maximum return maximum
@staticmethod @staticmethod
def _minimum_value(schedules): def _minimum_value(schedules):
minimum = 1000 minimum = inf
for schedule in schedules: for schedule in schedules:
for value in schedule.values: minimum = min(minimum, min(schedule.values))
if value < minimum:
minimum = value
return minimum return minimum
@property @property

View File

@ -8,8 +8,9 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
import uuid import uuid
import copy import copy
import numpy
from typing import List, Union, TypeVar from typing import List, Union, TypeVar
import numpy
from hub.city_model_structure.building_demand.occupancy import Occupancy from hub.city_model_structure.building_demand.occupancy import Occupancy
from hub.city_model_structure.building_demand.appliances import Appliances from hub.city_model_structure.building_demand.appliances import Appliances
from hub.city_model_structure.building_demand.lighting import Lighting from hub.city_model_structure.building_demand.lighting import Lighting
@ -28,7 +29,12 @@ class ThermalZone:
ThermalZone class ThermalZone class
""" """
def __init__(self, thermal_boundaries, parent_internal_zone, volume, footprint_area, usage_name=None): def __init__(self, thermal_boundaries,
parent_internal_zone,
volume,
footprint_area,
number_of_storeys,
usage_name=None):
self._id = None self._id = None
self._parent_internal_zone = parent_internal_zone self._parent_internal_zone = parent_internal_zone
self._footprint_area = footprint_area self._footprint_area = footprint_area
@ -38,10 +44,13 @@ class ThermalZone:
self._indirectly_heated_area_ratio = None self._indirectly_heated_area_ratio = None
self._infiltration_rate_system_on = None self._infiltration_rate_system_on = None
self._infiltration_rate_system_off = None self._infiltration_rate_system_off = None
self._infiltration_rate_area_system_on = None
self._infiltration_rate_area_system_off = None
self._volume = volume self._volume = volume
self._ordinate_number = None self._ordinate_number = None
self._view_factors_matrix = None self._view_factors_matrix = None
self._total_floor_area = None self._total_floor_area = None
self._number_of_storeys = number_of_storeys
self._usage_name = usage_name self._usage_name = usage_name
self._usage_from_parent = False self._usage_from_parent = False
if usage_name is None: if usage_name is None:
@ -57,9 +66,21 @@ class ThermalZone:
self._domestic_hot_water = None self._domestic_hot_water = None
self._usages = None self._usages = None
@property
def parent_internal_zone(self) -> InternalZone:
"""
Get the internal zone to which this thermal zone belongs
:return: InternalZone
"""
return self._parent_internal_zone
@property @property
def usages(self): def usages(self):
# example 70-office_30-residential """
Get the thermal zone usages including percentage with the format [percentage]-usage_[percentage]-usage...
Eg: 70-office_30-residential
:return: str
"""
if self._usage_from_parent: if self._usage_from_parent:
self._usages = copy.deepcopy(self._parent_internal_zone.usages) self._usages = copy.deepcopy(self._parent_internal_zone.usages)
else: else:
@ -108,82 +129,62 @@ class ThermalZone:
Get thermal zone additional thermal bridge u value per footprint area W/m2K Get thermal zone additional thermal bridge u value per footprint area W/m2K
:return: None or float :return: None or float
""" """
self._additional_thermal_bridge_u_value = self.parent_internal_zone.thermal_archetype.extra_loses_due_to_thermal_bridges
return self._additional_thermal_bridge_u_value return self._additional_thermal_bridge_u_value
@additional_thermal_bridge_u_value.setter
def additional_thermal_bridge_u_value(self, value):
"""
Set thermal zone additional thermal bridge u value per footprint area W/m2K
:param value: float
"""
if value is not None:
self._additional_thermal_bridge_u_value = float(value)
@property @property
def effective_thermal_capacity(self) -> Union[None, float]: def effective_thermal_capacity(self) -> Union[None, float]:
""" """
Get thermal zone effective thermal capacity in J/m3K Get thermal zone effective thermal capacity in J/m3K
:return: None or float :return: None or float
""" """
self._effective_thermal_capacity = self._parent_internal_zone.thermal_archetype.thermal_capacity
return self._effective_thermal_capacity return self._effective_thermal_capacity
@effective_thermal_capacity.setter
def effective_thermal_capacity(self, value):
"""
Set thermal zone effective thermal capacity in J/m3K
:param value: float
"""
if value is not None:
self._effective_thermal_capacity = float(value)
@property @property
def indirectly_heated_area_ratio(self) -> Union[None, float]: def indirectly_heated_area_ratio(self) -> Union[None, float]:
""" """
Get thermal zone indirectly heated area ratio Get thermal zone indirectly heated area ratio
:return: None or float :return: None or float
""" """
self._indirectly_heated_area_ratio = self._parent_internal_zone.thermal_archetype.indirect_heated_ratio
return self._indirectly_heated_area_ratio return self._indirectly_heated_area_ratio
@indirectly_heated_area_ratio.setter
def indirectly_heated_area_ratio(self, value):
"""
Set thermal zone indirectly heated area ratio
:param value: float
"""
if value is not None:
self._indirectly_heated_area_ratio = float(value)
@property @property
def infiltration_rate_system_on(self): def infiltration_rate_system_on(self):
""" """
Get thermal zone infiltration rate system on in air changes per hour (ACH) Get thermal zone infiltration rate system on in air changes per second (1/s)
:return: None or float :return: None or float
""" """
self._infiltration_rate_system_on = self._parent_internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_on
return self._infiltration_rate_system_on return self._infiltration_rate_system_on
@infiltration_rate_system_on.setter
def infiltration_rate_system_on(self, value):
"""
Set thermal zone infiltration rate system on in air changes per hour (ACH)
:param value: float
"""
self._infiltration_rate_system_on = value
@property @property
def infiltration_rate_system_off(self): def infiltration_rate_system_off(self):
""" """
Get thermal zone infiltration rate system off in air changes per hour (ACH) Get thermal zone infiltration rate system off in air changes per second (1/s)
:return: None or float :return: None or float
""" """
self._infiltration_rate_system_off = self._parent_internal_zone.thermal_archetype.infiltration_rate_for_ventilation_system_off
return self._infiltration_rate_system_off return self._infiltration_rate_system_off
@infiltration_rate_system_off.setter @property
def infiltration_rate_system_off(self, value): def infiltration_rate_area_system_on(self):
""" """
Set thermal zone infiltration rate system on in air changes per hour (ACH) Get thermal zone infiltration rate system on in air changes per second (1/s)
:param value: float :return: None or float
""" """
self._infiltration_rate_system_off = value self._infiltration_rate_area_system_on = self._parent_internal_zone.thermal_archetype.infiltration_rate_area_for_ventilation_system_on
return self._infiltration_rate_area_system_on
@property
def infiltration_rate_area_system_off(self):
"""
Get thermal zone infiltration rate system off in air changes per second (1/s)
:return: None or float
"""
self._infiltration_rate_area_system_off = self._parent_internal_zone.thermal_archetype.infiltration_rate_area_for_ventilation_system_off
return self._infiltration_rate_area_system_off
@property @property
def volume(self): def volume(self):
@ -216,15 +217,43 @@ class ThermalZone:
Get thermal zone view factors matrix Get thermal zone view factors matrix
:return: [[float]] :return: [[float]]
""" """
return self._view_factors_matrix # todo: review method if windows not in window_ratio but in geometry
if self._view_factors_matrix is None:
total_area = 0
for thermal_boundary in self.thermal_boundaries:
total_area += thermal_boundary.opaque_area
for thermal_opening in thermal_boundary.thermal_openings:
total_area += thermal_opening.area
@view_factors_matrix.setter view_factors_matrix = []
def view_factors_matrix(self, value): for thermal_boundary_1 in self.thermal_boundaries:
""" values = []
Set thermal zone view factors matrix for thermal_boundary_2 in self.thermal_boundaries:
:param value: [[float]] value = 0
""" if thermal_boundary_1.id != thermal_boundary_2.id:
self._view_factors_matrix = value value = thermal_boundary_2.opaque_area / (total_area - thermal_boundary_1.opaque_area)
values.append(value)
for thermal_boundary in self.thermal_boundaries:
for thermal_opening in thermal_boundary.thermal_openings:
value = thermal_opening.area / (total_area - thermal_boundary_1.opaque_area)
values.append(value)
view_factors_matrix.append(values)
for thermal_boundary_1 in self.thermal_boundaries:
values = []
for thermal_opening_1 in thermal_boundary_1.thermal_openings:
for thermal_boundary_2 in self.thermal_boundaries:
value = thermal_boundary_2.opaque_area / (total_area - thermal_opening_1.area)
values.append(value)
for thermal_boundary in self.thermal_boundaries:
for thermal_opening_2 in thermal_boundary.thermal_openings:
value = 0
if thermal_opening_1.id != thermal_opening_2.id:
value = thermal_opening_2.area / (total_area - thermal_opening_1.area)
values.append(value)
view_factors_matrix.append(values)
self._view_factors_matrix = view_factors_matrix
return self._view_factors_matrix
@property @property
def usage_name(self) -> Union[None, str]: def usage_name(self) -> Union[None, str]:
@ -280,7 +309,7 @@ class ThermalZone:
@property @property
def mechanical_air_change(self) -> Union[None, float]: def mechanical_air_change(self) -> Union[None, float]:
""" """
Get thermal zone mechanical air change in air change per hour (ACH) Get thermal zone mechanical air change in air change per second (1/s)
:return: None or float :return: None or float
""" """
if self.usages is None: if self.usages is None:
@ -324,19 +353,19 @@ class ThermalZone:
_occupancy_reference = self.usages[0].occupancy _occupancy_reference = self.usages[0].occupancy
if _occupancy_reference.occupancy_schedules is not None: if _occupancy_reference.occupancy_schedules is not None:
_schedules = [] _schedules = []
for i_schedule in range(0, len(_occupancy_reference.occupancy_schedules)): for schedule_index, schedule_value in enumerate(_occupancy_reference.occupancy_schedules):
schedule = Schedule() schedule = Schedule()
schedule.type = _occupancy_reference.occupancy_schedules[i_schedule].type schedule.type = schedule_value.type
schedule.day_types = _occupancy_reference.occupancy_schedules[i_schedule].day_types schedule.day_types = schedule_value.day_types
schedule.data_type = _occupancy_reference.occupancy_schedules[i_schedule].data_type schedule.data_type = schedule_value.data_type
schedule.time_step = _occupancy_reference.occupancy_schedules[i_schedule].time_step schedule.time_step = schedule_value.time_step
schedule.time_range = _occupancy_reference.occupancy_schedules[i_schedule].time_range schedule.time_range = schedule_value.time_range
new_values = [] new_values = []
for i_value in range(0, len(_occupancy_reference.occupancy_schedules[i_schedule].values)): for i_value, _ in enumerate(schedule_value.values):
_new_value = 0 _new_value = 0
for usage in self.usages: for usage in self.usages:
_new_value += usage.percentage * usage.occupancy.occupancy_schedules[i_schedule].values[i_value] _new_value += usage.percentage * usage.occupancy.occupancy_schedules[schedule_index].values[i_value]
new_values.append(_new_value) new_values.append(_new_value)
schedule.values = new_values schedule.values = new_values
_schedules.append(schedule) _schedules.append(schedule)
@ -385,19 +414,19 @@ class ThermalZone:
_lighting_reference = self.usages[0].lighting _lighting_reference = self.usages[0].lighting
if _lighting_reference.schedules is not None: if _lighting_reference.schedules is not None:
_schedules = [] _schedules = []
for i_schedule in range(0, len(_lighting_reference.schedules)): for schedule_index, schedule_value in enumerate(_lighting_reference.schedules):
schedule = Schedule() schedule = Schedule()
schedule.type = _lighting_reference.schedules[i_schedule].type schedule.type = schedule_value.type
schedule.day_types = _lighting_reference.schedules[i_schedule].day_types schedule.day_types = schedule_value.day_types
schedule.data_type = _lighting_reference.schedules[i_schedule].data_type schedule.data_type = schedule_value.data_type
schedule.time_step = _lighting_reference.schedules[i_schedule].time_step schedule.time_step = schedule_value.time_step
schedule.time_range = _lighting_reference.schedules[i_schedule].time_range schedule.time_range = schedule_value.time_range
new_values = [] new_values = []
for i_value in range(0, len(_lighting_reference.schedules[i_schedule].values)): for i_value, _ in enumerate(schedule_value.values):
_new_value = 0 _new_value = 0
for usage in self.usages: for usage in self.usages:
_new_value += usage.percentage * usage.lighting.schedules[i_schedule].values[i_value] _new_value += usage.percentage * usage.lighting.schedules[schedule_index].values[i_value]
new_values.append(_new_value) new_values.append(_new_value)
schedule.values = new_values schedule.values = new_values
_schedules.append(schedule) _schedules.append(schedule)
@ -446,19 +475,19 @@ class ThermalZone:
_appliances_reference = self.usages[0].appliances _appliances_reference = self.usages[0].appliances
if _appliances_reference.schedules is not None: if _appliances_reference.schedules is not None:
_schedules = [] _schedules = []
for i_schedule in range(0, len(_appliances_reference.schedules)): for schedule_index, schedule_value in enumerate(_appliances_reference.schedules):
schedule = Schedule() schedule = Schedule()
schedule.type = _appliances_reference.schedules[i_schedule].type schedule.type = schedule_value.type
schedule.day_types = _appliances_reference.schedules[i_schedule].day_types schedule.day_types = schedule_value.day_types
schedule.data_type = _appliances_reference.schedules[i_schedule].data_type schedule.data_type = schedule_value.data_type
schedule.time_step = _appliances_reference.schedules[i_schedule].time_step schedule.time_step = schedule_value.time_step
schedule.time_range = _appliances_reference.schedules[i_schedule].time_range schedule.time_range = schedule_value.time_range
new_values = [] new_values = []
for i_value in range(0, len(_appliances_reference.schedules[i_schedule].values)): for i_value, _ in enumerate(schedule_value.values):
_new_value = 0 _new_value = 0
for usage in self.usages: for usage in self.usages:
_new_value += usage.percentage * usage.appliances.schedules[i_schedule].values[i_value] _new_value += usage.percentage * usage.appliances.schedules[schedule_index].values[i_value]
new_values.append(_new_value) new_values.append(_new_value)
schedule.values = new_values schedule.values = new_values
_schedules.append(schedule) _schedules.append(schedule)
@ -510,15 +539,15 @@ class ThermalZone:
_schedules_defined = False _schedules_defined = False
break break
for day, _schedule in enumerate(internal_gain.schedules): for day, _schedule in enumerate(internal_gain.schedules):
for v, value in enumerate(_schedule.values): for v_index, value in enumerate(_schedule.values):
values[v, day] += value * usage.percentage values[v_index, day] += value * usage.percentage
if _schedules_defined: if _schedules_defined:
_schedules = [] _schedules = []
for day in range(0, len(_days)): for day_index, day in enumerate(_days):
_schedule = copy.deepcopy(_base_schedule) _schedule = copy.deepcopy(_base_schedule)
_schedule.day_types = [_days[day]] _schedule.day_types = [day]
_schedule.values = values[:day] _schedule.values = values[:day_index]
_schedules.append(_schedule) _schedules.append(_schedule)
_internal_gain.average_internal_gain = _average_internal_gain _internal_gain.average_internal_gain = _average_internal_gain
@ -543,62 +572,62 @@ class ThermalZone:
if self.usages is None: if self.usages is None:
return None return None
if self._thermal_control is None: if self._thermal_control is not None:
self._thermal_control = ThermalControl() return self._thermal_control
_mean_heating_set_point = 0 self._thermal_control = ThermalControl()
_heating_set_back = 0 _mean_heating_set_point = 0
_mean_cooling_set_point = 0 _heating_set_back = 0
for usage in self.usages: _mean_cooling_set_point = 0
_mean_heating_set_point += usage.percentage * usage.thermal_control.mean_heating_set_point for usage in self.usages:
_heating_set_back += usage.percentage * usage.thermal_control.heating_set_back _mean_heating_set_point += usage.percentage * usage.thermal_control.mean_heating_set_point
_mean_cooling_set_point += usage.percentage * usage.thermal_control.mean_cooling_set_point _heating_set_back += usage.percentage * usage.thermal_control.heating_set_back
self._thermal_control.mean_heating_set_point = _mean_heating_set_point _mean_cooling_set_point += usage.percentage * usage.thermal_control.mean_cooling_set_point
self._thermal_control.heating_set_back = _heating_set_back self._thermal_control.mean_heating_set_point = _mean_heating_set_point
self._thermal_control.mean_cooling_set_point = _mean_cooling_set_point self._thermal_control.heating_set_back = _heating_set_back
self._thermal_control.mean_cooling_set_point = _mean_cooling_set_point
_thermal_control_reference = self.usages[0].thermal_control _thermal_control_reference = self.usages[0].thermal_control
_types_reference = [] _types_reference = []
if _thermal_control_reference.hvac_availability_schedules is not None: if _thermal_control_reference.hvac_availability_schedules is not None:
_types_reference.append([cte.HVAC_AVAILABILITY, _thermal_control_reference.hvac_availability_schedules]) _types_reference.append([cte.HVAC_AVAILABILITY, _thermal_control_reference.hvac_availability_schedules])
if _thermal_control_reference.heating_set_point_schedules is not None: if _thermal_control_reference.heating_set_point_schedules is not None:
_types_reference.append([cte.HEATING_SET_POINT, _thermal_control_reference.heating_set_point_schedules]) _types_reference.append([cte.HEATING_SET_POINT, _thermal_control_reference.heating_set_point_schedules])
if _thermal_control_reference.cooling_set_point_schedules is not None: if _thermal_control_reference.cooling_set_point_schedules is not None:
_types_reference.append([cte.COOLING_SET_POINT, _thermal_control_reference.cooling_set_point_schedules]) _types_reference.append([cte.COOLING_SET_POINT, _thermal_control_reference.cooling_set_point_schedules])
for i_type in range(0, len(_types_reference)): for i_type, _ in enumerate(_types_reference):
_schedules = [] _schedules = []
_schedule_type = _types_reference[i_type][1] _schedule_type = _types_reference[i_type][1]
for i_schedule in range(0, len(_schedule_type)): for i_schedule, schedule_value in enumerate(_schedule_type):
schedule = Schedule() schedule = Schedule()
schedule.type = _schedule_type[i_schedule].type schedule.type = schedule_value.type
schedule.day_types = _schedule_type[i_schedule].day_types schedule.day_types = schedule_value.day_types
schedule.data_type = _schedule_type[i_schedule].data_type schedule.data_type = schedule_value.data_type
schedule.time_step = _schedule_type[i_schedule].time_step schedule.time_step = schedule_value.time_step
schedule.time_range = _schedule_type[i_schedule].time_range schedule.time_range = schedule_value.time_range
new_values = []
for i_value in range(0, len(_schedule_type[i_schedule].values)):
_new_value = 0
for usage in self.usages:
if _types_reference[i_type][0] == cte.HVAC_AVAILABILITY:
_new_value += usage.percentage * \
usage.thermal_control.hvac_availability_schedules[i_schedule].values[i_value]
elif _types_reference[i_type][0] == cte.HEATING_SET_POINT:
_new_value += usage.percentage * \
usage.thermal_control.heating_set_point_schedules[i_schedule].values[i_value]
elif _types_reference[i_type][0] == cte.COOLING_SET_POINT:
_new_value += usage.percentage * \
usage.thermal_control.cooling_set_point_schedules[i_schedule].values[i_value]
new_values.append(_new_value)
schedule.values = new_values
_schedules.append(schedule)
if i_type == 0:
self._thermal_control.hvac_availability_schedules = _schedules
elif i_type == 1:
self._thermal_control.heating_set_point_schedules = _schedules
elif i_type == 2:
self._thermal_control.cooling_set_point_schedules = _schedules
new_values = []
for i_value, _ in enumerate(schedule_value.values):
_new_value = 0
for usage in self.usages:
if _types_reference[i_type][0] == cte.HVAC_AVAILABILITY:
_new_value += usage.percentage * \
usage.thermal_control.hvac_availability_schedules[i_schedule].values[i_value]
elif _types_reference[i_type][0] == cte.HEATING_SET_POINT:
_new_value += usage.percentage * \
usage.thermal_control.heating_set_point_schedules[i_schedule].values[i_value]
elif _types_reference[i_type][0] == cte.COOLING_SET_POINT:
_new_value += usage.percentage * \
usage.thermal_control.cooling_set_point_schedules[i_schedule].values[i_value]
new_values.append(_new_value)
schedule.values = new_values
_schedules.append(schedule)
if i_type == 0:
self._thermal_control.hvac_availability_schedules = _schedules
elif i_type == 1:
self._thermal_control.heating_set_point_schedules = _schedules
elif i_type == 2:
self._thermal_control.cooling_set_point_schedules = _schedules
return self._thermal_control return self._thermal_control
@property @property
@ -612,9 +641,12 @@ class ThermalZone:
_mean_peak_flow = 0 _mean_peak_flow = 0
_mean_service_temperature = 0 _mean_service_temperature = 0
for usage in self.usages: for usage in self.usages:
_mean_peak_density_load += usage.percentage * usage.domestic_hot_water.density if usage.domestic_hot_water.density is not None:
_mean_peak_flow += usage.percentage * usage.domestic_hot_water.peak_flow _mean_peak_density_load += usage.percentage * usage.domestic_hot_water.density
_mean_service_temperature += usage.percentage * usage.domestic_hot_water.service_temperature if usage.domestic_hot_water.peak_flow is not None:
_mean_peak_flow += usage.percentage * usage.domestic_hot_water.peak_flow
if usage.domestic_hot_water.service_temperature is not None:
_mean_service_temperature += usage.percentage * usage.domestic_hot_water.service_temperature
self._domestic_hot_water.density = _mean_peak_density_load self._domestic_hot_water.density = _mean_peak_density_load
self._domestic_hot_water.peak_flow = _mean_peak_flow self._domestic_hot_water.peak_flow = _mean_peak_flow
self._domestic_hot_water.service_temperature = _mean_service_temperature self._domestic_hot_water.service_temperature = _mean_service_temperature
@ -622,19 +654,19 @@ class ThermalZone:
_domestic_hot_water_reference = self.usages[0].domestic_hot_water _domestic_hot_water_reference = self.usages[0].domestic_hot_water
if _domestic_hot_water_reference.schedules is not None: if _domestic_hot_water_reference.schedules is not None:
_schedules = [] _schedules = []
for i_schedule in range(0, len(_domestic_hot_water_reference.schedules)): for schedule_index, schedule_value in enumerate(_domestic_hot_water_reference.schedules):
schedule = Schedule() schedule = Schedule()
schedule.type = _domestic_hot_water_reference.schedules[i_schedule].type schedule.type = schedule_value.type
schedule.day_types = _domestic_hot_water_reference.schedules[i_schedule].day_types schedule.day_types = schedule_value.day_types
schedule.data_type = _domestic_hot_water_reference.schedules[i_schedule].data_type schedule.data_type = schedule_value.data_type
schedule.time_step = _domestic_hot_water_reference.schedules[i_schedule].time_step schedule.time_step = schedule_value.time_step
schedule.time_range = _domestic_hot_water_reference.schedules[i_schedule].time_range schedule.time_range = schedule_value.time_range
new_values = [] new_values = []
for i_value in range(0, len(_domestic_hot_water_reference.schedules[i_schedule].values)): for i_value, _ in enumerate(schedule_value.values):
_new_value = 0 _new_value = 0
for usage in self.usages: for usage in self.usages:
_new_value += usage.percentage * usage.domestic_hot_water.schedules[i_schedule].values[i_value] _new_value += usage.percentage * usage.domestic_hot_water.schedules[schedule_index].values[i_value]
new_values.append(_new_value) new_values.append(_new_value)
schedule.values = new_values schedule.values = new_values
_schedules.append(schedule) _schedules.append(schedule)
@ -645,15 +677,8 @@ class ThermalZone:
@property @property
def total_floor_area(self): def total_floor_area(self):
""" """
Get the total floor area of this thermal zone Get the total floor area of this thermal zone in m2
:return: float :return: float
""" """
self._total_floor_area = self.footprint_area * self._number_of_storeys
return self._total_floor_area return self._total_floor_area
@total_floor_area.setter
def total_floor_area(self, value):
"""
Set the total floor area of this thermal zone
:param value: float
"""
self._total_floor_area = value

View File

@ -173,7 +173,7 @@ class Usage:
@property @property
def mechanical_air_change(self) -> Union[None, float]: def mechanical_air_change(self) -> Union[None, float]:
""" """
Get usage zone mechanical air change in air change per hour (ACH) Get usage zone mechanical air change in air change per second (1/s)
:return: None or float :return: None or float
""" """
return self._mechanical_air_change return self._mechanical_air_change
@ -181,7 +181,7 @@ class Usage:
@mechanical_air_change.setter @mechanical_air_change.setter
def mechanical_air_change(self, value): def mechanical_air_change(self, value):
""" """
Set usage zone mechanical air change in air change per hour (ACH) Set usage zone mechanical air change in air change per second (1/s)
:param value: float :param value: float
""" """
if value is not None: if value is not None:

View File

@ -1,57 +0,0 @@
"""
Bus system module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
"""
from typing import List
from hub.city_model_structure.city_object import CityObject
from hub.city_model_structure.attributes.polygon import Polygon
from hub.city_model_structure.transport.bus_network import BusNetwork
from hub.city_model_structure.transport.bus_node import BusNode
from hub.city_model_structure.transport.bus import Bus
class BusSystem(CityObject):
"""
BusSystem(CityObject) class
"""
def __init__(self, name, surfaces):
super().__init__(name, surfaces)
self._bus_routes = None
self._bus_network = None
self._buses = None
self._restricted_polygons = None
@property
def bus_routes(self) -> List[BusNode]:
"""
Add explanation here
:return: [BusNode]
"""
return self._bus_routes
@property
def bus_network(self) -> BusNetwork:
"""
Add explanation here
:return: BusNetwork
"""
return self._bus_network
@property
def buses(self) -> List[Bus]:
"""
Add explanation here
:return: [Bus]
"""
return self._buses
@property
def restricted_polygons(self) -> List[Polygon]:
"""
Add explanation here
:return: [Polygon]
"""
return self._restricted_polygons

View File

@ -8,29 +8,30 @@ Code contributors: Peter Yefi peteryefi@gmail.com
from __future__ import annotations from __future__ import annotations
import bz2 import bz2
import logging
import sys
import pickle
import math
import copy import copy
import pyproj import logging
from typing import List, Union import math
from pyproj import Transformer import pickle
import sys
import pathlib
import os
from pathlib import Path from pathlib import Path
from typing import List, Union
import pyproj
from pandas import DataFrame from pandas import DataFrame
from pyproj import Transformer
from hub.city_model_structure.building import Building from hub.city_model_structure.building import Building
from hub.city_model_structure.buildings_cluster import BuildingsCluster
from hub.city_model_structure.city_object import CityObject from hub.city_model_structure.city_object import CityObject
from hub.city_model_structure.city_objects_cluster import CityObjectsCluster from hub.city_model_structure.city_objects_cluster import CityObjectsCluster
from hub.city_model_structure.buildings_cluster import BuildingsCluster
from hub.city_model_structure.iot.station import Station from hub.city_model_structure.iot.station import Station
from hub.city_model_structure.level_of_detail import LevelOfDetail from hub.city_model_structure.level_of_detail import LevelOfDetail
from hub.city_model_structure.parts_consisting_building import PartsConsistingBuilding from hub.city_model_structure.parts_consisting_building import PartsConsistingBuilding
from hub.helpers.geometry_helper import GeometryHelper from hub.helpers.geometry_helper import GeometryHelper
from hub.helpers.location import Location from hub.helpers.location import Location
import hub.helpers.constants as cte import hub.helpers.constants as cte
from hub.city_model_structure.energy_system import EnergySystem
import pandas as pd
class City: class City:
@ -57,24 +58,21 @@ class City:
self._city_objects = None self._city_objects = None
self._energy_systems = None self._energy_systems = None
self._fuels = None self._fuels = None
self._machines = None
self._stations = [] self._stations = []
self._lca_materials = None
self._level_of_detail = LevelOfDetail() self._level_of_detail = LevelOfDetail()
self._city_objects_dictionary = {} self._city_objects_dictionary = {}
self._city_objects_alias_dictionary = {} self._city_objects_alias_dictionary = {}
self._energy_systems_connection_table = None
self._generic_energy_systems = None self._generic_energy_systems = None
def _get_location(self) -> Location: def _get_location(self) -> Location:
if self._location is None: if self._location is None:
gps = pyproj.CRS('EPSG:4326') # LatLon with WGS84 datum used by GPS units and Google Earth gps = pyproj.CRS('EPSG:4326') # LatLon with WGS84 datum used by GPS units and Google Earth
try: try:
if self._srs_name in GeometryHelper.srs_transformations.keys(): if self._srs_name in GeometryHelper.srs_transformations:
self._srs_name = GeometryHelper.srs_transformations[self._srs_name] self._srs_name = GeometryHelper.srs_transformations[self._srs_name]
input_reference = pyproj.CRS(self.srs_name) # Projected coordinate system from input data input_reference = pyproj.CRS(self.srs_name) # Projected coordinate system from input data
except pyproj.exceptions.CRSError as err: except pyproj.exceptions.CRSError as err:
logging.error('Invalid projection reference system, please check the input data. (e.g. in CityGML files: srs_name)') logging.error('Invalid projection reference system, please check the input data.')
raise pyproj.exceptions.CRSError from err raise pyproj.exceptions.CRSError from err
transformer = Transformer.from_crs(input_reference, gps) transformer = Transformer.from_crs(input_reference, gps)
coordinates = transformer.transform(self.lower_corner[0], self.lower_corner[1]) coordinates = transformer.transform(self.lower_corner[0], self.lower_corner[1])
@ -84,14 +82,26 @@ class City:
@property @property
def country_code(self): def country_code(self):
""" """
Get models country code Get city country code
:return: str :return: str
""" """
return self._get_location().country return self._get_location().country
@property @property
def location(self): def region_code(self):
return self._get_location().city """
Get city region name
:return: str
"""
return self._get_location().region_code
@property
def location(self) -> Location:
"""
Get city location
:return: Location
"""
return self._get_location()
@property @property
def name(self): def name(self):
@ -103,6 +113,15 @@ class City:
return self._get_location().city return self._get_location().city
return self._name return self._name
@name.setter
def name(self, value):
"""
Set city name
:param value:str
"""
if value is not None:
self._name = str(value)
@property @property
def climate_reference_city(self) -> Union[None, str]: def climate_reference_city(self) -> Union[None, str]:
""" """
@ -152,9 +171,6 @@ class City:
if self.buildings is not None: if self.buildings is not None:
for building in self.buildings: for building in self.buildings:
self._city_objects.append(building) self._city_objects.append(building)
if self.energy_systems is not None:
for energy_system in self.energy_systems:
self._city_objects.append(energy_system)
return self._city_objects return self._city_objects
@property @property
@ -203,8 +219,11 @@ class City:
return None return None
def add_building_alias(self, building, alias): def add_building_alias(self, building, alias):
"""
Add an alias to the building
"""
building_index = self._city_objects_dictionary[building.name] building_index = self._city_objects_dictionary[building.name]
if alias in self._city_objects_alias_dictionary.keys(): if alias in self._city_objects_alias_dictionary:
self._city_objects_alias_dictionary[alias].append(building_index) self._city_objects_alias_dictionary[alias].append(building_index)
else: else:
self._city_objects_alias_dictionary[alias] = [building_index] self._city_objects_alias_dictionary[alias] = [building_index]
@ -218,7 +237,6 @@ class City:
if new_city_object.type == 'building': if new_city_object.type == 'building':
if self._buildings is None: if self._buildings is None:
self._buildings = [] self._buildings = []
new_city_object._alias_dictionary = self._city_objects_alias_dictionary
self._buildings.append(new_city_object) self._buildings.append(new_city_object)
self._city_objects_dictionary[new_city_object.name] = len(self._buildings) - 1 self._city_objects_dictionary[new_city_object.name] = len(self._buildings) - 1
if new_city_object.aliases is not None: if new_city_object.aliases is not None:
@ -242,17 +260,17 @@ class City:
""" """
if city_object.type != 'building': if city_object.type != 'building':
raise NotImplementedError(city_object.type) raise NotImplementedError(city_object.type)
if self._buildings is None or self._buildings == []: if not self._buildings:
sys.stderr.write('Warning: impossible to remove city_object, the city is empty\n') logging.warning('impossible to remove city_object, the city is empty\n')
else: else:
if city_object in self._buildings: if city_object in self._buildings:
self._buildings.remove(city_object) self._buildings.remove(city_object)
# regenerate hash map # regenerate hash map
self._city_objects_dictionary.clear() self._city_objects_dictionary = {}
self._city_objects_alias_dictionary.clear() self._city_objects_alias_dictionary = {}
for i, city_object in enumerate(self._buildings): for i, _building in enumerate(self._buildings):
self._city_objects_dictionary[city_object.name] = i self._city_objects_dictionary[_building.name] = i
for alias in city_object.aliases: for alias in _building.aliases:
if alias in self._city_objects_alias_dictionary: if alias in self._city_objects_alias_dictionary:
self._city_objects_alias_dictionary[alias].append(i) self._city_objects_alias_dictionary[alias].append(i)
else: else:
@ -266,15 +284,6 @@ class City:
""" """
return self._srs_name return self._srs_name
@name.setter
def name(self, value):
"""
Set city name
:param value:str
"""
if value is not None:
self._name = str(value)
@staticmethod @staticmethod
def load(city_filename) -> City: def load(city_filename) -> City:
""" """
@ -282,9 +291,28 @@ class City:
:param city_filename: city filename :param city_filename: city filename
:return: City :return: City
""" """
if sys.platform == 'win32':
pathlib.PosixPath = pathlib.WindowsPath
elif sys.platform == 'linux':
pathlib.WindowsPath = pathlib.PosixPath
with open(city_filename, 'rb') as file: with open(city_filename, 'rb') as file:
return pickle.load(file) return pickle.load(file)
@staticmethod
def load_compressed(compressed_city_filename, destination_filename) -> City:
"""
Load a city from compressed_city_filename
:param compressed_city_filename: Compressed pickle as source
:param destination_filename: Pickle file as destination
:return: City
"""
with open(str(compressed_city_filename), 'rb') as source, open(str(destination_filename), 'wb') as destination:
destination.write(bz2.decompress(source.read()))
loaded_city = City.load(destination_filename)
os.unlink(destination_filename)
return loaded_city
def save(self, city_filename): def save(self, city_filename):
""" """
Save a city into the given filename Save a city into the given filename
@ -300,8 +328,8 @@ class City:
:param city_filename: destination city filename :param city_filename: destination city filename
:return: None :return: None
""" """
with bz2.BZ2File(city_filename, 'wb') as f: with bz2.BZ2File(city_filename, 'wb') as file:
pickle.dump(self, f) pickle.dump(self, file)
def region(self, center, radius) -> City: def region(self, center, radius) -> City:
""" """
@ -391,14 +419,6 @@ class City:
""" """
return self._parts_consisting_buildings return self._parts_consisting_buildings
@property
def energy_systems(self) -> Union[List[EnergySystem], None]:
"""
Get energy systems belonging to the city
:return: None or [EnergySystem]
"""
return self._energy_systems
@property @property
def stations(self) -> [Station]: def stations(self) -> [Station]:
""" """
@ -446,7 +466,35 @@ class City:
return copy.deepcopy(self) return copy.deepcopy(self)
def merge(self, city) -> City: def merge(self, city) -> City:
raise NotImplementedError('This method needs to be reimplemented') """
Return a merged city combining the current city and the given one
:return: City
"""
merged_city = self.copy
for building in city.buildings:
if merged_city.city_object(building.name) is None:
# building is new so added to the city
merged_city.add_city_object(copy.deepcopy(building))
else:
# keep the one with less radiation
parameter_city_building_total_radiation = 0
for surface in building.surfaces:
if surface.global_irradiance:
parameter_city_building_total_radiation += surface.global_irradiance[cte.YEAR][0]
merged_city_building_total_radiation = 0
for surface in merged_city.city_object(building.name).surfaces:
if surface.global_irradiance:
merged_city_building_total_radiation += surface.global_irradiance[cte.YEAR][0]
if merged_city_building_total_radiation == 0:
merged_city.remove_city_object(merged_city.city_object(building.name))
merged_city.add_city_object(building)
elif merged_city_building_total_radiation > parameter_city_building_total_radiation > 0:
merged_city.remove_city_object(merged_city.city_object(building.name))
merged_city.add_city_object(building)
return merged_city
@property @property
def level_of_detail(self) -> LevelOfDetail: def level_of_detail(self) -> LevelOfDetail:
@ -456,24 +504,6 @@ class City:
""" """
return self._level_of_detail return self._level_of_detail
@property
def energy_systems_connection_table(self) -> Union[None, DataFrame]:
"""
Get energy systems connection table which includes at least two columns: energy_system_type and associated_building
and may also include dimensioned_energy_system and connection_building_to_dimensioned_energy_system
:return: DataFrame
"""
return self._energy_systems_connection_table
@energy_systems_connection_table.setter
def energy_systems_connection_table(self, value):
"""
Set energy systems connection table which includes at least two columns: energy_system_type and associated_building
and may also include dimensioned_energy_system and connection_building_to_dimensioned_energy_system
:param value: DataFrame
"""
self._energy_systems_connection_table = value
@property @property
def generic_energy_systems(self) -> dict: def generic_energy_systems(self) -> dict:
""" """

View File

@ -37,13 +37,14 @@ class CityObject:
self._max_z = ConfigurationHelper().min_coordinate self._max_z = ConfigurationHelper().min_coordinate
self._centroid = None self._centroid = None
self._volume = None self._volume = None
self._external_temperature = dict() self._external_temperature = {}
self._ground_temperature = dict() self._ground_temperature = {}
self._global_horizontal = dict() self._global_horizontal = {}
self._diffuse = dict() self._diffuse = {}
self._beam = dict() self._direct_normal = {}
self._sensors = [] self._sensors = []
self._neighbours = None self._neighbours = None
self._beam = {}
@property @property
def level_of_detail(self) -> LevelOfDetail: def level_of_detail(self) -> LevelOfDetail:
@ -81,6 +82,10 @@ class CityObject:
@volume.setter @volume.setter
def volume(self, value): def volume(self, value):
"""
Set city object volume in cubic meters
:param value: float
"""
self._volume = value self._volume = value
@property @property
@ -172,7 +177,7 @@ class CityObject:
def external_temperature(self) -> {float}: def external_temperature(self) -> {float}:
""" """
Get external temperature surrounding the city object in Celsius Get external temperature surrounding the city object in Celsius
:return: dict{DataFrame(float)} :return: dict{dict{[float]}}
""" """
return self._external_temperature return self._external_temperature
@ -180,11 +185,10 @@ class CityObject:
def external_temperature(self, value): def external_temperature(self, value):
""" """
Set external temperature surrounding the city object in Celsius Set external temperature surrounding the city object in Celsius
:param value: dict{DataFrame(float)} :param value: dict{dict{[float]}}
""" """
self._external_temperature = value self._external_temperature = value
# todo: this is the new format we will use to get rid of the data frames
@property @property
def ground_temperature(self) -> dict: def ground_temperature(self) -> dict:
""" """
@ -205,50 +209,50 @@ class CityObject:
@property @property
def global_horizontal(self) -> dict: def global_horizontal(self) -> dict:
""" """
Get global horizontal radiation surrounding the city object in W/m2 Get global horizontal radiation surrounding the city object in J/m2
:return: dict{DataFrame(float)} :return: dict{dict{[float]}}
""" """
return self._global_horizontal return self._global_horizontal
@global_horizontal.setter @global_horizontal.setter
def global_horizontal(self, value): def global_horizontal(self, value):
""" """
Set global horizontal radiation surrounding the city object in W/m2 Set global horizontal radiation surrounding the city object in J/m2
:param value: dict{DataFrame(float)} :param value: dict{dict{[float]}}
""" """
self._global_horizontal = value self._global_horizontal = value
@property @property
def diffuse(self) -> dict: def diffuse(self) -> dict:
""" """
Get diffuse radiation surrounding the city object in W/m2 Get diffuse radiation surrounding the city object in J/m2
:return: dict{DataFrame(float)} :return: dict{dict{[float]}}
""" """
return self._diffuse return self._diffuse
@diffuse.setter @diffuse.setter
def diffuse(self, value): def diffuse(self, value):
""" """
Set diffuse radiation surrounding the city object in W/m2 Set diffuse radiation surrounding the city object in J/m2
:param value: dict{DataFrame(float)} :param value: dict{dict{[float]}}
""" """
self._diffuse = value self._diffuse = value
@property @property
def beam(self) -> dict: def direct_normal(self) -> dict:
""" """
Get beam radiation surrounding the city object in W/m2 Get beam radiation surrounding the city object in J/m2
:return: dict{DataFrame(float)} :return: dict{dict{[float]}}
""" """
return self._beam return self._direct_normal
@beam.setter @direct_normal.setter
def beam(self, value): def direct_normal(self, value):
""" """
Set beam radiation surrounding the city object in W/m2 Set beam radiation surrounding the city object in J/m2
:param value: dict{DataFrame(float)} :param value: dict{dict{[float]}}
""" """
self._beam = value self._direct_normal = value
@property @property
def lower_corner(self): def lower_corner(self):
@ -299,3 +303,19 @@ class CityObject:
Set the list of neighbour_objects and their properties associated to the current city_object Set the list of neighbour_objects and their properties associated to the current city_object
""" """
self._neighbours = value self._neighbours = value
@property
def beam(self) -> dict:
"""
Get beam radiation surrounding the city object in J/m2
:return: dict{dict{[float]}}
"""
return self._beam
@beam.setter
def beam(self, value):
"""
Set beam radiation surrounding the city object in J/m2
:param value: dict{dict{[float]}}
"""
self._beam = value

Some files were not shown because too many files have changed in this diff Show More