============================= test session starts ==============================
platform darwin -- Python 3.7.0, pytest-7.2.0, pluggy-1.0.0 -- /Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/bin/python3.7
cachedir: .pytest_cache
rootdir: /Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX, configfile: pyproject.toml
plugins: cov-4.0.0
collecting ... collected 349 items

docs/test_power.py::test_W PASSED                                        [  0%]
docs/test_power.py::test_kW PASSED                                       [  0%]
docs/test_power.py::test_Btu_hr PASSED                                   [  0%]
docs/test_power.py::test_kBtu_aliases PASSED                             [  1%]
docs/test_power.py::test_kBtu_hr PASSED                                  [  1%]
tests/test_PHPP/test_shape_file.py::test_load_all_shape_files[version0] PASSED [  1%]
tests/test_PHPP/test_shape_file.py::test_load_all_shape_files[version1] PASSED [  2%]
tests/test_PHPP/test_shape_file.py::test_load_all_shape_files[version2] PASSED [  2%]
tests/test_PHPP/test_shape_file.py::test_load_all_shape_files[version3] PASSED [  2%]
tests/test_PHPP/test_shape_file.py::test_load_all_shape_files[version4] PASSED [  2%]
tests/test_PHPP/test_xl_app.py::test_xl_app_default <- tests/test_to_PHPP/test_xl_app.py PASSED [  3%]
tests/test_PHPP/test_xl_app.py::test_xl_app_get_WorkBook_success <- tests/test_to_PHPP/test_xl_app.py PASSED [  3%]
tests/test_PHPP/test_xl_app.py::test_xl_app_get_WorkBook_fail <- tests/test_to_PHPP/test_xl_app.py PASSED [  3%]
tests/test_PHPP/test_xl_app.py::test_xl_app_silent_mode <- tests/test_to_PHPP/test_xl_app.py PASSED [  4%]
tests/test_PHPP/test_xl_app.py::test_xl_app_unprotect_all_sheets <- tests/test_to_PHPP/test_xl_app.py PASSED [  4%]
tests/test_PHPP/test_xl_app.py::test_xl_app_create_new_worksheet <- tests/test_to_PHPP/test_xl_app.py PASSED [  4%]
tests/test_PHPP/test_xl_app.py::test_xl_app_create_new_worksheet_already_in_raises_ValueError <- tests/test_to_PHPP/test_xl_app.py PASSED [  4%]
tests/test_PHPP/test_xl_app.py::test_xl_app_get_sheet_by_name <- tests/test_to_PHPP/test_xl_app.py PASSED [  5%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_int_value[100000] <- tests/test_to_PHPP/test_xl_app.py PASSED [  5%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_int_value[1] <- tests/test_to_PHPP/test_xl_app.py PASSED [  5%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_int_value[0] <- tests/test_to_PHPP/test_xl_app.py PASSED [  6%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_int_value[-1] <- tests/test_to_PHPP/test_xl_app.py PASSED [  6%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_int_value[-100000] <- tests/test_to_PHPP/test_xl_app.py PASSED [  6%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_string_value[test] <- tests/test_to_PHPP/test_xl_app.py PASSED [  6%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_string_value[another] <- tests/test_to_PHPP/test_xl_app.py PASSED [  7%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_string_value[] <- tests/test_to_PHPP/test_xl_app.py PASSED [  7%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_list_of_ints <- tests/test_to_PHPP/test_xl_app.py PASSED [  7%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_raise_Attribute_error <- tests/test_to_PHPP/test_xl_app.py PASSED [  8%]
tests/test_PHPP/test_xl_app.py::test_xl_app_write_raise_WriteValue_error <- tests/test_to_PHPP/test_xl_app.py PASSED [  8%]
tests/test_PHPP/test_xl_data.py::test_XlItem <- tests/test_to_PHPP/test_xl_data.py PASSED [  8%]
tests/test_PHPP/test_xl_data.py::test_XlItem_covert_M <- tests/test_to_PHPP/test_xl_data.py PASSED [  8%]
tests/test_PHPP/test_xl_data.py::test_XlItem_covert_FT <- tests/test_to_PHPP/test_xl_data.py PASSED [  9%]
tests/test_PHPP/test_xl_data.py::test_XlItem_list <- tests/test_to_PHPP/test_xl_data.py PASSED [  9%]
tests/test_PHPP/test_xl_data_XlItem.py::test_XlItem <- tests/test_to_PHPP/test_xl_data_XlItem.py PASSED [  9%]
tests/test_PHPP/test_xl_data_XlItem.py::test_XlItem_covert_M <- tests/test_to_PHPP/test_xl_data_XlItem.py PASSED [ 10%]
tests/test_PHPP/test_xl_data_XlItem.py::test_XlItem_covert_FT <- tests/test_to_PHPP/test_xl_data_XlItem.py PASSED [ 10%]
tests/test_PHPP/test_xl_data_XlItem.py::test_XlItem_list <- tests/test_to_PHPP/test_xl_data_XlItem.py PASSED [ 10%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_xl_item_merge_none <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 10%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_xl_item_different_sheets_do_not_merge <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 11%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_xl_item_different_rows_do_not_merge <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 11%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_xl_item_merge_single_group <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 11%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_xl_item_merge_2_groups <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 12%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_xl_item_merge_3_groups_in_order <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 12%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_xl_item_merge_3_groups_out_of_order <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 12%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_manual_add_item <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 12%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_manual_add_item_exception <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 13%]
tests/test_PHPP/test_xl_data_XlItem_List.py::test_XlItem_List <- tests/test_to_PHPP/test_xl_data_XlItem_List.py PASSED [ 13%]
tests/test_PHPP/test_xl_data_colums.py::test_xl_ord <- tests/test_to_PHPP/test_xl_data_colums.py PASSED [ 13%]
tests/test_PHPP/test_xl_data_colums.py::test_xl_chr <- tests/test_to_PHPP/test_xl_data_colums.py PASSED [ 14%]
tests/test_PHPP/test_xl_data_colums.py::test_xl_chr_equals_xl_ord <- tests/test_to_PHPP/test_xl_data_colums.py PASSED [ 14%]
tests/test_PHPP/test_xl_data_colums.py::test_col_offset <- tests/test_to_PHPP/test_xl_data_colums.py PASSED [ 14%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_are_touching.py::test_are_touching_true PASSED [ 14%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_are_touching.py::test_are_touching_false PASSED [ 15%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_find_connected_components.py::test_find_connected_components_single_face PASSED [ 15%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_find_connected_components.py::test_find_connected_components_two_faces PASSED [ 15%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_find_connected_components.py::test_find_connected_components_three_faces PASSED [ 16%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_find_connected_components.py::test_find_connected_components_two_connected_faces PASSED [ 16%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_sort_faces_by_co_planar.py::test_sort_two_planar_faces_by_co_planar PASSED [ 16%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_sort_faces_by_co_planar.py::test_sort_two_non_planar_faces_by_co_planar PASSED [ 16%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_sort_faces_by_type.py::test_sort_faces_by_type PASSED [ 17%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_sort_faces_by_type.py::test_sort_faces_by_type_all_same PASSED [ 17%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_sort_faces_by_type.py::test_sort_faces_by_type_all_different PASSED [ 17%]
tests/test_from_HBJSON/test_cleanup_merge_faces/test_sort_faces_by_type.py::test_sort_faces_by_type_mixed PASSED [ 18%]
tests/test_from_HBJSON/test_create_assemblies/test_create_shade.py::test_create_phx_shade_from_hb_shade PASSED [ 18%]
tests/test_from_HBJSON/test_create_assemblies/test_create_window_type.py::test_create_phx_window_from_hb_window_default PASSED [ 18%]
tests/test_from_HBJSON/test_create_assemblies/test_create_window_type.py::test_create_phx_window_from_hb_window_with_ph_glazing PASSED [ 18%]
tests/test_from_HBJSON/test_create_assemblies/test_create_window_type.py::test_create_phx_window_from_hb_window_with_ph_frame PASSED [ 19%]
tests/test_from_HBJSON/test_create_assemblies/test_create_window_type.py::test_creat_phx_window_from_hb_window_with_ph_frame_and_ph_glazing PASSED [ 19%]
tests/test_from_HBJSON/test_create_project/test_convert_hbjson_to_PHX_Project.py::test_convert_model_PhxProject[Default_Model_Single_Zone.hbjson-None] PASSED [ 19%]
tests/test_from_HBJSON/test_create_project/test_convert_hbjson_to_PHX_Project.py::test_convert_model_PhxProject[Multi_Room_Complete.hbjson-None] FAILED [ 20%]
tests/test_from_HBJSON/test_read_HBJSON_file/test_convert_hbjson_dict_to_hb_model.py::test_read_default_single_zone_model_no_conversion[Default_Model_Single_Zone.hbjson-None] PASSED [ 20%]
tests/test_from_HBJSON/test_read_HBJSON_file/test_convert_hbjson_dict_to_hb_model.py::test_read_default_single_zone_model_no_conversion[Multi_Room_Complete.hbjson-None] FAILED [ 20%]
tests/test_from_HBJSON/test_read_HBJSON_file/test_read_hb_json_from_file.py::test_read_default_single_zone_model PASSED [ 20%]
tests/test_from_HBJSON/test_read_HBJSON_file/test_read_hb_json_from_file.py::test_read_default_single_zone_room PASSED [ 21%]
tests/test_from_HBJSON/test_read_HBJSON_file/test_read_hb_json_from_file.py::test_read_not_a_real_file PASSED [ 21%]
tests/test_from_WUFI/test_patterns_from_WUFI/test_new_xml_util_patterns_occupancy.py::test_vent_patterns_match ERROR [ 21%]
tests/test_from_WUFI/test_patterns_from_WUFI/test_new_xml_util_patterns_ventilation.py::test_vent_patterns_match ERROR [ 22%]
tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_assemblies.py::test_assembly_types_match ERROR [ 22%]
tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_assemblies.py::test_assembly_type_layers_match ERROR [ 22%]
tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_assemblies.py::test_assembly_type_materials_match ERROR [ 22%]
tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_data.py::test_project_data ERROR [ 23%]
tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_windows.py::test_project_data ERROR [ 23%]
tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_windows.py::test_window_type_attributes_match ERROR [ 23%]
tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_windows.py::test_window_FrameElememt_attributes_match ERROR [ 24%]
tests/test_from_WUFI/test_project_from_WUFI/test_read_xml_file.py::test_read_xml_file PASSED [ 24%]
tests/test_from_WUFI/test_project_from_WUFI/test_read_xml_file.py::test_read_xml_and_convert_to_phx_project PASSED [ 24%]
tests/test_from_WUFI/test_variants_from_WUFI/test_building_components.py::test_building_all_components_are_equal ERROR [ 24%]
tests/test_from_WUFI/test_variants_from_WUFI/test_buildings.py::test_variant_buildings ERROR [ 25%]
tests/test_from_WUFI/test_variants_from_WUFI/test_variants.py::test_project_variants ERROR [ 25%]
tests/test_model/test_building/test_Building.py::test_default_Building PASSED [ 25%]
tests/test_model/test_building/test_Building.py::test_add_single_component PASSED [ 26%]
tests/test_model/test_building/test_Building.py::test_add_multiple_components PASSED [ 26%]
tests/test_model/test_building/test_Building.py::test_add_single_zone PASSED [ 26%]
tests/test_model/test_building/test_Building.py::test_add_multiple_zones PASSED [ 26%]
tests/test_model/test_building/test_Building.py::test_group_compos_by_assembly_with_single_compo PASSED [ 27%]
tests/test_model/test_building/test_Building.py::test_zones_with_ventilation PASSED [ 27%]
tests/test_model/test_building/test_Zone.py::test_default_zone PASSED    [ 27%]
tests/test_model/test_building/test_Zone.py::test_add_exhaust_device PASSED [ 28%]
tests/test_model/test_certification/test_PH_Building.py::test_default_PH_Building PASSED [ 28%]
tests/test_model/test_certification/test_PH_Building.py::test_add_single_foundation PASSED [ 28%]
tests/test_model/test_components/test_ApertureElement.py::test_default_aperture_element PASSED [ 28%]
tests/test_model/test_components/test_ApertureElement.py::test_aperture_element_with_rect_polygon PASSED [ 29%]
tests/test_model/test_components/test_ComponentOpaque.py::test_default_component_opaque PASSED [ 29%]
tests/test_model/test_components/test_ComponentOpaque.py::test_add_with_empty_poly_ids PASSED [ 29%]
tests/test_model/test_components/test_ComponentOpaque.py::test_add_polygons PASSED [ 30%]
tests/test_model/test_constructions/test_assembly.py::test_default_assembly PASSED [ 30%]
tests/test_model/test_constructions/test_assembly.py::test_set_assembly_identifier PASSED [ 30%]
tests/test_model/test_constructions/test_window_type.py::test_default_window_type PASSED [ 30%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_default_ElecEquipment PASSED [ 31%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxDishwasher PASSED [ 31%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxClothesWasher PASSED [ 31%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxClothesDryer PASSED [ 32%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxRefrigerator PASSED [ 32%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxFreezer PASSED [ 32%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxFridgeFreezer PASSED [ 32%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxCooktop PASSED [ 33%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxMEL PASSED [ 33%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxLightingInterior PASSED [ 33%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxLightingExterior PASSED [ 34%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxLightingGarage PASSED [ 34%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxCustomElec PASSED [ 34%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxCustomLighting PASSED [ 34%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxCustomMEL PASSED [ 35%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxElevatorHydraulic PASSED [ 35%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxElevatorGearedTraction PASSED [ 35%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipment.py::test_PhxElevatorGearlessTraction PASSED [ 36%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipmentCollection.py::test_empty_collection PASSED [ 36%]
tests/test_model/test_elec_equip/test_PhxElectricalEquipmentCollection.py::test_add_single_equip_to_collection PASSED [ 36%]
tests/test_model/test_geometry/test_Graphics3D.py::test_empty_Graphics3D PASSED [ 36%]
tests/test_model/test_geometry/test_Graphics3D.py::test_add_single_polygon PASSED [ 37%]
tests/test_model/test_geometry/test_Graphics3D.py::test_add_two_polygon PASSED [ 37%]
tests/test_model/test_geometry/test_Polygon.py::test_blank_Polygon PASSED [ 37%]
tests/test_model/test_geometry/test_Polygon.py::test_multiple_Polygon_id_numbers PASSED [ 38%]
tests/test_model/test_geometry/test_Polygon.py::test_add_vertices_to_Polygon PASSED [ 38%]
tests/test_model/test_geometry/test_Polygon.py::test_polgon_calculate_area_simple_rectangle_1 PASSED [ 38%]
tests/test_model/test_geometry/test_Polygon.py::test_polgon_calculate_area_simple_rectangle_2 PASSED [ 38%]
tests/test_model/test_geometry/test_Polygon.py::test_polgon_calculate_area_irregular_polygon_1 PASSED [ 39%]
tests/test_model/test_geometry/test_Polygon.py::test_polgon_calculate_area_irregular_polygon_2 PASSED [ 39%]
tests/test_model/test_geometry/test_Polygon.py::test_3D_polygon_calculate_area PASSED [ 39%]
tests/test_model/test_geometry/test_Polygon.py::test_add_child_poly_id PASSED [ 40%]
tests/test_model/test_geometry/test_Polygon.py::test_cube_polygons_cardinal_orientation_on_axis PASSED [ 40%]
tests/test_model/test_geometry/test_Polygon.py::test_cube_polygons_vertical_orientation_on_axis PASSED [ 40%]
tests/test_model/test_geometry/test_Polygon.py::test_cube_polygons_cardinal_orientation_rotated PASSED [ 40%]
tests/test_model/test_geometry/test_Polygon.py::test_cube_polygons_vertical_orientation_rotated PASSED [ 41%]
tests/test_model/test_geometry/test_Polygon.py::test_sq_pyramid_polygons_vertical_orientation_on_axis PASSED [ 41%]
tests/test_model/test_geometry/test_Polygon.py::test_sq_pyramid_polygons_cardinal_orientation_on_axis PASSED [ 41%]
tests/test_model/test_geometry/test_Polygon.py::test_sq_pyramid_polygons_vertical_orientation_rotated PASSED [ 42%]
tests/test_model/test_geometry/test_Polygon.py::test_sq_pyramid_polygons_cardinal_orientation_rotated PASSED [ 42%]
tests/test_model/test_geometry/test_Vertix.py::test_blank_vertix PASSED  [ 42%]
tests/test_model/test_geometry/test_Vertix.py::test_multiple_vertices_id_num PASSED [ 42%]
tests/test_model/test_geometry/test_Vertix.py::test_vertix_equality PASSED [ 43%]
tests/test_model/test_geometry/test_Vertix.py::test_vertix_not_equal PASSED [ 43%]
tests/test_model/test_geometry/test_Vertix.py::test_set_of_vertices PASSED [ 43%]
tests/test_model/test_geometry/test_Vertix.py::test_unique_key PASSED    [ 44%]
tests/test_model/test_hvac/test_base.py::test_PhxUsageProfile_add PASSED [ 44%]
tests/test_model/test_hvac/test_base.py::test_add_default_PhxMechEquipmentParams PASSED [ 44%]
tests/test_model/test_hvac/test_base.py::test_r_add_default_PhxMechEquipmentParams PASSED [ 44%]
tests/test_model/test_hvac/test_base.py::test_sum_default_PhxMechEquipmentParams PASSED [ 45%]
tests/test_model/test_hvac/test_base.py::test_add_mixed_PhxMechEquipmentParams PASSED [ 45%]
tests/test_model/test_hvac/test_base.py::test_PhxMechanicalEquipment PASSED [ 45%]
tests/test_model/test_hvac/test_base.py::test_add_default_PhxMechanicalEquipment PASSED [ 46%]
tests/test_model/test_hvac/test_base.py::test_r_add_default_PhxMechanicalEquipment PASSED [ 46%]
tests/test_model/test_hvac/test_base.py::test_add_mixed_PhxMechanicalEquipment PASSED [ 46%]
tests/test_model/test_hvac/test_collection.py::test_default_PhxMechanicalEquipmentCollection PASSED [ 46%]
tests/test_model/test_hvac/test_collection.py::test_get_mech_device_by_key PASSED [ 47%]
tests/test_model/test_hvac/test_collection.py::test_get_mech_device_by_NONE_key PASSED [ 47%]
tests/test_model/test_hvac/test_collection.py::test_get_mech_device_by_id PASSED [ 47%]
tests/test_model/test_hvac/test_collection.py::test_get_mech_device_by_NONE_id PASSED [ 48%]
tests/test_model/test_hvac/test_collection.py::test_add_ventilation_device PASSED [ 48%]
tests/test_model/test_hvac/test_collection.py::test_get_ventilation_device PASSED [ 48%]
tests/test_model/test_hvac/test_collection.py::test_add_exhaust_ventilation_device_collection PASSED [ 48%]
tests/test_model/test_hvac/test_collection.py::test_get_exhaust_ventilation_device PASSED [ 49%]
tests/test_model/test_hvac/test_collection.py::test_get_exhaust_ventilation_device_error PASSED [ 49%]
tests/test_model/test_hvac/test_collection.py::test_add_supportive_device_collection PASSED [ 49%]
tests/test_model/test_hvac/test_collection.py::test_get_supportive_device PASSED [ 50%]
tests/test_model/test_hvac/test_collection.py::test_get_supportive_device_error PASSED [ 50%]
tests/test_model/test_hvac/test_collection.py::test_group_homogenous_supportive_devices PASSED [ 50%]
tests/test_model/test_hvac/test_collection.py::test_group_mixed_supportive_devices PASSED [ 51%]
tests/test_model/test_hvac/test_collection.py::test_merge_homogeneous_supportive_devices PASSED [ 51%]
tests/test_model/test_hvac/test_collection.py::test_merge_mixed_supportive_devices PASSED [ 51%]
tests/test_model/test_hvac/test_collection.py::test_merge_homogeneous_DHW_recirc_supportive_devices PASSED [ 51%]
tests/test_model/test_hvac/test_collection.py::test_merge_homogeneous_DHW_storage_supportive_devices PASSED [ 52%]
tests/test_model/test_hvac/test_collection.py::test_merge_homogeneous_Heat_Recirc_supportive_devices PASSED [ 52%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingDevice FAILED [ 52%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingVentilationParams FAILED [ 53%]
tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingVentilationParams FAILED [ 53%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingVentilation FAILED [ 53%]
tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingVentilation FAILED [ 53%]
tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingVentilation FAILED [ 54%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingRecirculationParams FAILED [ 54%]
tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingRecirculationParams FAILED [ 54%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingRecirculation FAILED [ 55%]
tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingRecirculation FAILED [ 55%]
tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingRecirculation FAILED [ 55%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingDehumidificationParams FAILED [ 55%]
tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingDehumidificationParams FAILED [ 56%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingDehumidification FAILED [ 56%]
tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingDehumidification FAILED [ 56%]
tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingDehumidification FAILED [ 57%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingPanelParams FAILED [ 57%]
tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingPanelParams FAILED [ 57%]
tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingPanel FAILED [ 57%]
tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingPanel FAILED [ 58%]
tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingPanel FAILED [ 58%]
tests/test_model/test_hvac/test_heat_pumps.py::test_default_PhxHeatPumpAnnual PASSED [ 58%]
tests/test_model/test_hvac/test_heat_pumps.py::test_default_PhxHeatPumpMonthly PASSED [ 59%]
tests/test_model/test_hvac/test_heat_pumps.py::test_default_PhxHeatPumpMonthlyParams_set_monthly_COPs PASSED [ 59%]
tests/test_model/test_hvac/test_heat_pumps.py::test_default_PhxHeatPumpMonthlyParams_set_monthly_temps PASSED [ 59%]
tests/test_model/test_hvac/test_heat_pumps.py::test_default_PhxHeatPumpHotWater PASSED [ 59%]
tests/test_model/test_hvac/test_heat_pumps.py::test_default_PhxHeatPumpCombined PASSED [ 60%]
tests/test_model/test_hvac/test_heating.py::test_PhxHeatingDevice PASSED [ 60%]
tests/test_model/test_hvac/test_heating.py::test_default_PhxHeaterElectric PASSED [ 60%]
tests/test_model/test_hvac/test_heating.py::test_default_PhxHeaterBoilerFossil PASSED [ 61%]
tests/test_model/test_hvac/test_heating.py::test_PhxHeaterBoilerFossil_set_fuel PASSED [ 61%]
tests/test_model/test_hvac/test_heating.py::test_default_PhxHeaterBoilerWood PASSED [ 61%]
tests/test_model/test_hvac/test_heating.py::test_PhxHeaterBoilerWood_set_fuel PASSED [ 61%]
tests/test_model/test_hvac/test_heating.py::test_default_PhxHeaterDistrictHeat PASSED [ 62%]
tests/test_model/test_hvac/test_piping_elements.py::test_empty_PhxPipeElement PASSED [ 62%]
tests/test_model/test_hvac/test_piping_elements.py::test_add_single_segment_to_PhxPipeElement PASSED [ 62%]
tests/test_model/test_hvac/test_piping_elements.py::test_add_multiple_segment_to_PhxPipeElement PASSED [ 63%]
tests/test_model/test_hvac/test_piping_segments.py::test_PhxPipeSegment_heat_loss_coefficient_1 PASSED [ 63%]
tests/test_model/test_hvac/test_piping_segments.py::test_PhxPipeSegment_heat_loss_coefficient_2 PASSED [ 63%]
tests/test_model/test_hvac/test_renewable_equip.py::test_add_default_PhxDevicePhotovoltaicParams PASSED [ 63%]
tests/test_model/test_hvac/test_renewable_equip.py::test_default_PhxDevicePhotovoltaic PASSED [ 64%]
tests/test_model/test_hvac/test_supportive_device.py::test_add_PhxSupportiveDeviceParams PASSED [ 64%]
tests/test_model/test_hvac/test_supportive_device.py::test_add_default_PhxSupportiveDevice PASSED [ 64%]
tests/test_model/test_hvac/test_ventilation.py::test_add_default_PhxVentilatorParams PASSED [ 65%]
tests/test_model/test_hvac/test_ventilation.py::test_add_mixed_PhxVentilatorParams PASSED [ 65%]
tests/test_model/test_hvac/test_ventilation.py::test_default_PhxVentilator PASSED [ 65%]
tests/test_model/test_hvac/test_ventilation.py::test__PhxVentilator_params PASSED [ 65%]
tests/test_model/test_hvac/test_ventilation.py::test_add_default_PhxVentilator PASSED [ 66%]
tests/test_model/test_hvac/test_ventilation.py::test_add_mixed_PhxVentilator PASSED [ 66%]
tests/test_model/test_hvac/test_ventilation.py::test_range_hood PASSED   [ 66%]
tests/test_model/test_hvac/test_ventilation.py::test_add_error PASSED    [ 67%]
tests/test_model/test_hvac/test_ventilation.py::test_add_range_hoods PASSED [ 67%]
tests/test_model/test_hvac/test_ventilation.py::test_dryer PASSED        [ 67%]
tests/test_model/test_hvac/test_ventilation.py::test_add_dryers PASSED   [ 67%]
tests/test_model/test_hvac/test_ventilation.py::test_user_determined PASSED [ 68%]
tests/test_model/test_hvac/test_ventilation.py::test_add_user_determined PASSED [ 68%]
tests/test_model/test_hvac/test_water.py::test_add_default_PhxHotWaterTankParams PASSED [ 68%]
tests/test_model/test_hvac/test_water.py::test_add_mixed_PhxHotWaterTankParams PASSED [ 69%]
tests/test_model/test_hvac/test_water.py::test_default_PhxHotWaterTank PASSED [ 69%]
tests/test_model/test_hvac/test_water.py::test_PhxHotWaterTank_quantity PASSED [ 69%]
tests/test_model/test_hvac/test_water.py::test_add_default_PhxHotWaterTank PASSED [ 69%]
tests/test_model/test_hvac/test_water.py::test_add_mixed_PhxHotWaterTank PASSED [ 70%]
tests/test_model/test_loads/test_ventilation.py::test_default_vent_load PASSED [ 70%]
tests/test_model/test_loads/test_ventilation.py::test_vent_load_has_ventilation PASSED [ 70%]
tests/test_model/test_loads/test_ventilation.py::test_vent_load_does_not_have_ventilation PASSED [ 71%]
tests/test_model/test_project/test_Project.py::test_blank_project PASSED [ 71%]
tests/test_model/test_project/test_Project.py::test_project_data PASSED  [ 71%]
tests/test_model/test_project/test_Project.py::test_add_variant_to_project PASSED [ 71%]
tests/test_model/test_project/test_Project.py::test_add_assembly_to_project PASSED [ 72%]
tests/test_model/test_project/test_Variant.py::test_blank_variant PASSED [ 72%]
tests/test_model/test_project/test_Variant.py::test_variant_with_zone PASSED [ 72%]
tests/test_model/test_schedules/test_vent_periods.py::test_default_vent_util_period PASSED [ 73%]
tests/test_model/test_schedules/test_vent_periods.py::test_custom_vent_util_periods PASSED [ 73%]
tests/test_model/test_schedules/test_vent_schedules.py::test_vent_schedule_empty PASSED [ 73%]
tests/test_model/test_schedules/test_vent_schedules.py::test_vent_schedule_empty_force_24 PASSED [ 73%]
tests/test_model/test_schedules/test_vent_schedules.py::test_vent_schedule_custom PASSED [ 74%]
tests/test_model/test_schedules/test_vent_schedules.py::test_vent_schedule_over_24_force_24 PASSED [ 74%]
tests/test_model/test_schedules/test_vent_schedules.py::test_vent_schedules_different_hash PASSED [ 74%]
tests/test_model/test_spaces/test_spaces.py::test_default_room_ventilation PASSED [ 75%]
tests/test_model/test_spaces/test_spaces.py::test_space_no_vent PASSED   [ 75%]
tests/test_model/test_util_patterns/test_util_pattern_collection.py::test_empty_util_collection PASSED [ 75%]
tests/test_model/test_util_patterns/test_util_pattern_collection.py::test_add_no_schedule_to_collection PASSED [ 75%]
tests/test_model/test_util_patterns/test_util_pattern_collection.py::test_add_single_schedule_to_collection PASSED [ 76%]
tests/test_model/test_util_patterns/test_util_pattern_collection.py::test_add_multiple_schedules_to_collection PASSED [ 76%]
tests/test_to_WUFI_xml/test_xml_builder.py::test_generate_WUFI_XML_from_object PASSED [ 76%]
tests/test_to_WUFI_xml/test_xml_converter.py::test_get_PHX_object_conversion_schema_with_valid_PHX PASSED [ 77%]
tests/test_to_WUFI_xml/test_xml_converter.py::test_get_PHX_object_conversion_schema_with_valid_PHX_schema_name_override PASSED [ 77%]
tests/test_to_WUFI_xml/test_xml_converter.py::test_get_PHX_object_conversion_schema_with_valid_PHX_error PASSED [ 77%]
tests/test_to_WUFI_xml/test_xml_writables.py::test_XML_Node PASSED       [ 77%]
tests/test_to_WUFI_xml/test_xml_writables.py::test_XML_List PASSED       [ 78%]
tests/test_to_WUFI_xml/test_building/test_PhxBuilding.py::test_default_PhxProject PASSED [ 78%]
tests/test_to_WUFI_xml/test_building/test_PhxComponent.py::test_default_PhxProject PASSED [ 78%]
tests/test_to_WUFI_xml/test_building/test_PhxZone.py::test_default_PhxProject PASSED [ 79%]
tests/test_to_WUFI_xml/test_certification/test_PhxPHCertification.py::test_default_PhxPHCertification PASSED [ 79%]
tests/test_to_WUFI_xml/test_certification/test_PhxPHCertification.py::test_customized_PhxPhiusCertification PASSED [ 79%]
tests/test_to_WUFI_xml/test_climate/test_PhxLocation.py::test_default_PhxLocation PASSED [ 79%]
tests/test_to_WUFI_xml/test_construction/test_PhxConstructionOpaque.py::test_default_PhxConstructionOpaque PASSED [ 80%]
tests/test_to_WUFI_xml/test_construction/test_PhxConstructionWindow.py::test_default_PhxConstructionWindow PASSED [ 80%]
tests/test_to_WUFI_xml/test_construction/test_PhxLayer.py::test_default_PhxLayer PASSED [ 80%]
tests/test_to_WUFI_xml/test_construction/test_PhxMaterial.py::test_default_PhxMaterial PASSED [ 81%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxClothesWasher.py::test_default_PhxDeviceClothesWasher PASSED [ 81%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceClothesDryer.py::test_default_PhxDeviceClothesDryer <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceClothesDryer.py PASSED [ 81%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceCooktop.py::test_default_PhxDeviceCooktop <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceCooktop.py PASSED [ 81%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceCustomElec.py::test_default_PhxDeviceCustomElec <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceCustomElec.py PASSED [ 82%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceCustomLighting.py::test_default_PhxDeviceCustomLighting <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceCustomLighting.py PASSED [ 82%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceCustomMEL.py::test_default_PhxDeviceCustomMEL <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceCustomMEL.py PASSED [ 82%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceDishwasher.py::test_default_PhxDeviceDishwasher PASSED [ 83%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceFreezer.py::test_default_PhxDeviceFreezer <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceFreezer.py PASSED [ 83%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceFridgeFreezer.py::test_default_PhxDeviceFridgeFreezer <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceFridgeFreezer.py PASSED [ 83%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceLightingExterior.py::test_default_PhxDeviceLightingExterior <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceLightingExterior.py PASSED [ 83%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceLightingGarage.py::test_default_PhxDeviceLightingGarage <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceLightingGarage.py PASSED [ 84%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceLightingInterior.py::test_default_PhxDeviceLightingInterior <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceLightingInterior.py PASSED [ 84%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceMEL.py::test_default_PhxDeviceMEL <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceMEL.py PASSED [ 84%]
tests/test_to_WUFI_xml/test_elec_equip/test_PhxDeviceRefrigerator.py::test_default_PhxDeviceRefrigerator <- tests/test_to_WUFI_xml/test_loads/test_elec_equip/test_PhxDeviceRefrigerator.py PASSED [ 85%]
tests/test_to_WUFI_xml/test_geometry/test_PhxGraphics3D.py::test_default_PhxGraphics3D PASSED [ 85%]
tests/test_to_WUFI_xml/test_geometry/test_PhxGraphics3D.py::test_PhxGraphics3D_with_single_polygon PASSED [ 85%]
tests/test_to_WUFI_xml/test_geometry/test_PhxGraphics3D.py::test_PhxGraphics3D_with_multiple_polygon PASSED [ 85%]
tests/test_to_WUFI_xml/test_geometry/test_PhxLineSegment.py::test_simple_line_length_x PASSED [ 86%]
tests/test_to_WUFI_xml/test_geometry/test_PhxLineSegment.py::test_simple_line_length_y PASSED [ 86%]
tests/test_to_WUFI_xml/test_geometry/test_PhxLineSegment.py::test_simple_line_length_z PASSED [ 86%]
tests/test_to_WUFI_xml/test_geometry/test_PhxLineSegment.py::test_simple_line_length_xyz PASSED [ 87%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygon.py::test_default_polygon_no_vertices PASSED [ 87%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygon.py::test_default_polygon_edit_normal_vector PASSED [ 87%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygon.py::test_default_polygon_with_two_vertices PASSED [ 87%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygon.py::test_default_polygon_with_single_polygon_inside PASSED [ 88%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygon.py::test_default_polygon_with_multiple_polygon_inside PASSED [ 88%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygonRectangular.py::test_default_polygon_rectangular_no_vertices PASSED [ 88%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygonRectangular.py::test_multiple_default_polygon_rectangular_no_vertices PASSED [ 89%]
tests/test_to_WUFI_xml/test_geometry/test_PhxPolygonRectangular.py::test_polygon_then_polygon_rectangular_no_vertices PASSED [ 89%]
tests/test_to_WUFI_xml/test_geometry/test_PhxVertix.py::test_single_Vertix_to_XML PASSED [ 89%]
tests/test_to_WUFI_xml/test_geometry/test_PhxVertix.py::test_multiple_Vertix_to_XML PASSED [ 89%]
tests/test_to_WUFI_xml/test_ground/test_PhxFoundation.py::test_default_PhxFoundation PASSED [ 90%]
tests/test_to_WUFI_xml/test_ground/test_PhxFoundation.py::test_default_PhxHeatedBasement PASSED [ 90%]
tests/test_to_WUFI_xml/test_ground/test_PhxFoundation.py::test_default_PhxUnHeatedBasement PASSED [ 90%]
tests/test_to_WUFI_xml/test_ground/test_PhxFoundation.py::test_default_PhxSlabOnGrade PASSED [ 91%]
tests/test_to_WUFI_xml/test_ground/test_PhxFoundation.py::test_default_PhxCrawlspace PASSED [ 91%]
tests/test_to_WUFI_xml/test_hvac/test_cooling/test_PhxCoolingDehumidification.py::test_default_PhxCoolingDehumidification PASSED [ 91%]
tests/test_to_WUFI_xml/test_hvac/test_cooling/test_PhxCoolingPanel.py::test_default_PhxCoolingPanel PASSED [ 91%]
tests/test_to_WUFI_xml/test_hvac/test_cooling/test_PhxCoolingRecirculation.py::test_default_PhxCoolingRecirculation PASSED [ 92%]
tests/test_to_WUFI_xml/test_hvac/test_cooling/test_PhxCoolingVentilation.py::test_default_PhxCoolingVentilation PASSED [ 92%]
tests/test_to_WUFI_xml/test_hvac/test_distribution/test_DistributionDHW.py::test_add_Trunk_to_HW_System PASSED [ 92%]
tests/test_to_WUFI_xml/test_hvac/test_distribution/test_DistributionDucting.py::test_Duct_Schema PASSED [ 93%]
tests/test_to_WUFI_xml/test_hvac/test_distribution/test_DistributionHeating.py::test_DistributionHeating_Class PASSED [ 93%]
tests/test_to_WUFI_xml/test_hvac/test_distribution/test_DistributionHeating.py::test_DistributionHeating_Schema PASSED [ 93%]
tests/test_to_WUFI_xml/test_hvac/test_heat_pumps/test_PhxHeaterHeatPumpAnnual.py::test_default_PhxHeaterHeatPumpAnnual_with_no_cooling PASSED [ 93%]
tests/test_to_WUFI_xml/test_hvac/test_heat_pumps/test_PhxHeaterHeatPumpAnnual.py::test_default_PhxHeaterHeatPumpAnnual_with_recirc_cooling FAILED [ 94%]
tests/test_to_WUFI_xml/test_hvac/test_heat_pumps/test_PhxHeaterHeatPumpCombined.py::test_default_PhxHeaterHeatPumpCombined PASSED [ 94%]
tests/test_to_WUFI_xml/test_hvac/test_heat_pumps/test_PhxHeaterHeatPumpHotWater.py::test_default_PhxHeaterHeatPumpHotWater PASSED [ 94%]
tests/test_to_WUFI_xml/test_hvac/test_heat_pumps/test_PhxHeaterHeatPumpMonthly.py::test_default_PhxHeaterHeatPumpMonthly PASSED [ 95%]
tests/test_to_WUFI_xml/test_hvac/test_heating/test_PhxHeaterBoilerFossil.py::test_default_PhxHeaterBoilerFossil PASSED [ 95%]
tests/test_to_WUFI_xml/test_hvac/test_heating/test_PhxHeaterBoilerWood.py::test_default_PhxHeaterBoilerWood PASSED [ 95%]
tests/test_to_WUFI_xml/test_hvac/test_heating/test_PhxHeaterDistrictHeat.py::test_default_PhxHeaterDistrictHeat PASSED [ 95%]
tests/test_to_WUFI_xml/test_hvac/test_heating/test_PhxHeaterElectric.py::test_default_PhxHeaterElectric PASSED [ 96%]
tests/test_to_WUFI_xml/test_hvac/test_renewable_equip/test_PhxDevicePhotovoltaic.py::test_default_PhxDevicePhotovoltaic PASSED [ 96%]
tests/test_to_WUFI_xml/test_hvac/test_ventilation/test_PhxVentilator.py::test_PhxRoomVentilator_id_num_increment PASSED [ 96%]
tests/test_to_WUFI_xml/test_hvac/test_ventilation/test_PhxVentilator.py::test_default_PhxRoomVentilation PASSED [ 97%]
tests/test_to_WUFI_xml/test_hvac/test_water/test_PhxHotWaterTank.py::test_default_PhxHotWaterTank PASSED [ 97%]
tests/test_to_WUFI_xml/test_project/test_PhxProject.py::test_default_PhxProject PASSED [ 97%]
tests/test_to_WUFI_xml/test_project/test_PhxProjectData.py::test_default_PhxProjectData PASSED [ 97%]
tests/test_to_WUFI_xml/test_project/test_PhxVariant.py::test_default_PhxProject PASSED [ 98%]
tests/test_to_WUFI_xml/test_reference_cases/test_to_project_geometry.py::test_all_aperture_areas_are_equivalent_after_conversion[to_xml_reference_cases0] PASSED [ 98%]
tests/test_to_WUFI_xml/test_reference_cases/test_to_project_geometry.py::test_all_aperture_areas_are_equivalent_after_conversion[to_xml_reference_cases1] FAILED [ 98%]
tests/test_to_WUFI_xml/test_reference_cases/test_xml_output.py::test_xml_output[to_xml_reference_cases0] PASSED [ 99%]
tests/test_to_WUFI_xml/test_reference_cases/test_xml_output.py::test_xml_output[to_xml_reference_cases1] FAILED [ 99%]
tests/test_to_WUFI_xml/test_schedule/test_PhxRoomVentilation.py::test_default_PhxRoomVentilation PASSED [ 99%]
tests/test_to_WUFI_xml/test_schedule/test_PhxUtilizationPatternVent.py::test_default_PhxRoomVentilation PASSED [100%]

==================================== ERRORS ====================================
__________________ ERROR at setup of test_vent_patterns_match __________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
__________________ ERROR at setup of test_vent_patterns_match __________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
_________________ ERROR at setup of test_assembly_types_match __________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
______________ ERROR at setup of test_assembly_type_layers_match _______________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
_____________ ERROR at setup of test_assembly_type_materials_match _____________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
_____________________ ERROR at setup of test_project_data ______________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
_____________________ ERROR at setup of test_project_data ______________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
_____________ ERROR at setup of test_window_type_attributes_match ______________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
_________ ERROR at setup of test_window_FrameElememt_attributes_match __________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
___________ ERROR at setup of test_building_all_components_are_equal ___________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
___________________ ERROR at setup of test_variant_buildings ___________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
___________________ ERROR at setup of test_project_variants ____________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

    @pytest.fixture
    def create_phx_project_from_hbjson() -> PhxProject:
        # -- Build the PhxProject from the HBJSON file
        SOURCE_HBJSON_FILE = Path("tests", "_source_hbjson", "Multi_Room_Complete.hbjson")
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(SOURCE_HBJSON_FILE)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_WUFI/conftest.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
---------------------------- Captured stderr setup -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
=================================== FAILURES ===================================
________ test_convert_model_PhxProject[Multi_Room_Complete.hbjson-None] ________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

filename = 'Multi_Room_Complete.hbjson', results = None

    @pytest.mark.parametrize(
        "filename,results",
        [
            ("Default_Model_Single_Zone.hbjson", None),
            ("Multi_Room_Complete.hbjson", None),
        ],
    )
    def test_convert_model_PhxProject(filename, results):
        file_path = Path("tests", "_source_hbjson", filename)
    
        # -- Build the HB-Model, convert to a PhxProject
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(file_path)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_from_HBJSON/test_create_project/test_convert_hbjson_to_PHX_Project.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
_ test_read_default_single_zone_model_no_conversion[Multi_Room_Complete.hbjson-None] _

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

filename = 'Multi_Room_Complete.hbjson', results = None

    @pytest.mark.parametrize(
        "filename,results",
        [
            ("Default_Model_Single_Zone.hbjson", None),
            ("Multi_Room_Complete.hbjson", None),
        ],
    )
    def test_read_default_single_zone_model_no_conversion(filename, results):
        file_path = Path("tests", "_source_hbjson", filename)
    
        d1 = read_HBJSON_file.read_hb_json_from_file(file_path)
>       m1 = read_HBJSON_file.convert_hbjson_dict_to_hb_model(d1)

tests/test_from_HBJSON/test_read_HBJSON_file/test_convert_hbjson_dict_to_hb_model.py:72: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
________________________ test_default_PhxCoolingDevice _________________________

reset_class_counters = None

    def test_default_PhxCoolingDevice(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingDevice()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingDevice'

tests/test_model/test_hvac/test_cooling.py:6: AttributeError
___________________ test_default_PhxCoolingVentilationParams ___________________

reset_class_counters = None

    def test_default_PhxCoolingVentilationParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingVentilationParams()
        p2 = cooling_params.PhxCoolingVentilationParams()
    
        p3 = p1 + p2
        assert p3 == p2 == p1
    
        # -- Base attrs
        assert p3.aux_energy == p1.aux_energy
        assert p3.aux_energy_dhw == p1.aux_energy_dhw
        assert p3.solar_fraction == p1.solar_fraction
        assert p3.in_conditioned_space == p1.in_conditioned_space
    
        # -- Class-specific attrs
>       assert p3.hp_type == p1.hp_type
E       AttributeError: 'PhxCoolingVentilationParams' object has no attribute 'hp_type'

tests/test_model/test_hvac/test_cooling.py:32: AttributeError
____________________ test_mixed_PhxCoolingVentilationParams ____________________

reset_class_counters = None

    def test_mixed_PhxCoolingVentilationParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingVentilationParams(
            single_speed=True,
            min_coil_temp=20,
            capacity=20,
            annual_COP=6,
        )
        p2 = cooling_params.PhxCoolingVentilationParams(
            single_speed=False,
            min_coil_temp=10,
            capacity=10,
            annual_COP=4,
        )
    
        p3 = p1 + p2
        assert p3 != p2 != p1
    
        # -- Base attrs
        assert p3.aux_energy == p1.aux_energy
        assert p3.aux_energy_dhw == p1.aux_energy_dhw
        assert p3.solar_fraction == p1.solar_fraction
        assert p3.in_conditioned_space == p1.in_conditioned_space
    
        # -- Class-specific attrs
>       assert p3.hp_type == p1.hp_type
E       AttributeError: 'PhxCoolingVentilationParams' object has no attribute 'hp_type'

tests/test_model/test_hvac/test_cooling.py:64: AttributeError
______________________ test_default_PhxCoolingVentilation ______________________

reset_class_counters = None

    def test_default_PhxCoolingVentilation(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingVentilation()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingVentilation'

tests/test_model/test_hvac/test_cooling.py:73: AttributeError
____________________ test_add_default_PhxCoolingVentilation ____________________

reset_class_counters = None

    def test_add_default_PhxCoolingVentilation(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingVentilation()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingVentilation'

tests/test_model/test_hvac/test_cooling.py:85: AttributeError
_____________________ test_add_mixed_PhxCoolingVentilation _____________________

reset_class_counters = None

    def test_add_mixed_PhxCoolingVentilation(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingVentilation()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingVentilation'

tests/test_model/test_hvac/test_cooling.py:100: AttributeError
__________________ test_default_PhxCoolingRecirculationParams __________________

reset_class_counters = None

    def test_default_PhxCoolingRecirculationParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingRecirculationParams()
        p2 = cooling_params.PhxCoolingRecirculationParams()
    
        p3 = p1 + p2
        assert p3 == p2 == p1
    
        # -- Base attrs
        assert p3.aux_energy == p1.aux_energy
        assert p3.aux_energy_dhw == p1.aux_energy_dhw
        assert p3.solar_fraction == p1.solar_fraction
        assert p3.in_conditioned_space == p1.in_conditioned_space
    
        # -- Class-specific attrs
>       assert p3.hp_type == p1.hp_type
E       AttributeError: 'PhxCoolingRecirculationParams' object has no attribute 'hp_type'

tests/test_model/test_hvac/test_cooling.py:147: AttributeError
___________________ test_mixed_PhxCoolingRecirculationParams ___________________

reset_class_counters = None

    def test_mixed_PhxCoolingRecirculationParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingRecirculationParams(
            single_speed=True,
            min_coil_temp=20,
            capacity=20,
            annual_COP=6,
            flow_rate_m3_hr=100,
            flow_rate_variable=True,
        )
        p2 = cooling_params.PhxCoolingRecirculationParams(
            single_speed=False,
            min_coil_temp=10,
            capacity=10,
            annual_COP=4,
            flow_rate_m3_hr=50,
            flow_rate_variable=False,
        )
    
        p3 = p1 + p2
        assert p3 != p2 != p1
    
        # -- Base attrs
        assert p3.aux_energy == p1.aux_energy
        assert p3.aux_energy_dhw == p1.aux_energy_dhw
        assert p3.solar_fraction == p1.solar_fraction
        assert p3.in_conditioned_space == p1.in_conditioned_space
    
        # -- Class-specific attrs
>       assert p3.hp_type == p1.hp_type
E       AttributeError: 'PhxCoolingRecirculationParams' object has no attribute 'hp_type'

tests/test_model/test_hvac/test_cooling.py:185: AttributeError
_____________________ test_default_PhxCoolingRecirculation _____________________

reset_class_counters = None

    def test_default_PhxCoolingRecirculation(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingRecirculation()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingRecirculation'

tests/test_model/test_hvac/test_cooling.py:196: AttributeError
___________________ test_add_default_PhxCoolingRecirculation ___________________

reset_class_counters = None

    def test_add_default_PhxCoolingRecirculation(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingRecirculation()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingRecirculation'

tests/test_model/test_hvac/test_cooling.py:208: AttributeError
____________________ test_add_mixed_PhxCoolingRecirculation ____________________

reset_class_counters = None

    def test_add_mixed_PhxCoolingRecirculation(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingRecirculation()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingRecirculation'

tests/test_model/test_hvac/test_cooling.py:226: AttributeError
________________ test_default_PhxCoolingDehumidificationParams _________________

reset_class_counters = None

    def test_default_PhxCoolingDehumidificationParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingDehumidificationParams()
        p2 = cooling_params.PhxCoolingDehumidificationParams()
    
        p3 = p1 + p2
        assert p3 == p2 == p1
    
        # -- Base attrs
        assert p3.aux_energy == p1.aux_energy
        assert p3.aux_energy_dhw == p1.aux_energy_dhw
        assert p3.solar_fraction == p1.solar_fraction
        assert p3.in_conditioned_space == p1.in_conditioned_space
    
        # -- Class-specific attrs
>       assert p3.hp_type == p1.hp_type
E       AttributeError: 'PhxCoolingDehumidificationParams' object has no attribute 'hp_type'

tests/test_model/test_hvac/test_cooling.py:280: AttributeError
_________________ test_mixed_PhxCoolingDehumidificationParams __________________

reset_class_counters = None

    def test_mixed_PhxCoolingDehumidificationParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingDehumidificationParams(annual_COP=6, useful_heat_loss=False)
        p2 = cooling_params.PhxCoolingDehumidificationParams(annual_COP=4, useful_heat_loss=True)
    
        p3 = p1 + p2
        assert p3 != p2 != p1
    
        # -- Base attrs
        assert p3.aux_energy == p1.aux_energy
        assert p3.aux_energy_dhw == p1.aux_energy_dhw
        assert p3.solar_fraction == p1.solar_fraction
        assert p3.in_conditioned_space == p1.in_conditioned_space
    
        # -- Class-specific attrs
>       assert p3.hp_type == p1.hp_type
E       AttributeError: 'PhxCoolingDehumidificationParams' object has no attribute 'hp_type'

tests/test_model/test_hvac/test_cooling.py:300: AttributeError
___________________ test_default_PhxCoolingDehumidification ____________________

reset_class_counters = None

    def test_default_PhxCoolingDehumidification(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingDehumidification()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingDehumidification'

tests/test_model/test_hvac/test_cooling.py:307: AttributeError
_________________ test_add_default_PhxCoolingDehumidification __________________

reset_class_counters = None

    def test_add_default_PhxCoolingDehumidification(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingDehumidification()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingDehumidification'

tests/test_model/test_hvac/test_cooling.py:319: AttributeError
__________________ test_add_mixed_PhxCoolingDehumidification ___________________

reset_class_counters = None

    def test_add_mixed_PhxCoolingDehumidification(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingDehumidification()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingDehumidification'

tests/test_model/test_hvac/test_cooling.py:333: AttributeError
______________________ test_default_PhxCoolingPanelParams ______________________

reset_class_counters = None

    def test_default_PhxCoolingPanelParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingPanelParams()
        p2 = cooling_params.PhxCoolingPanelParams()
    
        p3 = p1 + p2
        assert p3 == p2 == p1
    
        # -- Base attrs
        assert p3.aux_energy == p1.aux_energy
        assert p3.aux_energy_dhw == p1.aux_energy_dhw
        assert p3.solar_fraction == p1.solar_fraction
        assert p3.in_conditioned_space == p1.in_conditioned_space
    
        # -- Class-specific attrs
>       assert p3.hp_type == p1.hp_type
E       AttributeError: 'PhxCoolingPanelParams' object has no attribute 'hp_type'

tests/test_model/test_hvac/test_cooling.py:373: AttributeError
_______________________ test_mixed_PhxCoolingPanelParams _______________________

reset_class_counters = None

    def test_mixed_PhxCoolingPanelParams(reset_class_counters):
        p1 = cooling_params.PhxCoolingPanelParams(
            hp_type=hvac.HeatPumpType.ANNUAL,
>           annual_COP=6,
        )
E       TypeError: __init__() got an unexpected keyword argument 'hp_type'

tests/test_model/test_hvac/test_cooling.py:381: TypeError
_________________________ test_default_PhxCoolingPanel _________________________

reset_class_counters = None

    def test_default_PhxCoolingPanel(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingPanel()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingPanel'

tests/test_model/test_hvac/test_cooling.py:404: AttributeError
_______________________ test_add_default_PhxCoolingPanel _______________________

reset_class_counters = None

    def test_add_default_PhxCoolingPanel(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingPanel()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingPanel'

tests/test_model/test_hvac/test_cooling.py:416: AttributeError
________________________ test_add_mixed_PhxCoolingPanel ________________________

reset_class_counters = None

    def test_add_mixed_PhxCoolingPanel(reset_class_counters):
>       dev_1 = cooling_params.PhxCoolingPanel()
E       AttributeError: module 'PHX.model.hvac.cooling_params' has no attribute 'PhxCoolingPanel'

tests/test_model/test_hvac/test_cooling.py:429: AttributeError
___________ test_default_PhxHeaterHeatPumpAnnual_with_recirc_cooling ___________

reset_class_counters = None

    def test_default_PhxHeaterHeatPumpAnnual_with_recirc_cooling(reset_class_counters):
        h1 = heat_pumps.PhxHeatPumpAnnual()
        h1.usage_profile.space_heating = True
    
        # -- Set all the cooling 'off'
        h1.params_cooling.ventilation.used = False
        h1.params_cooling.recirculation.used = True
        h1.params_cooling.dehumidification.used = False
        h1.params_cooling.panel.used = False
    
        coll = collection.PhxMechanicalSystemCollection()
        coll.add_new_mech_device(h1.identifier, h1)
        result = generate_WUFI_XML_from_object(coll, _header="")
>       assert xml_string_to_list(result) == ["a"]
E       assert ['<Systems count="1">', '<System index="0">', '<Name>Ideal Air System</Name>', '<Type choice="User defined (ideal system)">1</Type>', '<IdentNr>1</IdentNr>', '<ZonesCoverage count="1">', '<ZoneCoverage index="0">', '<IdentNrZone>1</IdentNrZone>', '<CoverageHeating>1.0</CoverageHeating>', '<CoverageCooling>1.0</CoverageCooling>', '<CoverageVentilation>1.0</CoverageVentilation>', '<CoverageHumidification>1.0</CoverageHumidification>', '<CoverageDehumidification>1.0</CoverageDehumidification>', '</ZoneCoverage>', '</ZonesCoverage>', '<Devices count="1">', '<Device index="0">', '<Name>_unnamed_equipment_</Name>', '<IdentNr>1</IdentNr>', '<SystemType>5</SystemType>', '<TypeDevice>5</TypeDevice>', '<UsedFor_Heating>true</UsedFor_Heating>', '<UsedFor_DHW>false</UsedFor_DHW>', '<UsedFor_Cooling>false</UsedFor_Cooling>', '<UsedFor_Ventilation>false</UsedFor_Ventilation>', '<UsedFor_Humidification>false</UsedFor_Humidification>', '<UsedFor_Dehumidification>false</UsedFor_Dehumidification>', '<PH_Parameters>', '<AuxiliaryEnergy/>', '<AuxiliaryEnergyDHW/>', '<InConditionedSpace>true</InConditionedSpace>', '<AnnualCOP/>', '<TotalSystemPerformanceRatioHeatGenerator/>', '<HPType>3</HPType>', '</PH_Parameters>', '<DHW_Parameters>', '<CoverageWithinSystem>0.0</CoverageWithinSystem>', '<Unit>0.0</Unit>', '<Selection>1</Selection>', '</DHW_Parameters>', '<Heating_Parameters>', '<CoverageWithinSystem>0.0</CoverageWithinSystem>', '<Unit>0.0</Unit>', '<Selection>1</Selection>', '</Heating_Parameters>', '</Device>', '</Devices>', '<PHDistribution>', '<DistributionDHW>', '<CalculationMethodIndividualPipes>4</CalculationMethodIndividualPipes>', '<DemandRecirculation>true</DemandRecirculation>', '<SelectionhotWaterFixtureEff>1</SelectionhotWaterFixtureEff>', '<NumberOfBathrooms>1</NumberOfBathrooms>', '<AllPipesAreInsulated>true</AllPipesAreInsulated>', '<SelectionUnitsOrFloors>2</SelectionUnitsOrFloors>', '<PipeMaterialSimplifiedMethod>2</PipeMaterialSimplifiedMethod>', '<PipeDiameterSimplifiedMethod>25.4</PipeDiameterSimplifiedMethod>', '<TemperatureRoom_WR>20.0</TemperatureRoom_WR>', '<DesignFlowTemperature_WR>60.0</DesignFlowTemperature_WR>', '<DailyRunningHoursCirculation_WR>0.0</DailyRunningHoursCirculation_WR>', '<LengthCirculationPipes_WR>0</LengthCirculationPipes_WR>', '<HeatLossCoefficient_WR/>', '<LengthIndividualPipes_WR>0</LengthIndividualPipes_WR>', '<ExteriorPipeDiameter_WR/>', '<Truncs count="0"/>', '</DistributionDHW>', '<DistributionCooling>', '<CoolingViaRecirculation>true</CoolingViaRecirculation>', '<RecirculatingAirOnOff>false</RecirculatingAirOnOff>', '<MaxRecirculationAirCoolingPower>10.0</MaxRecirculationAirCoolingPower>', '<MinTempCoolingCoilRecirculatingAir>12.0</MinTempCoolingCoilRecirculatingAir>', '<RecirculationCoolingCOP>4.0</RecirculationCoolingCOP>', '<RecirculationAirVolume>100.0</RecirculationAirVolume>', '<ControlledRecirculationVolumeFlow>true</ControlledRecirculationVolumeFlow>', '</DistributionCooling>', '<DistributionVentilation count="0"/>', '<UseDefaultValues>false</UseDefaultValues>', '<DeviceInConditionedSpace>true</DeviceInConditionedSpace>', '<SupportiveDevices count="0"/>', '</PHDistribution>', '</System>', '</Systems>'] == ['a']
E         At index 0 diff: '<Systems count="1">' != 'a'
E         Left contains 81 more items, first extra item: '<System index="0">'
E         Full diff:
E           [
E         -  'a',
E         +  '<Systems count="1">',
E         +  '<System index="0">',
E         +  '<Name>Ideal Air System</Name>',
E         +  '<Type choice="User defined (ideal system)">1</Type>',
E         +  '<IdentNr>1</IdentNr>',
E         +  '<ZonesCoverage count="1">',
E         +  '<ZoneCoverage index="0">',
E         +  '<IdentNrZone>1</IdentNrZone>',
E         +  '<CoverageHeating>1.0</CoverageHeating>',
E         +  '<CoverageCooling>1.0</CoverageCooling>',
E         +  '<CoverageVentilation>1.0</CoverageVentilation>',
E         +  '<CoverageHumidification>1.0</CoverageHumidification>',
E         +  '<CoverageDehumidification>1.0</CoverageDehumidification>',
E         +  '</ZoneCoverage>',
E         +  '</ZonesCoverage>',
E         +  '<Devices count="1">',
E         +  '<Device index="0">',
E         +  '<Name>_unnamed_equipment_</Name>',
E         +  '<IdentNr>1</IdentNr>',
E         +  '<SystemType>5</SystemType>',
E         +  '<TypeDevice>5</TypeDevice>',
E         +  '<UsedFor_Heating>true</UsedFor_Heating>',
E         +  '<UsedFor_DHW>false</UsedFor_DHW>',
E         +  '<UsedFor_Cooling>false</UsedFor_Cooling>',
E         +  '<UsedFor_Ventilation>false</UsedFor_Ventilation>',
E         +  '<UsedFor_Humidification>false</UsedFor_Humidification>',
E         +  '<UsedFor_Dehumidification>false</UsedFor_Dehumidification>',
E         +  '<PH_Parameters>',
E         +  '<AuxiliaryEnergy/>',
E         +  '<AuxiliaryEnergyDHW/>',
E         +  '<InConditionedSpace>true</InConditionedSpace>',
E         +  '<AnnualCOP/>',
E         +  '<TotalSystemPerformanceRatioHeatGenerator/>',
E         +  '<HPType>3</HPType>',
E         +  '</PH_Parameters>',
E         +  '<DHW_Parameters>',
E         +  '<CoverageWithinSystem>0.0</CoverageWithinSystem>',
E         +  '<Unit>0.0</Unit>',
E         +  '<Selection>1</Selection>',
E         +  '</DHW_Parameters>',
E         +  '<Heating_Parameters>',
E         +  '<CoverageWithinSystem>0.0</CoverageWithinSystem>',
E         +  '<Unit>0.0</Unit>',
E         +  '<Selection>1</Selection>',
E         +  '</Heating_Parameters>',
E         +  '</Device>',
E         +  '</Devices>',
E         +  '<PHDistribution>',
E         +  '<DistributionDHW>',
E         +  '<CalculationMethodIndividualPipes>4</CalculationMethodIndividualPipes>',
E         +  '<DemandRecirculation>true</DemandRecirculation>',
E         +  '<SelectionhotWaterFixtureEff>1</SelectionhotWaterFixtureEff>',
E         +  '<NumberOfBathrooms>1</NumberOfBathrooms>',
E         +  '<AllPipesAreInsulated>true</AllPipesAreInsulated>',
E         +  '<SelectionUnitsOrFloors>2</SelectionUnitsOrFloors>',
E         +  '<PipeMaterialSimplifiedMethod>2</PipeMaterialSimplifiedMethod>',
E         +  '<PipeDiameterSimplifiedMethod>25.4</PipeDiameterSimplifiedMethod>',
E         +  '<TemperatureRoom_WR>20.0</TemperatureRoom_WR>',
E         +  '<DesignFlowTemperature_WR>60.0</DesignFlowTemperature_WR>',
E         +  '<DailyRunningHoursCirculation_WR>0.0</DailyRunningHoursCirculation_WR>',
E         +  '<LengthCirculationPipes_WR>0</LengthCirculationPipes_WR>',
E         +  '<HeatLossCoefficient_WR/>',
E         +  '<LengthIndividualPipes_WR>0</LengthIndividualPipes_WR>',
E         +  '<ExteriorPipeDiameter_WR/>',
E         +  '<Truncs count="0"/>',
E         +  '</DistributionDHW>',
E         +  '<DistributionCooling>',
E         +  '<CoolingViaRecirculation>true</CoolingViaRecirculation>',
E         +  '<RecirculatingAirOnOff>false</RecirculatingAirOnOff>',
E         +  '<MaxRecirculationAirCoolingPower>10.0</MaxRecirculationAirCoolingPower>',
E         +  '<MinTempCoolingCoilRecirculatingAir>12.0</MinTempCoolingCoilRecirculatingAir>',
E         +  '<RecirculationCoolingCOP>4.0</RecirculationCoolingCOP>',
E         +  '<RecirculationAirVolume>100.0</RecirculationAirVolume>',
E         +  '<ControlledRecirculationVolumeFlow>true</ControlledRecirculationVolumeFlow>',
E         +  '</DistributionCooling>',
E         +  '<DistributionVentilation count="0"/>',
E         +  '<UseDefaultValues>false</UseDefaultValues>',
E         +  '<DeviceInConditionedSpace>true</DeviceInConditionedSpace>',
E         +  '<SupportiveDevices count="0"/>',
E         +  '</PHDistribution>',
E         +  '</System>',
E         +  '</Systems>',
E           ]

tests/test_to_WUFI_xml/test_hvac/test_heat_pumps/test_PhxHeaterHeatPumpAnnual.py:110: AssertionError
_ test_all_aperture_areas_are_equivalent_after_conversion[to_xml_reference_cases1] _

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

to_xml_reference_cases = (PosixPath('tests/_source_hbjson/Multi_Room_Complete.hbjson'), PosixPath('tests/_reference_xml/Multi_Room_Complete.xml'))

    def test_all_aperture_areas_are_equivalent_after_conversion(to_xml_reference_cases):
        # -- Get the test-case file paths
        hbjson_file, xml_file = to_xml_reference_cases
    
        # -- HB Model
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(hbjson_file)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_to_WUFI_xml/test_reference_cases/test_to_project_geometry.py:11: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
___________________ test_xml_output[to_xml_reference_cases1] ___________________

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
>                       hvac_class.from_dict_abridged(hvac, schedules)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1329: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy.hvac.idealair.IdealAirSystem'>
data = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
schedule_dict = {'Always On': ScheduleRuleset: Always On [default day: Always On_Day Schedule] [0 rules], 'Generic Office Cooling': Sc...g': ScheduleRuleset: Generic Office Heating [default day: OfficeMedium HTGSETP_SCH_YES_OPTIMUM_Default] [2 rules], ...}

    @classmethod
    def from_dict_abridged(cls, data, schedule_dict):
        """Create a IdealAirSystem object from an abridged dictionary.
    
        Args:
            data: A IdealAirSystemAbridged dictionary in following the format below.
            schedule_dict: A dictionary with schedule identifiers as keys and honeybee
                schedule objects as values (either ScheduleRuleset or
                ScheduleFixedInterval). These will be used to assign the schedules
                to the Setpoint object.
    
        .. code-block:: python
    
            {
            "type": 'IdealAirSystemAbridged',
            "identifier": 'Warehouse1 Ideal Loads Air System',  # identifier for the HVAC
            "economizer_type": 'DifferentialDryBulb',  # Economizer type
            "demand_controlled_ventilation": True,  # Demand controlled ventilation
            "sensible_heat_recovery": 0.75,  # Sensible heat recovery effectiveness
            "latent_heat_recovery": 0.7,  # Latent heat recovery effectiveness
            "heating_air_temperature": 40,  # Heating supply air temperature
            "cooling_air_temperature": 15,  # Cooling supply air temperature
            "heating_limit": 'autosize',  # Max size of the heating system in Watts
            "cooling_limit": 'autosize',  # Max size of the cooling system in Watts
            "heating_availability": "Warehouse Heating Control",  # schedule identifier
            "cooling_availability": "Warehouse Cooling Control",  # schedule identifier
            }
        """
        assert data['type'] == 'IdealAirSystemAbridged', \
            'Expected IdealAirSystemAbridged dictionary. Got {}.'.format(data['type'])
    
        # extract the key features and properties of the HVAC
        econ, dcv, sensible, latent, heat_temp, cool_temp, heat_limit, cool_limit = \
            cls._properties_from_dict(data)
    
        # extract the schedules
        heat_avail = None
        cool_avail = None
        if 'heating_availability' in data and data['heating_availability'] is not None:
            try:
                heat_avail = schedule_dict[data['heating_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
        if 'cooling_availability' in data and data['cooling_availability'] is not None:
            try:
                cool_avail = schedule_dict[data['cooling_availability']]
            except KeyError as e:
                raise ValueError('Failed to find {} in the schedule_dict.'.format(e))
    
        new_obj = cls(data['identifier'], econ, dcv, sensible, latent, heat_temp,
                      cool_temp, heat_limit, cool_limit, heat_avail, cool_avail)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        if 'user_data' in data and data['user_data'] is not None:
            new_obj.user_data = data['user_data']
        if 'properties' in data and data['properties'] is not None:
>           new_obj.properties._load_extension_attr_from_dict(data['properties'])

.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py:437: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = IdealAirSystemProperties: Room_7_64afc318 Ideal Loads Air System
property_dict = {'ph': {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'dis...': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}, 'type': 'IdealAirSystemProperties'}

    def _load_extension_attr_from_dict(self, property_dict):
        """Get attributes for extensions from a dictionary of the properties.
    
        This method should be called within the from_dict method of each
        honeybee-energy object. Specifically, this method should be called on
        the host object after it has been created from a dictionary but lacks
        any of the extension attributes in the dictionary.
    
        Args:
            property_dict: A dictionary of properties for the object (ie.
                ScheduleRulesetProperties, OpaqueConstructionProperties).
                These will be used to load attributes from the dictionary and
                assign them to the object on which this method is called.
        """
        for atr in self._extension_attributes:
            var = getattr(self, atr)
            if not hasattr(var, 'from_dict'):
                continue
    
            atr_prop_dict = property_dict.get(atr, None)
            if not atr_prop_dict:
                # the property_dict possesses no properties for that extension
                continue
>           setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))

.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py:75: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.properties.hvac.idealair.IdealAirSystemPhProperties'>
_input_dict = {'cooling_systems': [{'annual_COP': 2.0, 'capacity': 10.0, 'cooling_class_name': 'PhCoolingRecirculation', 'display_na...55C': 41, ...}, {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}], 'id_num': 0, ...}
host = IdealAirSystem: Room_7_64afc318 Ideal Loads Air System

    @classmethod
    def from_dict(cls, _input_dict, host):
        # type: (Dict[str, Any], Any) -> IdealAirSystemPhProperties
        valid_types = ("IdealAirSystemPhProperties", "IdealAirSystemPhPropertiesAbridged")
        if _input_dict["type"] not in valid_types:
            raise IdealAirSystemPhProperties_FromDictError(
                valid_types, _input_dict["type"]
            )
    
        new_prop = cls(host)
        new_prop.id_num = _input_dict["id_num"]
    
        vent_sys_dict = _input_dict["ventilation_system"]
        if vent_sys_dict:
            ph_vent_sys = ventilation.PhVentilationSystem.from_dict(vent_sys_dict)
            new_prop.ventilation_system = ph_vent_sys
    
        for htg_sys_dict in _input_dict.get("heating_systems", []):
>           htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)

.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py:110: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'honeybee_energy_ph.hvac.heating.PhHeatingSystemBuilder'>
_input_dict = {'COP_1': 3.4, 'COP_2': 4.12, 'ambient_temp_1': -3.33, 'ambient_temp_2': 12.0, ...}

    @classmethod
    def from_dict(cls, _input_dict):
        # type: (dict[str, Any]) -> PhHeatingSystem
        """Find the right appliance constructor class from the module based on the 'type' name."""
        valid_class_types = [
            nm for nm in dir(sys.modules[__name__]) if nm.startswith("Ph")
        ]
    
        heating_type = _input_dict["heating_type"]
        if heating_type not in valid_class_types:
>           raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
E           honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py:266: UnknownPhHeatingTypeError

During handling of the above exception, another exception occurred:

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
>               var.apply_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee/properties.py:387: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Model Energy Properties: [host: unnamed]
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply the energy properties of a dictionary to the host Model of this object.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully apply the energy properties.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
        _, constructions, construction_sets, _, schedules, program_types, hvacs, shws = \
>           self.load_properties_from_dict(data)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1004: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    @staticmethod
    def load_properties_from_dict(data):
        """Load model energy properties of a dictionary to Python objects.
    
        Loaded objects include Materials, Constructions, ConstructionSets,
        ScheduleTypeLimits, Schedules, and ProgramTypes.
    
        The function is called when re-serializing a Model object from a dictionary
        to load honeybee_energy objects into their Python object form before
        applying them to the Model geometry.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
                Note that this dictionary must have ModelEnergyProperties in order
                for this method to successfully load the energy properties.
    
        Returns:
            A tuple with eight elements
    
            -   materials -- A dictionary with identifiers of materials as keys
                and Python material objects as values.
    
            -   constructions -- A dictionary with identifiers of constructions
                as keys and Python construction objects as values.
    
            -   construction_sets -- A dictionary with identifiers of construction
                sets as keys and Python construction set objects as values.
    
            -   schedule_type_limits -- A dictionary with identifiers of schedule
                type limits as keys and Python schedule type limit objects as values.
    
            -   schedules -- A dictionary with identifiers of schedules as keys
                and Python schedule objects as values.
    
            -   program_types -- A dictionary with identifiers of program types
                as keys and Python program type objects as values.
    
            -   hvacs -- A dictionary with identifiers of HVAC systems as keys
                and Python HVACSystem objects as values.
    
            -   shws -- A dictionary with identifiers of SHW systems as keys
                and Python SHWSystem objects as values.
        """
        assert 'energy' in data['properties'], \
            'Dictionary possesses no ModelEnergyProperties.'
    
        # process all schedule type limits in the ModelEnergyProperties dictionary
        schedule_type_limits = {}
        if 'schedule_type_limits' in data['properties']['energy'] and \
                data['properties']['energy']['schedule_type_limits'] is not None:
            for t_lim in data['properties']['energy']['schedule_type_limits']:
                try:
                    schedule_type_limits[t_lim['identifier']] = \
                        ScheduleTypeLimit.from_dict(t_lim)
                except Exception as e:
                    invalid_dict_error(t_lim, e)
    
        # process all schedules in the ModelEnergyProperties dictionary
        schedules = {}
        if 'schedules' in data['properties']['energy'] and \
                data['properties']['energy']['schedules'] is not None:
            for sched in data['properties']['energy']['schedules']:
                try:
                    if sched['type'] in SCHEDULE_TYPES:
                        schedules[sched['identifier']] = dict_to_schedule(sched)
                    else:
                        schedules[sched['identifier']] = dict_abridged_to_schedule(
                            sched, schedule_type_limits)
                except Exception as e:
                    invalid_dict_error(sched, e)
    
        # process all materials in the ModelEnergyProperties dictionary
        materials = {}
        if 'materials' in data['properties']['energy'] and \
                data['properties']['energy']['materials'] is not None:
            for mat in data['properties']['energy']['materials']:
                try:
                    materials[mat['identifier']] = dict_to_material(mat)
                except Exception as e:
                    invalid_dict_error(mat, e)
    
        # process all constructions in the ModelEnergyProperties dictionary
        constructions = {}
        if 'constructions' in data['properties']['energy'] and \
                data['properties']['energy']['constructions'] is not None:
            for cnstr in data['properties']['energy']['constructions']:
                try:
                    if cnstr['type'] in CONSTRUCTION_TYPES:
                        constructions[cnstr['identifier']] = dict_to_construction(cnstr)
                    else:
                        constructions[cnstr['identifier']] = \
                            dict_abridged_to_construction(cnstr, materials, schedules)
                except Exception as e:
                    invalid_dict_error(cnstr, e)
    
        # process all construction sets in the ModelEnergyProperties dictionary
        construction_sets = {}
        if 'construction_sets' in data['properties']['energy'] and \
                data['properties']['energy']['construction_sets'] is not None:
            for c_set in data['properties']['energy']['construction_sets']:
                try:
                    if c_set['type'] == 'ConstructionSet':
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict(c_set)
                    else:
                        construction_sets[c_set['identifier']] = \
                            ConstructionSet.from_dict_abridged(c_set, constructions)
                except Exception as e:
                    invalid_dict_error(c_set, e)
    
        # process all ProgramType in the ModelEnergyProperties dictionary
        program_types = {}
        if 'program_types' in data['properties']['energy'] and \
                data['properties']['energy']['program_types'] is not None:
            for p_typ in data['properties']['energy']['program_types']:
                try:
                    if p_typ['type'] == 'ProgramType':
                        program_types[p_typ['identifier']] = ProgramType.from_dict(p_typ)
                    else:
                        program_types[p_typ['identifier']] = \
                            ProgramType.from_dict_abridged(p_typ, schedules)
                except Exception as e:
                    invalid_dict_error(p_typ, e)
    
        # process all HVAC systems in the ModelEnergyProperties dictionary
        hvacs = {}
        if 'hvacs' in data['properties']['energy'] and \
                data['properties']['energy']['hvacs'] is not None:
            for hvac in data['properties']['energy']['hvacs']:
                hvac_class = HVAC_TYPES_DICT[hvac['type'].replace('Abridged', '')]
                try:
                    hvacs[hvac['identifier']] = \
                        hvac_class.from_dict_abridged(hvac, schedules)
                except Exception as e:
>                   invalid_dict_error(hvac, e)

.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py:1331: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

invalid_dict = {'cooling_air_temperature': 13.0, 'cooling_limit': {'type': 'Autosize'}, 'demand_controlled_ventilation': True, 'economizer_type': 'DifferentialDryBulb', ...}
error = UnknownPhHeatingTypeError('Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only t...\', \'PhHeatingFossilBoiler\', \'PhHeatingSystem\', \'PhHeatingSystemBuilder\', \'PhHeatingWoodBoiler\'] are allowed?')

    def invalid_dict_error(invalid_dict, error):
        """Raise a ValueError for an invalid dictionary that failed to serialize.
    
        This error message will include the identifier (and display_name) if they are
        present within the invalid_dict, making it easier for ens users to find the
        invalid object within large objects like Models.
    
        Args:
            invalid_dict: A dictionary of an invalid honeybee object that failed
                to serialize.
            error:
        """
        obj_type = invalid_dict['type'].replace('Abridged', '') \
            if 'type' in invalid_dict else 'Honeybee Object'
        obj_id = invalid_dict['identifier'] if 'identifier' in invalid_dict else ''
        full_id = '{}[{}]'.format(invalid_dict['display_name'], obj_id) \
            if 'display_name' in invalid_dict else obj_id
>       raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
E       ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E       Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/typing.py:322: ValueError

During handling of the above exception, another exception occurred:

to_xml_reference_cases = (PosixPath('tests/_source_hbjson/Multi_Room_Complete.hbjson'), PosixPath('tests/_reference_xml/Multi_Room_Complete.xml'))

    def test_xml_output(to_xml_reference_cases) -> None:
        _reload_phx_classes()
        _reset_phx_class_counters()
    
        # -- Get the test-case file paths
        hbjson_file, xml_file = to_xml_reference_cases
    
        # -- HB Model
        hb_json_dict = read_HBJSON_file.read_hb_json_from_file(hbjson_file)
>       hb_model = read_HBJSON_file.convert_hbjson_dict_to_hb_model(hb_json_dict)

tests/test_to_WUFI_xml/test_reference_cases/test_xml_output.py:17: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
PHX/from_HBJSON/read_HBJSON_file.py:72: in convert_hbjson_dict_to_hb_model
    hb_model: model.Model = model.Model.from_dict(_data)
.venv/lib/python3.7/site-packages/honeybee/model.py:216: in from_dict
    model.properties.apply_properties_from_dict(data)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ModelProperties: unnamed
data = {'angle_tolerance': 1.0, 'display_name': 'unnamed', 'identifier': 'unnamed_37e99e5d', 'orphaned_shades': [{'display_na...693036, -0.9848077530122081, 0.0]}, 'type': 'Face3D'}, 'identifier': 'Shade_5b4e562d', 'is_detached': True, ...}], ...}

    def apply_properties_from_dict(self, data):
        """Apply extension properties from a Model dictionary to the host Model.
    
        Args:
            data: A dictionary representation of an entire honeybee-core Model.
        """
        for atr in self._extension_attributes:
            if atr not in data['properties'] or data['properties'][atr] is None:
                continue
            var = getattr(self, atr)
            if var and not hasattr(var, 'apply_properties_from_dict'):
                continue
            try:
                var.apply_properties_from_dict(data)
            except Exception as e:
                import traceback
                traceback.print_exc()
                raise Exception(
>                   'Failed to apply {} properties to the Model: {}'.format(atr, e))
E               Exception: Failed to apply energy properties to the Model: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
E               Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

.venv/lib/python3.7/site-packages/honeybee/properties.py:392: Exception
----------------------------- Captured stderr call -----------------------------
Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1329, in load_properties_from_dict
    hvac_class.from_dict_abridged(hvac, schedules)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/hvac/idealair.py", line 437, in from_dict_abridged
    new_obj.properties._load_extension_attr_from_dict(data['properties'])
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/extension.py", line 75, in _load_extension_attr_from_dict
    setattr(self, '_' + atr, var.__class__.from_dict(atr_prop_dict, self.host))
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/properties/hvac/idealair.py", line 110, in from_dict
    htg_sys = heating.PhHeatingSystemBuilder.from_dict(htg_sys_dict)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy_ph/hvac/heating.py", line 266, in from_dict
    raise UnknownPhHeatingTypeError(valid_class_types, heating_type)
honeybee_energy_ph.hvac.heating.UnknownPhHeatingTypeError: Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/properties.py", line 387, in apply_properties_from_dict
    var.apply_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1004, in apply_properties_from_dict
    self.load_properties_from_dict(data)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee_energy/properties/model.py", line 1331, in load_properties_from_dict
    invalid_dict_error(hvac, e)
  File "/Users/em/Dropbox/bldgtyp-00/00_PH_Tools/PHX/.venv/lib/python3.7/site-packages/honeybee/typing.py", line 322, in invalid_dict_error
    raise ValueError('{} "{}" is invalid:\n{}'.format(obj_type, full_id, error))
ValueError: IdealAirSystem "Room_7_64afc318 Ideal Loads Air System" is invalid:
Error: Unknown HBPH-Heating-SubSystem type? Got: "PhHeatingHeatPumpRatedMonthly" but only types: ['PhHeatingDirectElectric', 'PhHeatingDistrict', 'PhHeatingFossilBoiler', 'PhHeatingSystem', 'PhHeatingSystemBuilder', 'PhHeatingWoodBoiler'] are allowed?
=========================== short test summary info ============================
FAILED tests/test_from_HBJSON/test_create_project/test_convert_hbjson_to_PHX_Project.py::test_convert_model_PhxProject[Multi_Room_Complete.hbjson-None]
FAILED tests/test_from_HBJSON/test_read_HBJSON_file/test_convert_hbjson_dict_to_hb_model.py::test_read_default_single_zone_model_no_conversion[Multi_Room_Complete.hbjson-None]
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingDevice
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingVentilationParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingVentilationParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingVentilation
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingVentilation
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingVentilation
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingRecirculationParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingRecirculationParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingRecirculation
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingRecirculation
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingRecirculation
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingDehumidificationParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingDehumidificationParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingDehumidification
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingDehumidification
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingDehumidification
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingPanelParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_mixed_PhxCoolingPanelParams
FAILED tests/test_model/test_hvac/test_cooling.py::test_default_PhxCoolingPanel
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_default_PhxCoolingPanel
FAILED tests/test_model/test_hvac/test_cooling.py::test_add_mixed_PhxCoolingPanel
FAILED tests/test_to_WUFI_xml/test_hvac/test_heat_pumps/test_PhxHeaterHeatPumpAnnual.py::test_default_PhxHeaterHeatPumpAnnual_with_recirc_cooling
FAILED tests/test_to_WUFI_xml/test_reference_cases/test_to_project_geometry.py::test_all_aperture_areas_are_equivalent_after_conversion[to_xml_reference_cases1]
FAILED tests/test_to_WUFI_xml/test_reference_cases/test_xml_output.py::test_xml_output[to_xml_reference_cases1]
ERROR tests/test_from_WUFI/test_patterns_from_WUFI/test_new_xml_util_patterns_occupancy.py::test_vent_patterns_match
ERROR tests/test_from_WUFI/test_patterns_from_WUFI/test_new_xml_util_patterns_ventilation.py::test_vent_patterns_match
ERROR tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_assemblies.py::test_assembly_types_match
ERROR tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_assemblies.py::test_assembly_type_layers_match
ERROR tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_assemblies.py::test_assembly_type_materials_match
ERROR tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_data.py::test_project_data
ERROR tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_windows.py::test_project_data
ERROR tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_windows.py::test_window_type_attributes_match
ERROR tests/test_from_WUFI/test_project_from_WUFI/test_new_xml_project_windows.py::test_window_FrameElememt_attributes_match
ERROR tests/test_from_WUFI/test_variants_from_WUFI/test_building_components.py::test_building_all_components_are_equal
ERROR tests/test_from_WUFI/test_variants_from_WUFI/test_buildings.py::test_variant_buildings
ERROR tests/test_from_WUFI/test_variants_from_WUFI/test_variants.py::test_project_variants
================== 26 failed, 311 passed, 12 errors in 3.09s ===================
