-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/development' into T98_estimate_b…
…ug_fix
- Loading branch information
Showing
12 changed files
with
681 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
pvdeg.symbolic.calc\_df\_symbolic | ||
================================= | ||
|
||
.. currentmodule:: pvdeg.symbolic | ||
|
||
.. autofunction:: calc_df_symbolic |
6 changes: 6 additions & 0 deletions
6
docs/source/_autosummary/pvdeg.symbolic.calc_kwarg_floats.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
pvdeg.symbolic.calc\_kwarg\_floats | ||
================================== | ||
|
||
.. currentmodule:: pvdeg.symbolic | ||
|
||
.. autofunction:: calc_kwarg_floats |
6 changes: 6 additions & 0 deletions
6
docs/source/_autosummary/pvdeg.symbolic.calc_kwarg_timeseries.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
pvdeg.symbolic.calc\_kwarg\_timeseries | ||
====================================== | ||
|
||
.. currentmodule:: pvdeg.symbolic | ||
|
||
.. autofunction:: calc_kwarg_timeseries |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
.. Please when editing this file make sure to keep it matching the | ||
docs in ../configuration.rst:reference_to_examples | ||
pvdeg.symbolic | ||
============== | ||
|
||
.. automodule:: pvdeg.symbolic | ||
|
||
.. this is crazy | ||
Function Overview | ||
----------------- | ||
|
||
.. autosummary:: | ||
:toctree: | ||
:nosignatures: | ||
|
||
|
||
pvdeg.symbolic.calc_df_symbolic | ||
pvdeg.symbolic.calc_kwarg_floats | ||
pvdeg.symbolic.calc_kwarg_timeseries | ||
|
||
|
||
|
||
|
||
.. this is crazy | ||
.. | ||
Functions | ||
--------- | ||
|
||
|
||
.. autofunction:: calc_df_symbolic | ||
|
||
.. _sphx_glr_backref_pvdeg.symbolic.calc_df_symbolic: | ||
|
||
.. minigallery:: pvdeg.symbolic.calc_df_symbolic | ||
:add-heading: | ||
|
||
.. autofunction:: calc_kwarg_floats | ||
|
||
.. _sphx_glr_backref_pvdeg.symbolic.calc_kwarg_floats: | ||
|
||
.. minigallery:: pvdeg.symbolic.calc_kwarg_floats | ||
:add-heading: | ||
|
||
.. autofunction:: calc_kwarg_timeseries | ||
|
||
.. _sphx_glr_backref_pvdeg.symbolic.calc_kwarg_timeseries: | ||
|
||
.. minigallery:: pvdeg.symbolic.calc_kwarg_timeseries | ||
:add-heading: | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
""" | ||
Collections of functions to enable arbitrary symbolic expression evaluation for simple models | ||
""" | ||
|
||
import sympy as sp | ||
import pandas as pd | ||
import numpy as np | ||
|
||
# from latex2sympy2 import latex2sympy # this potentially useful but if someone has to use this then they proboably wont be able to figure out the rest | ||
# parse: latex -> sympy using latex2sympy2 if nessesscary | ||
|
||
|
||
def calc_kwarg_floats( | ||
expr: sp.core.mul.Mul, | ||
kwarg: dict, | ||
) -> float: | ||
""" | ||
Calculate a symbolic sympy expression using a dictionary of values | ||
Parameters: | ||
---------- | ||
expr: sp.core.mul.Mul | ||
symbolic sympy expression to calculate values on. | ||
kwarg: dict | ||
dictionary of kwarg values for the function, keys must match | ||
sympy symbols. | ||
Returns: | ||
-------- | ||
res: float | ||
calculated value from symbolic equation | ||
""" | ||
res = expr.subs(kwarg).evalf() | ||
return res | ||
|
||
|
||
def calc_df_symbolic( | ||
expr: sp.core.mul.Mul, | ||
df: pd.DataFrame, | ||
) -> pd.Series: | ||
""" | ||
Calculate the expression over the entire dataframe. | ||
Parameters: | ||
---------- | ||
expr: sp.core.mul.Mul | ||
symbolic sympy expression to calculate values on. | ||
df: pd.DataFrame | ||
pandas dataframe containing column names matching the sympy symbols. | ||
""" | ||
variables = set(map(str, list(expr.free_symbols))) | ||
if not variables.issubset(df.columns.values): | ||
raise ValueError(f""" | ||
all expression variables need to be in dataframe cols | ||
expr symbols : {expr.free_symbols}") | ||
dataframe cols : {df.columns.values} | ||
""") | ||
|
||
res = df.apply(lambda row: calc_kwarg_floats(expr, row.to_dict()), axis=1) | ||
return res | ||
|
||
|
||
def _have_same_indices(series_list): | ||
if not series_list: | ||
return True | ||
|
||
if not isinstance(series_list, pd.Series): | ||
return False | ||
|
||
first_index = series_list[0].index | ||
|
||
same_indicies = all(s.index.equals(first_index) for s in series_list[1:]) | ||
all_series = all(isinstance(value, pd.Series) for value in series_list) | ||
|
||
return same_indicies and all_series | ||
|
||
|
||
def _have_same_length(series_list): | ||
if not series_list: | ||
return True | ||
|
||
first_length = series_list[0].shape[0] | ||
return all(s.shape[0] == first_length for s in series_list[1:]) | ||
|
||
|
||
def calc_kwarg_timeseries( | ||
expr, | ||
kwarg, | ||
): | ||
# check for equal length among timeseries. no nesting loops allowed, no functions can be dependent on their previous results values | ||
numerics, timeseries, series_length = {}, {}, 0 | ||
for key, val in kwarg.items(): | ||
if isinstance(val, (pd.Series, np.ndarray)): | ||
timeseries[key] = val | ||
series_length = len(val) | ||
elif isinstance(val, (int, float)): | ||
numerics[key] = val | ||
else: | ||
raise ValueError(f"only simple numerics or timeseries allowed") | ||
|
||
if not _have_same_length(list(timeseries.values())): | ||
raise NotImplementedError( | ||
f"arrays/series are different lengths. fix mismatched length. otherwise arbitrary symbolic solution is too complex for solver. nested loops or loops dependent on previous results not supported." | ||
) | ||
|
||
# calculate the expression. we will seperately calculate all values and store then in a timeseries of the same shape. if a user wants to sum the values then they can | ||
if _have_same_indices(list(timeseries.values())): | ||
index = list(timeseries.values())[0].index | ||
else: | ||
index = pd.RangeIndex(start=0, stop=series_length) | ||
res = pd.Series(index=index, dtype=float) | ||
|
||
for i in range(series_length): | ||
# calculate at each point and save value | ||
iter_dict = { | ||
key: value.values[i] for key, value in timeseries.items() | ||
} # pandas indexing will break like this in future versions, we could only | ||
|
||
iter_dict = {**numerics, **iter_dict} | ||
|
||
# we are still getting timeseries at this point | ||
res.iloc[i] = float(expr.subs(iter_dict).evalf()) | ||
|
||
return res |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,6 +71,7 @@ docs = [ | |
test = [ | ||
"pytest", | ||
"pytest-cov", | ||
"sympy", | ||
] | ||
books = [ | ||
"jupyter-book", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import pytest | ||
import os | ||
import json | ||
import numpy as np | ||
import pandas as pd | ||
import sympy as sp # not a dependency, may cause issues | ||
import pvdeg | ||
from pvdeg import TEST_DATA_DIR | ||
|
||
WEATHER = pd.read_csv( | ||
os.path.join(TEST_DATA_DIR, r"weather_day_pytest.csv"), | ||
index_col=0, | ||
parse_dates=True | ||
) | ||
|
||
with open(os.path.join(TEST_DATA_DIR, "meta.json"), "r") as file: | ||
META = json.load(file) | ||
|
||
# D = k_d * E * Ileak | ||
# degradation rate, D | ||
# degradation constant, k_d | ||
|
||
# electric field, E = Vbias / d | ||
# Vbias, potential diference between cells and frame | ||
# d, encapsulant thickness | ||
|
||
# leakage current, Ileak = Vbias / Rencap | ||
# Vbias, potential diference between cells and frame | ||
# Rencap, resistance of encapsulant | ||
|
||
k_d, Vbias, Rencap, d = sp.symbols('k_d Vbias Rencap d') | ||
pid = k_d * (Vbias / d) * (Vbias / Rencap) | ||
|
||
pid_kwarg = { | ||
'Vbias' : 1000, | ||
'Rencap' : 1e9, | ||
'd': 0.0005, | ||
'k_d': 1e-9, | ||
} | ||
|
||
def test_symbolic_floats(): | ||
res = pvdeg.symbolic.calc_kwarg_floats( | ||
expr=pid, | ||
kwarg=pid_kwarg | ||
) | ||
|
||
assert res == pytest.approx(2e-9) | ||
|
||
def test_symbolic_df(): | ||
pid_df = pd.DataFrame([pid_kwarg] * 5) | ||
|
||
res_series = pvdeg.symbolic.calc_df_symbolic( | ||
expr=pid, | ||
df=pid_df | ||
) | ||
|
||
pid_values = pd.Series([2e-9]*5) | ||
|
||
pd.testing.assert_series_equal(res_series, pid_values, check_dtype=False) | ||
|
||
|
||
def test_symbolic_timeseries(): | ||
lnR_0, I, X, Ea, k, T = sp.symbols('lnR_0 I X Ea k T') | ||
ln_R_D_expr = lnR_0 * I**X * sp.exp( (-Ea)/(k * T) ) | ||
|
||
module_temps = pvdeg.temperature.module( | ||
weather_df=WEATHER, | ||
meta=META, | ||
conf="open_rack_glass_glass" | ||
) | ||
poa_irradiance = pvdeg.spectral.poa_irradiance( | ||
weather_df=WEATHER, | ||
meta=META | ||
) | ||
|
||
module_temps_k = module_temps + 273.15 # convert C -> K | ||
poa_global = poa_irradiance['poa_global'] # take only the global irradiance series from the total irradiance dataframe | ||
poa_global_kw = poa_global / 1000 # [W/m^2] -> [kW/m^2] | ||
|
||
values_kwarg = { | ||
'Ea': 62.08, # activation energy, [kJ/mol] | ||
'k': 8.31446e-3, # boltzmans constant, [kJ/(mol * K)] | ||
'T': module_temps_k, # module temperature, [K] | ||
'I': poa_global_kw, # module plane of array irradiance, [W/m2] | ||
'X': 0.0341, # irradiance relation, [unitless] | ||
'lnR_0': 13.72, # prefactor degradation [ln(%/h)] | ||
} | ||
|
||
res = pvdeg.symbolic.calc_kwarg_timeseries( | ||
expr=ln_R_D_expr, | ||
kwarg=values_kwarg | ||
).sum() | ||
|
||
assert res == pytest.approx(6.5617e-09) | ||
|
||
def test_calc_df_symbolic_bad(): | ||
expr = sp.symbols('not_in_columns') | ||
df = pd.DataFrame([[1,2,3,5]],columns=['a','b','c','d']) | ||
|
||
with pytest.raises(ValueError): | ||
pvdeg.symbolic.calc_df_symbolic(expr=expr, df=df) | ||
|
||
def test_calc_kwarg_timeseries_bad_type(): | ||
# try passing an invalid argument type | ||
with pytest.raises(ValueError, match="only simple numerics or timeseries allowed"): | ||
pvdeg.symbolic.calc_kwarg_timeseries(expr=None, kwarg={'bad':pd.DataFrame()}) | ||
|
||
def test_calc_kwarg_timeseries_bad_mismatch_lengths(): | ||
# arrays of different lengths | ||
with pytest.raises(NotImplementedError, match="arrays/series are different lengths. fix mismatched length. otherwise arbitrary symbolic solution is too complex for solver. nested loops or loops dependent on previous results not supported."): | ||
pvdeg.symbolic.calc_kwarg_timeseries(expr=None, kwarg={'len1':np.zeros((5,)), 'len2':np.zeros(10,)}) | ||
|
||
def test_calc_kwarg_timeseries_no_index(): | ||
|
||
v1, v2 = sp.symbols('v1 v2') | ||
expr = v1 * v2 |
Oops, something went wrong.