|
15 | 15 | import numpy as np |
16 | 16 | import pandas as pd |
17 | 17 | import matplotlib.pyplot as plt |
| 18 | +from scipy.optimize import curve_fit |
18 | 19 |
|
19 | 20 | from itertools import product |
20 | 21 | from functools import partial |
21 | 22 | from datetime import datetime |
22 | 23 | from time import time |
| 24 | +from typing import Callable |
23 | 25 |
|
24 | 26 |
|
25 | 27 | class MR_E_ratio: |
@@ -118,11 +120,11 @@ def simulation_prerun(self): |
118 | 120 | self.solpos["azimuth"], |
119 | 121 | ) |
120 | 122 | self.time_params = { |
121 | | - "apparent_zenith": self.solpos["apparent_zenith"], |
122 | | - "aoi": self.aoi, |
| 123 | + "apparent_zenith": self.solpos["apparent_zenith"].to_numpy(), |
| 124 | + "aoi": self.aoi.to_numpy(), |
123 | 125 | "relative_airmass": self.locus.get_airmass(solar_position=self.solpos)[ |
124 | 126 | "airmass_relative" |
125 | | - ], |
| 127 | + ].to_numpy(), |
126 | 128 | "dayofyear": np.fromiter( |
127 | 129 | map(day_of_year, self.datetimes), dtype=np.float64 |
128 | 130 | ), |
@@ -306,3 +308,72 @@ def plot_results( |
306 | 308 | plt.close() |
307 | 309 |
|
308 | 310 | self.processing_time["plot_results"] = time() - start_time |
| 311 | + |
| 312 | + def optimization_from_model( |
| 313 | + self, model: Callable = None, model_inputs: tuple = None, **kwargs |
| 314 | + ): |
| 315 | + """ |
| 316 | + Optimize a model to fit generated data. |
| 317 | +
|
| 318 | + Parameters |
| 319 | + ---------- |
| 320 | + model : Callable |
| 321 | + Function with the model to be optimised. |
| 322 | + model_inputs : str or iterable of str |
| 323 | + Order and parameters of ``model``. Must be any of: |
| 324 | + * ``datetime`` |
| 325 | + * ``apparent_zenith``, ``aoi``, ``relative_airmass`` or ``dayofyear`` |
| 326 | + * any parameter name provided to ``simulate_from_product`` |
| 327 | + **kwargs : |
| 328 | + Redirected to ``scipy.optimize.curve_fit``. |
| 329 | +
|
| 330 | + Returns |
| 331 | + ------- |
| 332 | + ``scipy.optimize.curve_fit``'s return values |
| 333 | + """ |
| 334 | + start_time = time() # Initialize start time of block |
| 335 | + |
| 336 | + if isinstance(model_inputs, str): |
| 337 | + model_inputs = (model_inputs,) |
| 338 | + |
| 339 | + # number of inputs from user: n-left-most columns |
| 340 | + n_inputs = len(self.input_keys) |
| 341 | + # Fitting data |
| 342 | + ydata = self.results.iloc[:, n_inputs:].to_numpy().flatten() |
| 343 | + # Prepare input vector |
| 344 | + xdata = [] # length of each value must be |
| 345 | + dates_len = len(self.datetimes) |
| 346 | + try: |
| 347 | + for var_name in model_inputs: |
| 348 | + # broadcast all inputs to match ydata |
| 349 | + if var_name in self.input_keys: |
| 350 | + xdata.append(self.results[var_name].to_numpy().repeat(dates_len)) |
| 351 | + elif var_name in self.time_params.keys(): |
| 352 | + xdata.append( |
| 353 | + np.tile(self.time_params[var_name], self.results.shape[0]) |
| 354 | + ) |
| 355 | + elif var_name in {"datetime"}: |
| 356 | + xdata.append( |
| 357 | + np.tile(self.datetimes.to_numpy(), self.results.shape[0]) |
| 358 | + ) |
| 359 | + else: |
| 360 | + raise ValueError(f"'{var_name}' is not a valid parameter name!") |
| 361 | + |
| 362 | + except TypeError: |
| 363 | + raise TypeError( |
| 364 | + "Provide a valid model input names vector. Must be iterable" |
| 365 | + + " of strings, and that input will be provided to 'model'" |
| 366 | + + f" in the same order.\nYou provided {model_inputs}" |
| 367 | + ) |
| 368 | + |
| 369 | + ## This is kept here for debug purposes: check valid representation as 1D arrays |
| 370 | + # fig, axs = plt.subplots(len(model_inputs)) |
| 371 | + # for i, name in enumerate(model_inputs): |
| 372 | + # axs[i].set_title(name) |
| 373 | + # axs[i].scatter(xdata[i], ydata) |
| 374 | + # plt.show() |
| 375 | + |
| 376 | + curve_fit_results = curve_fit(model, xdata, ydata, nan_policy="omit", **kwargs) |
| 377 | + |
| 378 | + self.processing_time["optimization_from_model"] = time() - start_time |
| 379 | + return curve_fit_results |
0 commit comments