diff --git a/energyplus_calibrator/calibration_class.py b/energyplus_calibrator/calibration_class.py index 167f8be08b115a66746d5e65081b5e75d461a9a1..e41b6488c705232788c1500133428f7f85132158 100644 --- a/energyplus_calibrator/calibration_class.py +++ b/energyplus_calibrator/calibration_class.py @@ -41,7 +41,6 @@ class EnergyPlusCalibrator: error_metric: Union[str, List[str]] = 'NMBE', n_cpu: int = 1, force_same: Optional[Dict[str, Tuple[str, float]]] = None, - envelope_calibration: bool = False, res_name_add: Optional[str] = None): """ Initialize the EnergyPlus Calibrator. @@ -61,7 +60,6 @@ class EnergyPlusCalibrator: error_metric: Error metric for calibration ('NMBE', 'CVRMSE', or 'RMSE') n_cpu: Number of CPU cores to use force_same: Parameters to force to same values - envelope_calibration: Whether to calibrate envelope parameters res_name_add: Additional string for result file names Raises: @@ -89,14 +87,15 @@ class EnergyPlusCalibrator: force_same=force_same, after_sim_functions=[calc_after_sim_function], energyplus_install_dir=energyplus_install_dir, - n_cpu=n_cpu, - envelope_model=envelope_calibration) + n_cpu=n_cpu) + self.cd = cd self.energyplus_version = energyplus_version + self.calc_after_sim_function = calc_after_sim_function self.model_path = model_path self.epw_filepath = epw_file_path self.energyplus_install_dir = energyplus_install_dir - self.envelope_calibration = envelope_calibration + self.n_cpu = n_cpu self.tuner_params = tuner_paras self.force_same = force_same @@ -380,20 +379,33 @@ class EnergyPlusCalibrator: ) def create_plots(self, - parameters_opt: Dict[str, Union[float, str]]) -> List[str]: + calibrated_model_path: Path) -> List[str]: """ Create visualization plots comparing original, optimized, and measured data. Args: - parameters_opt: Optimized parameter values + calibrated_model_path: Path to the optimized .idf file Returns: List of created file names """ to_copy = [] - - df_sim_opt = self.energy_plus_api.simulate(parameters=parameters_opt) + + energy_plus_api = EnergyPlusAPI( + cd=self.cd, + override_cd=True, + model_path=calibrated_model_path, + epw_filepath=self.epw_filepath, + delete_temp_files=True, + energyplus_version=self.energyplus_version, + force_same=None, # This is important to chek + after_sim_functions=[self.calc_after_sim_function], + energyplus_install_dir=self.energyplus_install_dir, + n_cpu=self.n_cpu) + energy_plus_api.set_sim_setup(sim_setup=self.simulation_setup) + + df_sim_opt = energy_plus_api.simulate() df_sim_orig = self._temp_dfs res_dict = {} @@ -493,25 +505,21 @@ class EnergyPlusCalibrator: 3. Visualization plots 4. Copies all files to results directory """ - - with open(Path(self.energy_plus_api.working_directory) / - 'calibrated_parameters.json', 'r') as f: + wd = Path(self.energy_plus_api.working_directory) + with open(wd / 'calibrated_parameters.json', 'r') as f: parameters = json.load(f) calibrated_model = self.energy_plus_api.ep_json.copy() + parameters = self.energy_plus_api._change_forced_parameters(parameters) calibrated_model = self.energy_plus_api.change_model_dict_with_parameters( model_dict=calibrated_model, parameters=parameters ) - parameters_use = parameters.copy() - - with open(Path(self.energy_plus_api.working_directory) / - 'calibrated_parameters.json', 'w') as f: + with open(wd / 'calibrated_parameters.json', 'w') as f: json.dump(parameters, f) - calibrated_model_path = Path( - self.energy_plus_api.working_directory) / 'calibrated_model.json' + calibrated_model_path = wd / 'calibrated_model.json' with open(calibrated_model_path, 'w') as f: json.dump(calibrated_model, f) @@ -534,23 +542,23 @@ class EnergyPlusCalibrator: results_folder = calibration_results_folder / folder_name results_folder.mkdir(parents=True) - to_copy = ['calibrated_model.json', 'calibrated_model.idf', 'calibrated_parameters.json', ] try: - to_copy2 = self.create_plots(parameters_opt=parameters_use) + to_copy2 = self.create_plots( + calibrated_model_path=wd / "calibrated_model.idf" + ) except Exception as e: print(f'Error in creating plots: {str(e)}. Do again manually') to_copy2 = [] to_copy += to_copy2 - source_path = Path(self.energy_plus_api.working_directory) for file in to_copy: - _source_path = source_path / file + _source_path = wd / file shutil.copy(_source_path, results_folder) diff --git a/energyplus_calibrator/energy_plus_api.py b/energyplus_calibrator/energy_plus_api.py index aab0dadd2461326351193df621dc8f5dee0eee50..7933ca892f0f71927a513cdb1c254a2903ce0bf2 100644 --- a/energyplus_calibrator/energy_plus_api.py +++ b/energyplus_calibrator/energy_plus_api.py @@ -384,7 +384,45 @@ class EnergyPlusAPI(SimulationAPI): return filepath return None + + def _change_forced_parameters(self, + parameters: Dict[str, Any]) -> Dict[str, Any]: + """ + Update parameters according to forced parameter relationships. + + Args: + parameters: Dictionary of simulation parameters + + Returns: + Updated parameter dictionary with forced relationships applied + Raises: + ValueError: If force_from is not a tuple or string + NameError: If force_from[1] value is not "min" for tuple inputs + """ + if self.force_same is None: + return parameters + + if parameters is None or len(parameters) == 0: + return parameters + + for force_to, force_from in self.force_same.items(): + if isinstance(force_from[0], str): + parameters[force_to] = parameters[force_from[0]] + \ + force_from[1] + continue + elif not isinstance(force_from[0], tuple): + raise ValueError('force_from must be a tuple or a string') + + #force_from is a tuple + if force_from[1] == 'min': + parameters[force_to] = min([parameters[i] for i in force_from[0]]) + else: + raise NameError('force_from[1] must be "min"') + + return parameters + + def change_model_dict_with_parameters( self, model_dict: Dict[str, Any], @@ -457,30 +495,11 @@ class EnergyPlusAPI(SimulationAPI): """ parameters = kwargs.pop("parameters", None) - - if parameters is None or len(parameters) == 0: - dont_force_same = True - else: - dont_force_same = False + # Must happen here, because here, parameters might still by empty + parameters = self._change_forced_parameters(parameters) parameters = self._add_sim_setup_parameters(parameters) - if self.force_same is not None and not dont_force_same: - for force_to, force_from in self.force_same.items(): - if isinstance(force_from[0], str): - parameters[force_to] = parameters[force_from[0]] + \ - force_from[1] - continue - elif not isinstance(force_from[0], tuple): - raise ValueError('force_from must be a tuple or a string') - - #force_from is a tuple - if force_from[1] == 'min': - parameters[force_to] = min([parameters[i] for i in force_from[0]]) - else: - raise NameError('force_from[1] must be "min"') - - for key in self._set_output_timestep: self._set_output_timestep[key] = self.sim_setup.dataframe_interval parameters.update(self._set_output_timestep) @@ -490,8 +509,7 @@ class EnergyPlusAPI(SimulationAPI): raise ValueError("Inputs not supported") fail_on_error = kwargs.get("fail_on_error", True) - ep_json_new = self.ep_json.copy() - + ep_json_new = self.ep_json.copy() ep_json_new = self.change_model_dict_with_parameters(ep_json_new, parameters)