diff --git a/dao/prog/da_base.py b/dao/prog/da_base.py index ca9da407..5b8ee363 100644 --- a/dao/prog/da_base.py +++ b/dao/prog/da_base.py @@ -603,7 +603,7 @@ def calc_solar_predictions( :param vanaf: datetime start :param tot: datetime tot :param interval: 15"min of 1 hour of None, als None wordt self.interval genomen - :param ml_prediction: boolean default None(= from config) + :param _ml_prediction: boolean default None(= from config) :return: """ from dao.prog.solar_predictor import SolarPredictor @@ -627,12 +627,19 @@ def calc_solar_predictions( solar_prog = solar_predictor.predict_solar_device( solar_option, vanaf, tot ) + if solar_prog.isnull().any().any(): + logging.warning( + f"NaN-waarden aangetroffen in voorspelling van {solar_name}" + f"Deze zijn op '0' gezet" + ) + solar_prog.fillna(0, inplace=True) except FileNotFoundError as ex: logging.warning(ex) logging.info( f"Voor {solar_option['name']} is geen model " f"en dus wordt DAO-predictor gebruikt" ) + result = self.calc_solar_predictions( solar_option, vanaf, tot, interval=interval, _ml_prediction=False ) diff --git a/dao/prog/da_graph.py b/dao/prog/da_graph.py index 8108a758..2fc8e687 100644 --- a/dao/prog/da_graph.py +++ b/dao/prog/da_graph.py @@ -300,8 +300,8 @@ def build(self, df, options, show=True): ax.set_xlabel(haxis["title"]) num_xas = len(df.index) if num_xas > 12: - ax.xaxis.set_major_locator(ticker.MultipleLocator(round(num_xas / 12))) - ax.xaxis.set_minor_locator(ticker.MultipleLocator(1)) + ax.xaxis.set_major_locator(ticker.MultipleLocator(12)) + ax.xaxis.set_minor_locator(ticker.MultipleLocator(2)) if len(str(xlabels[0])) > 2: ax.set_xticks( ax.get_xticks(), ax.get_xticklabels(), rotation=45, ha="right" diff --git a/dao/prog/da_report.py b/dao/prog/da_report.py index 7b1979fb..b760a014 100644 --- a/dao/prog/da_report.py +++ b/dao/prog/da_report.py @@ -1118,7 +1118,8 @@ def copy_col_df( :return: de ingevuld copy_to """ dt = copy_from[col_name].dtype - if dt == float: + # Use is_numeric_dtype to catch all numeric types (float64, int64, etc.) + if pd.api.types.is_numeric_dtype(dt): copy_to[col_name] = 0.0 else: copy_to[col_name] = "" diff --git a/dao/prog/day_ahead.py b/dao/prog/day_ahead.py index ab0721d7..6c849118 100644 --- a/dao/prog/day_ahead.py +++ b/dao/prog/day_ahead.py @@ -286,6 +286,10 @@ def calc_optimum( # nieuwe universele methode end = prog_data["tijd"].iloc[-1] + if self.interval == "1hour": + end += datetime.timedelta(hours=1) + else: + end += datetime.timedelta(minutes=15) for s in range(solar_num): solar_prog = self.calc_solar_predictions( self.solar[s], start_interval_dt, end, self.interval @@ -1089,7 +1093,10 @@ def calc_optimum( heat_rate = ( power_boiler * cop_boiler * self.interval_s / (spec_heat_boiler * 1000) ) - max_steps = int((boiler_setpoint - boiler_ondergrens) / heat_rate) + 1 + boiler_end_temp = max( + boiler_ondergrens, boiler_act_temp - U * boiler_cooling + ) + max_steps = math.ceil((boiler_setpoint - boiler_end_temp) / heat_rate) # interval-index waarop boiler kan worden verwarmd if boiler_instant_start or (boiler_act_temp <= boiler_ondergrens): boiler_start_index = 0 @@ -3234,10 +3241,15 @@ def calc_optimum( totals = False if totals: - df_accu[b].at[df_accu[b].index[-1], "uur"] = "Totaal" - df_accu[b].at[df_accu[b].index[-1], "eff"] = "--" - df_accu[b].at[df_accu[b].index[-1], "o_eff"] = "--" - df_accu[b].at[df_accu[b].index[-1], "SoC"] = pd.NA + # Kolom "uur" kan string "Totaal" krijgen door eerst naar object te casten + df_accu[b].iloc[:, 0] = df_accu[b].iloc[:, 0].astype(object) + df_accu[b].iloc[:, 0] = df_accu[b].iloc[:, 0].astype(object) + df_accu[b].iloc[-1, 0] = "Totaal" + df_accu[b].iloc[-1, 2] = np.nan # eff (ac->dc) + df_accu[b].iloc[-1, 6] = np.nan # eff (dc->bat) + df_accu[b].iloc[-1, 8] = np.nan # o_eff + df_accu[b].iloc[-1, 9] = np.nan # SoC + logging.info( f"In- en uitgaande energie per {self.interval_name} batterij " f"{self.battery_options[b]['name']}" @@ -3750,7 +3762,7 @@ def calc_optimum( if wf > 0: sum_weight_factor += wf sum_power += wf * discharge_stages[b][ds]["power"] - if sum_weight_factor < 0.95: + if 0.10 <= sum_weight_factor < 0.95: new_state = battery_state_on_value balance = False netto_vermogen_bat = -round(sum_power / sum_weight_factor) diff --git a/dao/prog/solar_predictor.py b/dao/prog/solar_predictor.py index 11a4b2cf..a0b16657 100644 --- a/dao/prog/solar_predictor.py +++ b/dao/prog/solar_predictor.py @@ -1023,6 +1023,16 @@ def predict_solar_device( :param end: eind-tijdstip voorspelling :return: dataframe met berekende voorspellingen per uur """ + + def check_prediction(prediction, irradiance): + if irradiance <= 0.0: + result = 0 + else: + result = prediction + if result < 0.0: + result = 0 + return result + name = self.config.get(["name"], solar_dict, "default") self.solar_name = name.replace(" ", "_") self.tilt = self.get_property_from_dict("tilt", solar_dict, 45) @@ -1039,7 +1049,13 @@ def predict_solar_device( prognose = latest_dt < end weather_data = self.get_weatherdata(start, end, prognose=prognose) prediction = self.predict(weather_data) - prediction["prediction"] = prediction["prediction"].round(3) + weather_data.reset_index(inplace=True) + prediction["irradiance"] = weather_data["irradiance"] + prediction["prediction"] = prediction.apply( + lambda x: check_prediction(x["prediction"], x["irradiance"]), axis=1 + ) + prediction["prediction"].round(3) + prediction.drop("irradiance", axis=1, inplace=True) logging.info(f"ML prediction {self.solar_name}\n{prediction}") return prediction diff --git a/release-testing/CHANGELOG.md b/release-testing/CHANGELOG.md index fa5f2ae5..0951030b 100644 --- a/release-testing/CHANGELOG.md +++ b/release-testing/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog 刀 DAO # Day Ahead Optimizer +# 2026.02.2.rc1 +- Update several python modules +- Adjusted the footer of the webpages (reported by steynovitch) +- Removed NaN-values from ml-preditions +- Fix error in reports (reported by @steynovich) +- Correct totals battery summary +- Fix error when period with minimum power is less than 1 minuut (reported by @Dogooder) +- Fix negative or small values >0 in ml-predictions when irradiance==0 (reported by @Dogooder) + # 2026.02.1.rc1 Add missing module tzdata diff --git a/release-testing/config.yaml b/release-testing/config.yaml index ce8a0290..9d0f3db8 100644 --- a/release-testing/config.yaml +++ b/release-testing/config.yaml @@ -1,6 +1,6 @@ --- name: 刀 Day Ahead Optimizer (TESTING) -version: 2026.02.1.rc1 +version: 2026.02.2.rc1 stage: experimental slug: day_ahead_opt-testing description: Beta version of DAO. Use only for testing!