From efac4c394869aadb614f427523812b1950115017 Mon Sep 17 00:00:00 2001 From: s_ranjbar Date: Wed, 10 Apr 2024 10:27:10 -0400 Subject: [PATCH] Costing initiated The classes and scripts of costs library are copied in scripts folder fix: updating the energy system catalogue parameter importer fix: units are fixed in the sizing and simulation modules fix: adding costing workflow feat: new function created to store current and new system analysis results fix: updating the code to implement all the changes feat: new attributes added to energy system catalogue fix: samll bug in calculating capital cost of TES is solved feat: a new method for calculating peak dhw demand is created in building class fix: small bug in generation system class of CDM is fixed fix: small issues in current system simulation and sizing modules are resolved feat: new class called EnergySystemsSimulationFactory is created to handle all the system simulation models fix: the operational cost class is modified and completed fix: slight changes before merge --- .../cost/montreal_complete_cost_catalog.py | 23 +- .../data_models/cost/fuel.py | 32 +- .../generation_system.cpython-39.pyc | Bin 3348 -> 3601 bytes .../non_pv_generation_system.cpython-39.pyc | Bin 10333 -> 11617 bytes .../pv_generation_system.cpython-39.pyc | Bin 5516 -> 5569 bytes .../thermal_storage_system.cpython-39.pyc | Bin 3839 -> 4148 bytes .../energy_systems/generation_system.py | 11 +- .../non_pv_generation_system.py | 57 ++- .../energy_systems/pv_generation_system.py | 7 +- .../energy_systems/thermal_storage_system.py | 14 +- .../montreal_custom_catalog.cpython-39.pyc | Bin 7647 -> 7643 bytes ...eal_future_system_catalogue.cpython-39.pyc | Bin 14508 -> 14875 bytes .../energy_systems/montreal_custom_catalog.py | 2 +- .../montreal_future_system_catalogue.py | 50 ++- hub/city_model_structure/building.py | 78 ++++ .../generation_system.cpython-39.pyc | Bin 4211 -> 4877 bytes .../non_pv_generation_system.cpython-39.pyc | Bin 12484 -> 14199 bytes .../thermal_storage_system.cpython-39.pyc | Bin 2887 -> 3320 bytes .../energy_systems/generation_system.py | 21 +- .../non_pv_generation_system.py | 89 +++- .../energy_systems/thermal_storage_system.py | 17 + hub/data/costs/montreal_costs_completed.xml | 144 +++--- .../montreal_future_systems.xml | 411 ++++++++++++++---- hub/helpers/constants.py | 1 + ...om_energy_system_parameters.cpython-39.pyc | Bin 5407 -> 5405 bytes ...e_energy_systems_parameters.cpython-39.pyc | Bin 6762 -> 6888 bytes ...ontreal_custom_energy_system_parameters.py | 2 +- ...ntreal_future_energy_systems_parameters.py | 18 +- main.py | 22 +- scripts/costs/capital_costs.py | 347 ++++++++++----- scripts/costs/cost.py | 57 ++- scripts/costs/end_of_life_costs.py | 4 +- scripts/costs/total_maintenance_costs.py | 4 +- scripts/costs/total_operational_costs.py | 139 +++--- scripts/energy_system_analysis_report.py | 113 ++--- scripts/energy_system_retrofit_results.py | 59 +++ scripts/energy_system_sizing.py | 34 +- ...gy_system_sizing_and_simulation_factory.py | 33 ++ scripts/random_assignation.py | 9 +- scripts/system_simulation.py | 25 +- .../system_simulation_models/archetype13.py | 100 +++++ .../test_systems_catalog.cpython-39.pyc | Bin 2539 -> 2051 bytes .../test_systems_factory.cpython-39.pyc | Bin 6499 -> 5244 bytes tests/test_systems_catalog.py | 6 +- tests/test_systems_factory.py | 2 +- 45 files changed, 1430 insertions(+), 501 deletions(-) create mode 100644 scripts/energy_system_retrofit_results.py create mode 100644 scripts/energy_system_sizing_and_simulation_factory.py create mode 100644 scripts/system_simulation_models/archetype13.py diff --git a/hub/catalog_factories/cost/montreal_complete_cost_catalog.py b/hub/catalog_factories/cost/montreal_complete_cost_catalog.py index c66668ca..b41aec5d 100644 --- a/hub/catalog_factories/cost/montreal_complete_cost_catalog.py +++ b/hub/catalog_factories/cost/montreal_complete_cost_catalog.py @@ -146,16 +146,31 @@ class MontrealNewCatalog(Catalog): fuel_variable_units = item['variable']['@cost_unit'] fuel_fixed_monthly = None fuel_fixed_peak = None - if fuel_type == 'electricity': + density = None + density_unit = None + lower_heating_value = None + lower_heating_value_unit = None + if fuel_type == 'Electricity': fuel_fixed_monthly = float(item['fixed_monthly']['#text']) fuel_fixed_peak = float(item['fixed_power']['#text']) / 1000 - elif fuel_type == 'gas': - fuel_fixed_monthly = float(item['fixed_monthly']['#text']) + else: + if item['fixed_monthly']: + fuel_fixed_monthly = float(item['fixed_monthly']['#text']) + if item['density']: + density = float(item['density']['#text']) + density_unit = item['density']['@density_unit'] + if item['lower_heating_value']: + lower_heating_value = float(item['lower_heating_value']['#text']) + lower_heating_value_unit = item['lower_heating_value']['@lhv_unit'] fuel = Fuel(fuel_type, fixed_monthly=fuel_fixed_monthly, fixed_power=fuel_fixed_peak, variable=fuel_variable, - variable_units=fuel_variable_units) + variable_units=fuel_variable_units, + density=density, + density_unit=density_unit, + lower_heating_value=lower_heating_value, + lower_heating_value_unit=lower_heating_value_unit) fuels.append(fuel) heating_equipment_maintenance = float(entry['maintenance']['heating_equipment']['#text']) / 1000 cooling_equipment_maintenance = float(entry['maintenance']['cooling_equipment']['#text']) / 1000 diff --git a/hub/catalog_factories/data_models/cost/fuel.py b/hub/catalog_factories/data_models/cost/fuel.py index f785cc59..5eb4866c 100644 --- a/hub/catalog_factories/data_models/cost/fuel.py +++ b/hub/catalog_factories/data_models/cost/fuel.py @@ -16,13 +16,21 @@ class Fuel: fixed_monthly=None, fixed_power=None, variable=None, - variable_units=None): + variable_units=None, + density=None, + density_unit=None, + lower_heating_value=None, + lower_heating_value_unit=None): self._fuel_type = fuel_type self._fixed_monthly = fixed_monthly self._fixed_power = fixed_power self._variable = variable self._variable_units = variable_units + self._density = density + self._density_unit = density_unit + self._lower_heating_value = lower_heating_value + self._lower_heating_value_unit = lower_heating_value_unit @property def type(self): @@ -58,13 +66,33 @@ class Fuel: """ return self._variable, self._variable_units + @property + def density(self) -> Union[tuple[None, None], tuple[float, str]]: + """ + Get fuel density in given units + :return: None, None or float, str + """ + return self._density, self._density_unit + + @property + def lower_heating_value(self) -> Union[tuple[None, None], tuple[float, str]]: + """ + Get lower heating value in given units + :return: None, None or float, str + """ + return self._lower_heating_value, self._lower_heating_value_unit + 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] + 'units': self.variable[1], + 'density': self.density[0], + 'density unit': self.density[1], + 'lower heating value': self.lower_heating_value[0], + 'lower heating value unit': self.lower_heating_value[1] } } diff --git a/hub/catalog_factories/data_models/energy_systems/__pycache__/generation_system.cpython-39.pyc b/hub/catalog_factories/data_models/energy_systems/__pycache__/generation_system.cpython-39.pyc index f24740fdfa33410f66e60ef3ff8346d27b82f306..8166640084e47fcb5a739f513df9502ca9a0347f 100644 GIT binary patch delta 838 zcmZuvPiqrV5PxrzY&N_3C#Eskq`D1Sx=4e92kl7_EFKz=;=x>&nD?Sv?Iz4_4v|QO z77rc-9Zv=CiYGsTNYDBK*6$!99z+nGm(mu(4!mLB{N~SlznM4ot7frRD42{y94rq$ znIDUPc|)h`b;fwOPljY&E?8F+yqgkDT-G96W<*WY_t-A)7KJSud#qa$+9q=s@ttHg2 zr8)n51~yvheWR>eXgbFa>D}5XvcW!94YFd*7fX06Gp(OJ_Rk^|2&YN8_$~8dDFKbG za1usx+lwY6c^RMU8y6N;JO@kVCS*W`fNS_$f122BfzTWHGJBASRtde0TgHz>^bDbQ zFwFf*L~DfJ!)N9O?qmu(>!w4pE+Z#;D4j#*oZ-OfIpIk5{ej_pKFY^v+D>yb?kDHVl8zUHwBt#Q9rz)u#LQ5&dJ}8nwaS4htP|SdO zESy*BWI0Zjz#u?vPS_@8@N}AS_RqBNyH$OiH;WoKv!G%+ZUI)XlK)zsuYy1Gt^a_y BtQ7zN delta 619 zcmZvZJxc>Y5QcX*m%Ga)cjm+BCGi^!IVI6DWza^81i?nGQE?+g(S*%40aIyZA%wBA zv(!!ytOP}Dtpp*Ze?SYtN_18*0l9%0-rZ;3*<-FVa+*{OX$k_qM0Bb zON7wo35qIlDJL%D)D7@7WSM7io2QP5YjL(poD5vEVp_+}zmS??F3J%FPy_i8C)i0V zjs^>XUFPU;AJ|bYh_IogoT-*{GI&)E?I34phlC=IB?oc! zc+!VzZDDo!7R!T77?0v^F3anbm_88O{JKM+)O< zbawTIs5h#@Yl%@gNbX2X$dZ6g#CE5o>@T^$O!?{~ltQ6N`bg3Tr7dlF6{qdi@opQZj@Qht zDX}aCiHb)>CDbdGejpwnh!*kA4}2gBl@LD&5EZJ7f~b%XANlAHDuRk~XY(K}yFy}D zd-v{~Gk5OJ+_`t=_Fhf-(q3;Jhga^3baYk!2fj;udyIYKrT@ZnoXVwmg-baUK2@b` zot@{;@uO@q70k5sEjD1C%IJOS?n&Ug>nLA zr^2C0$DX7cK6EO+21y?QK5|kBfO4W^Q zMxM|N`9#`KX(a@FDCDTLL%ud5d1R_Sp;A3No>MEWvYwqP=8SY+)rz`2p{5Nvqve#O zH2Ypn+0oZ%VXf6<>BT}JH!U0LR6(U_qe#_C7_{1Ol6vtcQ*|o_X|WyyhU;E!_9HpS zW@Q)!S0CVAH(ic1o^A+u8ll_sI0<0$1)lcei|$?)dzjXTVl#Z~d0S{;pARgfCeNg- z#K)dyy7{A44@T<2@xXpDZo*BX9tzmxXpa%31`DK*b+}!_74? z;-3}-JL;VXOd$1CPm>*G9RdY>bO#E~RT446THtiV57&bdR03l{Gqzj$dKUsu!l%J5 zC^ig;d+{sD(-u4S-3UBovgbkpc)qbGLH8|mX|?OJ9)ah~F4K*M5VE7)gTRa637!?& z>?rpla1Je<7b13)4G6r3R=VI2PV2i(n$TfK9YEkb3^acsgzYGU2)qxSEuXhp#mx|+ zAHg3j7lr+H1~wva5h*eFv-M3&Bb$)%CA{9oqUtS6#%5%EgN!*#bPJ+a;HB`_mMGgW zKfz4wg~tAs$Fe6nq_SI8oaOH$&3Q&SmF}36)sP1{)!pKSt7dg=91> zhx)DFk7MJK_p5A1uXvYo?!s&@{|Ef+Z>w3v;HnUWAt5|7_CI_gX@jso(9=^YF|t#t zy_0v?I7xRR(9Lu3S&;Nw*M$r^zI-`RV`0YPS+9Vj^OI1SnwN8kfvk$)eFw3d3Mqc&4;i#ff&5m{q(K zSX#B5N;1=#oLcH$IWJ~BuMCV8BPB)ZK?i9)6EYKSCTf{r#gVcz2W2&ovIzEm18^+S1>eS{e#$ZcWt*F_eM{MXqiiQo7S3ilrw5qEmM-a}y>KHwbo)f55L?3{ zZW8v3hZ}h3(*1+;FK16FW)`ilL?0i6t6~VQiPi3Hd)Srih8tp^&yW2pZp;pNnlPqe zS7H}@A8(prHIK4W6WziD%XyTA!hiU@>@H<~RlKsC$)$BYqvmq5ydo;@XQs@9LN#hW zeDt#@+`QAt@pv~gaqIn?uNR0k41H2Pj7wpNOMyDddgD2b9)oA33xR)PXyG6No4SR+ E0sE?TZ2$lO delta 1882 zcmaKsTWs4@7{`4Q*LAbRNt>i;(#COao7`lDZ0iPXlU>!ZA-Z;4hC+p##z6{7)^MC6 zTJgffMd}2CecCiG8WV53@Gv6rFf@cfngkM>fIXl{lNdZ8p*5vzYi!1+gySk)k?35}p_8Ih&$sT7 zvm~HT>Qk?B#l#yN&lQu8b84X-gxi52+;XasL?_vdY}|+4iQTvg3LoNi@@6mO`Ez|b zhl!3EwHNO4xDYL@$!0Ym`9kvsN_i&~J zTnoM^kJw?mQ1}Su2Cv7$cF030T;#wPkHfLhdCP@;7?DrFH}r-4sNF2=PrQmq5*EB} zh=$*{oM;lMFX6lJx0dP@s^6fRgtz@(*wZ@ze?{K4B=bmq4<8TTuv81Ew&C>150>g~ zRDXt7qVu`OaD<;fjVH_9um^?Tup15xoUl5IRxk)-F%hzIE;obsFncqeEVK8b@CWvP zvv+J{UzR5~AL(RovCH0v!k-*?VhhSnmxAqJrKvDWP&$sG+;a>#AB#n-_QQUh!$IJ< zKkn-`6X;L)B_4HVWQ;SegOt3+>%s>L{E~czOvCO}WLrC+=~bgitBcFc>e6w|STUNF zdY7@ZT~jrL%1iY|!3`%k@Cu$H|rcJIrWBQ|YYoT@MB%e6`Y P7lAX_YV)F-;NkoSPV0k$ diff --git a/hub/catalog_factories/data_models/energy_systems/__pycache__/pv_generation_system.cpython-39.pyc b/hub/catalog_factories/data_models/energy_systems/__pycache__/pv_generation_system.cpython-39.pyc index 7fc22430038fb82cb91c6630d99d744b2f85cb4b..489baa3452e7fb635dc4d7dbe32c6541d28783a1 100644 GIT binary patch delta 311 zcmeCtKB&!`$ji&c00b8fs-^wh$lJrn$Uk`&<6kLZkRTL**dRsBKwO+Oc_WiJTa8c( zQ!mHlGfdvRQJh7od8y@zNja&S9FtX;Qv`1D=auFrr545Kr^T1%WtMDiXVzp?6~4t% zoSKtX!~!&>h!sfO;x4W%E=kReFR3g@En))+voF5N!pOo2WKO=qx`UBrb1&O{4lWTO z-x-LD`zDw1H*(Z4WC^CQ%@NxCo4Rpo}vR7k5qW<8Ne1VVfhk zSzMroQCRR6Q)WsPS8jevYK}r)Vs2^?$L3{%xr|Dhf<+*26mf$HLl6PCqDTtF<(%v$ iY&y9?IFiw6@-1Np*7q#`Si~kPiYPPkZFUo>W&{B3rb4;^ diff --git a/hub/catalog_factories/data_models/energy_systems/__pycache__/thermal_storage_system.cpython-39.pyc b/hub/catalog_factories/data_models/energy_systems/__pycache__/thermal_storage_system.cpython-39.pyc index 8ee8d1d881afa339a167407250fda2793b60f30d..31ec81fcad14bf60fe0beec89c478ce31511fc7d 100644 GIT binary patch delta 1003 zcmZuv&1=+95YNkIH`(kaySCep-EC`HY;CI6Dhh%ts0-TDB8XH{N(@QV$hwu6D0D3p zu~0$K!(2pr6weCv13d~}{10*yEa=&Lbte6wNG8l=9>3pv?>F=2@9>79JDR2nXuYo{ zZcdfn>0emANNcq41qNTRm?v1G$E=Ju2tQfJ@UvdU>su8bGppqFdjqS2RbCS$!8&kN zFspF|45b2HaiXRt$7~16W%LYsI18V}3#DakS+Mh-fV)`BR6)u-s>S{GZQqH4&Mn(r z4BEEq+;iL@S~7K5%e)_m`%+P8NN@5zbdgkHIruGYRLi*F#$Fhuj2H%d$bPDh?Ewa5 z3EGF*B&_8=rlNz0&cK}fIu)%VdIr%^IITQLWz>*yK4EMs>#2-7GG^gr{#zR$>uA_${1|ip-!j~JE zPU*hOh_GW>{L_(5^zaFMRUazL!X4eqtZQ(w&|n&@6lPcj-WIOqsOs)v7|M?vn{WE< zFt{Hsa4Mx+O&2~tw_uid4bB$p3h`nw@W3q|YEg$dwTP2yGs}|)rct$I`>yW=PH5%q zwzK5(SaugXk>82_fB$i0-#`yf!uR5|%%nfkmed1V#qmg3f6`UTQ zCkkkMIaRf-MWT#!CaO4H5No#Www*9^{dU{7_h}d*UNUObLJd7Uia}wLBryE8bX{bs zyzB5wEBDfZ6TM|B{0RQzOYmGD15-Ce>lCKP3rWqKgjw)RZkYU*#A+WB}6GRaa zC7=gyb(0*$i>E+DMbAMzdm8X2{s9BxC0aF+L}hBIkLvlp>TbTSwb@_$)KJPQ!}#&J zbmN;`RquJHMr(}p8A8G$u32Ph9@)ADU-@L0ZtC63Eb^Z)!J_(A7PjHG)t{Dm(-u(e zV%nJLIas&ORy0E;AA}WX*^jJH!m9lm9*CE2omxZ_jyY?AkEQ8u!wgjogc%u=J^!7^RDp#e0jV~eW5Y|#Ihr4ky(p6 zcLE;ELl@}K&@s7N9@dc?-%Rx4FwIKwcz7={nLD~QSxP2L|C@Ob)eD&EJZ#7l&W6|& z4G7g?RU7O?*nsQm^g}Yf9A_^-3~ zk|cjG@`0u!okJ*dAsEMRQ%Q?g-G36geywUqKM2d_DD?a*R(1;d{sDO9x2>%ArDP7)x@Y-&tAQF^|F|F^5n^@iP8C{$QDH79)9`uoA2?TZ)QFZtPBL?K)}0&eb3+T zF2~;luuDLu?ve#TGC#WCI-Or?Jbmh!n$EKnau9HU!4k`hxKc1` zH!up#W7#O+a#<-VSS{8_A4K&74l$r;NvmSrP>Kasx3DA|m`EaNr99L^c#nU0o&;WOH^ev0-}K9fD-`T=OkG5J~R;+&Z1aJ(G8+pU;KysHuGK}1M-)HnRW6%1e1wj zs5rtPIqg$H^hYpVur-Si#8ry_qjH>uqY$nty8ic}rp&vc5uvAt#ad0jk0lI^_=Mq_ zSWP8dmDQ3?V&EU7U&Gn^uz;d}oSBZ|n~Xx2JjC9{4yZB)I1NYxU{}P&Ao=TCYX`>! zfQyNP1wF|m1B%nfk(_XbzKisSH{6m8bwz)&DZ65sF`f^p($;!^Ue#{RyTa>X7nmRW zF6eL;5CQOp6LcZgxv@)!xg6_t_;`aHgbvY9%`FS(z-(}`-`mkk>Vm2*sp%qnDsvjC zrDwkjS0yj$gkYY*b<~@Ij~C<@ZHWC^?uxdAiRHa4R~GRoAsS)Z$LQk=)| zZtHX6x3ys+=95I9Fu92=R)Y6Gzb0b6Y{^ld$TX}s7L09wAk5g(xoTd-&bOs|$JG+P zRoCpS7lZ`Mja7rx8tc+8EsqAfp;;2p21o%ifDXU~z(v3nfGvt!UGzn3u6dTNSWcNe bMhC=fV?ZeyP9{u8@|mOI6jyUzrSiW3HIN@= delta 1448 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 TEG3o!F7-5$&+@H32MzxMEESY& delta 2412 zcmb7_ZETZO6vv<2_jTPXE394jO4-(}YxmlTB3TqrVnQTrh_J0Kdmf-$*KN1$VB?m7 zK^X`-cM}yAvmv5HK=Jt?Vj?7vs4XM~r#x@alx;VR)jKblkJB%rd2=?=#H`X-eC3 zKz+{Gs^!($D<F5RKULp0| zOf;|tSqC#ZP(7#~)BtJ#HG&#JO`s;wMWDr?cF+<~FQ^B!5!A~bYml2@&*)GSMhY#3)limB8lSp#2gDO(;hF&)!012ZxcD`q9k!@R7K zHL+&a!dh7yGqY^D{ZK)|Cu;U#%~RTuD~zm@T=%KRENirt3*~q)?2pEWhr?t3zTj|h zb0{2&jU{!RG_G{6a#ixb3hdJh^t>X>+wecN=^`>@Og9a{)bc)}o zDc~JekM^pel5e-R@oP0jR*i<-@&BWtFjq6alz-P!#skF$^>6E5vNnJ@<)u={_A4Xt z(D0xViA7TxD=v!jP=nlB7zt~ z%nLfq_uFbUzerrQ4dmeg<-sF5d0GRVY~!o!#iWk=?YDTa#6BSxAF@Dakr@*!5gqJi zTIh1p<4!o3Bi)}4ISbk*3*J+G>X38b-2(5>PO=5Y*-V-tvr-}lY`I{|16wZGG%SbZ z&T0~QXG2K=_2Eg0lczXpMnn;UlY zvJHj+3|yuBT-oY!5ko|q3zS(-WOiby23twMRgA+!W$R*>x+ik`l(if^UBtg5Zu zhx<$-L_2tKbN~@Vh^BfTMoIp8RTb$}f2eX3vX7TI2T6qQcXm2(y~`jj6kUuT!hMpxe}eB?Tn^DHtYAgTa_W zL%}c|gO=e}rbQGP% zA&9W^w!*NqR_q}561&czab5`tu#Ku8*BD4g<~)+-^}%plSxM;-b?HPFsqNmu0QZWr z3nr#johzG6>_wla5n~8Z_`T8;8Qz_s%X~*wa|O*EmZ(CME5eEpCt#P`=LLKBN*fLS zK}8M)lSZNO!T+B~D*t&WwHr1%!f)2yXg!IAox*)}RFo?!eHX2w@OcaOz{FWq=XR6Y zm(b@hVxG@yVEdGS{Rj-k*NFSD%w^)c8g^FqqJQSY5e<^mhLs3C4HMAO{WyP+mwT4L zty$@*(NBZnA|LTwUi=Qeb*YV1*Chy}PpbK6I zdKU0UVOL}zGPFHX(FYB_b%>5tY)XYS(Q0%O?g1Q$k2@z(5q^wlNQvGe>ZO)0VikLc zj6}3lAns{fMsoN-+w1Mv{7FkR7L0}Z29?;hA(>())7=RC*eD()#nYkZ5f=~_d56zq WtTmOH@=a@b+~*{psIU0;X#WK~j66>O diff --git a/hub/catalog_factories/energy_systems/montreal_custom_catalog.py b/hub/catalog_factories/energy_systems/montreal_custom_catalog.py index d731f781..d3e37e36 100644 --- a/hub/catalog_factories/energy_systems/montreal_custom_catalog.py +++ b/hub/catalog_factories/energy_systems/montreal_custom_catalog.py @@ -87,7 +87,7 @@ class MontrealCustomCatalog(Catalog): cooling_efficiency=cooling_efficiency, electricity_efficiency=electricity_efficiency, energy_storage_systems=storage_systems, - dual_supply_capability=False + domestic_hot_water=False ) _equipments.append(generation_system) diff --git a/hub/catalog_factories/energy_systems/montreal_future_system_catalogue.py b/hub/catalog_factories/energy_systems/montreal_future_system_catalogue.py index d2eb35c3..f234ace4 100644 --- a/hub/catalog_factories/energy_systems/montreal_future_system_catalogue.py +++ b/hub/catalog_factories/energy_systems/montreal_future_system_catalogue.py @@ -121,12 +121,30 @@ class MontrealFutureSystemCatalogue(Catalog): 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) - dual_supply_capability = None - if non_pv['dual_supply_capability'] is not None: - if non_pv['dual_supply_capability'] == 'True': - dual_supply_capability = True + dhw = None + if non_pv['domestic_hot_water'] is not None: + if non_pv['domestic_hot_water'] == 'True': + dhw = True else: - dual_supply_capability = False + 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 + + heat_supply_temperature = None + cooling_supply_temperature = None + number_of_units = non_pv['number_of_units'] non_pv_component = NonPvGenerationSystem(system_id=system_id, name=name, @@ -160,7 +178,12 @@ class MontrealFutureSystemCatalogue(Catalog): cooling_efficiency_curve=cooling_efficiency_curve, distribution_systems=distribution_systems, energy_storage_systems=energy_storage_systems, - dual_supply_capability=dual_supply_capability) + domestic_hot_water=dhw, + heat_supply_temperature=heat_supply_temperature, + cooling_supply_temperature=cooling_supply_temperature, + number_of_units=number_of_units, + 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'] @@ -187,7 +210,7 @@ class MontrealFutureSystemCatalogue(Catalog): 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 - + number_of_units = pv['number_of_units'] pv_component = PvGenerationSystem(system_id=system_id, name=name, system_type=system_type, @@ -205,7 +228,8 @@ class MontrealFutureSystemCatalogue(Catalog): width=width, height=height, distribution_systems=distribution_systems, - energy_storage_systems=energy_storage_systems) + energy_storage_systems=energy_storage_systems, + number_of_units=number_of_units) generation_components.append(pv_component) return generation_components @@ -284,6 +308,7 @@ class MontrealFutureSystemCatalogue(Catalog): layers = [insulation_layer, tank_layer] nominal_capacity = tes['nominal_capacity'] losses_ratio = tes['losses_ratio'] + heating_coil_capacity = None storage_component = ThermalStorageSystem(storage_id=storage_id, model_name=model_name, type_energy_stored=type_energy_stored, @@ -295,7 +320,8 @@ class MontrealFutureSystemCatalogue(Catalog): height=height, layers=layers, maximum_operating_temperature=maximum_operating_temperature, - storage_medium=medium) + storage_medium=medium, + heating_coil_capacity=heating_coil_capacity) storage_components.append(storage_component) for template in template_storages: @@ -303,7 +329,7 @@ class MontrealFutureSystemCatalogue(Catalog): storage_type = template['storage_type'] type_energy_stored = template['type_energy_stored'] maximum_operating_temperature = template['maximum_operating_temperature'] - height = template['physical_characteristics']['height'] + 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) @@ -322,6 +348,7 @@ class MontrealFutureSystemCatalogue(Catalog): nominal_capacity = template['nominal_capacity'] losses_ratio = template['losses_ratio'] volume = template['physical_characteristics']['volume'] + heating_coil_capacity = None storage_component = ThermalStorageSystem(storage_id=storage_id, model_name=model_name, type_energy_stored=type_energy_stored, @@ -333,7 +360,8 @@ class MontrealFutureSystemCatalogue(Catalog): height=height, layers=layers, maximum_operating_temperature=maximum_operating_temperature, - storage_medium=medium) + storage_medium=medium, + heating_coil_capacity=heating_coil_capacity) storage_components.append(storage_component) return storage_components diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index c01e5bf2..95f0f167 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -91,6 +91,9 @@ class Building(CityObject): else: logging.error('Building %s [%s] has an unexpected surface type %s.', self.name, self.aliases, surface.type) self._heating_consumption_disaggregated = {} + self._domestic_hot_water_peak_load = None + self._fuel_consumption_breakdown = {} + self._domestic_how_water_consumption_disaggregated = {} @property def shell(self) -> Polyhedron: @@ -470,6 +473,22 @@ class Building(CityObject): results[cte.YEAR] = [max(monthly_values)] 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 for x in monthly_values] + results[cte.YEAR] = [max(monthly_values)] + return results + @property def eave_height(self): """ @@ -841,6 +860,21 @@ class Building(CityObject): """ self._heating_consumption_disaggregated = value + @property + def domestic_how_water_consumption_disaggregated(self) -> dict: + """ + Get energy consumed for heating from different fuels in J + return: dict + """ + return self._domestic_how_water_consumption_disaggregated + + @domestic_how_water_consumption_disaggregated.setter + def domestic_how_water_consumption_disaggregated(self, value): + """ + Get energy consumed for heating from different fuels in J + return: dict + """ + self._domestic_how_water_consumption_disaggregated = value @property def lower_corner(self): @@ -855,3 +889,47 @@ class Building(CityObject): Get building upper corner. """ return [self._max_x, self._max_y, self._max_z] + + @property + def fuel_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: + if demand_type == cte.COOLING: + fuel_breakdown[cte.ELECTRICITY][cte.COOLING] = self.cooling_consumption[cte.YEAR][0] / 3600 + elif demand_type == cte.HEATING: + heating_fuels = [generation_system.fuel_type for generation_system in generation_systems] + if len(heating_fuels) > 1: + for fuel in heating_fuels: + if fuel == cte.ELECTRICITY: + fuel_breakdown[cte.ELECTRICITY][cte.HEATING] = self._heating_consumption_disaggregated[cte.ELECTRICITY][cte.YEAR][0] + elif fuel not in fuel_breakdown.keys(): + fuel_breakdown[fuel] = {cte.HEATING: self._heating_consumption_disaggregated[fuel][cte.YEAR][0]} + else: + fuel_breakdown[fuel][cte.HEATING] = self._heating_consumption_disaggregated[fuel][cte.YEAR][0] + else: + fuel = heating_fuels[0] + if fuel == cte.ELECTRICITY: + fuel_breakdown[cte.ELECTRICITY][cte.HEATING] = self.heating_consumption[cte.YEAR][0] + elif fuel not in fuel_breakdown.keys(): + fuel_breakdown[fuel] = {cte.HEATING: self.heating_consumption[cte.YEAR][0]} + else: + fuel_breakdown[fuel][cte.HEATING] = self.heating_consumption[cte.YEAR][0] + elif demand_type == cte.DOMESTIC_HOT_WATER: + for generation_system in generation_systems: + if generation_system.fuel_type == cte.ELECTRICITY: + fuel_breakdown[cte.ELECTRICITY][cte.DOMESTIC_HOT_WATER] = self.domestic_hot_water_consumption[cte.YEAR][0] + elif generation_system.fuel_type not in fuel_breakdown.keys(): + fuel_breakdown[generation_system.fuel_type] = {cte.DOMESTIC_HOT_WATER: self.domestic_hot_water_consumption[cte.YEAR][0]} + else: + 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 diff --git a/hub/city_model_structure/energy_systems/__pycache__/generation_system.cpython-39.pyc b/hub/city_model_structure/energy_systems/__pycache__/generation_system.cpython-39.pyc index eccddbabdb79d9d0d8117abe4780d961ececaa44..4d2de077ea9344ec2f963e480c00bba2c6a3687b 100644 GIT binary patch delta 1411 zcmbtUJ#5oJ6!s-);y6y+mX=azUANGrrl_bA46Uk4<*x!kBvcrh3Kb4f&qq7B2gwQr0c{1r^W_+8O((;5MVr=wIbl zP$`FVS5k(&@yi$hR)v0xokEA`RQzD}I;W)!4w9&-@|47FIc(RQz`lfu=9*gIRD5p@ zg$WN^<{C%3>DTx@u?M#0wX@BkBwbF_@)=l?9V~SJ2|HmGX1qwL=GUrtkScA9n$jm6-NxheNODQ;V>P>3_*Vkyhuj3v z>BHGpG=l9K*wPr;4854TEz7OQ1^_r2X*MW%rJiAAC^h0S`gAApB{RTWh!oQ^Vo8~5(AU3~* z-C%64y<-q~l!)^JJtGZKP2#C5iJd|!hJ;iCMTJGRgN)Hz($iRJn*NkV`u)bGrFo58 yOKV&Pga@wD3{HEF1cNSnzD`6-y#z{1mrX=6T5@FEgXdyGZ+#D4=L=t@2S delta 894 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 UUV@owcz9?zo$c-FP4#yF4?C6;ssI20 delta 2655 zcmb`JT}&KR6vubAuuE}2_~`Be3p2~k!m_e_Yzu6yMM`U>)fNOq?3cqZ40My-oz2}H zC?tj^CN{B{Ha!nD8tV&{plNA?Q6FloiP4uDO*CpYY1G)n_+a!6eb9RDP8aAh^Ck(G z%lyy%{m%3#)Y(+O2uadNlc03v3M*q3+>yNtQ{22 zl)_KjBj71r4AtYmq+z{j#9p>CsU%`aM4XnzjG(BQl$vP+r^653btT|<&rWgxpZFyB92OC0C~5#b|X#%g;9;;ah~P%IQF!n5=j;k@T4)4EU0&vGM2dk{sy|K}y)xK~!cva6yA8X@bI{O2 z9zM&uv8r>$_Hg1W=)!|7;Dk3_9b7jZt!MNE3Lk1SWAXGf=^0ey!5d5P2_!ska6LxQ zeJFgPL5Hgj=oYaX#;V-RSGn1}S9O-_HDc{Y;d_mBqk6EcFhMA<@i0GPI)1p0wL4Vv zGIziT^&|?vYN&ee!Nx)qs@byqo)FV%VKT1F`~eHzE8IaNv@d*0;RL1{f!N$9`R{w}?;UKo1Vw2lu(duEUgH_;PT{ z7hpIf@Nm`l4x5Y7Lp_6ogNTa20Plq5`r)m)*!)4J4SwT!Sa3PP8#n~l{X^^#>Z(UP zibC`OaeOQ7Q$EP_Angv@Z$ubCp%1rt@5oR5C3Oz?FvE?2r*|7==+x zs1@FAJX5WQ^im|;ZT8p&X5*}hte-V=!5i#|5&n4;rgHe{CKA=dy9y38Eo)_yRr9*l zp3owD%*g6E3NzFyN(Qlur$ikM)$`nu-^AL7PqUl{@zKxOlScScD4e7CI`W)`XZ7%K zv6*Ks!28WHew6Aj(nhZm1Qaf7yce*G>2fr*FkLtwuERreIV|xfs6Iv;J)Vfd67nL9 z3Sx_Qf9AC8o05|<5i>C*>64U1N=eGejBipQzBHbZ@r;<16*cXn3*Zxz6BrYp@TG_{ zp-OZNM0R>3KI&JP6h6zk^=}|6aBG*ptWblucD5FU%yTOAw*F74RfYS>I89D(G9(l} zV)E5$4e@ZTqq~k?ShAM1oXN-}cefz)VnFCuoOHoI9bNNu!xOqk3EgIdepg8mZRj>3 zw3rDkGeV1j&=n+f+6lGG+66(1<4GyW@wgyty2|i0gxSoUxK@xB{0&};mJk!hf2s424^jq*%W?=iTP5r1%B^3S+;Ri%JmxISd{$_ DS`Si= diff --git a/hub/city_model_structure/energy_systems/__pycache__/thermal_storage_system.cpython-39.pyc b/hub/city_model_structure/energy_systems/__pycache__/thermal_storage_system.cpython-39.pyc index c27735b2eddb3f577f02897987c86168167ee326..cb1c354330149703128170f0de36ce1d8e622d75 100644 GIT binary patch delta 814 zcmX>u_Ct~{k(ZZ?0SE-fRiZ_@+Q)IFgQz)a(eHs%d7AYPFGoDc-EZn2f+=alBAN>5@_oV=K2 zvbqpZa3xa_FG!BHEHS4v6-`HxFi=W)ax7~*cJne)Gt)Clv?sAGmlFf3ED`|`sFoIq z1I3IdA7!h;ZbVLEWol8eHONUd?D=w%xQvkkN;m^uVmR4~BVPdBcawK=C^33ZzQb`{ zP8yd9GC+x7kO}qnTna$oo?4;+4NV15Xez)$Qz0`?Aw039q!_HsswlOjv?$L?AuT6A zu>>qs1hxa+4M5{>iDHeOXrNRv6OdqFtI`U|n0$~~7`wvBQk>fAE?_%?3At`^JDa?& zCSwsd&{(ViFAJ1N24bkqntYS*a@xj#V)PbUacW6PY7sbYikLwLfP%LO6hcKHZMS&i z+R XnruailV@@@2_RAhm?bz_n_C6|lvA#5 delta 520 zcmew%d0dPyk(ZZ?0SLZdVM{CE*vL1Hk#WN0rHtJ??F=joQ9Qv6nu51PCNE?cnViLB z&!{$e6_YwUP>|72lWp=nCZEYp%uCt91~73={=mFJh8t*B5kH&|0J3hemF4G@=B7$b z-ovs*T@Wa;lBtLXB*9vim{XdHro2c9D5W%+o6B(W3Dz*|24 Union[None, List[EnergyStorageSystem]]: + def energy_storage_systems(self) -> Union[None, List[ThermalStorageSystem], List[ElectricalStorageSystem]]: """ Get energy storage systems connected to this generation system :return: [EnergyStorageSystem] @@ -138,3 +141,19 @@ class GenerationSystem(ABC): :param value: [EnergyStorageSystem] """ self._energy_storage_systems = value + + @property + def number_of_units(self): + """ + Get number of a specific generation unit + :return: int + """ + return self._number_of_units + + @number_of_units.setter + def number_of_units(self, value): + """ + Set number of a specific generation unit + :return: int + """ + self._number_of_units = value diff --git a/hub/city_model_structure/energy_systems/non_pv_generation_system.py b/hub/city_model_structure/energy_systems/non_pv_generation_system.py index fea1d7b2..e5df16cd 100644 --- a/hub/city_model_structure/energy_systems/non_pv_generation_system.py +++ b/hub/city_model_structure/energy_systems/non_pv_generation_system.py @@ -42,7 +42,11 @@ class NonPvGenerationSystem(GenerationSystem): self._cooling_output_curve = None self._cooling_fuel_consumption_curve = None self._cooling_efficiency_curve = None - self._dual_supply_capability = None + self._domestic_hot_water = None + self._heat_supply_temperature = None + self._cooling_supply_temperature = None + self._reversible = None + self._simultaneous_heat_cold = None @property def nominal_heat_output(self): @@ -429,21 +433,90 @@ class NonPvGenerationSystem(GenerationSystem): self._cooling_efficiency_curve = value @property - def dual_supply_capability(self): + def domestic_hot_water(self): """ - Get the capability of the generation component for simultaneous heat and cold production + Get the capability of generating domestic hot water :return: bool """ - return self._dual_supply_capability + return self._domestic_hot_water - @dual_supply_capability.setter - def dual_supply_capability(self, value): + @domestic_hot_water.setter + def domestic_hot_water(self, value): """ - Set the capability of the generation component for simultaneous heat and cold production + Set the capability of generating domestic hot water :return: bool """ - self._dual_supply_capability = value + self._domestic_hot_water = value + @property + def heat_supply_temperature(self): + """ + Get the hourly heat supply temperature + :return: list + """ + return self._heat_supply_temperature + + @heat_supply_temperature.setter + def heat_supply_temperature(self, value): + """ + set the hourly heat supply temperature + :param value: + :return: list + """ + self._heat_supply_temperature = value + + @property + def cooling_supply_temperature(self): + """ + Get the hourly cooling supply temperature + :return: list + """ + return self._heat_supply_temperature + + @cooling_supply_temperature.setter + def cooling_supply_temperature(self, value): + """ + set the hourly cooling supply temperature + :param value: + :return: list + """ + self._cooling_supply_temperature = value + + @property + def reversibility(self): + """ + Get the capability of generating both heating and cooling + + :return: bool + """ + return self._reversible + + @reversibility.setter + def reversibility(self, value): + """ + Set the capability of generating domestic hot water + + :return: bool + """ + self._reversible = value + + @property + def simultaneous_heat_cold(self): + """ + Get the capability of generating both heating and cooling at the same time + + :return: bool + """ + return self._simultaneous_heat_cold + + @simultaneous_heat_cold.setter + def simultaneous_heat_cold(self, value): + """ + Set the capability of generating domestic hot water at the same time + + :return: bool + """ + self._simultaneous_heat_cold = value diff --git a/hub/city_model_structure/energy_systems/thermal_storage_system.py b/hub/city_model_structure/energy_systems/thermal_storage_system.py index 0f7f6a12..30e1579e 100644 --- a/hub/city_model_structure/energy_systems/thermal_storage_system.py +++ b/hub/city_model_structure/energy_systems/thermal_storage_system.py @@ -22,6 +22,7 @@ class ThermalStorageSystem(EnergyStorageSystem): self._height = None self._layers = None self._maximum_operating_temperature = None + self._heating_coil_capacity = None @property def volume(self): @@ -86,3 +87,19 @@ class ThermalStorageSystem(EnergyStorageSystem): :param value: float """ self._maximum_operating_temperature = value + + @property + def heating_coil_capacity(self): + """ + Get heating coil capacity in Watts + :return: float + """ + return self._maximum_operating_temperature + + @heating_coil_capacity.setter + def heating_coil_capacity(self, value): + """ + Set heating coil capacity in Watts + :param value: float + """ + self._heating_coil_capacity = value diff --git a/hub/data/costs/montreal_costs_completed.xml b/hub/data/costs/montreal_costs_completed.xml index 64be368c..5ce1fde9 100644 --- a/hub/data/costs/montreal_costs_completed.xml +++ b/hub/data/costs/montreal_costs_completed.xml @@ -38,38 +38,33 @@ 25 - 47.62 - 47.62 + 550 + 550 15 - 47.62 - 47.62 + 700 + 700 15 - 47.62 - 47.62 + 850 + 850 15 - 47.62 - 47.62 + 480 + 480 15 - - 47.62 - 47.62 - 15 - - 47.62 - 47.62 + 350 + 350 15 - 47.62 - 47.62 + 300 + 300 15 @@ -90,26 +85,26 @@ - 47.62 - 47.62 + 50 + 50 15 - 47.62 - 47.62 + 80 + 80 15 - 47.62 - 47.62 + 850 + 850 15 - 47.62 - 47.62 + 50 + 50 15 @@ -128,20 +123,30 @@ - - 12.27 - 0 - 0.075 + + 12.27 + 0 + 0.075 + + - + 17.71 0.0640 + 0.777 + 47.1 - + + 1.2 + 0.846 + 42.6 - - 0.04 + + + 0.04 + + 18 @@ -201,78 +206,73 @@ 25 - 47.62 - 47.62 + 550 + 550 15 - 47.62 - 47.62 + 700 + 700 15 - 47.62 - 47.62 + 850 + 850 15 - 47.62 - 47.62 + 480 + 480 15 - - 47.62 - 47.62 - 15 - - 47.62 - 47.62 + 350 + 350 15 - 47.62 - 47.62 + 300 + 300 15 - + 622.86 622.86 15 - + - 0 + 0 0 15 - + 47.62 47.62 15 - + - 47.62 - 47.62 + 50 + 50 15 - 47.62 - 47.62 + 80 + 80 15 - 47.62 - 47.62 + 850 + 850 15 - 47.62 - 47.62 + 50 + 50 15 @@ -291,20 +291,28 @@ - + 12.27 0 0.075 - + 17.71 0.0640 + 0.777 + 47.1 - + + 1.2 + 0.846 + 42.6 - - 0.04 + + + 0.04 + + 18 diff --git a/hub/data/energy_systems/montreal_future_systems.xml b/hub/data/energy_systems/montreal_future_systems.xml index 769f87bd..4437fe62 100644 --- a/hub/data/energy_systems/montreal_future_systems.xml +++ b/hub/data/energy_systems/montreal_future_systems.xml @@ -5,11 +5,11 @@ 1 Water - - - - - + + + + + 981.0 4180.0 0.6 @@ -26,7 +26,7 @@ 4.7 23.5 0.95 - True + False natural gas @@ -50,7 +50,11 @@ - + True + + + + False 2 @@ -62,7 +66,7 @@ 6.15 30.8 0.95 - True + False natural gas @@ -86,7 +90,11 @@ - + True + + + + False 3 @@ -98,7 +106,7 @@ 8.8 44 0.95 - True + False natural gas @@ -122,7 +130,11 @@ - + True + + + + False 4 @@ -134,7 +146,7 @@ 12.3 61.5 0.95 - True + False natural gas @@ -158,7 +170,11 @@ - + True + + + + False 5 @@ -170,7 +186,7 @@ 4.0 35.2 0.95 - True + False natural gas @@ -194,7 +210,11 @@ - + True + + + + False 6 @@ -206,7 +226,7 @@ 4.0 35.2 0.95 - False + False natural gas @@ -230,7 +250,11 @@ - + True + + + + False 7 @@ -242,7 +266,7 @@ 2.5 25.0 0.96 - + False natural gas @@ -266,7 +290,11 @@ - + True + + + + False 8 @@ -278,7 +306,7 @@ 3.2 32.0 0.96 - + False natural gas @@ -302,7 +330,11 @@ - + True + + + + False 9 @@ -314,7 +346,7 @@ 4.5 45.0 0.95 - True + False natural gas @@ -338,7 +370,11 @@ - + True + + + + False 10 @@ -350,7 +386,7 @@ 3.5 35.0 0.95 - True + False natural gas @@ -374,7 +410,11 @@ - + True + + + + False 11 @@ -386,7 +426,7 @@ 5.3 53.0 0.95 - True + False natural gas @@ -410,7 +450,11 @@ - + True + + + + False 12 @@ -430,6 +474,7 @@ 1.048 + 13 @@ -441,10 +486,10 @@ 0 51.7 3.32 - + True Electricity Air - water + Water @@ -471,7 +516,11 @@ - False + False + + + + False 14 @@ -483,10 +532,10 @@ 0 279.3 3.07 - + True Electricity Air - water + Water @@ -513,7 +562,11 @@ - False + False + + + + False 15 @@ -525,10 +578,10 @@ 0 557 3.46 - + True Electricity Air - water + Water @@ -555,7 +608,11 @@ - False + False + + + + False 16 @@ -567,7 +624,7 @@ 0.90 - + False natural gas @@ -593,7 +650,11 @@ 6 - + True + + + + False 17 @@ -605,7 +666,7 @@ 0.95 - + False electricity @@ -631,11 +692,15 @@ 6 - + False + + + + False 18 - template Air-to-Water heat pump + template Air-to-Water heat pump with storage heat pump @@ -643,10 +708,10 @@ 3 - + True electricity Air - water + Water @@ -669,11 +734,15 @@ 6 - True + True + + + + True 19 - template Groundwater-to-Water heat pump + template Groundwater-to-Water heat pump with storage heat pump @@ -681,10 +750,10 @@ 3.5 - + True electricity Ground - water + Water @@ -707,11 +776,15 @@ 6 - True + True + + + + True 20 - template Water-to-Water heat pump + template Water-to-Water heat pump with storage heat pump @@ -719,10 +792,10 @@ 3.5 - + True electricity Water - water + Water @@ -745,7 +818,11 @@ 6 - True + True + + + + False 21 @@ -757,7 +834,7 @@ 0.90 - + False natural gas @@ -781,7 +858,11 @@ - + True + + + + False 22 @@ -793,7 +874,7 @@ 0.95 - + False electricity @@ -817,7 +898,11 @@ - + True + + + + False 23 @@ -829,10 +914,10 @@ 3 - + True electricity Air - water + Water @@ -853,7 +938,11 @@ - True + True + + + + True 24 @@ -865,10 +954,10 @@ 3.5 - + True electricity Ground - water + Water @@ -889,7 +978,11 @@ - True + True + + + + True 25 @@ -901,10 +994,10 @@ 3.5 - + True electricity Water - water + Water @@ -925,7 +1018,11 @@ - True + True + + + + True 26 @@ -945,7 +1042,51 @@ 1.0 + + False + + 27 + template domestic hot water heat pump + heat pump + + + + + + 3.5 + + electricity + Water + Water + + + + + + + + + + + + + + + + + + + + + 7 + + True + + + + False + @@ -970,8 +1111,9 @@ 1 sensible - - + + + 2 @@ -995,8 +1137,9 @@ 1 sensible - - + + + 3 @@ -1020,8 +1163,9 @@ 1 sensible - - + + + 4 @@ -1044,8 +1188,9 @@ 1 sensible - - + + + 5 @@ -1069,15 +1214,16 @@ 1 sensible - - + + + 6 template Hot Water Storage Tank thermal - HF 200 - + + 95.0 1 @@ -1088,47 +1234,74 @@ 0 1.5 Steel - + 1 sensible - - + + + + + + 7 + template Hot Water Storage Tank with Heating Coil + thermal + + + 95.0 + + 1 + 90.0 + + + 2 + 0 + 1.5 + Steel + + + + 1 + + sensible + + + 1 Polyurethane - - - - - - - + + + + + + + 0.028 2 Steel - - - - - - - + + + + + + + 18 - + - + @@ -1226,7 +1399,45 @@ 26 + + 8 + 4 pipe system with air source heat pump storage and gas boiler + schemas/ASHP+TES+GasBoiler.jpg + + heating + cooling + + + 21 + 18 + + + + 9 + 4 pipe system with air source heat pump storage and electric boiler + schemas/ASHP+TES+GasBoiler.jpg + + heating + cooling + + + 22 + 18 + + + + 10 + Domestic Hot Water Heat Pump with Coiled Storage + schemas/ASHP+TES+GasBoiler.jpg + + domestic_hot_water + + + 27 + + + PV+ASHP+GasBoiler+TES @@ -1306,6 +1517,14 @@ 6 + + PV+4Pipe+DHW + + 7 + 8 + 10 + + diff --git a/hub/helpers/constants.py b/hub/helpers/constants.py index 23fab290..a48718bd 100644 --- a/hub/helpers/constants.py +++ b/hub/helpers/constants.py @@ -292,6 +292,7 @@ WOOD = 'Wood' GAS = 'Gas' DIESEL = 'Diesel' COAL = 'Coal' +BIOMASS = 'Biomass' AIR = 'Air' WATER = 'Water' GEOTHERMAL = 'Geothermal' diff --git a/hub/imports/energy_systems/__pycache__/montreal_custom_energy_system_parameters.cpython-39.pyc b/hub/imports/energy_systems/__pycache__/montreal_custom_energy_system_parameters.cpython-39.pyc index 6c52da3bac95e6d124a8ac5df825a085d7cbce1d..4e8f384abb74ddbaf9b4e6e2542fab88b98c2098 100644 GIT binary patch delta 74 zcmbQQHCKx_k(ZZ?0SN9?D5TkLv?4qbOe#q8C&h%`n;TWSD3quR4Xgv}z)j#La0|G_ls9db zev1$+v1u>`zG#5^!uT>KPuv8^1+fU&znH29s1B&Tsp>EU;|VdoSz_p>WF@(it(I?D zOXL#VL99i?hv*Cq<$nJU$wO7NwYpJBgzs?-0 z7oq7koajB@%WrXA&+7{M_vTOvqUqrm^lzxaHRGuwT&)yL?;5+wMGzl(t#DK@AtFTq zH30Uo)R&>t^5bF@(O6K`~ajA3{ XBUQ(UXppi@vMnkl0mI4fBE^3J(1fh@ delta 538 zcmXYtO=uHA6vt;~KeL;}+Rg69HpQm4>c(1YX?yY7gNR7Mk3*p>+iuciO=>zj5d#x* zkRl$0%3O*k6&0@%PkIx2_uwfPJxWjBiZ|bc4$PnTfA9C+eEeBCUd~6koP}VW{M2jT z7Cz^1Xn13AOFJa~BxGq3Bv7d%<56N({4*|Le^53bU_2?FWcKwLSv)TktAvU52OkhxlYR{ykjBi6vp za}10snY{^)@1j7zGt92n#k*X2ZfLN2mA0bhyYAOR-@TqUGe4Ya-NzJtbha-T@owfN7hv^_hF}pLUsCL-x5tIFz+V(Lq}Bt>3;%Bgfp%obNC)(N&8f~gIuz@vRxIVYtvU`0m$ E1DWuJ(EtDd diff --git a/hub/imports/energy_systems/montreal_custom_energy_system_parameters.py b/hub/imports/energy_systems/montreal_custom_energy_system_parameters.py index 348df9fe..00f84d3b 100644 --- a/hub/imports/energy_systems/montreal_custom_energy_system_parameters.py +++ b/hub/imports/energy_systems/montreal_custom_energy_system_parameters.py @@ -113,7 +113,7 @@ class MontrealCustomEnergySystemParameters: else: _generic_storage_system = ThermalStorageSystem() _generic_storage_system.type_energy_stored = 'thermal' - _generation_system.energy_storage_systems = [_generic_storage_system] + _generation_system.energy_storage_systems = _generic_storage_system _generation_systems.append(_generation_system) return _generation_systems diff --git a/hub/imports/energy_systems/montreal_future_energy_systems_parameters.py b/hub/imports/energy_systems/montreal_future_energy_systems_parameters.py index 542608a0..8612f845 100644 --- a/hub/imports/energy_systems/montreal_future_energy_systems_parameters.py +++ b/hub/imports/energy_systems/montreal_future_energy_systems_parameters.py @@ -140,12 +140,13 @@ class MontrealFutureEnergySystemParameters: _generation_system.cooling_output_curve = archetype_generation_system.cooling_output_curve _generation_system.cooling_fuel_consumption_curve = archetype_generation_system.cooling_fuel_consumption_curve _generation_system.cooling_efficiency_curve = archetype_generation_system.cooling_efficiency_curve - _generation_system.dual_supply_capability = archetype_generation_system.dual_supply_capability + _generation_system.domestic_hot_water = archetype_generation_system.domestic_hot_water _generation_system.nominal_electricity_output = archetype_generation_system.nominal_electricity_output _generation_system.source_medium = archetype_generation_system.source_medium _generation_system.heat_efficiency = archetype_generation_system.heat_efficiency _generation_system.cooling_efficiency = archetype_generation_system.cooling_efficiency _generation_system.electricity_efficiency = archetype_generation_system.electricity_efficiency + _generation_system.reversibility = archetype_generation_system.reversibility _generic_storage_system = None if archetype_generation_system.energy_storage_systems is not None: _storage_systems = [] @@ -155,11 +156,18 @@ class MontrealFutureEnergySystemParameters: _generic_storage_system.type_energy_stored = 'electrical' else: _generic_storage_system = ThermalStorageSystem() - _generic_storage_system.type_energy_stored = 'thermal' + _generic_storage_system.type_energy_stored = storage_system.type_energy_stored + _generic_storage_system.height = storage_system.height + _generic_storage_system.layers = storage_system.layers + _generic_storage_system.storage_medium = storage_system.storage_medium _storage_systems.append(_generic_storage_system) - _generation_system.energy_storage_systems = [_storage_systems] - if archetype_generation_system.dual_supply_capability: - _generation_system.dual_supply_capability = True + _generation_system.energy_storage_systems = _storage_systems + if archetype_generation_system.domestic_hot_water: + _generation_system.domestic_hot_water = True + if archetype_generation_system.reversibility: + _generation_system.reversibility = True + if archetype_generation_system.simultaneous_heat_cold: + _generation_system.simultaneous_heat_cold = True _generation_systems.append(_generation_system) return _generation_systems diff --git a/main.py b/main.py index e9c69db6..f89d37c9 100644 --- a/main.py +++ b/main.py @@ -12,6 +12,11 @@ from hub.exports.exports_factory import ExportsFactory from scripts.energy_system_analysis_report import EnergySystemAnalysisReport from scripts import random_assignation from hub.imports.energy_systems_factory import EnergySystemsFactory +from scripts.energy_system_sizing import SystemSizing +from scripts.system_simulation import SystemSimulation +from scripts.costs.cost import Cost +from costs.constants import CURRENT_STATUS, SKIN_RETROFIT, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV, SYSTEM_RETROFIT_AND_PV +from scripts.energy_system_retrofit_results import system_results, new_system_results # Specify the GeoJSON file path geojson_file = process_geojson(x=-73.5681295982132, y=45.49218262677643, diff=0.0001) @@ -33,8 +38,21 @@ ExportsFactory('sra', city, output_path).export() sra_path = (output_path / f'{city.name}_sra.xml').resolve() subprocess.run(['sra', str(sra_path)]) ResultFactory('sra', city, output_path).enrich() -# Run EnergyPlus workflow energy_plus_workflow(city) random_assignation.call_random(city.buildings, random_assignation.residential_systems_percentage) EnergySystemsFactory('montreal_custom', city).enrich() -EnergySystemAnalysisReport(city, output_path).create_report() +SystemSizing(city.buildings).montreal_custom() +current_system = system_results(city.buildings) +new_system = system_results(city.buildings) +EnergySystemAnalysisReport(city, output_path).create_report(current_system, new_system) + +for building in city.buildings: + costs = Cost(building, retrofit_scenario=SYSTEM_RETROFIT_AND_PV).life_cycle + Cost(building, retrofit_scenario=SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV).life_cycle.to_csv(output_path / f'{building.name}_lcc.csv') + costs.loc['global_capital_costs', f'Scenario {SYSTEM_RETROFIT_AND_PV}'].to_csv(output_path / f'{building.name}_global_capital.csv') + costs.loc['global_operational_costs', f'Scenario {SYSTEM_RETROFIT_AND_PV}'].to_csv( + output_path / f'{building.name}_global_operational.csv') + + + + diff --git a/scripts/costs/capital_costs.py b/scripts/costs/capital_costs.py index 654218af..49f7efd0 100644 --- a/scripts/costs/capital_costs.py +++ b/scripts/costs/capital_costs.py @@ -11,15 +11,16 @@ import pandas as pd import numpy_financial as npf from hub.city_model_structure.building import Building import hub.helpers.constants as cte -from costs.configuration import Configuration -from costs.constants import SKIN_RETROFIT, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV, SYSTEM_RETROFIT_AND_PV -from costs.cost_base import CostBase +from scripts.costs.configuration import Configuration +from scripts.costs.constants import SKIN_RETROFIT, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV, SYSTEM_RETROFIT_AND_PV +from scripts.costs.cost_base import CostBase class CapitalCosts(CostBase): """ Capital costs class """ + def __init__(self, building: Building, configuration: Configuration): super().__init__(building, configuration) self._yearly_capital_costs = pd.DataFrame( @@ -28,25 +29,28 @@ class CapitalCosts(CostBase): 'B2010_opaque_walls', 'B2020_transparent', 'B3010_opaque_roof', - 'B10_superstructure', - 'D301010_photovoltaic_system', - 'D3020_heat_generating_systems', - 'D3030_cooling_generation_systems', + 'B1010_superstructure', + 'D2010_photovoltaic_system', + 'D3020_heat_and_cooling_generating_systems', 'D3040_distribution_systems', - 'D3080_other_hvac_ahu', + 'D3050_other_hvac_ahu', + 'D3060_storage_systems', + 'D40_dhw', 'D5020_lighting_and_branch_wiring' ], dtype='float' ) self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'] = 0 - self._yearly_capital_costs.loc[0]['B2020_transparent'] = 0 + self._yearly_capital_costs.loc[0, 'B2020_transparent'] = 0 self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'] = 0 - self._yearly_capital_costs.loc[0]['B10_superstructure'] = 0 - self._yearly_capital_costs.loc[0, 'D3020_heat_generating_systems'] = 0 - self._yearly_capital_costs.loc[0, 'D3030_cooling_generation_systems'] = 0 + self._yearly_capital_costs.loc[0, 'B1010_superstructure'] = 0 + self._yearly_capital_costs.loc[0, 'D2010_photovoltaic_system'] = 0 + self._yearly_capital_costs.loc[0, 'D3020_heat_and_cooling_generating_systems'] = 0 self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'] = 0 self._yearly_capital_costs.loc[0, 'D3080_other_hvac_ahu'] = 0 - self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] = 0 + self._yearly_capital_costs.loc[0, 'D3060_storage_systems'] = 0 + self._yearly_capital_costs.loc[0, 'D40_dhw'] = 0 + # self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] = 0 self._yearly_capital_incomes = pd.DataFrame( index=self._rng, @@ -60,26 +64,32 @@ class CapitalCosts(CostBase): self._yearly_capital_incomes.loc[0, 'Subsidies construction'] = 0 self._yearly_capital_incomes.loc[0, 'Subsidies HVAC'] = 0 self._yearly_capital_incomes.loc[0, 'Subsidies PV'] = 0 + self._yearly_capital_costs.fillna(0, inplace=True) + self._own_capital = 1 - self._configuration.percentage_credit + self._surface_pv = 0 + for roof in self._building.roofs: + self._surface_pv += roof.solid_polygon.area * roof.solar_collectors_area_reduction_factor def calculate(self) -> tuple[pd.DataFrame, pd.DataFrame]: + if self._configuration.retrofit_scenario in (SKIN_RETROFIT, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): + self.skin_capital_cost() + if self._configuration.retrofit_scenario in (SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): + self.energy_system_capital_cost() + + self.skin_yearly_capital_costs() + self.yearly_energy_system_costs() + self.yearly_incomes() + return self._yearly_capital_costs, self._yearly_capital_incomes + + def skin_capital_cost(self): """ - Calculate capital cost - :return: pd.DataFrame, pd.DataFrame + calculating skin costs + :return: """ surface_opaque = 0 surface_transparent = 0 surface_roof = 0 surface_ground = 0 - capital_cost_pv = 0 - capital_cost_opaque = 0 - capital_cost_ground = 0 - capital_cost_transparent = 0 - capital_cost_roof = 0 - capital_cost_heating_equipment = 0 - capital_cost_cooling_equipment = 0 - capital_cost_distribution_equipment = 0 - capital_cost_other_hvac_ahu = 0 - capital_cost_lighting = 0 for thermal_zone in self._building.thermal_zones_from_internal_zones: for thermal_boundary in thermal_zone.thermal_boundaries: @@ -91,144 +101,222 @@ class CapitalCosts(CostBase): surface_opaque += thermal_boundary.opaque_area * (1 - thermal_boundary.window_ratio) surface_transparent += thermal_boundary.opaque_area * thermal_boundary.window_ratio - peak_heating = self._building.heating_peak_load[cte.YEAR][0] / 1000 - peak_cooling = self._building.cooling_peak_load[cte.YEAR][0] / 1000 - - surface_pv = 0 - for roof in self._building.roofs: - surface_pv += roof.solid_polygon.area * roof.solar_collectors_area_reduction_factor - - self._yearly_capital_costs.fillna(0, inplace=True) - own_capital = 1 - self._configuration.percentage_credit - if self._configuration.retrofit_scenario in (SKIN_RETROFIT, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): - chapter = self._capital_costs_chapter.chapter('B_shell') - capital_cost_opaque = surface_opaque * chapter.item('B2010_opaque_walls').refurbishment[0] - capital_cost_transparent = surface_transparent * chapter.item('B2020_transparent').refurbishment[0] - capital_cost_roof = surface_roof * chapter.item('B3010_opaque_roof').refurbishment[0] - capital_cost_ground = surface_ground * chapter.item('B10_superstructure').refurbishment[0] - self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'] = capital_cost_opaque * own_capital - self._yearly_capital_costs.loc[0]['B2020_transparent'] = capital_cost_transparent * own_capital - self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'] = capital_cost_roof * own_capital - self._yearly_capital_costs.loc[0]['B10_superstructure'] = capital_cost_ground * own_capital - - if self._configuration.retrofit_scenario in (SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): - chapter = self._capital_costs_chapter.chapter('D_services') - capital_cost_pv = surface_pv * chapter.item('D301010_photovoltaic_system').initial_investment[0] - capital_cost_heating_equipment = peak_heating * chapter.item('D3020_heat_generating_systems').initial_investment[0] - capital_cost_cooling_equipment = peak_cooling * chapter.item('D3030_cooling_generation_systems').initial_investment[0] - capital_cost_distribution_equipment = peak_cooling * chapter.item('D3040_distribution_systems').initial_investment[0] - capital_cost_other_hvac_ahu = peak_cooling * chapter.item('D3080_other_hvac_ahu').initial_investment[0] - capital_cost_lighting = self._total_floor_area * chapter.item('D5020_lighting_and_branch_wiring').initial_investment[0] - self._yearly_capital_costs.loc[0]['D301010_photovoltaic_system'] = capital_cost_pv - self._yearly_capital_costs.loc[0, 'D3020_heat_generating_systems'] = capital_cost_heating_equipment * own_capital - self._yearly_capital_costs.loc[0, 'D3030_cooling_generation_systems'] = capital_cost_cooling_equipment * own_capital - self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'] = capital_cost_distribution_equipment * own_capital - self._yearly_capital_costs.loc[0, 'D3080_other_hvac_ahu'] = capital_cost_other_hvac_ahu * own_capital - self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] = capital_cost_lighting * own_capital + chapter = self._capital_costs_chapter.chapter('B_shell') + capital_cost_opaque = surface_opaque * chapter.item('B2010_opaque_walls').refurbishment[0] + capital_cost_transparent = surface_transparent * chapter.item('B2020_transparent').refurbishment[0] + capital_cost_roof = surface_roof * chapter.item('B3010_opaque_roof').refurbishment[0] + capital_cost_ground = surface_ground * chapter.item('B1010_superstructure').refurbishment[0] + self._yearly_capital_costs.loc[0, 'B2010_opaque_walls'] = capital_cost_opaque * self._own_capital + self._yearly_capital_costs.loc[0, 'B2020_transparent'] = capital_cost_transparent * self._own_capital + self._yearly_capital_costs.loc[0, 'B3010_opaque_roof'] = capital_cost_roof * self._own_capital + self._yearly_capital_costs.loc[0, 'B1010_superstructure'] = capital_cost_ground * self._own_capital + capital_cost_skin = capital_cost_opaque + capital_cost_ground + capital_cost_transparent + capital_cost_roof + return capital_cost_opaque, capital_cost_transparent, capital_cost_roof, capital_cost_ground, capital_cost_skin + def skin_yearly_capital_costs(self): + skin_capital_cost = self.skin_capital_cost() for year in range(1, self._configuration.number_of_years): - chapter = self._capital_costs_chapter.chapter('D_services') - costs_increase = math.pow(1 + self._configuration.consumer_price_index, year) self._yearly_capital_costs.loc[year, 'B2010_opaque_walls'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_opaque * self._configuration.percentage_credit + skin_capital_cost[0] * self._configuration.percentage_credit ) ) self._yearly_capital_costs.loc[year, 'B2020_transparent'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_transparent * self._configuration.percentage_credit + skin_capital_cost[1] * self._configuration.percentage_credit ) ) self._yearly_capital_costs.loc[year, 'B3010_opaque_roof'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_roof * self._configuration.percentage_credit + skin_capital_cost[2] * self._configuration.percentage_credit ) ) - self._yearly_capital_costs.loc[year, 'B10_superstructure'] = ( + self._yearly_capital_costs.loc[year, 'B1010_superstructure'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_ground * self._configuration.percentage_credit + skin_capital_cost[3] * self._configuration.percentage_credit ) ) - self._yearly_capital_costs.loc[year, 'D3020_heat_generating_systems'] = ( + + def energy_system_capital_cost(self): + chapter = self._capital_costs_chapter.chapter('D_services') + energy_system_components = self.system_components() + system_components = energy_system_components[0] + component_categories = energy_system_components[1] + component_sizes = energy_system_components[-1] + capital_cost_heating_and_cooling_equipment = 0 + capital_cost_domestic_hot_water_equipment = 0 + capital_cost_energy_storage_equipment = 0 + capital_cost_distribution_equipment = 0 + capital_cost_lighting = 0 + capital_cost_pv = self._surface_pv * chapter.item('D2010_photovoltaic_system').initial_investment[0] + # capital_cost_lighting = self._total_floor_area * \ + # chapter.item('D5020_lighting_and_branch_wiring').initial_investment[0] + for (i, component) in enumerate(system_components): + if component_categories[i] == 'generation': + capital_cost_heating_and_cooling_equipment += chapter.item(component).initial_investment[0] * component_sizes[i] + elif component_categories[i] == 'dhw': + capital_cost_domestic_hot_water_equipment += chapter.item(component).initial_investment[0] * \ + component_sizes[i] + elif component_categories[i] == 'distribution': + capital_cost_distribution_equipment += chapter.item(component).initial_investment[0] * \ + component_sizes[i] + else: + capital_cost_energy_storage_equipment += chapter.item(component).initial_investment[0] * component_sizes[i] + + self._yearly_capital_costs.loc[0, 'D2010_photovoltaic_system'] = capital_cost_pv + self._yearly_capital_costs.loc[0, 'D3020_heat_and_cooling_generating_systems'] = ( + capital_cost_heating_and_cooling_equipment * self._own_capital) + self._yearly_capital_costs.loc[0, 'D3040_distribution_systems'] = ( + capital_cost_distribution_equipment * self._own_capital) + self._yearly_capital_costs.loc[0, 'D3060_storage_systems'] = ( + capital_cost_energy_storage_equipment * self._own_capital) + self._yearly_capital_costs.loc[0, 'D40_dhw'] = ( + capital_cost_domestic_hot_water_equipment * self._own_capital) + # self._yearly_capital_costs.loc[0, 'D5020_lighting_and_branch_wiring'] = capital_cost_lighting * self._own_capital + capital_cost_hvac = capital_cost_heating_and_cooling_equipment + capital_cost_distribution_equipment + capital_cost_energy_storage_equipment + capital_cost_domestic_hot_water_equipment + return (capital_cost_pv, capital_cost_heating_and_cooling_equipment, capital_cost_distribution_equipment, + capital_cost_energy_storage_equipment, capital_cost_domestic_hot_water_equipment, capital_cost_lighting, capital_cost_hvac) + + def yearly_energy_system_costs(self): + chapter = self._capital_costs_chapter.chapter('D_services') + system_investment_costs = self.energy_system_capital_cost() + system_components = self.system_components()[0] + component_categories = self.system_components()[1] + component_sizes = self.system_components()[2] + for year in range(1, self._configuration.number_of_years): + costs_increase = math.pow(1 + self._configuration.consumer_price_index, year) + self._yearly_capital_costs.loc[year, 'D2010_photovoltaic_system'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_heating_equipment * self._configuration.percentage_credit + system_investment_costs[0] * self._configuration.percentage_credit ) ) - self._yearly_capital_costs.loc[year, 'D3030_cooling_generation_systems'] = ( + self._yearly_capital_costs.loc[year, 'D3020_heat_and_cooling_generating_systems'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_cooling_equipment * self._configuration.percentage_credit + system_investment_costs[1] * self._configuration.percentage_credit ) ) self._yearly_capital_costs.loc[year, 'D3040_distribution_systems'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_distribution_equipment * self._configuration.percentage_credit + system_investment_costs[2] * self._configuration.percentage_credit ) ) - self._yearly_capital_costs.loc[year, 'D3080_other_hvac_ahu'] = ( + self._yearly_capital_costs.loc[year, 'D3060_storage_systems'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_other_hvac_ahu * self._configuration.percentage_credit + system_investment_costs[3] * self._configuration.percentage_credit ) ) - self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] = ( + self._yearly_capital_costs.loc[year, 'D40_dhw'] = ( -npf.pmt( self._configuration.interest_rate, self._configuration.credit_years, - capital_cost_lighting * self._configuration.percentage_credit + system_investment_costs[4] * self._configuration.percentage_credit ) ) - - if (year % chapter.item('D3020_heat_generating_systems').lifetime) == 0: - reposition_cost_heating_equipment = ( - peak_heating * chapter.item('D3020_heat_generating_systems').reposition[0] * costs_increase - ) - self._yearly_capital_costs.loc[year, 'D3020_heat_generating_systems'] += reposition_cost_heating_equipment - - if (year % chapter.item('D3030_cooling_generation_systems').lifetime) == 0: - reposition_cost_cooling_equipment = ( - peak_cooling * chapter.item('D3030_cooling_generation_systems').reposition[0] * costs_increase - ) - self._yearly_capital_costs.loc[year, 'D3030_cooling_generation_systems'] += reposition_cost_cooling_equipment - - if (year % chapter.item('D3080_other_hvac_ahu').lifetime) == 0: - reposition_cost_hvac_ahu = ( - peak_cooling * chapter.item('D3080_other_hvac_ahu').reposition[0] * costs_increase - ) - self._yearly_capital_costs.loc[year, 'D3080_other_hvac_ahu'] = reposition_cost_hvac_ahu - - if (year % chapter.item('D5020_lighting_and_branch_wiring').lifetime) == 0: - reposition_cost_lighting = ( - self._total_floor_area * chapter.item('D5020_lighting_and_branch_wiring').reposition[0] * costs_increase - ) - self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] += reposition_cost_lighting - + # self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] = ( + # -npf.pmt( + # self._configuration.interest_rate, + # self._configuration.credit_years, + # system_investment_costs[5] * self._configuration.percentage_credit + # ) + # ) + # if (year % chapter.item('D5020_lighting_and_branch_wiring').lifetime) == 0: + # reposition_cost_lighting = ( + # self._total_floor_area * chapter.item('D5020_lighting_and_branch_wiring').reposition[0] * costs_increase + # ) + # self._yearly_capital_costs.loc[year, 'D5020_lighting_and_branch_wiring'] += reposition_cost_lighting if self._configuration.retrofit_scenario in (SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV): - if (year % chapter.item('D301010_photovoltaic_system').lifetime) == 0: - self._yearly_capital_costs.loc[year]['D301010_photovoltaic_system'] += ( - surface_pv * chapter.item('D301010_photovoltaic_system').reposition[0] * costs_increase + if (year % chapter.item('D2010_photovoltaic_system').lifetime) == 0: + self._yearly_capital_costs.loc[year, 'D2010_photovoltaic_system'] += ( + self._surface_pv * chapter.item('D2010_photovoltaic_system').reposition[0] * costs_increase ) - capital_cost_skin = capital_cost_opaque + capital_cost_ground + capital_cost_transparent + capital_cost_roof - capital_cost_hvac = ( - capital_cost_heating_equipment + - capital_cost_cooling_equipment + - capital_cost_distribution_equipment + - capital_cost_other_hvac_ahu + capital_cost_lighting - ) + for (i, component) in enumerate(system_components): + if (year % chapter.item(component).lifetime) == 0 and year != (self._configuration.number_of_years - 1): + if component_categories[i] == 'generation': + reposition_cost_heating_and_cooling_equipment = chapter.item(component).reposition[0] * component_sizes[i] * costs_increase + self._yearly_capital_costs.loc[year, 'D3020_heat_and_cooling_generating_systems'] += reposition_cost_heating_and_cooling_equipment + elif component_categories[i] == 'dhw': + reposition_cost_domestic_hot_water_equipment = chapter.item(component).reposition[0] * component_sizes[i] * costs_increase + self._yearly_capital_costs.loc[year, 'D40_dhw'] += reposition_cost_domestic_hot_water_equipment + elif component_categories[i] == 'distribution': + reposition_cost_distribution_equipment = chapter.item(component).reposition[0] * component_sizes[i] * costs_increase + self._yearly_capital_costs.loc[year, 'D3040_distribution_systems'] += reposition_cost_distribution_equipment + else: + reposition_cost_energy_storage_equipment = chapter.item(component).initial_investment[0] * component_sizes[i] * costs_increase + self._yearly_capital_costs.loc[year, 'D3060_storage_systems'] += reposition_cost_energy_storage_equipment + + def system_components(self): + system_components = [] + component_categories = [] + sizes = [] + energy_systems = self._building.energy_systems + for energy_system in energy_systems: + demand_types = energy_system.demand_types + generation_systems = energy_system.generation_systems + distribution_systems = energy_system.distribution_systems + for generation_system in generation_systems: + if generation_system.system_type != cte.PHOTOVOLTAIC: + heating_capacity = generation_system.nominal_heat_output or 0 + cooling_capacity = generation_system.nominal_cooling_output or 0 + installed_capacity = max(heating_capacity, cooling_capacity) + if cte.DOMESTIC_HOT_WATER in demand_types and cte.HEATING not in demand_types: + component_categories.append('dhw') + sizes.append(installed_capacity) + if generation_system.system_type == cte.HEAT_PUMP: + system_components.append(self.heat_pump_type(generation_system, domestic_how_water=True)) + elif generation_system.system_type == cte.BOILER and generation_system.fuel_type == cte.ELECTRICITY: + system_components.append(self.boiler_type(generation_system)) + else: + system_components.append('D302010_template_heat') + elif cte.HEATING or cte.COOLING in demand_types: + component_categories.append('generation') + sizes.append(installed_capacity) + if generation_system.system_type == cte.HEAT_PUMP: + item_type = self.heat_pump_type(generation_system) + system_components.append(item_type) + elif generation_system.system_type == cte.BOILER: + item_type = self.boiler_type(generation_system) + system_components.append(item_type) + else: + if cte.COOLING in demand_types and cte.HEATING not in demand_types: + system_components.append('D302090_template_cooling') + else: + system_components.append('D302010_template_heat') + + if generation_system.energy_storage_systems is not None: + energy_storage_systems = generation_system.energy_storage_systems + for storage_system in energy_storage_systems: + if storage_system.type_energy_stored == 'thermal': + component_categories.append('thermal storage') + sizes.append(storage_system.volume or 0) + system_components.append('D306010_storage_tank') + if distribution_systems is not None: + for distribution_system in distribution_systems: + component_categories.append('distribution') + sizes.append(self._building.cooling_peak_load[cte.YEAR][0] / 3.6e6) + system_components.append('D3040_distribution_systems') + return system_components, component_categories, sizes + + def yearly_incomes(self): + capital_cost_skin = self.skin_capital_cost()[-1] + system_investment_cost = self.energy_system_capital_cost() + capital_cost_hvac = system_investment_cost[-1] + capital_cost_pv = system_investment_cost[0] self._yearly_capital_incomes.loc[0, 'Subsidies construction'] = ( capital_cost_skin * self._archetype.income.construction_subsidy/100 @@ -236,4 +324,41 @@ class CapitalCosts(CostBase): self._yearly_capital_incomes.loc[0, 'Subsidies HVAC'] = capital_cost_hvac * self._archetype.income.hvac_subsidy/100 self._yearly_capital_incomes.loc[0, 'Subsidies PV'] = capital_cost_pv * self._archetype.income.photovoltaic_subsidy/100 self._yearly_capital_incomes.fillna(0, inplace=True) - return self._yearly_capital_costs, self._yearly_capital_incomes + + @staticmethod + def heat_pump_type(generation_system, domestic_how_water=False): + source_medium = generation_system.source_medium + supply_medium = generation_system.supply_medium + if domestic_how_water: + heat_pump_item = 'D4010_hot_water_heat_pump' + else: + if source_medium == cte.AIR and supply_medium == cte.WATER: + heat_pump_item = 'D302020_air_to_water_heat_pump' + elif source_medium == cte.AIR and supply_medium == cte.AIR: + heat_pump_item = 'D302050_air_to_air_heat_pump' + elif source_medium == cte.GROUND and supply_medium == cte.WATER: + heat_pump_item = 'D302030_ground_to_water_heat_pump' + elif source_medium == cte.GROUND and supply_medium == cte.AIR: + heat_pump_item = 'D302100_ground_to_air_heat_pump' + elif source_medium == cte.WATER and supply_medium == cte.WATER: + heat_pump_item = 'D302040_water_to_water_heat_pump' + elif source_medium == cte.WATER and supply_medium == cte.AIR: + heat_pump_item = 'D302110_water_to_air_heat_pump' + else: + heat_pump_item = 'D302010_template_heat' + return heat_pump_item + + @staticmethod + def boiler_type(generation_system): + fuel = generation_system.fuel_type + if fuel == cte.ELECTRICITY: + boiler_item = 'D302080_electrical_boiler' + elif fuel == cte.GAS: + boiler_item = 'D302070_natural_gas_boiler' + else: + boiler_item = 'D302010_template_heat' + return boiler_item + + + + diff --git a/scripts/costs/cost.py b/scripts/costs/cost.py index 7204b508..b040f063 100644 --- a/scripts/costs/cost.py +++ b/scripts/costs/cost.py @@ -11,10 +11,14 @@ import pandas as pd import numpy_financial as npf from hub.city_model_structure.building import Building from hub.helpers.dictionaries import Dictionaries - -from costs.configuration import Configuration -from costs import CapitalCosts, EndOfLifeCosts, TotalMaintenanceCosts, TotalOperationalCosts, TotalOperationalIncomes -from costs.constants import CURRENT_STATUS +from scripts.costs.configuration import Configuration +from scripts.costs.capital_costs import CapitalCosts +from scripts.costs.end_of_life_costs import EndOfLifeCosts +from scripts.costs.total_maintenance_costs import TotalMaintenanceCosts +from scripts.costs.total_operational_costs import TotalOperationalCosts +from scripts.costs.total_operational_incomes import TotalOperationalIncomes +from scripts.costs.constants import CURRENT_STATUS, SKIN_RETROFIT, SYSTEM_RETROFIT_AND_PV, SKIN_RETROFIT_AND_SYSTEM_RETROFIT_AND_PV +import hub.helpers.constants as cte class Cost: @@ -31,25 +35,23 @@ class Cost: consumer_price_index=0.04, electricity_peak_index=0.05, electricity_price_index=0.05, - gas_price_index=0.05, + fuel_price_index=0.05, discount_rate=0.03, retrofitting_year_construction=2020, - factories_handler='montreal_custom', + factories_handler='montreal_new', retrofit_scenario=CURRENT_STATUS, dictionary=None): if dictionary is None: dictionary = Dictionaries().hub_function_to_montreal_custom_costs_function self._building = building - fuel_type = 0 - if "gas" in building.energy_systems_archetype_name: - fuel_type = 1 + fuel_type = self._building.fuel_consumption_breakdown.keys() self._configuration = Configuration(number_of_years, percentage_credit, interest_rate, credit_years, consumer_price_index, electricity_peak_index, electricity_price_index, - gas_price_index, + fuel_price_index, discount_rate, retrofitting_year_construction, factories_handler, @@ -79,28 +81,37 @@ class Cost: global_operational_costs = TotalOperationalCosts(self._building, self._configuration).calculate() global_maintenance_costs = TotalMaintenanceCosts(self._building, self._configuration).calculate() global_operational_incomes = TotalOperationalIncomes(self._building, self._configuration).calculate() + df_capital_costs_skin = ( global_capital_costs['B2010_opaque_walls'] + global_capital_costs['B2020_transparent'] + global_capital_costs['B3010_opaque_roof'] + - global_capital_costs['B10_superstructure'] + global_capital_costs['B1010_superstructure'] ) df_capital_costs_systems = ( - global_capital_costs['D3020_heat_generating_systems'] + - global_capital_costs['D3030_cooling_generation_systems'] + - global_capital_costs['D3080_other_hvac_ahu'] + + global_capital_costs['D3020_heat_and_cooling_generating_systems'] + + global_capital_costs['D3040_distribution_systems'] + + global_capital_costs['D3050_other_hvac_ahu'] + + global_capital_costs['D3060_storage_systems'] + + global_capital_costs['D40_dhw'] + global_capital_costs['D5020_lighting_and_branch_wiring'] + - global_capital_costs['D301010_photovoltaic_system'] + global_capital_costs['D2010_photovoltaic_system'] ) df_end_of_life_costs = global_end_of_life_costs['End_of_life_costs'] - df_operational_costs = ( - global_operational_costs['Fixed_costs_electricity_peak'] + - global_operational_costs['Fixed_costs_electricity_monthly'] + - global_operational_costs['Variable_costs_electricity'] + - global_operational_costs['Fixed_costs_gas'] + - global_operational_costs['Variable_costs_gas'] - ) + operational_costs_list = [ + global_operational_costs['Fixed Costs Electricity Peak'], + global_operational_costs['Fixed Costs Electricity Monthly'], + global_operational_costs['Variable Costs Electricity'] + ] + additional_costs = [ + global_operational_costs[f'Fixed Costs {fuel}'] for fuel in + self._building.fuel_consumption_breakdown.keys() if fuel != cte.ELECTRICITY + ] + [ + global_operational_costs[f'Variable Costs {fuel}'] for fuel in + self._building.fuel_consumption_breakdown.keys() if fuel != cte.ELECTRICITY + ] + df_operational_costs = sum(operational_costs_list + additional_costs) df_maintenance_costs = ( global_maintenance_costs['Heating_maintenance'] + global_maintenance_costs['Cooling_maintenance'] + @@ -116,7 +127,7 @@ class Cost: life_cycle_costs_capital_skin = self._npv_from_list(df_capital_costs_skin.values.tolist()) life_cycle_costs_capital_systems = self._npv_from_list(df_capital_costs_systems.values.tolist()) life_cycle_costs_end_of_life_costs = self._npv_from_list(df_end_of_life_costs.values.tolist()) - life_cycle_operational_costs = self._npv_from_list(df_operational_costs.values.tolist()) + life_cycle_operational_costs = self._npv_from_list([df_operational_costs]) life_cycle_maintenance_costs = self._npv_from_list(df_maintenance_costs.values.tolist()) life_cycle_operational_incomes = self._npv_from_list(df_operational_incomes.values.tolist()) life_cycle_capital_incomes = self._npv_from_list(df_capital_incomes.values.tolist()) diff --git a/scripts/costs/end_of_life_costs.py b/scripts/costs/end_of_life_costs.py index f7d0d63f..8dacfecc 100644 --- a/scripts/costs/end_of_life_costs.py +++ b/scripts/costs/end_of_life_costs.py @@ -9,8 +9,8 @@ import math import pandas as pd from hub.city_model_structure.building import Building -from costs.configuration import Configuration -from costs.cost_base import CostBase +from scripts.costs.configuration import Configuration +from scripts.costs.cost_base import CostBase class EndOfLifeCosts(CostBase): diff --git a/scripts/costs/total_maintenance_costs.py b/scripts/costs/total_maintenance_costs.py index 081df443..13cdc3a0 100644 --- a/scripts/costs/total_maintenance_costs.py +++ b/scripts/costs/total_maintenance_costs.py @@ -43,8 +43,8 @@ class TotalMaintenanceCosts(CostBase): roof_area += roof.solid_polygon.area surface_pv = roof_area * 0.5 - peak_heating = building.heating_peak_load[cte.YEAR][0] - peak_cooling = building.cooling_peak_load[cte.YEAR][0] + peak_heating = building.heating_peak_load[cte.YEAR][0] / 3.6e6 + peak_cooling = building.cooling_peak_load[cte.YEAR][0] / 3.6e6 maintenance_heating_0 = peak_heating * archetype.operational_cost.maintenance_heating maintenance_cooling_0 = peak_cooling * archetype.operational_cost.maintenance_cooling diff --git a/scripts/costs/total_operational_costs.py b/scripts/costs/total_operational_costs.py index e38ac5f5..338baab1 100644 --- a/scripts/costs/total_operational_costs.py +++ b/scripts/costs/total_operational_costs.py @@ -1,8 +1,7 @@ """ Total operational costs module SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2023 Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -Code contributor Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +Copyright © 2024 Project Coder Saeed Ranjbar saeed.ranjbar@mail.concordia.ca Code contributor Oriol Gavalda Torrellas oriol.gavalda@concordia.ca """ import math @@ -11,27 +10,22 @@ import pandas as pd from hub.city_model_structure.building import Building import hub.helpers.constants as cte -from costs.configuration import Configuration -from costs.cost_base import CostBase -from costs.peak_load import PeakLoad +from scripts.costs.configuration import Configuration +from scripts.costs.cost_base import CostBase +from scripts.costs.peak_load import PeakLoad class TotalOperationalCosts(CostBase): """ - End of life costs class + Total Operational costs class """ def __init__(self, building: Building, configuration: Configuration): super().__init__(building, configuration) + columns_list = self.columns() self._yearly_operational_costs = pd.DataFrame( index=self._rng, - columns=[ - 'Fixed_costs_electricity_peak', - 'Fixed_costs_electricity_monthly', - 'Variable_costs_electricity', - 'Fixed_costs_gas', - 'Variable_costs_gas' - ], + columns=columns_list, dtype='float' ) @@ -41,67 +35,70 @@ class TotalOperationalCosts(CostBase): :return: pd.DataFrame """ building = self._building + fuel_consumption_breakdown = building.fuel_consumption_breakdown archetype = self._archetype total_floor_area = self._total_floor_area - factor_residential = total_floor_area / 80 - # todo: split the heating between fuels - fixed_gas_cost_year_0 = 0 - variable_gas_cost_year_0 = 0 - electricity_heating = 0 - domestic_hot_water_electricity = 0 - # todo: each fuel has different units that have to be processed - if self._configuration.fuel_type == 1: - fixed_gas_cost_year_0 = archetype.operational_cost.fuels[1].fixed_monthly * 12 * factor_residential - variable_gas_cost_year_0 = ( - (building.heating_consumption[cte.YEAR][0] + building.domestic_hot_water_consumption[cte.YEAR][0]) - / (1000 * cte.WATTS_HOUR_TO_JULES) * archetype.operational_cost.fuels[1].variable[0] - ) - if self._configuration.fuel_type == 0: - electricity_heating = building.heating_consumption[cte.YEAR][0] / 1000 - domestic_hot_water_electricity = building.domestic_hot_water_consumption[cte.YEAR][0] / 1000 - - electricity_cooling = building.cooling_consumption[cte.YEAR][0] / 1000 - electricity_lighting = building.lighting_electrical_demand[cte.YEAR][0] / 1000 - electricity_plug_loads = building.appliances_electrical_demand[cte.YEAR][0] / 1000 - electricity_distribution = 0 - total_electricity_consumption = ( - electricity_heating + electricity_cooling + electricity_lighting + domestic_hot_water_electricity + - electricity_plug_loads + electricity_distribution - ) - - # todo: change when peak electricity demand is coded. Careful with factor residential - peak_electricity_load = PeakLoad(building).electricity_peak_load + if archetype.function == 'residential': + factor = total_floor_area / 80 + else: + factor = 1 + total_electricity_consumption = sum(self._building.fuel_consumption_breakdown[cte.ELECTRICITY].values()) + peak_electricity_load = PeakLoad(self._building).electricity_peak_load peak_load_value = peak_electricity_load.max(axis=1) peak_electricity_demand = peak_load_value[1] / 1000 # self._peak_electricity_demand adapted to kW - variable_electricity_cost_year_0 = ( - total_electricity_consumption / cte.WATTS_HOUR_TO_JULES * archetype.operational_cost.fuels[0].variable[0] - ) - peak_electricity_cost_year_0 = peak_electricity_demand * archetype.operational_cost.fuels[0].fixed_power * 12 - monthly_electricity_cost_year_0 = archetype.operational_cost.fuels[0].fixed_monthly * 12 * factor_residential - - for year in range(1, self._configuration.number_of_years + 1): - price_increase_electricity = math.pow(1 + self._configuration.electricity_price_index, year) - price_increase_peak_electricity = math.pow(1 + self._configuration.electricity_peak_index, year) - price_increase_gas = math.pow(1 + self._configuration.gas_price_index, year) - self._yearly_operational_costs.at[year, 'Fixed_costs_electricity_peak'] = ( - peak_electricity_cost_year_0 * price_increase_peak_electricity - ) - self._yearly_operational_costs.at[year, 'Fixed_costs_electricity_monthly'] = ( - monthly_electricity_cost_year_0 * price_increase_peak_electricity - ) - if not isinstance(variable_electricity_cost_year_0, pd.DataFrame): - variable_costs_electricity = variable_electricity_cost_year_0 * price_increase_electricity - else: - variable_costs_electricity = float(variable_electricity_cost_year_0.iloc[0] * price_increase_electricity) - self._yearly_operational_costs.at[year, 'Variable_costs_electricity'] = ( - variable_costs_electricity - ) - self._yearly_operational_costs.at[year, 'Fixed_costs_gas'] = fixed_gas_cost_year_0 * price_increase_gas - self._yearly_operational_costs.at[year, 'Variable_costs_gas'] = ( - variable_gas_cost_year_0 * price_increase_peak_electricity - ) - self._yearly_operational_costs.at[year, 'Variable_costs_gas'] = ( - variable_gas_cost_year_0 * price_increase_peak_electricity - ) + fuels = archetype.operational_cost.fuels + for fuel in fuels: + if fuel.type in fuel_consumption_breakdown.keys(): + if fuel.type == cte.ELECTRICITY: + variable_electricity_cost_year_0 = ( + total_electricity_consumption / cte.WATTS_HOUR_TO_JULES * fuel.variable[0] + ) + peak_electricity_cost_year_0 = peak_electricity_demand * fuel.fixed_power * 12 + monthly_electricity_cost_year_0 = fuel.fixed_monthly * 12 * factor + for year in range(1, self._configuration.number_of_years + 1): + price_increase_electricity = math.pow(1 + self._configuration.electricity_price_index, year) + price_increase_peak_electricity = math.pow(1 + self._configuration.electricity_peak_index, year) + self._yearly_operational_costs.at[year, 'Fixed Costs Electricity Peak'] = ( + peak_electricity_cost_year_0 * price_increase_peak_electricity + ) + self._yearly_operational_costs.at[year, 'Fixed Costs Electricity Monthly'] = ( + monthly_electricity_cost_year_0 * price_increase_peak_electricity + ) + if not isinstance(variable_electricity_cost_year_0, pd.DataFrame): + variable_costs_electricity = variable_electricity_cost_year_0 * price_increase_electricity + else: + variable_costs_electricity = float(variable_electricity_cost_year_0.iloc[0] * price_increase_electricity) + self._yearly_operational_costs.at[year, 'Variable Costs Electricity'] = ( + variable_costs_electricity + ) + else: + fuel_fixed_cost = fuel.fixed_monthly * 12 * factor + if fuel.type == cte.BIOMASS: + conversion_factor = 1 + else: + conversion_factor = fuel.density[0] + variable_cost_fuel = ( + (sum(fuel_consumption_breakdown[fuel.type].values())/(1e6*fuel.lower_heating_value[0] * conversion_factor)) * fuel.variable[0]) + for year in range(1, self._configuration.number_of_years + 1): + price_increase_gas = math.pow(1 + self._configuration.gas_price_index, year) + self._yearly_operational_costs.at[year, f'Fixed Costs {fuel.type}'] = fuel_fixed_cost * price_increase_gas + self._yearly_operational_costs.at[year, f'Variable Costs {fuel.type}'] = ( + variable_cost_fuel * price_increase_gas) self._yearly_operational_costs.fillna(0, inplace=True) + return self._yearly_operational_costs + + def columns(self): + columns_list = [] + fuels = [key for key in self._building.fuel_consumption_breakdown.keys()] + for fuel in fuels: + if fuel == cte.ELECTRICITY: + columns_list.append('Fixed Costs Electricity Peak') + columns_list.append('Fixed Costs Electricity Monthly') + columns_list.append('Variable Costs Electricity') + else: + columns_list.append(f'Fixed Costs {fuel}') + columns_list.append(f'Variable Costs {fuel}') + + return columns_list + diff --git a/scripts/energy_system_analysis_report.py b/scripts/energy_system_analysis_report.py index 09b88482..9f44a288 100644 --- a/scripts/energy_system_analysis_report.py +++ b/scripts/energy_system_analysis_report.py @@ -6,7 +6,6 @@ import matplotlib.colors as mcolors from matplotlib import cm from scripts.report_creation import LatexReport - class EnergySystemAnalysisReport: def __init__(self, city, output_path): self.city = city @@ -196,50 +195,48 @@ class EnergySystemAnalysisReport: self.report.add_table(table_data, caption=f'{building.name} Information', first_column_width=1.5) - def building_existing_system_info(self, building): - existing_archetype = building.energy_systems_archetype_name - fuels = [] - system_schematic = "-" - heating_system = "-" - cooling_system = "-" - dhw = "-" - electricity = "Grid" - hvac_ec = format((building.heating_consumption[cte.YEAR][0] + building.cooling_consumption[cte.YEAR][0]) / 3.6e9, - '.2f') - dhw_ec = format(building.domestic_hot_water_consumption[cte.YEAR][0] / 1e6, '.2f') - on_site_generation = "-" - yearly_operational_cost = "-" - life_cycle_cost = "-" - for energy_system in building.energy_systems: - if cte.HEATING and cte.DOMESTIC_HOT_WATER in energy_system.demand_types: - heating_system = energy_system.name - dhw = energy_system.name - elif cte.DOMESTIC_HOT_WATER in energy_system.demand_types: - dhw = energy_system.name - elif cte.HEATING in energy_system.demand_types: - heating_system = energy_system.name - elif cte.COOLING in energy_system.demand_types: - cooling_system = energy_system.name - for generation_system in energy_system.generation_systems: - fuels.append(generation_system.fuel_type) - if generation_system.system_type == cte.PHOTOVOLTAIC: - electricity = "Grid-tied PV" + def building_system_retrofit_results(self, building_name, current_system, new_system): + current_system_archetype = current_system[f'{building_name}']['Energy System Archetype'] + current_system_heating = current_system[f'{building_name}']['Heating System'] + current_system_cooling = current_system[f'{building_name}']['Cooling System'] + current_system_dhw = current_system[f'{building_name}']['DHW System'] + current_system_pv = current_system[f'{building_name}']['Photovoltaic System Capacity'] + current_system_heating_fuel = current_system[f'{building_name}']['Heating Fuel'] + current_system_hvac_consumption = current_system[f'{building_name}']['Yearly HVAC Energy Consumption (MWh)'] + current_system_dhw_consumption = current_system[f'{building_name}']['DHW Energy Consumption (MWH)'] + current_pv_production = current_system[f'{building_name}']['PV Yearly Production (kWh)'] + current_capital_cost = current_system[f'{building_name}']['Energy System Capital Cost (CAD)'] + current_operational = current_system[f'{building_name}']['Energy System Average Yearly Operational Cost (CAD)'] + current_lcc = current_system[f'{building_name}']['Energy System Life Cycle Cost (CAD)'] + new_system_archetype = new_system[f'{building_name}']['Energy System Archetype'] + new_system_heating = new_system[f'{building_name}']['Heating System'] + new_system_cooling = new_system[f'{building_name}']['Cooling System'] + new_system_dhw = new_system[f'{building_name}']['DHW System'] + new_system_pv = new_system[f'{building_name}']['Photovoltaic System Capacity'] + new_system_heating_fuel = new_system[f'{building_name}']['Heating Fuel'] + new_system_hvac_consumption = new_system[f'{building_name}']['Yearly HVAC Energy Consumption (MWh)'] + new_system_dhw_consumption = new_system[f'{building_name}']['DHW Energy Consumption (MWH)'] + new_pv_production = new_system[f'{building_name}']['PV Yearly Production (kWh)'] + new_capital_cost = new_system[f'{building_name}']['Energy System Capital Cost (CAD)'] + new_operational = new_system[f'{building_name}']['Energy System Average Yearly Operational Cost (CAD)'] + new_lcc = new_system[f'{building_name}']['Energy System Life Cycle Cost (CAD)'] energy_system_table_data = [ ["Detail", "Existing System", "Proposed System"], - ["Energy System Archetype", existing_archetype, "-"], - ["System Schematic", system_schematic, system_schematic], - ["Heating System", heating_system, "-"], - ["Cooling System", cooling_system, "-"], - ["DHW System", dhw, "-"], - ["Electricity", electricity, "-"], - ["Fuel(s)", str(fuels), "-"], - ["HVAC Energy Consumption (MWh)", hvac_ec, "-"], - ["DHW Energy Consumption (MWH)", dhw_ec, "-"], - ["Yearly Operational Cost (CAD)", yearly_operational_cost, "-"], - ["Life Cycle Cost (CAD)", life_cycle_cost, "-"] + ["Energy System Archetype", current_system_archetype, new_system_archetype], + ["Heating System", current_system_heating, new_system_heating], + ["Cooling System", current_system_cooling, new_system_cooling], + ["DHW System", current_system_dhw, new_system_dhw], + ["Photovoltaic System Capacity", current_system_pv, new_system_pv], + ["Heating Fuel", current_system_heating_fuel, new_system_heating_fuel], + ["Yearly HVAC Energy Consumption (MWh)", current_system_hvac_consumption, new_system_hvac_consumption], + ["DHW Energy Consumption (MWH)", current_system_dhw_consumption, new_system_dhw_consumption], + ["PV Yearly Production (kWh)", current_pv_production, new_pv_production], + ["Energy System Capital Cost (CAD)", current_capital_cost, new_capital_cost], + ["Energy System Average Yearly Operational Cost (CAD)", current_operational, new_operational], + ["Energy System Life Cycle Cost (CAD)", current_lcc, new_lcc] ] - self.report.add_table(energy_system_table_data, caption=f'Building {building.name} Energy System Characteristics') + self.report.add_table(energy_system_table_data, caption=f'Building {building_name} Energy System Characteristics') def building_fuel_consumption_breakdown(self, building): save_directory = self.output_path @@ -255,19 +252,23 @@ class EnergySystemAnalysisReport: # Iterate through energy systems of the building for energy_system in building.energy_systems: for demand_type in energy_system.demand_types: - for generation_system in energy_system.generation_systems: - consumption = 0 - if demand_type == cte.HEATING: - consumption = building.heating_consumption[cte.YEAR][0] / 3.6e9 - elif demand_type == cte.DOMESTIC_HOT_WATER: - consumption = building.domestic_hot_water_consumption[cte.YEAR][0] / 1e6 - elif demand_type == cte.COOLING: - consumption = building.cooling_consumption[cte.YEAR][0] / 3.6e9 - - if generation_system.fuel_type == cte.ELECTRICITY: - fuel_breakdown[demand_type]["Electricity"] += consumption - else: - fuel_breakdown[demand_type]["Gas"] += consumption + if demand_type == cte.HEATING: + consumption = building.heating_consumption[cte.YEAR][0] / 3.6e9 + for generation_system in energy_system.generation_systems: + if generation_system.fuel_type == cte.ELECTRICITY: + fuel_breakdown[demand_type]["Electricity"] += consumption + else: + fuel_breakdown[demand_type]["Gas"] += consumption + elif demand_type == cte.DOMESTIC_HOT_WATER: + consumption = building.domestic_hot_water_consumption[cte.YEAR][0] / 1e6 + for generation_system in energy_system.generation_systems: + if generation_system.fuel_type == cte.ELECTRICITY: + fuel_breakdown[demand_type]["Electricity"] += consumption + else: + fuel_breakdown[demand_type]["Gas"] += consumption + elif demand_type == cte.COOLING: + consumption = building.cooling_consumption[cte.YEAR][0] / 3.6e9 + fuel_breakdown[demand_type]["Electricity"] += consumption electricity_labels = ['Appliance', 'Lighting'] electricity_sizes = [fuel_breakdown['Appliance'], fuel_breakdown['Lighting']] @@ -317,7 +318,7 @@ class EnergySystemAnalysisReport: plt.savefig(save_directory / f'{building.name}_energy_consumption_breakdown.png', dpi=300) plt.close() - def create_report(self): + def create_report(self, current_system, new_system): os.chdir(self.output_path) self.report.add_section('Current Status') self.building_energy_info() @@ -329,7 +330,7 @@ class EnergySystemAnalysisReport: self.load_duration_curves() for building in self.city.buildings: self.individual_building_info(building) - self.building_existing_system_info(building) + self.building_system_retrofit_results(building_name=building.name, current_system=current_system, new_system=new_system) self.building_fuel_consumption_breakdown(building) self.report.add_image(f'{building.name}_energy_consumption_breakdown.png', caption=f'Building {building.name} Consumption by source and sector breakdown') diff --git a/scripts/energy_system_retrofit_results.py b/scripts/energy_system_retrofit_results.py new file mode 100644 index 00000000..f3f1cd4a --- /dev/null +++ b/scripts/energy_system_retrofit_results.py @@ -0,0 +1,59 @@ +import hub.helpers.constants as cte + + +def system_results(buildings): + system_performance_summary = {} + fields = ["Energy System Archetype", "Heating System", "Cooling System", "DHW System", + "Photovoltaic System Capacity", "Heating Fuel", "Yearly HVAC Energy Consumption (MWh)", + "DHW Energy Consumption (MWH)", "PV Yearly Production (kWh)", + "Energy System Capital Cost (CAD)", "Energy System Average Yearly Operational Cost (CAD)", + "Energy System Life Cycle Cost (CAD)"] + for building in buildings: + system_performance_summary[f'{building.name}'] = {} + for field in fields: + system_performance_summary[f'{building.name}'][field] = '-' + + for building in buildings: + fuels = [] + system_performance_summary[f'{building.name}']['Energy System Archetype'] = building.energy_systems_archetype_name + energy_systems = building.energy_systems + for energy_system in energy_systems: + demand_types = energy_system.demand_types + if cte.HEATING and cte.DOMESTIC_HOT_WATER in demand_types: + system_performance_summary[f'{building.name}']['Heating System'] = energy_system.name + system_performance_summary[f'{building.name}']['DHW System'] = energy_system.name + for generation_system in energy_system.generation_systems: + fuels.append(generation_system.fuel_type) + elif cte.DOMESTIC_HOT_WATER in energy_system.demand_types: + system_performance_summary[f'{building.name}']['DHW System'] = energy_system.name + elif cte.HEATING in energy_system.demand_types: + system_performance_summary[f'{building.name}']['Heating System'] = energy_system.name + for generation_system in energy_system.generation_systems: + fuels.append(generation_system.fuel_type) + elif cte.COOLING in energy_system.demand_types: + system_performance_summary[f'{building.name}']['Cooling System'] = energy_system.name + for generation_system in energy_system.generation_systems: + if generation_system.system_type == cte.PHOTOVOLTAIC: + system_performance_summary[f'{building.name}'][ + 'Photovoltaic System Capacity'] = generation_system.nominal_electricity_output or str(0) + heating_fuels = ", ".join(fuels) + system_performance_summary[f'{building.name}']['Heating Fuel'] = heating_fuels + system_performance_summary[f'{building.name}']['Yearly HVAC Energy Consumption (MWh)'] = format( + (building.heating_consumption[cte.YEAR][0] + building.cooling_consumption[cte.YEAR][0]) / 3.6e9, '.2f') + system_performance_summary[f'{building.name}']['DHW Energy Consumption (MWH)'] = format( + building.domestic_hot_water_consumption[cte.YEAR][0] / 1e6, '.2f') + return system_performance_summary + + +def new_system_results(buildings): + new_system_performance_summary = {} + fields = ["Energy System Archetype", "Heating System", "Cooling System", "DHW System", + "Photovoltaic System Capacity", "Heating Fuel", "Yearly HVAC Energy Consumption (MWh)", + "DHW Energy Consumption (MWH)", "PV Yearly Production (kWh)", + "Energy System Capital Cost (CAD)", "Energy System Average Yearly Operational Cost (CAD)", + "Energy System Life Cycle Cost (CAD)"] + for building in buildings: + new_system_performance_summary[f'{building.name}'] = {} + for field in fields: + new_system_performance_summary[f'{building.name}'][field] = '-' + return new_system_performance_summary diff --git a/scripts/energy_system_sizing.py b/scripts/energy_system_sizing.py index 42b53886..7f49d6af 100644 --- a/scripts/energy_system_sizing.py +++ b/scripts/energy_system_sizing.py @@ -17,18 +17,21 @@ class SystemSizing: def hvac_sizing(self): for building in self.buildings: - peak_heating_demand = building.heating_peak_load[cte.YEAR][0] - peak_cooling_demand = building.cooling_peak_load[cte.YEAR][0] + peak_heating_demand = building.heating_peak_load[cte.YEAR][0] / 3600 + peak_cooling_demand = building.cooling_peak_load[cte.YEAR][0] / 3600 if peak_heating_demand > peak_cooling_demand: sizing_demand = peak_heating_demand for system in building.energy_systems: if 'Heating' in system.demand_types: for generation in system.generation_systems: if generation.system_type == 'Heat Pump': + if generation.source_medium == cte.AIR: + generation.source_temperature = building.external_temperature generation.nominal_heat_output = 0.6 * sizing_demand / 1000 - for storage in generation.energy_storage_systems: - if storage.type_energy_stored == 'thermal': - storage.volume = (sizing_demand * 1000) / (cte.WATER_HEAT_CAPACITY*cte.WATER_DENSITY) + if generation.energy_storage_systems is not None: + for storage in generation.energy_storage_systems: + if storage.type_energy_stored == 'thermal': + storage.volume = (sizing_demand * 1000) / (cte.WATER_HEAT_CAPACITY*cte.WATER_DENSITY) elif generation.system_type == 'Boiler': generation.nominal_heat_output = 0.4 * sizing_demand / 1000 @@ -40,6 +43,27 @@ class SystemSizing: if generation.system_type == 'Heat Pump': generation.nominal_heat_output = sizing_demand / 1000 + def montreal_custom(self): + for building in self.buildings: + energy_systems = building.energy_systems + for energy_system in energy_systems: + demand_types = energy_system.demand_types + generation_systems = energy_system.generation_systems + if cte.HEATING in demand_types: + if len(generation_systems) == 1: + for generation in generation_systems: + generation.nominal_heat_output = building.heating_peak_load[cte.YEAR][0] / 3.6e6 + else: + for generation in generation_systems: + generation.nominal_heat_output = building.heating_peak_load[cte.YEAR][0] / (len(generation_systems) * 3.6e6) + elif cte.COOLING in demand_types: + if len(generation_systems) == 1: + for generation in generation_systems: + generation.nominal_cooling_output = building.cooling_peak_load[cte.YEAR][0] / 3.6e6 + else: + for generation in generation_systems: + generation.nominal_heat_output = building.cooling_peak_load[cte.YEAR][0] / (len(generation_systems) * 3.6e6) + diff --git a/scripts/energy_system_sizing_and_simulation_factory.py b/scripts/energy_system_sizing_and_simulation_factory.py new file mode 100644 index 00000000..adef8151 --- /dev/null +++ b/scripts/energy_system_sizing_and_simulation_factory.py @@ -0,0 +1,33 @@ +""" +EnergySystemSizingSimulationFactory retrieve the energy system archetype sizing and simulation module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Saeed Ranjbar saeed.ranjbar@mail.concordia.ca +""" + +from scripts.system_simulation_models.archetype13 import Archetype13 + + +class EnergySystemsSimulationFactory: + """ + EnergySystemsFactory class + """ + + def __init__(self, handler, building): + self._handler = '_' + handler.lower() + self._building = building + + def _archetype13(self): + """ + Enrich the city by using the sizing and simulation model developed for archetype13 of montreal_future_systems + """ + Archetype13(self._building).enrich_buildings() + self._building.level_of_detail.energy_systems = 2 + self._building.level_of_detail.energy_systems = 2 + + def enrich(self): + """ + Enrich the city given to the class using the class given handler + :return: None + """ + getattr(self, self._handler, lambda: None)() diff --git a/scripts/random_assignation.py b/scripts/random_assignation.py index 4c2b558a..d607c259 100644 --- a/scripts/random_assignation.py +++ b/scripts/random_assignation.py @@ -15,8 +15,8 @@ from hub.city_model_structure.building import Building energy_systems_format = 'montreal_custom' # parameters: -residential_systems_percentage = {'system 1 gas': 44, - 'system 1 electricity': 6, +residential_systems_percentage = {'system 1 gas': 100, + 'system 1 electricity': 0, 'system 2 gas': 0, 'system 2 electricity': 0, 'system 3 and 4 gas': 0, @@ -25,10 +25,11 @@ residential_systems_percentage = {'system 1 gas': 44, 'system 5 electricity': 0, 'system 6 gas': 0, 'system 6 electricity': 0, - 'system 8 gas': 44, - 'system 8 electricity': 6} + 'system 8 gas': 0, + 'system 8 electricity': 0} residential_new_systems_percentage = {'PV+ASHP+GasBoiler+TES': 100, + 'PV+4Pipe+DHW': 0, 'PV+ASHP+ElectricBoiler+TES': 0, 'PV+GSHP+GasBoiler+TES': 0, 'PV+GSHP+ElectricBoiler+TES': 0, diff --git a/scripts/system_simulation.py b/scripts/system_simulation.py index 8fbe9148..24759f26 100644 --- a/scripts/system_simulation.py +++ b/scripts/system_simulation.py @@ -9,10 +9,10 @@ from hub.helpers.monthly_values import MonthlyValues class SystemSimulation: - def __init__(self, building): + def __init__(self, building, out_path): self.building = building self.energy_systems = building.energy_systems - self.heating_demand = building.heating_demand[cte.HOUR] + self.heating_demand = [0] + building.heating_demand[cte.HOUR] self.cooling_demand = building.cooling_demand self.dhw_demand = building.domestic_hot_water_heat_demand self.T_out = building.external_temperature[cte.HOUR] @@ -20,9 +20,10 @@ class SystemSimulation: self.maximum_cooling_demand = building.cooling_peak_load[cte.YEAR][0] self.name = building.name self.energy_system_archetype = building.energy_systems_archetype_name + self.out_path = out_path def archetype1(self): - out_path = (Path(__file__).parent / 'out_files') + out_path = self.out_path T, T_sup, T_ret, m_ch, m_dis, q_hp, q_aux = [0] * len(self.heating_demand), [0] * len( self.heating_demand), [0] * len(self.heating_demand), [0] * len(self.heating_demand), [0] * len( self.heating_demand), [0] * len(self.heating_demand), [0] * len(self.heating_demand) @@ -36,7 +37,7 @@ class SystemSimulation: if cte.ELECTRICITY not in energy_system.demand_types: generation_systems = energy_system.generation_systems for generation_system in generation_systems: - if generation_system.system_type == cte.HEAT_PUMP: + if generation_system.system_type == cte.HEAT_PUMP and cte.HEATING in energy_system.demand_types: hp_cap = generation_system.nominal_heat_output hp_efficiency = float(generation_system.heat_efficiency) for storage in generation_system.energy_storage_systems: @@ -79,15 +80,15 @@ class SystemSimulation: factor = 8 else: factor = 4 - m_dis[i + 1] = self.maximum_heating_demand / (cte.WATER_HEAT_CAPACITY * factor) + m_dis[i + 1] = self.maximum_heating_demand / (cte.WATER_HEAT_CAPACITY * factor * 3600) t_return = T[i + 1] - self.heating_demand[i + 1] / (m_dis[i + 1] * cte.WATER_HEAT_CAPACITY) if m_dis[i + 1] == 0 or (m_dis[i + 1] > 0 and t_return < 25): T_ret[i + 1] = max(25, T[i + 1]) else: - T_ret[i + 1] = T[i + 1] - self.heating_demand[i + 1] / (m_dis[i + 1] * cte.WATER_HEAT_CAPACITY) + T_ret[i + 1] = T[i + 1] - self.heating_demand[i + 1] / (m_dis[i + 1] * cte.WATER_HEAT_CAPACITY * 3600) tes_output = m_dis[i + 1] * cte.WATER_HEAT_CAPACITY * (T[i + 1] - T_ret[i + 1]) - if tes_output < self.heating_demand[i + 1]: - q_aux[i + 1] = self.heating_demand[i + 1] - tes_output + if tes_output < (self.heating_demand[i + 1] / 3600): + q_aux[i + 1] = (self.heating_demand[i + 1] / 3600) - tes_output aux_fuel[i + 1] = (q_aux[i + 1] * dt) / 50e6 boiler_consumption[i + 1] = q_aux[i + 1] / boiler_efficiency heating_consumption[i + 1] = boiler_consumption[i + 1] + hp_electricity[i + 1] @@ -99,11 +100,11 @@ class SystemSimulation: output_file.writerow(['T', 'T_sup', 'T_ret', 'm_ch', 'm_dis', 'q_hp', 'hp_electricity', 'aux_fuel', 'q_aux', 'heating_demand']) # Write data output_file.writerows(data) - return heating_consumption, hp_electricity, boiler_consumption + return heating_consumption, hp_electricity, boiler_consumption, T_sup def enrich(self): - if self.energy_system_archetype == 'PV+ASHP+GasBoiler+TES': - building_new_heating_consumption, building_heating_electricity_consumption, building_heating_gas_consumption = ( + if self.energy_system_archetype == 'PV+ASHP+GasBoiler+TES' or 'PV+4Pipe+DHW': + building_new_heating_consumption, building_heating_electricity_consumption, building_heating_gas_consumption, supply_temperature = ( self.archetype1()) self.building.heating_consumption[cte.HOUR] = building_new_heating_consumption self.building.heating_consumption[cte.MONTH] = MonthlyValues.get_total_month(self.building.heating_consumption[cte.HOUR]) @@ -112,6 +113,8 @@ class SystemSimulation: for energy_system in self.building.energy_systems: if cte.HEATING in energy_system.demand_types: for generation_system in energy_system.generation_systems: + if generation_system.system_type == cte.HEAT_PUMP: + generation_system.heat_supply_temperature = supply_temperature disaggregated_consumption[generation_system.fuel_type] = {} if generation_system.fuel_type == cte.ELECTRICITY: disaggregated_consumption[generation_system.fuel_type][ diff --git a/scripts/system_simulation_models/archetype13.py b/scripts/system_simulation_models/archetype13.py new file mode 100644 index 00000000..03057cdc --- /dev/null +++ b/scripts/system_simulation_models/archetype13.py @@ -0,0 +1,100 @@ +import math + +import hub.helpers.constants as cte + + +class Archetype13: + def __init__(self, building, output_path): + self._pv_system = building.energy_systems[0] + self._hvac_system = building.energy_systems[1] + self._dhw_system = building.energy_systems[-1] + self._heating_peak_load = building.heating_peak_load[cte.YEAR][0] + self._cooling_peak_load = building.cooling_peak_load[cte.YEAR][0] + self._domestic_hot_water_peak_load = building.domestic_hot_water_peak_load[cte.YEAR][0] + self._hourly_heating_demand = [0] + [demand / 3600 for demand in building.heating_demand[cte.HOUR]] + self._hourly_cooling_demand = [demand / 3600 for demand in building.cooling_demand[cte.HOUR]] + self._hourly_dhw_demand = building.domestic_hot_water_heat_demand[cte.HOUR] + self._output_path = output_path + self._t_out = building.external_temperature + + def hvac_sizing(self): + storage_factor = 3 + heat_pump = self._hvac_system.generation_systems[0] + boiler = self._hvac_system.generation_systems[1] + thermal_storage = heat_pump.energy_storage_systems[0] + heat_pump.nominal_heat_output = round(0.5 * self._heating_peak_load / 3600) + heat_pump.nominal_cooling_output = round(self._cooling_peak_load / 3600) + boiler.nominal_heat_output = round(0.5 * self._heating_peak_load / 3600) + thermal_storage.volume = round( + (self._heating_peak_load * storage_factor) / (cte.WATER_HEAT_CAPACITY * cte.WATER_DENSITY * 30)) + return heat_pump, boiler, thermal_storage + + def hvac_simulation(self): + hp, boiler, tes = self.hvac_sizing() + if hp.source_medium == cte.AIR: + hp.source_temperature = self._t_out[cte.HOUR] + # Heating System Simulation + variable_names = ["t_sup", "t_tank", "t_ret", "m_ch", "m_dis", "q_hp", "q_boiler", "hp_cop", + "hp_electricity", "boiler_gas", "boiler_consumption", "heating_consumption"] + num_hours = len(self._hourly_heating_demand) + variables = {name: [0] * num_hours for name in variable_names} + (t_sup, t_tank, t_ret, m_ch, m_dis, q_hp, q_boiler, hp_cop, + hp_electricity, boiler_gas, boiler_consumption, heating_consumption) = [variables[name] for name in variable_names] + t_tank[0] = 30 + dt = 3600 + hp_heating_cap = hp.nominal_heat_output + hp_efficiency = float(hp.heat_efficiency) + boiler_efficiency = float(boiler.heat_efficiency) + v, h = float(tes.volume), float(tes.height) + r_tot = sum(float(layer.thickness) / float(layer.material.conductivity) for layer in + tes.layers) + u_tot = 1 / r_tot + d = math.sqrt((4 * v) / (math.pi * h)) + a_side = math.pi * d * h + a_top = math.pi * d ** 2 / 4 + ua = u_tot * (2 * a_top + a_side) + for i in range(len(self._hourly_heating_demand) - 1): + t_tank[i + 1] = (t_tank[i] + + ((m_ch[i] * (t_sup[i] - t_tank[i])) + + (ua * (self._t_out[i] - t_tank[i] + 5)) / cte.WATER_HEAT_CAPACITY - + m_dis[i] * (t_tank[i] - t_ret[i])) * (dt / (cte.WATER_DENSITY * v))) + if t_tank[i + 1] < 40: + q_hp[i + 1] = hp_heating_cap + m_ch[i + 1] = q_hp[i + 1] / (cte.WATER_HEAT_CAPACITY * 7) + t_sup[i + 1] = (q_hp[i + 1] / (m_ch[i + 1] * cte.WATER_HEAT_CAPACITY)) + t_tank[i + 1] + elif 45 <= t_tank[i + 1] < 55 and q_hp[i] == 0: + q_hp[i + 1] = 0 + m_ch[i + 1] = 0 + t_sup[i + 1] = t_tank[i + 1] + elif 45 <= t_tank[i + 1] < 55 and q_hp[i] > 0: + q_hp[i + 1] = hp_heating_cap + m_ch[i + 1] = q_hp[i + 1] / (cte.WATER_HEAT_CAPACITY * 3) + t_sup[i + 1] = (q_hp[i + 1] / (m_ch[i + 1] * cte.WATER_HEAT_CAPACITY)) + t_tank[i + 1] + else: + q_hp[i + 1], m_ch[i + 1], t_sup[i + 1] = 0, 0, t_tank[i + 1] + + hp_electricity[i + 1] = q_hp[i + 1] / hp_efficiency + if self._hourly_heating_demand[i + 1] == 0: + m_dis[i + 1], t_return, t_ret[i + 1] = 0, t_tank[i + 1], t_tank[i + 1] + else: + if self._hourly_heating_demand[i + 1] > 0.5 * self._heating_peak_load: + factor = 8 + else: + factor = 4 + m_dis[i + 1] = self._heating_peak_load / (cte.WATER_HEAT_CAPACITY * factor * 3600) + t_return = t_tank[i + 1] - self._hourly_heating_demand[i + 1] / (m_dis[i + 1] * cte.WATER_HEAT_CAPACITY) + if m_dis[i + 1] == 0 or (m_dis[i + 1] > 0 and t_return < 25): + t_ret[i + 1] = max(25, t_tank[i + 1]) + else: + t_ret[i + 1] = t_tank[i + 1] - self._hourly_heating_demand[i + 1] / (m_dis[i + 1] * cte.WATER_HEAT_CAPACITY * 3600) + tes_output = m_dis[i + 1] * cte.WATER_HEAT_CAPACITY * (t_tank[i + 1] - t_ret[i + 1]) + if tes_output < (self._hourly_heating_demand[i + 1] / 3600): + q_boiler[i + 1] = (self._hourly_heating_demand[i + 1] / 3600) - tes_output + boiler_gas[i + 1] = (q_boiler[i + 1] * dt) / 50e6 + boiler_consumption[i + 1] = q_boiler[i + 1] / boiler_efficiency + heating_consumption[i + 1] = boiler_consumption[i + 1] + hp_electricity[i + 1] + data = list(zip(t_tank, t_sup, t_ret, m_ch, m_dis, q_hp, hp_electricity, boiler_gas, q_boiler, + self._hourly_heating_demand)) + + def enrich_buildings(self): + self.hvac_sizing() diff --git a/tests/__pycache__/test_systems_catalog.cpython-39.pyc b/tests/__pycache__/test_systems_catalog.cpython-39.pyc index 26324a1a32e2b8cafab22017119221443a342310..923b2ef711237652b278d6f41adc866c5cbb357a 100644 GIT binary patch delta 166 zcmaDY+$_MC$ji&c00gVIs-+$0*vOa2$S56P)tmBl5gx$(LADXBR*nR)5)<@rU~X*v1jo7GtkFiy7N5S1_D0h(9D4kTWJj4t8? zv7|vPULc`4S(43;QFC$xo8{zA4%x|D*t8~J;4orjoy^Rs#i%&hm{XC11*ndLi9>vH H9A_^8j(#aC delta 268 zcmZn`crDDA$ji&c00iM)2UCrCHu5DhGOA22V3g%aVQXQCVohNWX3*r=JdrVriP3TL zA7%ySjMAjdHY^7ijd=6&i%K%$6LV9GGLsXFSb>JTWCIdK>>vVUXb~q!ObW!}1rjSK z8?f0iuAH3DW+|;yl3H96k77c6ab&L&B!)ciCv3L86>Pa*_U0B3*=M|CJu29vB_oZy#Ts8 BLj3>$ diff --git a/tests/__pycache__/test_systems_factory.cpython-39.pyc b/tests/__pycache__/test_systems_factory.cpython-39.pyc index f6c71e1a7de00ebfb7059c1284c2e867cadac0c6..1458d3dc6e9e640b2bf5ba908e66199fe1a6029a 100644 GIT binary patch delta 508 zcmY*U%SyvQ6zw$C#F&I?8(#=^q0-oj;1d&Ask!OeV$ z1pmQ}8xa@Xir?Ty!5Q&6FlTYjx#ynyJouJ299>r<{5{)IXLLU*^$Ze!|& zgPuH*%qQ^3k?h5T-@y0bl!XOrhT)=>94a9T~Q>CeQLix z;f>@`WLH4jv@Oh!8?vFi<#t5eYee24#2Km)WoxOk<~O-a%=;*$d6c8ydPap>>$#T- zP9k(j*F#lw=zRz!gv11EPGkViAwgh};nVNZ+$cU|8LMFyu&P=U>w8+m)(t=KG@sk> zB}iyZ`xPdB=-EoST!hxKifuQ#%au;(q*l@$2NSo59mkKKHm}&0(=wZIvL8RK|2s&J z!z0M2!}1KudPP}8^dMMXBxCGn|LkAi&gM2(*RuuPS+?s9OtM?~O<%EW;?^|X#H3!) z$t6}=B1!nOlk`Z9$I~u$T&=5!&WIQKScM}pxPXjAK8jv1(U>yO-OZa3zaTV5C)6n# zR(pq%ZhUtz!5LD5d(R{80KI%?gxS5yWQ^9;;e9d-=gt8|;30q*rA&&zVPpt6>yF7h zWUm4VU;$VJt^r}72k3qc$aRPn)fzExCTd=$h+yQdHGOz;N1@gc>APp3N zRbUOcf94=BE9a3(Kg%oVKhvWu8}tJ(f3Khn^VC!arvDkyj%wPLOR(P1UbNg6!GFd@ zuAH8`fGF#aX(`&zXTo$ZI;T%T?g3B&$_&4!N$RZ6BM4mVW(1b!7^CcQ;NC?P5F$}g e5>xC+ioBRS$!Dg>W0oZg^hZ=fOI|rPE&K**KmHj2 diff --git a/tests/test_systems_catalog.py b/tests/test_systems_catalog.py index d41e5c07..839107c2 100644 --- a/tests/test_systems_catalog.py +++ b/tests/test_systems_catalog.py @@ -39,11 +39,11 @@ class TestSystemsCatalog(TestCase): catalog_categories = catalog.names() archetypes = catalog.names('archetypes') - self.assertEqual(12, len(archetypes['archetypes'])) + self.assertEqual(13, len(archetypes['archetypes'])) systems = catalog.names('systems') - self.assertEqual(7, len(systems['systems'])) + self.assertEqual(10, len(systems['systems'])) generation_equipments = catalog.names('generation_equipments') - self.assertEqual(26, len(generation_equipments['generation_equipments'])) + self.assertEqual(27, len(generation_equipments['generation_equipments'])) with self.assertRaises(ValueError): catalog.names('unknown') diff --git a/tests/test_systems_factory.py b/tests/test_systems_factory.py index c4c086e3..442e5be5 100644 --- a/tests/test_systems_factory.py +++ b/tests/test_systems_factory.py @@ -114,7 +114,7 @@ class TestSystemsFactory(TestCase): ResultFactory('insel_monthly_energy_balance', self._city, self._output_path).enrich() for building in self._city.buildings: - building.energy_systems_archetype_name = 'PV+ASHP+GasBoiler+TES' + building.energy_systems_archetype_name = 'PV+4Pipe+DHW' EnergySystemsFactory('montreal_future', self._city).enrich() # Need to assign energy systems to buildings: for building in self._city.buildings: