└── main.py /main.py: -------------------------------------------------------------------------------- 1 | #Reference 2 | #Jones(1999) "Reduced state-variable tomato growth model" 3 | #Jones(1991) "A dynamix tomato growth and yield model(TOMGRO)" 4 | #Dimokas(2009) "Calibration and validation of a biological model..." 5 | #Heuvelink(1994) "Dry-matter partitioning in a tomato crop:Comparison of two simulation models" 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | import matplotlib.animation as animation 10 | import random 11 | from mpl_toolkits.mplot3d import Axes3D 12 | from scipy.interpolate import griddata 13 | import pandas as pd 14 | from keras.utils import * 15 | from keras.models import * 16 | from keras.layers import * 17 | from keras import regularizers 18 | import datetime 19 | import random as rnd 20 | from sklearn.metrics import r2_score 21 | import math 22 | from tqdm import tqdm 23 | 24 | import warnings 25 | warnings.filterwarnings("ignore") 26 | 27 | ########## 28 | # dN/dt : The rate of node development 29 | def fN(T): 30 | #Heuvelink(1994) & Jones(1991) 31 | if(T > 12 and T <= 28): 32 | return 1.0 + 0.0281 * (T - 28) 33 | elif(T > 28 and T < 50): 34 | return 1.0 - 0.0455 * (T - 28) 35 | else: 36 | return 0 37 | return 0 38 | 39 | def dNdt(fN_): 40 | Nm = 0.5 #P Jones(1991) 41 | #("dNdt:", Nm * fN_) 42 | return Nm * fN_ 43 | 44 | ########## 45 | # d(LAI)/dt : The rate of LAI(Leaf Area Index) development 46 | 47 | def lambdas(Td): 48 | return 1.0 49 | 50 | def dLAIdt(LAI, dens, N, lambda_, dNdt_): 51 | sigma = 0.038 #P Maximum leaf area expansion per node, coefficient in expolinear equation; Jones(1999) 52 | beta = 0.169 #P Coefficient in expolinear equation; Jones(1999) 53 | Nb = 16.0 #P Coefficient in expolinear equation, projection of linear segment of LAI vs N to horizontal axis; Jones(1999) 54 | LAImax = 4.0 #P Jones(1999) 55 | 56 | if(LAI > LAImax): 57 | return 0 58 | 59 | a = np.exp(beta * (N - Nb)) 60 | return dens * sigma * lambda_ * a * dNdt_ / (1 + a) 61 | 62 | ########## 63 | # dW/dt 64 | def dWdt(LAI, dWfdt_, GRnet_, dens, dNdt_): 65 | LAImax = 4.0 #P Jones(1999) 66 | if(LAI >= LAImax): 67 | p1 = 2.0 #P Jones(1999) 68 | else: 69 | p1 = 0 70 | Vmax = 8.0 #P Jones(1999) 71 | 72 | a = dWfdt_ + (Vmax - p1) * dens * dNdt_ 73 | b = GRnet_ - p1 * dens * dNdt_ 74 | #print(a, b) 75 | return min(a, b) 76 | 77 | # dWdt_max 78 | 79 | ########## 80 | # dWmdt 81 | def Df(T): 82 | # The rate of development or aging of fruit at temperature T; Jones(1991) 83 | if(T > 9 and T <= 28): 84 | return 0.0017 * T - 0.015 85 | elif(T > 28 and T <= 35): 86 | return 0.032 87 | else: 88 | return 0 89 | 90 | def dWmdt(Df_, Wf, Wm, N): 91 | NFF = 22.0 #P Jones(1999) 92 | kF = 5.0 #P Jones(1999) 93 | 94 | if(N <= NFF + kF): 95 | return 0 96 | return Df_ * (Wf - Wm) 97 | 98 | ########## 99 | # dWfdt : The rate of Fruit dry weight TRY IT NOW 100 | 101 | def fR(N): 102 | #root phenology-dependent fraction; Jones(1991) 103 | if(N >= 30): 104 | return 0.07 105 | return -0.0046 * N + 0.2034 106 | 107 | def LFmax(CO2): 108 | #maximum leaf photosyntehstic rate;Jones(1991) 109 | #CO2[ppm]? 110 | tau = 0.0693 #P carbon dioxide use efficiency; Jones(1991) 111 | return tau * CO2 112 | 113 | def PGRED(T): 114 | #function to modify Pg under suboptimal daytime temperatures; Jones(1991) 115 | if(T > 0 and T <= 12): 116 | return 1.0 / 12.0 * T 117 | elif(T > 12 and T < 35): 118 | return 1.0 119 | else: 120 | return 0 121 | return 0 122 | 123 | def Pg(LFmax_, PGRED_, PPFD, LAI): 124 | D = 2.593 #P coefficient to convert Pg from CO2 to CH2O; Jones(1991) 125 | K = 0.58 #P light extinction coefficient; Jones(1991) 126 | m = 0.1 #P leaf light transmission coefficient; Jones(1991) 127 | Qe = 0.0645 #P leaf quantum efficiency; Jones(1991) 128 | 129 | #if(PPFD > 250): 130 | # PPFD = 0 131 | a = D * LFmax_ * PGRED_ / K 132 | b = np.log(((1-m) * LFmax_ + Qe * K * PPFD) / 133 | ((1-m) * LFmax_ + Qe * K * PPFD * np.exp(-1 * K * LAI))) 134 | return a * b 135 | 136 | def Rm(T, W, Wm): 137 | #Jones(1999) 138 | #Hourly Data! 139 | Q10 = 1.4 #P Jones(1991) 140 | rm = 0.016 #P Jones(1999) 141 | return Q10 ** ((T-20)/10) * rm * (W - Wm) 142 | 143 | def GRnet(Pg_, Rm_, fR_): 144 | E = 0.717 #P convert efficiency; Dimokas(2009) 145 | #print("GRnet", Pg_, Rm_, fR_) 146 | return max(0, E * (Pg_ - Rm_) * (1 - fR_)) 147 | 148 | def fF(Td): 149 | #Jones(1991) 150 | if(Td > 8 and Td <= 28): 151 | return 0.0017 * Td - 0.0147 152 | elif(Td > 28): 153 | return 0.032 154 | else: 155 | return 0 156 | return 0 157 | 158 | def g(T_daytime): 159 | T_CRIT = 24.4 #P mean daytime temperature above which fruits abortion start; Jones(1999) 160 | if(T_daytime <= T_CRIT): 161 | return 0 162 | return max(0, 1.0 - 0.154 * (T_daytime - T_CRIT)) 163 | 164 | def dWfdt(GRnet_, fF_, N, g_): 165 | NFF = 22.0 #P Nodes per plant when first fruit appears; Jones(1999) 166 | alpha_F = 0.80 #P Maximum partitioning of new growth to fruit; Jones(1999) 167 | v = 0.135 #P Transition coefficient between vegetative and full fruit growth; Jones(1999) 168 | fF_ = 0.5 #P ORIGINAL 169 | if(N <= NFF): 170 | return 0 171 | #print(GRnet_, fF_, 1 - np.exp(v*(NFF-N)), g_) 172 | return GRnet_ * alpha_F * fF_ * (1 - np.exp(v*(NFF-N))) * g_ 173 | 174 | ########## 175 | def calc(inT, inPPFD, start_hour): 176 | # Initial Variables #Jones (1999) 177 | N = 6.0 178 | LAI = 0.006 179 | W = 0.28 180 | Wm = 0.0 181 | Wf = 0.0 182 | CO2 = 350 183 | 184 | # Growth data per day 185 | N_hist = np.empty(0) 186 | LAI_hist = np.empty(0) 187 | W_hist = np.empty(0) 188 | Wm_hist = np.empty(0) 189 | Wf_hist = np.empty(0) 190 | 191 | # Growth rate per day 192 | delN = np.empty(0) 193 | delLAI = np.empty(0) 194 | delW = np.empty(0) 195 | delWm = np.empty(0) 196 | delWf = np.empty(0) 197 | 198 | Tday = np.empty(0) 199 | 200 | length = int(len(inT)/24)*24 201 | 202 | for i in range(0, length, 24): 203 | # Reset varialbes 204 | dNdt_ = 0 205 | Td = 0 # Change! 206 | Tdaytime = 0 207 | PPFDd = 0 208 | 209 | # for daily tempereture 210 | for h in range(24): 211 | Td += inT[i+h] 212 | PPFDd += inPPFD[i+h] 213 | new_start_hour = start_hour + h 214 | if(new_start_hour > 24): new_start_hour -= 24 215 | if(new_start_hour > 7 and new_start_hour < 17): #14時 216 | Tdaytime += inT[i+h] 217 | Td /= 24 # average dialy temperature 218 | PPFDd /= 24 # average dialy temperature 219 | Tdaytime /= 9 220 | fN_ = fN(Td) 221 | dNdt_ += dNdt(fN_) 222 | 223 | # dN/dt 224 | #fN_ = fN(Td) 225 | #dNdt_ += dNdt(fN_) 226 | 227 | # d(LAI)/dt 228 | lambda_ = lambdas(Td) 229 | dLAIdt_ = dLAIdt(LAI=LAI, dens=3.10, N=N, lambda_=lambda_, dNdt_=dNdt_) 230 | 231 | # dWfdt 232 | fR_ = fR(N) 233 | LFmax_ = LFmax(CO2) 234 | PGRED_ = PGRED(Td) 235 | Pg_ = Pg(LFmax_, PGRED_, PPFDd, LAI) 236 | Rm_ = Rm(Td, W, Wm) 237 | GRnet_ = GRnet(Pg_, Rm_, fR_) 238 | fF_ = fF(Td) 239 | g_ = g(Tdaytime) 240 | dWfdt_ = dWfdt(GRnet_, fF_, N, g_) 241 | 242 | # dWdt 243 | dWdt_ = dWdt(LAI=LAI, dWfdt_=dWfdt_, GRnet_=GRnet_, dens=3.10, dNdt_=dNdt_) 244 | 245 | # dWmdt 246 | Df_ = Df(Td) 247 | dWmdt_ = dWmdt(Df_, Wf, Wm, N) 248 | 249 | # Reload 250 | N += dNdt_ 251 | LAI += dLAIdt_ 252 | Wf += dWfdt_ 253 | W += dWdt_ 254 | Wm += dWmdt_ 255 | 256 | # Save 257 | N_hist = np.append(N_hist, N) 258 | LAI_hist = np.append(LAI_hist, LAI) 259 | W_hist = np.append(W_hist, W) 260 | Wf_hist = np.append(Wf_hist, Wf) 261 | Wm_hist = np.append(Wm_hist, Wm) 262 | delN = np.append(delN, dNdt_) 263 | delLAI = np.append(delLAI, dLAIdt_) 264 | delW = np.append(delW, dWdt_) 265 | delWf = np.append(delWf, dWfdt_) 266 | delWm = np.append(delWm, dWmdt_) 267 | Tday = np.append(Tday, Tdaytime) 268 | """ 269 | N_hist = np.array(N_hist) 270 | LAI_hist = np.array(LAI_hist) 271 | W_hist = np.array(W_hist) 272 | Wf_hist = np.array(Wf_hist) 273 | Wm_hist = np.array(Wm_hist) 274 | delN = np.array(delN) 275 | delLAI = np.array(delLAI) 276 | delW = np.array(delW) 277 | delWf = np.array(delWf) 278 | delWm = np.array(delWm) 279 | """ 280 | 281 | # X:Final Value, X_hist:Values per Day, delX:Growth Rate per Day 282 | return {"N":N, "LAI":LAI, "Wf":Wf, "W":W, "Wm":Wm, "Tday":Tday, 283 | "N_hist":N_hist, "LAI_hist":LAI_hist,"W_hist":W_hist,"Wf_hist":Wf_hist,"Wm_hist":Wm_hist, 284 | "delN":delN, "delLAI":delLAI, "delWf":delWf, "delW":delW, "delWm":delWm} 285 | 286 | def pseudoClimate(filename, rng, fix=0): 287 | df = pd.read_csv(filename, sep=',', index_col='date') 288 | data_range = pd.date_range('2018-08-20 01:00:00',periods=rng,freq='d') 289 | 290 | """ 291 | #グラフ表示 292 | data_range = pd.date_range('2019-03-20 01:00:00',periods=5160,freq='H') 293 | plt.plot(data_range, df['temperature']) 294 | plt.show() 295 | plt.plot(data_range, df['PPFD']) 296 | plt.show() 297 | """ 298 | 299 | #print(df['temperature']) 300 | return {'date':data_range, 'T':df['temperature'], 'PPFD':df['PPFD']} 301 | 302 | def calcDay(startday, period, hour=0): 303 | date_formatted = datetime.datetime.strptime(startday, "%Y/%m/%d %H:%M") 304 | after = date_formatted + datetime.timedelta(days=period) + datetime.timedelta(hours=hour) 305 | return after.strftime("%Y/%-m/%-d %-H:%M") 306 | 307 | def calcHour(startday, period): 308 | date_formatted = datetime.datetime.strptime(startday, "%Y/%m/%d %H:%M") 309 | after = date_formatted + datetime.timedelta(hours=period) 310 | return after.strftime("%Y/%-m/%-d %-H:%M") 311 | 312 | def getStartHour(timestr): 313 | date_formatted = datetime.datetime.strptime(timestr, "%Y/%m/%d %H:%M") 314 | hour = date_formatted.hour 315 | return hour 316 | 317 | def dayPlot(startday, y, title="", xlabel="", ylabel=""): 318 | print(startday) 319 | data_range = pd.date_range(startday, periods=len(y), freq='d') 320 | plt.rcParams["font.size"] = 12 321 | plt.xticks(rotation=90) 322 | if(title!=""): 323 | plt.title(title, fontsize=18) 324 | if(xlabel!=""): 325 | plt.xlabel(xlabel, fontsize=18) 326 | if(ylabel!=""): 327 | plt.ylabel(ylabel, fontsize=18) 328 | plt.plot(data_range, y) 329 | plt.show() 330 | 331 | def dayPlot2D(startday, y, title=["",""], xlabel=["",""], ylabel=["",""]): 332 | data_range = pd.date_range(startday, periods=len(y[0]), freq='d') 333 | plt.rcParams["font.size"] = 12 334 | 335 | fig, (axL, axR) = plt.subplots(ncols=2, figsize=(10,4)) 336 | fig.autofmt_xdate(rotation=90) 337 | 338 | axL.plot(data_range, y[0], linewidth=2) 339 | axL.set_title(title[0], fontsize=18) 340 | axL.set_xlabel(xlabel[0], fontsize=18) 341 | axL.set_ylabel(ylabel[0], fontsize=18) 342 | axL.grid(True) 343 | 344 | axR.plot(data_range, y[1], linewidth=2) 345 | axR.set_title(title[1], fontsize=18) 346 | axR.set_xlabel(xlabel[1], fontsize=18) 347 | axR.set_ylabel(ylabel[1], fontsize=18) 348 | axR.grid(True) 349 | 350 | fig.show() 351 | 352 | def splitInData(x, n=24, step=1): 353 | # x:Input data n:1つの成長率に対応する元々の入力データ数[時間] step:平均間隔 354 | # return np.array([[1日分のstepごとに平均化されたデータ]]) 355 | 356 | data_len = len(x) 357 | resX, tmpX = np.empty(0), np.empty(0) 358 | for j in range(0, data_len-step, step): 359 | resX = np.append(resX, np.average(x[j:j+step])) 360 | resX = resX.reshape(-1, int(n/step)) 361 | 362 | return resX 363 | 364 | def makeInPair(inputs, ave_range=1): 365 | # inputs:入力値のリスト[[Ts],[Ps],...](全て同じサイズであること), ave_range:平均化する単位時間 366 | res = np.empty(0) 367 | for i in range(len(inputs[0])): 368 | res = np.append(res, inputs[:,i]) 369 | 370 | return res.reshape(-1,2) 371 | 372 | def averageHist(x, y): 373 | print(x.shape, y.shape) 374 | mins = np.min(y) 375 | maxs = np.max(y) 376 | print(mins, maxs) 377 | hist_x, hist_y = [[] for i in range(20)], [[] for i in range(20)] 378 | res_ind, res_x, res_y = np.empty(0), np.empty(0), np.empty(0) 379 | 380 | print(1) 381 | for yi, val in enumerate(y): 382 | for i in range(20): 383 | if(val <= mins+(maxs-mins)/20*(i+1) and val >= mins+(maxs-mins)/20*i): 384 | hist_x[i].append(x[yi]) 385 | hist_y[i].append(val) 386 | 387 | print(2) 388 | ave_len = np.average(np.array([len(i) for i in hist_x])) 389 | ave_len = int(round(ave_len)) 390 | print(ave_len) 391 | 392 | print(3) 393 | for i in range(20): 394 | this_len = len(hist_x[i]) 395 | if(this_len == 0): continue 396 | for j in range(max(1,round(ave_len/this_len))): 397 | res_x = np.append(res_x, np.array(hist_x[i][:ave_len])) 398 | res_y = np.append(res_y, np.array(hist_y[i][:ave_len])) 399 | 400 | res_x = res_x.reshape(-1,7) 401 | res_y = res_y.reshape(-1,1) 402 | 403 | print(res_x.shape, res_y.shape) 404 | 405 | return res_x, res_y 406 | 407 | def splitOutData(y, n): 408 | resY = np.empty(0) 409 | for i in range(len(y)-1): 410 | deltaY = y[i+1] - y[i] 411 | resY = np.append(resY, deltaY) 412 | return resY 413 | 414 | def averageInData(x, n): 415 | """ 416 | #夜、日中の平均 417 | resX = np.empty(0) 418 | daytime, night = 0, 0 419 | for i in range(0, len(x)-n, 24): 420 | daytime = np.average(x[i+6:i+19]) 421 | night = np.average(x[i+0:i+6]) + np.average(x[i+19:i+24]) 422 | resX = np.append(resX, np.array([daytime, night/2])) 423 | return resX.reshape(-1 , 2) 424 | """ 425 | resX = np.empty(0) 426 | for i in range(0, len(x)-n, 24): 427 | average = np.average(x[i:24+i]) 428 | resX = np.append(resX, average) 429 | return resX 430 | 431 | def integrate(p): 432 | sumP = np.empty(0) 433 | for i in range(p.shape[0]): 434 | sumP = np.append(sumP, np.sum(p[0:i])) 435 | return sumP 436 | 437 | def detect_zero(xdata, ydata, id): 438 | x_zero_data = np.empty(0) 439 | x_other_data = np.empty(0) 440 | y_zero_data = np.empty(0) 441 | y_other_data = np.empty(0) 442 | for i, dat in enumerate(xdata): 443 | if(dat[id] == 0): 444 | x_zero_data = np.concatenate([x_zero_data, dat]) 445 | y_zero_data = np.append(y_zero_data, ydata[i]) 446 | else: 447 | x_other_data = np.concatenate([x_other_data, dat]) 448 | y_other_data = np.append(y_other_data, ydata[i]) 449 | return x_zero_data, y_zero_data, x_other_data, y_other_data 450 | 451 | def TOMGRO(startday, get_period=1, step=1, trainmode=False): 452 | # startday:定植時刻 get_period:最終定植時刻数 step:定植時刻間隔 453 | 454 | splitPeriod = 24 # hour 455 | ave_step = 24 456 | 457 | datas = pseudoClimate('drive/My Drive/weather_Tokyo.csv', 400) 458 | T = datas['T'] 459 | PPFD = datas['PPFD'] 460 | xT, xP = np.empty(0), np.empty(0) 461 | delN, delLAI, delW, delWf, delWm = np.empty(0), np.empty(0), np.empty(0), np.empty(0), np.empty(0) 462 | N_hist, LAI_hist, W_hist, Wf_hist, Wm_hist = np.empty(0), np.empty(0), np.empty(0), np.empty(0), np.empty(0) 463 | Tday = np.empty(0) 464 | 465 | # 1回のループ = period期間の植物の成長 466 | for i in tqdm(range(0,get_period,step)): 467 | endday = calcDay(startday, period=100) 468 | inT = np.array(T[startday:endday].tolist()) # ある日の1:00~99日後の1:00 469 | inP = np.array(PPFD[startday:endday].tolist()) 470 | startday = calcHour(startday, period=step) 471 | start_hour = getStartHour(startday) 472 | res = calc(inT, inP, start_hour) 473 | delN = np.append(delN, res['delN']) 474 | delLAI = np.append(delLAI, res['delLAI']) 475 | delW = np.append(delW, res['delW']) 476 | delWf = np.append(delWf, res['delWf']) 477 | delWm = np.append(delWm, res['delWm']) 478 | N_hist = np.append(N_hist, res['N_hist']) 479 | LAI_hist = np.append(LAI_hist, res['LAI_hist']) 480 | W_hist = np.append(W_hist, res['W_hist']) 481 | Wf_hist = np.append(Wf_hist, res['Wf_hist']) 482 | Wm_hist = np.append(Wm_hist, res['Wm_hist']) 483 | Tday = np.append(Tday, res['Tday']) 484 | 485 | # 入力データの作成(x = [[(その日のN,LAI,W,Wf,Wm),1日分のstepごとに平均化されたTs,Ps], [(N,LAI,W,Wf,Wm),T,P], [(N,LAI,W,Wf,Wm),T,P], ...]) 486 | if(i>0): 487 | xT = np.concatenate([xT, splitInData(inT, splitPeriod, step=ave_step)]) 488 | xP = np.concatenate([xP, splitInData(inP, splitPeriod, step=ave_step)]) 489 | else: 490 | xT = splitInData(inT, splitPeriod, step=ave_step) 491 | xP = splitInData(inP, splitPeriod, step=ave_step) 492 | x = np.concatenate([xT, xP], axis=1) 493 | x = np.insert(x, 0, Tday, axis=1) 494 | if(trainmode): 495 | x = np.insert(x, 0, Wm_hist, axis=1) 496 | x = np.insert(x, 0, Wf_hist, axis=1) 497 | x = np.insert(x, 0, W_hist, axis=1) 498 | x = np.insert(x, 0, LAI_hist, axis=1) 499 | x = np.insert(x, 0, N_hist, axis=1) 500 | 501 | return {'x':x, 'xT':xT, 'xP':xP, 502 | 'delN':delN, 'delLAI':delLAI, 'delW':delW, 'delWf':delWf, 'delWm':delWm, 503 | 'N_hist':N_hist, 'LAI_hist':LAI_hist, 'W_hist':W_hist, 'Wf_hist':Wf_hist, 'Wm_hist':Wm_hist} 504 | 505 | def getTrain(train_startday, days=360): 506 | step = 6 #[hour] 507 | period = days * 1 * 24 508 | traindata = TOMGRO(train_startday, get_period=period, step=step, trainmode=True) #get_period, step [hour] 509 | return traindata 510 | --------------------------------------------------------------------------------