Skip to content

Commit 98139e9

Browse files
committed
allows computation of summaries, function updates
1 parent 1cb4315 commit 98139e9

File tree

7 files changed

+258
-15
lines changed

7 files changed

+258
-15
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ share/python-wheels/
2626
*.egg
2727
MANIFEST
2828

29+
# Examples WIP
30+
examples/pbdm/
31+
2932
# PyInstaller
3033
# Usually these files are written by a python script from a template
3134
# before PyInstaller builds the exe, so as to inject date/other infos into it.
@@ -159,3 +162,5 @@ cython_debug/
159162
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
160163
#.idea/
161164
.vscode/settings.json
165+
examples/nesting.py
166+
examples/nesting2.py

psymple/custom_functions.py

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from scipy.optimize import root_scalar
66
from sympy.abc import x, a, b
77
from sympy import integrate, sympify
8+
from psymple import read_wx
89

910
T = sym.Symbol("T")
1011

@@ -26,7 +27,7 @@
2627
[19.4, 6.7],
2728
[19.3, 6.7],
2829
[20.4, 8.3],
29-
]
30+
]
3031

3132

3233
def DegreeDays_fun(
@@ -108,6 +109,7 @@ def FFTemperature_fun(
108109
return np.max((0.0, 1 - ((temp - temp_min - diff) / diff) ** 2))
109110

110111

112+
111113
class FFTemperature(sym.Function):
112114
#TODO: this could have a doit() method
113115
@classmethod
@@ -116,4 +118,121 @@ def eval(cls, model_day, temp_min, temp_max):
116118
return 0
117119

118120
def _eval_evalf(self, prec):
119-
return sym.Float(FFTemperature_fun(*self.args))._eval_evalf(prec)
121+
return sym.Float(FFTemperature_fun(*self.args))._eval_evalf(prec)
122+
123+
124+
wx = read_wx.Weather.readwx("c:\\Users\\georg\\OneDrive\\Documents\\IDEMS\\CASAS Global\\Modelling\\Python\\population-modeling-py-3\\examples\\pbdm\\sample_weather.txt","01","01","2000","12","31","2010", [0])[1:]
125+
wx = np.array(wx)
126+
127+
def temp_max_fun(
128+
time: float,
129+
temperature_data = wx
130+
):
131+
model_day = math.ceil(time)
132+
return temperature_data[model_day][5]
133+
134+
def temp_min_fun(
135+
time: float,
136+
temperature_data = wx
137+
):
138+
model_day = math.ceil(time)
139+
return temperature_data[model_day][6]
140+
141+
def solar_rad_fun(
142+
time: float,
143+
temperature_data = wx
144+
):
145+
model_day = math.ceil(time)
146+
return temperature_data[model_day][7]
147+
148+
def temp_fun(
149+
time: float,
150+
):
151+
temp_max = temp_max_fun(time)
152+
temp_min = temp_min_fun(time)
153+
#return (temp_max - temp_min)/2 * np.sin(np.float64(2*np.pi*(time+1/4))) + (temp_max + temp_min)/2
154+
return (temp_max + temp_min)/2
155+
156+
157+
class temp_max(sym.Function):
158+
#TODO: this could have a doit() method
159+
@classmethod
160+
def eval(cls, time):
161+
if isinstance(time, sym.Float) and time < 0:
162+
return 0
163+
164+
def _eval_evalf(self, prec):
165+
return sym.Float(temp_max_fun(*self.args))._eval_evalf(prec)
166+
167+
class temp_min(sym.Function):
168+
#TODO: this could have a doit() method
169+
@classmethod
170+
def eval(cls, time):
171+
if isinstance(time, sym.Float) and time < 0:
172+
return 0
173+
174+
def _eval_evalf(self, prec):
175+
return sym.Float(temp_min_fun(*self.args))._eval_evalf(prec)
176+
177+
class solar_rad(sym.Function):
178+
#TODO: this could have a doit() method
179+
@classmethod
180+
def eval(cls, time):
181+
if isinstance(time, sym.Float) and time < 0:
182+
return 0
183+
184+
def _eval_evalf(self, prec):
185+
return sym.Float(solar_rad_fun(*self.args))._eval_evalf(prec)
186+
187+
class temp(sym.Function):
188+
#TODO: this could have a doit() method
189+
@classmethod
190+
def eval(cls, time):
191+
if isinstance(time, sym.Float) and time < 0:
192+
return 0
193+
194+
def _eval_evalf(self, prec):
195+
return sym.Float(temp_fun(*self.args))._eval_evalf(prec)
196+
197+
def DD_fun(time, Del, T_min, T_max = 0, coeff_1 = 0, coeff_2 = 0):
198+
T = temp_fun(time)
199+
if T_max < T_min:
200+
return np.maximum(0.01,T-T_min)
201+
else:
202+
return np.maximum(0, Del*(coeff_1*(T-T_min)/(1+ coeff_2**(T - T_max))))
203+
204+
class DD(sym.Function):
205+
#TODO: this could have a doit() method
206+
@classmethod
207+
def eval(cls, time, Del, T_min, T_max = None, coeff_1 = None, coeff_2 = None):
208+
if isinstance(time, sym.Float) and time < 0:
209+
return 0
210+
211+
def doit(self, deep=False, **hints):
212+
time, *args = self.args
213+
if deep:
214+
time = time.doit(deep=deep, **hints)
215+
return sym.Float(DD_fun(time, *args))
216+
217+
def _eval_evalf(self, prec):
218+
return self.doit(deep=False)._eval_evalf(prec)
219+
#return sym.Float(DD_fun(*self.args))._eval_evalf(prec)
220+
#T = temp_fun(time)
221+
#return sym.Float(DD_fun(time=self.args[0], Del = self.args[1], T_min = self.args[2], T_max = self.args[3], coeff_1 = self.args[4], coeff_2 = self.args[5]))._eval_evalf(prec)
222+
223+
def ind_above_fun(base, comp):
224+
return 1 if comp>base else 0
225+
226+
class ind_above(sym.Function):
227+
@classmethod
228+
def eval(cls, base, comp):
229+
return ind_above_fun(base, comp)
230+
231+
def frac0_fun(numerator, denominator, default):
232+
return default if denominator == 0 else numerator/denominator
233+
234+
class frac0(sym.Function):
235+
@classmethod
236+
def eval(cls, numerator, denominator, default):
237+
if denominator == 0:
238+
return default

psymple/globals.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
11
import sympy as sym
2-
from psymple.custom_functions import DegreeDays, FFTemperature
2+
from psymple.custom_functions import (
3+
DegreeDays,
4+
FFTemperature,
5+
temp,
6+
DD,
7+
temp_fun,
8+
DD_fun,
9+
solar_rad_fun,
10+
ind_above_fun,
11+
frac0_fun,
12+
temp_min_fun,
13+
)
314

415
# from custom_functions import DegreeDays, FFTemperature
516

617

718
# The dictionary passed to sym.sympify and sym.lambdify to convert custom functions
819
# TODO: This should not be a global property.
9-
#sym_custom_ns = {}
10-
sym_custom_ns = {'DegreeDays': DegreeDays, 'FFTemperature': FFTemperature}
20+
# sym_custom_ns = {}
21+
sym_custom_ns = {
22+
"DegreeDays": DegreeDays,
23+
"FFTemperature": FFTemperature,
24+
"temp": temp_fun,
25+
"temp_min": temp_min_fun,
26+
"DD": DD_fun,
27+
"solar_rad": solar_rad_fun,
28+
"ind_above": ind_above_fun,
29+
"frac0": frac0_fun,
30+
}
1131

1232

1333
T = sym.Symbol("T")

psymple/ported_objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def substitute_symbol(self, old_symbol, new_symbol):
6161
# ]
6262
# self.equation = self.equation.subs(substitutions)
6363

64-
def get_free_symbols(self, global_symbols=set()):
64+
def get_free_symbols(self, global_symbols=set([sym.Symbol('T')])):
6565
assignment_symbols = self.expression.free_symbols
6666
return assignment_symbols - global_symbols - {self.symbol_wrapper.symbol}
6767

psymple/read_wx.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Created on Tue Feb 2 23:00:56 2021
4+
5+
@author: Jose Ricardo Cure
6+
"""
7+
class Weather(object):
8+
def __init__(self):
9+
super().__init__()
10+
11+
def filterWX(wx,startM,startD,startY,endM,endD,endY,weatherfile):
12+
'''locate dates to run in weather file and call main'''
13+
start=False
14+
end=False
15+
cont=3
16+
17+
keyStart= str(startM) +' '+ str(startD)+' ' + str(startY)
18+
keyEnd= str(endM)+' ' + str(endD)+' ' + str(endY)
19+
print(keyStart, keyEnd)
20+
for sList in (wx):
21+
if cont < len(wx):
22+
#cont = cont+1
23+
keyWX = str(wx[cont][0])+' ' + str(wx[cont][1])+' ' + str(wx[cont][2])
24+
if keyWX == keyStart:
25+
if start==False:
26+
index_start = cont
27+
start=True
28+
lat = float(wx[1][0])
29+
long = float(wx[1][1])
30+
month = int(wx[cont][0])
31+
day = int(wx[cont][1])
32+
year = int(wx[cont][2])
33+
tmax = float(wx[cont][3])
34+
tmin = float(wx[cont][4])
35+
solar = float(wx[cont][5])
36+
precip= float(wx[cont][6])
37+
rh = float(wx[cont][7])
38+
wind = float(wx[cont][8])
39+
#print(' first date to run '+ str(month) +' ' + str(day) + ' ' + str(year))
40+
weatherfile.append([lat,long,month,day,year, tmax,tmin,solar,precip,rh,wind])
41+
42+
elif keyWX == keyEnd:
43+
if end == False:
44+
index_finish = cont+1
45+
totdays = index_finish - cont
46+
keyWX = str(wx[cont][0])+' ' + str(wx[cont][1])+' ' + str(wx[cont][2])
47+
end=True
48+
#print(' last date to run '+ str(month) +' ' + str(day) + ' ' + str(year)+ ' ')
49+
#print( '.. total simulation days in this file = '+str(totdays) )
50+
51+
cont=cont+1
52+
if end==False and start == True:
53+
lat = float(wx[1][0])
54+
long = float(wx[1][1])
55+
month = int(wx[cont][0])
56+
day = int(wx[cont][1])
57+
year = int(wx[cont][2])
58+
tmax = float(wx[cont][3])
59+
tmin = float(wx[cont][4])
60+
solar = float(wx[cont][5])
61+
precip= float(wx[cont][6])
62+
rh = float(wx[cont][7])
63+
wind = float(wx[cont][8])
64+
weatherfile.append([lat,long,month,day,year, tmax,tmin,solar,precip,rh,wind])
65+
66+
67+
68+
def readwx(wx_dir,startM,startD,startY,endM,endD,endY,weatherfile):
69+
'''read weather file'''
70+
with open (wx_dir , 'r') as f: # use with to open your files, it close them automatically
71+
wx = [x.split() for x in f]
72+
wxfile= wx[0] # title of the wx file
73+
lat = float(wx[1][0])
74+
long = float(wx[1][1])
75+
print()
76+
print('weather file ', wxfile)
77+
print(' latitude '+ str(lat), ' longitude '+ str(long))
78+
print('.. original number of days in WX file = ' + str(len(wx)))
79+
Weather.filterWX(wx,startM,startD,startY,endM,endD,endY,weatherfile)
80+
return weatherfile
81+
82+
83+
84+
#in_locations = 'd:/fruit_flies/r_USA_MEX_observed_1980-2010_AgMERRA_coarse_test.txt'
85+
#weatherfile = Weather.run_locations(in_locations)
86+
87+
88+

psymple/system.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _create_variables(self, variables):
7979
for variable in variables:
8080
if variable.initial_value is None:
8181
print(f"Warning: Variable {variable.symbol} has no initial value")
82-
variable.initial_value = 0
82+
variable.initial_value = 0.0
8383
return Variables([SimVariable(variable) for variable in variables])
8484

8585
def _create_parameters(self, parameters):
@@ -92,6 +92,7 @@ def _create_parameters(self, parameters):
9292

9393
def _assign_update_rules(self, update_rules):
9494
combined_update_rules = update_rules._combine_update_rules()
95+
print(combined_update_rules)
9596
for rule in combined_update_rules:
9697
new_rule = SimUpdateRule.from_update_rule(
9798
rule, self.variables + self.time, self.parameters
@@ -165,34 +166,37 @@ def _wrap_for_solve_ivp(self, t, y):
165166
for v in self.variables
166167
]
167168

168-
def _advance_time(self, time_step):
169+
def _advance_time(self, time_step, summaries):
169170
self.time.update_buffer()
170171
for variable in self.variables:
171172
variable.update_buffer()
172173
for variable in self.variables:
173174
variable.update_time_series(time_step)
175+
for parameter in summaries:
176+
parameter.update_value()
174177
self.time.update_time_series(time_step)
175178

176-
def _advance_time_unit(self, n_steps):
179+
def _advance_time_unit(self, n_steps, summaries):
177180
if n_steps <= 0 or not isinstance(n_steps, int):
178181
raise ValueError(
179182
"Number of time steps in a day must be a positive integer, "
180183
f"not '{n_steps}'."
181184
)
182185
for i in range(n_steps):
183-
self._advance_time(1 / n_steps)
186+
self._advance_time(1 / n_steps, summaries)
184187

185-
def simulate(self, t_end, n_steps, mode="dscr"):
188+
def simulate(self, t_end, n_steps, mode="dscr", summaries = []):
186189
if t_end <= 0 or not isinstance(t_end, int):
187190
raise ValueError(
188191
"Simulation time must terminate at a positive integer, "
189192
f"not '{t_end}'."
190193
)
191194
self._compute_substitutions()
195+
summaries = [self.parameters[s] for s in summaries]
192196
if mode == "discrete" or mode == "dscr":
193197
print("dscr")
194198
for i in range(t_end):
195-
self._advance_time_unit(n_steps)
199+
self._advance_time_unit(n_steps, summaries)
196200
elif mode == "continuous" or mode == "cts":
197201
print("cts")
198202
sol = solve_ivp(
@@ -220,7 +224,10 @@ def plot_solution(self, variables, t_range=None):
220224
variables = {v: {} for v in variables}
221225
legend = []
222226
for var_name, options in variables.items():
223-
variable = self.variables[var_name]
227+
try:
228+
variable = self.variables[var_name]
229+
except:
230+
variable = self.parameters[var_name]
224231
if isinstance(options, str):
225232
plt.plot(t_series[sl], variable.time_series[sl], options)
226233
else:

0 commit comments

Comments
 (0)