From 94caa1c957819ca89c36660b73a1a24bd30c22a8 Mon Sep 17 00:00:00 2001 From: APMonitor Date: Tue, 12 Dec 2017 12:42:33 -0700 Subject: [PATCH 01/10] APMonitor Python Update Add try...except to a couple places to catch errors --- apm.py | 59 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/apm.py b/apm.py index aab0e7e..718098c 100644 --- a/apm.py +++ b/apm.py @@ -145,18 +145,22 @@ def get_file(server,app,filename): app = application name ''' # Retrieve IP address ip = get_ip(server) - # Web-server URL address - app = app.lower() - app.replace(" ","") - url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename - f = urllib.urlopen(url) - # Send request to web-server - file = f.read() - # Write the file - fh = open(filename,'w') - fh.write(file.replace('\r','')) - fh.close() - return (file) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] def set_option(server,app,name,value): '''Load APM option \n \ @@ -436,19 +440,24 @@ def get_file(server,app,filename): app = application name ''' # Retrieve IP address ip = get_ip(server) - # Web-server URL address - app = app.lower() - app.replace(" ","") - url = server.strip() + '/online/' + ip + '_' + app + '/' + filename - f = urllib.request.urlopen(url) - # Send request to web-server - file = f.read() - # Write the file - fh = open(filename,'w') - en_file = file.decode().replace('\r','') - fh.write(en_file) - fh.close() - return (file) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + def set_option(server,app,name,value): '''Load APM option \n \ From f0c65478b4f85372db4c72b4613dc3e4d0a81e4f Mon Sep 17 00:00:00 2001 From: Sean Lane <5761232+seanlane@users.noreply.github.com> Date: Wed, 31 Jan 2018 10:20:37 -0700 Subject: [PATCH 02/10] Update apm.py Add option to not print output from APM cmd. In a perfect world, I think it might be best to adjust the return value of method to return a tuple of `(response, output)` so the caller can do what they will with the output, or something similar, but that will likely cause a fair amount of refactoring. This change should keep the default behavior while allowing to opt out from printing input, for example when running APM from Gekko. I haven't spent an inordinate amount of time thinking over this change, so feedback is certainly welcome :) --- apm.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apm.py b/apm.py index 718098c..958b46f 100644 --- a/apm.py +++ b/apm.py @@ -20,7 +20,7 @@ if ver==2: # Python 2 - def cmd(server, app, aline): + def cmd(server, app, aline, disp=True): '''Send a request to the server \n \ server = address of server \n \ app = application name \n \ @@ -40,7 +40,8 @@ def cmd(server, app, aline): if not char: break elif char == '\n': - print(line) + if disp: + print(line) line = '' else: line += char From 0f9af9ca8c92b61838ee6045720d5326b96777a8 Mon Sep 17 00:00:00 2001 From: Sean Lane <5761232+seanlane@users.noreply.github.com> Date: Wed, 31 Jan 2018 10:23:25 -0700 Subject: [PATCH 03/10] Update apm.py Add same change to Python 3 branch --- apm.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apm.py b/apm.py index 958b46f..077fdbd 100644 --- a/apm.py +++ b/apm.py @@ -309,11 +309,12 @@ def load_meas(server,app,name,value): else: # Python 3+ - def cmd(server,app,aline): + def cmd(server,app,aline, disp=True): '''Send a request to the server \n \ server = address of server \n \ app = application name \n \ - aline = line to send to server \n''' + aline = line to send to server \n \ + disp = Print output \n''' try: # Web-server URL address url_base = server.strip() + '/online/apm_line.php' @@ -331,7 +332,8 @@ def cmd(server,app,aline): if not char: break elif char == '\n': - print(line) + if disp: + print(line) line = '' else: line += char From 4b722ef50e6dbe4c48e09304f47cc5eb22d28343 Mon Sep 17 00:00:00 2001 From: Sean Lane <5761232+seanlane@users.noreply.github.com> Date: Wed, 31 Jan 2018 10:24:40 -0700 Subject: [PATCH 04/10] Update apm.py Add comment to Python 2 branch as well --- apm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apm.py b/apm.py index 077fdbd..24b3864 100644 --- a/apm.py +++ b/apm.py @@ -24,7 +24,8 @@ def cmd(server, app, aline, disp=True): '''Send a request to the server \n \ server = address of server \n \ app = application name \n \ - aline = line to send to server \n''' + aline = line to send to server \n \ + disp = Print output \n''' try: # Web-server URL address url_base = string.strip(server) + '/online/apm_line.php' From adc447672bd0849c199a686df9fefa5fa9d93f5b Mon Sep 17 00:00:00 2001 From: okesontj Date: Wed, 31 Jan 2018 11:20:51 -0700 Subject: [PATCH 05/10] Added the setup file for uploading to pypi --- setup.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..69a74ca --- /dev/null +++ b/setup.py @@ -0,0 +1,13 @@ +from distutils.core import setup +setup( + name = 'APMonitor', + packages = ['APMonitor'], # this must be the same as the name above + version = '0.31', + description = 'Python package to interface with the APMonitor optimization suite', + author = 'John Hedengren', + author_email = 'john_hedengren@byu.edu', + url = 'https://github.com/okesontj/APMonitor', # use the URL to the github repo + download_url = 'https://github.com/okesontj/APMonitor/tarball/0.2', + keywords = ['optimization', 'MPC'], + classifiers = [], +) From 31143693426e5a6aa0474d66caf1ee18c523e65b Mon Sep 17 00:00:00 2001 From: APMonitor Date: Mon, 20 Aug 2018 07:27:57 -0600 Subject: [PATCH 06/10] APM Python Update for Python 3.7 --- demo/apm.py | 360 +++++++- example_cstr/apm.py | 1052 ++++++++++++++++++++++ example_cstr/main.py | 3 - example_diabetic/apm.py | 1052 ++++++++++++++++++++++ example_diabetic/control.py | 3 - example_diabetic/step.py | 3 - example_distillation/apm.py | 1052 ++++++++++++++++++++++ example_distillation/main_nlc.py | 3 - example_distillation/main_pid.py | 3 - example_distillation/main_step.py | 3 - example_distillation/test.py | 3 - example_hs71/apm.py | 1052 ++++++++++++++++++++++ example_hs71/main.py | 3 - example_lti_regression/apm.py | 1052 ++++++++++++++++++++++ example_lti_regression/lti_regression.py | 3 - example_minlp/apm.py | 1052 ++++++++++++++++++++++ example_nlc/apm.py | 1052 ++++++++++++++++++++++ example_tank_mhe/apm.py | 1052 ++++++++++++++++++++++ example_tank_nlc/apm.py | 1052 ++++++++++++++++++++++ 19 files changed, 9790 insertions(+), 65 deletions(-) create mode 100644 example_cstr/apm.py create mode 100644 example_diabetic/apm.py create mode 100644 example_distillation/apm.py create mode 100644 example_hs71/apm.py create mode 100644 example_lti_regression/apm.py create mode 100644 example_minlp/apm.py create mode 100644 example_nlc/apm.py create mode 100644 example_tank_mhe/apm.py create mode 100644 example_tank_nlc/apm.py diff --git a/demo/apm.py b/demo/apm.py index 07bcdd6..24b3864 100644 --- a/demo/apm.py +++ b/demo/apm.py @@ -20,11 +20,12 @@ if ver==2: # Python 2 - def cmd(server, app, aline): + def cmd(server, app, aline, disp=True): '''Send a request to the server \n \ server = address of server \n \ app = application name \n \ - aline = line to send to server \n''' + aline = line to send to server \n \ + disp = Print output \n''' try: # Web-server URL address url_base = string.strip(server) + '/online/apm_line.php' @@ -40,7 +41,8 @@ def cmd(server, app, aline): if not char: break elif char == '\n': - print(line) + if disp: + print(line) line = '' else: line += char @@ -145,18 +147,22 @@ def get_file(server,app,filename): app = application name ''' # Retrieve IP address ip = get_ip(server) - # Web-server URL address - app = app.lower() - app.replace(" ","") - url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename - f = urllib.urlopen(url) - # Send request to web-server - file = f.read() - # Write the file - fh = open(filename,'w') - fh.write(file.replace('\r','')) - fh.close() - return (file) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] def set_option(server,app,name,value): '''Load APM option \n \ @@ -304,11 +310,12 @@ def load_meas(server,app,name,value): else: # Python 3+ - def cmd(server,app,aline): + def cmd(server,app,aline, disp=True): '''Send a request to the server \n \ server = address of server \n \ app = application name \n \ - aline = line to send to server \n''' + aline = line to send to server \n \ + disp = Print output \n''' try: # Web-server URL address url_base = server.strip() + '/online/apm_line.php' @@ -326,7 +333,8 @@ def cmd(server,app,aline): if not char: break elif char == '\n': - print(line) + if disp: + print(line) line = '' else: line += char @@ -436,19 +444,24 @@ def get_file(server,app,filename): app = application name ''' # Retrieve IP address ip = get_ip(server) - # Web-server URL address - app = app.lower() - app.replace(" ","") - url = server.strip() + '/online/' + ip + '_' + app + '/' + filename - f = urllib.request.urlopen(url) - # Send request to web-server - file = f.read() - # Write the file - fh = open(filename,'w') - en_file = file.decode().replace('\r','') - fh.write(en_file) - fh.close() - return (file) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + def set_option(server,app,name,value): '''Load APM option \n \ @@ -596,13 +609,13 @@ def load_meas(server,app,name,value): response = f.read() return response -def solve(app,imode): +def solve(app,imode,web_option=False): ''' APM Solver for simulation, estimation, and optimization with both static (steady-state) and dynamic models. The dynamic modes can solve index 2+ DAEs without numerical differentiation. - y = solve(app,imode) + y = solve(app,imode,web_option=False) Function solve uploads the model file (apm) and optionally a data file (csv) with the same name to the web-server and performs @@ -613,8 +626,9 @@ def solve(app,imode): imode = simulation mode {1..7} steady-state dynamic sequential simulate 1 4 7 - estimate 2 5 8 (under dev) - optimize 3 6 9 (under dev) + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer Output: y.names = names of all variables y.values = tables of values corresponding to y.names @@ -657,8 +671,7 @@ def solve(app,imode): # default options # use or don't use web viewer - web = False - if web: + if web_option: set_option(server,app,'nlc.web',2) else: set_option(server,app,'nlc.web',0) @@ -679,7 +692,7 @@ def solve(app,imode): if status==1: # open web viewer if selected - if web: + if web_option: web(server,app) # retrieve solution and solution.csv z = get_solution(server,app) @@ -689,6 +702,277 @@ def solve(app,imode): print('Error: Did not converge to a solution') return [] +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + def plotter(y, subplots=1, save=False, filename='solution', format='png'): ''' The plotter will go through each of the variables in the output y and diff --git a/example_cstr/apm.py b/example_cstr/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_cstr/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_cstr/main.py b/example_cstr/main.py index a91f073..6ae39ed 100644 --- a/example_cstr/main.py +++ b/example_cstr/main.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import from apm import * diff --git a/example_diabetic/apm.py b/example_diabetic/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_diabetic/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_diabetic/control.py b/example_diabetic/control.py index ffebb93..09c7200 100644 --- a/example_diabetic/control.py +++ b/example_diabetic/control.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import from apm import * diff --git a/example_diabetic/step.py b/example_diabetic/step.py index e79f3be..03e4423 100644 --- a/example_diabetic/step.py +++ b/example_diabetic/step.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import from apm import * diff --git a/example_distillation/apm.py b/example_distillation/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_distillation/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_distillation/main_nlc.py b/example_distillation/main_nlc.py index e90636b..a13df9a 100644 --- a/example_distillation/main_nlc.py +++ b/example_distillation/main_nlc.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import from apm import * diff --git a/example_distillation/main_pid.py b/example_distillation/main_pid.py index 21c569d..c03674f 100644 --- a/example_distillation/main_pid.py +++ b/example_distillation/main_pid.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import from apm import * diff --git a/example_distillation/main_step.py b/example_distillation/main_step.py index bad04a3..a828e02 100644 --- a/example_distillation/main_step.py +++ b/example_distillation/main_step.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import from apm import * diff --git a/example_distillation/test.py b/example_distillation/test.py index ae0b00c..f1b0d1d 100644 --- a/example_distillation/test.py +++ b/example_distillation/test.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import from apm import * diff --git a/example_hs71/apm.py b/example_hs71/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_hs71/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_hs71/main.py b/example_hs71/main.py index d19a109..c24ff24 100644 --- a/example_hs71/main.py +++ b/example_hs71/main.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - # Import APM package from apm import * diff --git a/example_lti_regression/apm.py b/example_lti_regression/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_lti_regression/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_lti_regression/lti_regression.py b/example_lti_regression/lti_regression.py index 40e5770..78f9f69 100644 --- a/example_lti_regression/lti_regression.py +++ b/example_lti_regression/lti_regression.py @@ -1,6 +1,3 @@ -import sys -sys.path.append("../") - import numpy as np from apm import * diff --git a/example_minlp/apm.py b/example_minlp/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_minlp/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_nlc/apm.py b/example_nlc/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_nlc/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_tank_mhe/apm.py b/example_tank_mhe/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_tank_mhe/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve diff --git a/example_tank_nlc/apm.py b/example_tank_nlc/apm.py new file mode 100644 index 0000000..24b3864 --- /dev/null +++ b/example_tank_nlc/apm.py @@ -0,0 +1,1052 @@ +# Import +import csv +import math +import os +import random +import string +import time +import webbrowser +from contextlib import closing +import sys + +# Get Python version +ver = sys.version_info[0] +#print('Version: '+str(ver)) +if ver==2: # Python 2 + import urllib +else: # Python 3+ + import urllib.request, urllib.parse, urllib.error + #import socket + +if ver==2: # Python 2 + + def cmd(server, app, aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = string.strip(server) + '/online/apm_line.php' + app = app.lower() + app.replace(" ", "") + params = urllib.urlencode({'p': app, 'a': aline}) + f = urllib.urlopen(url_base, params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + char = f.read(1) + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + response = f.read() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = string.strip(server) + '/ip.php' + f = urllib.urlopen(url_base) + ip = string.strip(f.read()) + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + string.strip(mode) + '.t0' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + fh.write(solution.replace('\r','')) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.urlopen(url)) as f: + reader = csv.reader(f, delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + filename + f = urllib.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + fh.write(file.replace('\r','')) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = string.strip(server) + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = reader.next() + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(string.strip(name)) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = string.strip(server) + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = string.strip(server) + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.urlencode({'p':app,'n':name+'.MEAS','v':value}) + f = urllib.urlopen(url_base,params) + # Send request to web-server + response = f.read() + return response + +else: # Python 3+ + + def cmd(server,app,aline, disp=True): + '''Send a request to the server \n \ + server = address of server \n \ + app = application name \n \ + aline = line to send to server \n \ + disp = Print output \n''' + try: + # Web-server URL address + url_base = server.strip() + '/online/apm_line.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'a':aline}) + en_params = params.encode() + f = urllib.request.urlopen(url_base,en_params) + # Stream solution output + if(aline=='solve'): + line = '' + while True: + en_char = f.read(1) + char = en_char.decode() + if not char: + break + elif char == '\n': + if disp: + print(line) + line = '' + else: + line += char + # Send request to web-server + en_response = f.read() + response = en_response.decode() + except: + response = 'Failed to connect to server' + return response + + def load_model(server,app,filename): + '''Load APM model file \n \ + server = address of server \n \ + app = application name \n \ + filename = APM file name''' + # Load APM File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,' '+aline) + return + + def load_data(server,app,filename): + '''Load CSV data file \n \ + server = address of server \n \ + app = application name \n \ + filename = CSV file name''' + # Load CSV File + f = open(filename,'r') + aline = f.read() + f.close() + app = app.lower() + app.replace(" ","") + response = cmd(server,app,'csv '+aline) + return + + def get_ip(server): + '''Get current IP address \n \ + server = address of server''' + # get ip address for web-address lookup + url_base = server.strip() + '/ip.php' + f = urllib.request.urlopen(url_base) + fip = f.read() + ip = fip.decode().strip() + return ip + + def apm_t0(server,app,mode): + '''Retrieve restart file \n \ + server = address of server \n \ + app = application name \n \ + mode = {'ss','mpu','rto','sim','est','ctl'} ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + mode.strip() + '.t0' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + return solution + + def get_solution(server,app): + '''Retrieve solution results\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/results.csv' + f = urllib.request.urlopen(url) + # Send request to web-server + solution = f.read() + + # Write the file + sol_file = 'solution_' + app + '.csv' + fh = open(sol_file,'w') + # possible problem here if file isn't able to open (see MATLAB equivalent) + en_solution = solution.decode().replace('\r','') + fh.write(en_solution) + fh.close() + + # Use array package + from array import array + # Import CSV file from web server + with closing(urllib.request.urlopen(url)) as f: + fr = f.read() + de_f = fr.decode() + reader = csv.reader(de_f.splitlines(), delimiter=',') + y={} + for row in reader: + if len(row)==2: + y[row[0]] = float(row[1]) + else: + y[row[0]] = array('f', [float(col) for col in row[1:]]) + # Return solution + return y + + + def get_file(server,app,filename): + '''Retrieve any file from web-server\n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + try: + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + filename + f = urllib.request.urlopen(url) + # Send request to web-server + file = f.read() + # Write the file + fh = open(filename,'w') + en_file = file.decode().replace('\r','') + fh.write(en_file) + fh.close() + return (file) + except: + print('Could not retrieve ' + filename + ' from server') + return [] + + + def set_option(server,app,name,value): + '''Load APM option \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.option \n \ + value = numeric value of option ''' + aline = 'option %s = %f' %(name,value) + app = app.lower() + app.replace(" ","") + response = cmd(server,app,aline) + return response + + def web(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_oper.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_var(server,app): + '''Open APM web viewer in local browser \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '_var.htm' + webbrowser.get().open_new_tab(url) + return url + + def web_root(server,app): + '''Open APM root folder \n \ + server = address of server \n \ + app = application name ''' + # Retrieve IP address + ip = get_ip(server) + # Web-server URL address + app = app.lower() + app.replace(" ","") + url = server.strip() + '/online/' + ip + '_' + app + '/' + webbrowser.get().open_new_tab(url) + return url + + def classify(server,app,type,aline): + '''Classify parameter or variable as FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + type = {FV,MV,SV,CV} \n \ + aline = parameter or variable name ''' + x = 'info' + ' ' + type + ', ' + aline + app = app.lower() + app.replace(" ","") + response = cmd(server,app,x) + return response + + + def csv_data(filename): + '''Load CSV File into Python + A = csv_data(filename) + + Function csv_data extracts data from a comma + separated value (csv) file and returns it + to the array A''' + try: + f = open(filename, 'rb') + reader = csv.reader(f) + headers = next(reader) + c = [float] * (len(headers)) + A = {} + for h in headers: + A[h] = [] + for row in reader: + for h, v, conv in zip(headers, row, c): + A[h].append(conv(v)) + except ValueError: + A = {} + return A + + def csv_lookup(name,replay): + '''Lookup Index of CSV Column \n \ + name = parameter or variable name \n \ + replay = csv replay data to search''' + header = replay[0] + try: + i = header.index(name.strip()) + except ValueError: + i = -1 # no match + return i + + def csv_element(name,row,replay): + '''Retrieve CSV Element \n \ + name = parameter or variable name \n \ + row = row of csv file \n \ + replay = csv replay data to search''' + # get row number + if (row>len(replay)): row = len(replay)-1 + # get column number + col = csv_lookup(name,replay) + if (col>=0): value = float(replay[row][col]) + else: value = float('nan') + return value + + def get_attribute(server,app,name): + '''Retrieve options for FV, MV, SV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = {FV,MV,SV,CV}.{MEAS,MODEL,NEWVAL} \n \n \ + Valid name combinations \n \ + {FV,MV,CV}.MEAS \n \ + {SV,CV}.MODEL \n \ + {FV,MV}.NEWVAL ''' + # Web-server URL address + url_base = server.strip() + '/online/get_tag.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + value = eval(f.read()) + return value + + def load_meas(server,app,name,value): + '''Transfer measurement to server for FV, MV, or CV \n \ + server = address of server \n \ + app = application name \n \ + name = name of {FV,MV,CV} ''' + # Web-server URL address + url_base = server.strip() + '/online/meas.php' + app = app.lower() + app.replace(" ","") + params = urllib.parse.urlencode({'p':app,'n':name+'.MEAS','v':value}) + params_en = params.encode() + f = urllib.request.urlopen(url_base,params_en) + # Send request to web-server + response = f.read() + return response + +def solve(app,imode,web_option=False): + ''' + APM Solver for simulation, estimation, and optimization with both + static (steady-state) and dynamic models. The dynamic modes can solve + index 2+ DAEs without numerical differentiation. + + y = solve(app,imode,web_option=False) + + Function solve uploads the model file (apm) and optionally + a data file (csv) with the same name to the web-server and performs + a forward-time stepping integration of ODE or DAE equations + with the following arguments: + + Input: app = model (apm) and data file (csv) name + imode = simulation mode {1..7} + steady-state dynamic sequential + simulate 1 4 7 + estimate 2 5 8 + optimize 3 6 9 + web_option = open web-viewer + + Output: y.names = names of all variables + y.values = tables of values corresponding to y.names + y.nvar = number of variables + y.x = combined variables and values but variable + names may be modified to make them valid + characters (e.g. replace '[' with '') + ''' + + # server and application file names + server = 'http://byu.apmonitor.com' + app = app.lower() + app.replace(" ","") + app_model = app + '.apm' + app_data = app + '.csv' + + # randomize the application name + from random import randint + app = app + '_' + str(randint(1000,9999)) + + # clear previous application + cmd(server,app,'clear all') + + try: + # load model file + load_model(server,app,app_model) + except: + msg = 'Model file ' + app + '.apm does not exist' + print(msg) + return [] + + # check if data file exists (optional) + try: + # load data file + load_data(server,app,app_data) + except: + # data file is optional + print('Optional data file ' + app + '.csv does not exist') + pass + + # default options + # use or don't use web viewer + if web_option: + set_option(server,app,'nlc.web',2) + else: + set_option(server,app,'nlc.web',0) + + # internal nodes in the collocation (between 2 and 6) + set_option(server,app,'nlc.nodes',3) + # sensitivity analysis (default: 0 - off) + set_option(server,app,'nlc.sensitivity',0) + # simulation mode (1=ss, 2=mpu, 3=rto) + # (4=sim, 5=est, 6=nlc, 7=sqs) + set_option(server,app,'nlc.imode',imode) + + # attempt solution + solver_output = cmd(server,app,'solve') + + # check for successful solution + status = get_attribute(server,app,'nlc.appstatus') + + if status==1: + # open web viewer if selected + if web_option: + web(server,app) + # retrieve solution and solution.csv + z = get_solution(server,app) + return z + else: + print(solver_output) + print('Error: Did not converge to a solution') + return [] + +def sysid(data,ni,nu,ny): + import numpy as np + ''' + Identification of linear time-invariant models + + y = sysid(data,ni,nu,nu) + + Function sysid breaks up data into individual elements including: + column 0 = time + column 1-nu = inputs + column nu+1-nu+ny = outputs + + Input: data = data for the regression + ni = number of inputs + nu = number of input coefficients + ny = number of output coefficients + + Output: returns ypred (predicted outputs) + sysa.apm is written (model file with identified parameters) + There is optionally a plot and other output that appears + when flag "fast" is set to False + ''' + + fast = True # switch "False" for solution analysis + obj_scale = 1 + n = np.size(data,0) + p = np.size(data,1) + no = p - ni - 1 + if no<=0: + print('Data file needs at least 1 column for output data') + return + m = max(ny,nu) + + # first column is time + time = data[:,0].copy() + dt = time[1] - time[0] + + # inputs are the next column(s) + u_ss = data[0,1:1+ni].copy() + u = data[:,1:1+ni].copy() + + # outputs are the final column(s) + y_ss = data[0,1+ni:1+ni+no].copy() + y = data[:,1+ni:1+ni+no].copy() + + for i in range(n): + for j in range(ni): + u[i,j] = u[i,j] - u_ss[j] + for j in range(no): + y[i,j] = y[i,j] - y_ss[j] + + fid = open('myModel.apm','w') + fid.write('Objects\n') + fid.write(' sum_a[1:no] = sum(%i)\n'%ny) + fid.write(' sum_b[1:ni][1::no] = sum(%i)\n'%nu) + fid.write('End Objects\n') + fid.write(' \n') + fid.write('Connections\n') + fid.write(' a[1:ny][1::no] = sum_a[1::no].x[1:ny]\n') + fid.write(' b[1:nu][1::ni][1:::no] = sum_b[1::ni][1:::no].x[1:nu]\n') + fid.write(' sum_a[1:no] = sum_a[1:no].y\n') + fid.write(' sum_b[1:ni][1::no] = sum_b[1:ni][1::no].y\n') + fid.write('End Connections\n') + fid.write(' \n') + fid.write('Constants\n') + fid.write(' n = %s\n' %str(n)) + fid.write(' ni = %s\n'%str(ni)) + fid.write(' no = %s\n'%str(no)) + fid.write(' ny = %s\n'%str(ny)) + fid.write(' nu = %s\n'%str(nu)) + fid.write(' m = %s\n'%str(m)) + fid.write(' \n') + fid.write('Parameters\n') + fid.write(' a[1:ny][1::no] = 0 !>= -1 <= 1\n') + fid.write(' b[1:nu][1::ni][1:::no] = 0\n') + fid.write(' c[1:no] = 0\n') + fid.write(' u[1:n][1::ni]\n') + fid.write(' y[1:m][1::no]\n') + fid.write(' z[1:n][1::no]\n') + fid.write(' \n') + fid.write('Variables\n') + fid.write(' y[m+1:n][1::no] = 0\n') + fid.write(' sum_a[1:no] = 0 !<= 1\n') + fid.write(' sum_b[1:ni][1::no] = 0\n') + fid.write(' K[1:ni][1::no] = 0 !>=0 <= 10 \n') + fid.write(' \n') + fid.write('Equations\n') + fid.write(' y[m+1:n][1::no] = a[1][1::no]*y[m:n-1][1::no]') + for j in range(1,ni+1): + fid.write(' + b[1][%i][1::no]*u[m:n-1][%i]'%(j,j,)) + for i in range(2,nu+1): + fid.write(' + b[%i][%i][1::no]*u[m-%i:n-%i][%i]'%(i,j,i-1,i,j,)) + for i in range(2,ny+1): + fid.write(' + a[%i][1::no]*y[m-%i:n-%i][1::no]'%(i,i-1,i,)) + fid.write('\n') + fid.write(' K[1:ni][1::no] * (1 - sum_a[1::no]) = sum_b[1:ni][1::no]\n') + fid.write(' minimize %e * (y[m+1:n][1::no] - z[m+1:n][1::no])^2\n'%obj_scale) + fid.close() + + fid = open('myData.csv','w') + for j in range(1,ni+1): + for i in range(1,n+1): + fid.write('u[%i][%i], %e\n'%(i,j,u[i-1,j-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('z[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + for k in range(1,no+1): + for i in range(1,n+1): + fid.write('y[%i][%i], %e\n'%(i,k,y[i-1,k-1],)) + fid.close() + + s = 'http://byu.apmonitor.com' + #s = 'http://127.0.0.1' + a = 'apm_id' + apm(s,a,'clear all') + apm_load(s,a,'myModel.apm') + csv_load(s,a,'myData.csv') + + apm_option(s,a,'nlc.solver',3) + apm_option(s,a,'nlc.imode',2) + apm_option(s,a,'nlc.max_iter',100) + + for i in range(1,no+1): + name = 'c[' + str(i) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',0) + for k in range(1,no+1): + for i in range(1,ny+1): + name = 'a[' + str(i) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + for k in range(1,no+1): + for j in range(1,nu+1): + for i in range(1,nu+1): + name = 'b[' + str(i) + '][' + str(j) + '][' + str(k) + ']' + apm_info(s,a,'FV',name) + apm_option(s,a,name+'.status',1) + output = apm(s,a,'solve') + + # retrieve and visualize solution + sol = apm_sol(s,a) + ypred = np.empty((n,no)) + for j in range(no): + for i in range(n): + yn = 'y['+str(i+1)+']['+str(j+1)+']' + ypred[i,j] = sol[yn] + + if (not fast): + # open web-viewer + apm_web(s,a) + + import matplotlib.pyplot as plt + plt.figure() + plt.subplot(2,1,1) + for i in range(ni): + plt.plot(time,u[:,i]+u_ss[i]) + plt.subplot(2,1,2) + for i in range(no): + plt.plot(time,y[:,i]+y_ss[i],'r-') + plt.plot(time,ypred[:,i]+y_ss[i],'b--') + plt.show() + + success = apm_tag(s,a,'nlc.appstatus') + if success==1: + obj = apm_tag(s,a,'nlc.objfcnval') + if (not fast): + print(output) + print('Successful solution with objective: ' + str(obj)) + else: + print(output) + print('Unsuccessful, do not write sysa.apm') + return + + alpha = np.empty((ny,no)) + beta = np.empty((nu,ni,no)) + gamma = np.empty((no)) + for j in range(1,no+1): + for i in range(1,ny+1): + name = 'a['+str(i)+']['+str(j)+']' + alpha[i-1,j-1] = apm_tag(s,a,name+'.newval'); + for k in range(1,no+1): + for j in range(1,ni+1): + for i in range(1,nu+1): + name = 'b['+str(i)+']['+str(j)+']['+str(k)+']' + beta[i-1,j-1,k-1] = apm_tag(s,a,name+'.newval') + for i in range(1,no+1): + name = 'c['+str(i)+']' + gamma[i-1] = apm_tag(s,a,name+'.newval') + + name = 'sysa' + fid = open('sysa.apm','w') + fid.write('Objects\n') + fid.write(' '+name+' = arx\n') + fid.write('End Objects\n') + fid.write('\n') + fid.write('Connections\n') + if ni==1: + fid.write(' u = '+name+'.u\n') + else: + fid.write(' u[1:%i] = '%(ni,)+name+'.u[1:%i]\n'%(ni,)) + if no ==1: + fid.write(' y = '+name+'.y\n') + else: + fid.write(' y[1:%i] = '%(no,)+name+'.y[1:%i]\n'%(no,)) + fid.write('End Connections\n') + fid.write('\n') + fid.write('Model \n') + fid.write(' Parameters \n') + if ni==1: + fid.write(' u = 0\n') + else: + fid.write(' u[1:%i] = 0\n'%(ni,)) + fid.write(' End Parameters \n') + fid.write('\n') + fid.write(' Variables \n') + if no==1: + fid.write(' y = 0\n') + else: + fid.write(' y[1:%i] = 0\n'%(no,)) + fid.write(' End Variables \n') + fid.write('\n') + fid.write(' Equations \n') + fid.write(' ! add any additional equations here \n') + fid.write(' End Equations \n') + fid.write('End Model \n') + fid.write('\n') + fid.write('File '+name+'.txt\n') + fid.write(' %i ! m=number of inputs\n'%(ni,)) + fid.write(' %i ! p=number of outputs\n'%(no,)) + fid.write(' %i ! nu=number of input terms\n'%(nu,)) + fid.write(' %i ! ny=number of output terms\n'%(ny,)) + fid.write('End File\n') + fid.write('\n') + fid.write('! Alpha matrix (ny x p)\n') + fid.write('File '+name+'.alpha.txt \n') + for i in range(1,ny+1): + for j in range(1,no+1): + fid.write(' ') + if j<=no-1: + fid.write('%e , '%(alpha[i-1,j-1],)) + else: + fid.write('%e '%(alpha[i-1,j-1],)) # no comma + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Beta matrix (p x (nu x m))\n') + fid.write('File '+name+'.beta.txt \n') + for i in range(1,no+1): + for j in range(1,nu+1): + fid.write(' ') + for k in range(1,ni+1): + if k<=ni-1: + fid.write('%e , '%(beta[j-1,k-1,i-1],)) + else: + fid.write('%e '%(beta[j-1,k-1,i-1],)) + fid.write('\n') + fid.write('End File \n') + fid.write('\n') + fid.write('! Gamma vector (p x 1)\n') + fid.write('File '+name+'.gamma.txt \n') + for i in range(1,no+1): + fid.write('%e \n'%(gamma[i-1],)) + fid.write('End File \n') + fid.close() + + msg = 'Created ARX (Auto-Regressive eXogenous inputs) Model: sysa.apm' + print(msg) + + return ypred+y_ss + + +def plotter(y, subplots=1, save=False, filename='solution', format='png'): + ''' + The plotter will go through each of the variables in the output y and + create plots for them. The number of vertical subplots can be + specified and the plots can be saved in the same folder. + + This functionality is dependant on matplotlib, so this library must + be installed on the computer for the automatic plotter to work. + + The input y should be the output from the apm solution. This can be + retrieved from the server using the following line of code: + y = get_solution(server, app) + ''' + try: + import matplotlib.pyplot as plt + var_size = len(y) + colors = ['r-', 'g-', 'k-', 'b-'] + color_pick = 0 + if subplots > 9: + subplots = 9 + j = 1 + pltcount = 0 + start = True + for i in range(var_size): + if list(y)[i] != 'time' and list(y)[i][:3] != 'slk': + if j == 1: + if start != True: + plt.xlabel('time') + start = False + if save: + if pltcount != 0: + plt.savefig(filename + str(pltcount) + '.' + format, format=format) + pltcount += 1 + plt.figure() + else: + plt.gca().axes.get_xaxis().set_ticklabels([]) + plt.subplot(100*subplots+10+j) + plt.plot(y['time'], y[list(y)[i]], colors[color_pick], linewidth=2.0) + if color_pick == 3: + color_pick = 0 + else: + color_pick += 1 + plt.ylabel(list(y)[i]) + if subplots == 1: + plt.title(list(y)[i]) + if j == subplots or i+2 == var_size: + j = 1 + else: + j += 1 + plt.xlabel('time') + if save: + plt.savefig('plots/' + filename + str(pltcount) + '.' + format, format=format) + if pltcount <= 20: + plt.show() + except ImportError: + print('Dependent Packages not imported.') + print('Please install matplotlib package to use plotting features.') + except: + print('Graphs not created. Double check that the') + print('simulation/optimization was succesfull') + +# This code adds back compatibility with previous versions + +apm = cmd +apm_load = load_model +csv_load = load_data +apm_ip = get_ip +apm_sol = get_solution +apm_get = get_file +apm_option = set_option +apm_web = web +apm_web_var = web_var +apm_web_root = web_root +apm_info = classify +apm_tag = get_attribute +apm_meas = load_meas +apm_solve = solve From 0066556a7713daa3447183b2f6d29b5d006ec3ee Mon Sep 17 00:00:00 2001 From: APMonitor Date: Mon, 20 Aug 2018 07:37:48 -0600 Subject: [PATCH 07/10] APMonitor for Python 3.7 --- README | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README b/README index aef3413..0351df7 100644 --- a/README +++ b/README @@ -2,4 +2,6 @@ APMonitor Optimization Suite The APMonitor Modeling Language is optimization software for mixed-integer and differential algebraic equations. It is coupled with large-scale solvers for linear, quadratic, nonlinear, and mixed integer programming (LP, QP, NLP, MILP, MINLP). Modes of operation include data reconciliation, real-time optimization, dynamic simulation, and nonlinear predictive control. It is freely available through MATLAB, Python, or from a web browser interface. -One version (apm.py) now covers Python 2.7 and Python 3+. +Compatible with Python 2.7 and Python 3+. + +The APMonitor has a newer interface through the [GEKKO Optimization Suite](https://gekko.readthedocs.io/en/latest/). See a [list of 18 examples](https://apmonitor.com/wiki/index.php/Main/GekkoPythonOptimization) to get started with GEKKO. From 6efd1afaff5b1800fb1fc7780cbd5b275d0513f3 Mon Sep 17 00:00:00 2001 From: APMonitor Date: Mon, 20 Aug 2018 07:38:56 -0600 Subject: [PATCH 08/10] APMonitor for Python 3.7 --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From c971251de5adc63b33a281f4d29702c5c9d0318f Mon Sep 17 00:00:00 2001 From: APMonitor Date: Thu, 14 May 2020 07:04:00 -0600 Subject: [PATCH 09/10] Update README.txt --- example_nlc/README.txt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/example_nlc/README.txt b/example_nlc/README.txt index 2be8707..4f36fc2 100644 --- a/example_nlc/README.txt +++ b/example_nlc/README.txt @@ -1,13 +1,11 @@ -1. Download and extract python_apm.zip archive to your computer. +1. Download the archive and extract to your computer. -2. Run apm.py script +2. Run nlc.py script The script will load model.apm, model.csv, and other relevant information to the server. On the first and last cycles, the web viewer will be opened to visualize the solutions. -3. You can send feedback or questions to: +3. There is a newer version of the APM Python package called gekko. This Model Predictive Control example is in Python Gekko and APM Python at https://apmonitor.com/wiki/index.php/Main/Control with more information about MPC. - support@apmonitor.com +4. More information on gekko is at https://gekko.readthedocs.io/en/latest/ - or join the APM discussion group: - - https://groups.google.com/forum/#!forum/apmonitor +5. Post questions to StackOverflow with tag [gekko]: https://stackoverflow.com/questions/tagged/gekko From b6e1f1d75bde52766b3707628467554932cc8e2c Mon Sep 17 00:00:00 2001 From: APMonitor Date: Thu, 14 May 2020 07:04:46 -0600 Subject: [PATCH 10/10] Update README.txt --- example_nlc/README.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/example_nlc/README.txt b/example_nlc/README.txt index 4f36fc2..5cdc6e7 100644 --- a/example_nlc/README.txt +++ b/example_nlc/README.txt @@ -4,8 +4,11 @@ The script will load model.apm, model.csv, and other relevant information to the server. On the first and last cycles, the web viewer will be opened to visualize the solutions. -3. There is a newer version of the APM Python package called gekko. This Model Predictive Control example is in Python Gekko and APM Python at https://apmonitor.com/wiki/index.php/Main/Control with more information about MPC. +3. There is a newer version of the APM Python package called gekko. This Model Predictive Control +example is in Python Gekko and APM Python at https://apmonitor.com/wiki/index.php/Main/Control +with more information about MPC. 4. More information on gekko is at https://gekko.readthedocs.io/en/latest/ -5. Post questions to StackOverflow with tag [gekko]: https://stackoverflow.com/questions/tagged/gekko +5. Post questions to StackOverflow with tag [gekko]: + https://stackoverflow.com/questions/tagged/gekko