1 |
/* ASCEND modelling environment |
2 |
Copyright (C) 2006 Carnegie Mellon University |
3 |
|
4 |
This program is free software; you can redistribute it and/or modify |
5 |
it under the terms of the GNU General Public License as published by |
6 |
the Free Software Foundation; either version 2, or (at your option) |
7 |
any later version. |
8 |
|
9 |
This program is distributed in the hope that it will be useful, |
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
GNU General Public License for more details. |
13 |
|
14 |
You should have received a copy of the GNU General Public License |
15 |
along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 |
*/ |
17 |
|
18 |
#include <stdio.h> |
19 |
#include <string.h> |
20 |
|
21 |
#include <ascend/general/platform.h> |
22 |
#include <ascend/general/panic.h> |
23 |
#include <ascend/utilities/error.h> |
24 |
|
25 |
|
26 |
#include <ascend/compiler/child.h> |
27 |
#include <ascend/general/list.h> |
28 |
#include <ascend/compiler/module.h> |
29 |
#include <ascend/compiler/childinfo.h> |
30 |
#include <ascend/compiler/parentchild.h> |
31 |
#include <ascend/compiler/slist.h> |
32 |
#include <ascend/compiler/type_desc.h> |
33 |
#include <ascend/compiler/packages.h> |
34 |
#include <ascend/compiler/symtab.h> |
35 |
#include <ascend/compiler/instquery.h> |
36 |
#include <ascend/compiler/instmacro.h> |
37 |
#include <ascend/compiler/instance_types.h> |
38 |
|
39 |
#include <ascend/compiler/extfunc.h> |
40 |
|
41 |
#include "dr.h" |
42 |
|
43 |
#define DATAREADER_DEBUG 0 |
44 |
|
45 |
/*------------------------------------------------------------------------------ |
46 |
GLOBALS |
47 |
*/ |
48 |
|
49 |
static symchar *dr_symbols[3]; |
50 |
#define FILENAME_SYM dr_symbols[0] |
51 |
#define FORMAT_SYM dr_symbols[1] |
52 |
#define PARAM_SYM dr_symbols[2] |
53 |
|
54 |
/*------------------------------------------------------------------------------ |
55 |
BINDINGS FOR THE DATA READER TO THE ASCEND EXTERNAL FUNCTIONS API |
56 |
*/ |
57 |
|
58 |
ExtBBoxInitFunc asc_datareader_prepare; |
59 |
ExtBBoxFunc asc_datareader_calc; |
60 |
ExtBBoxFinalFunc asc_datareader_close; |
61 |
|
62 |
#ifndef ASC_EXPORT |
63 |
# error "Where is ASC_EXPORT?" |
64 |
#endif |
65 |
|
66 |
/** |
67 |
This is the function called from "IMPORT datareader" |
68 |
|
69 |
It sets up the functions in this external function library and tells ASCEND |
70 |
how many inputs and outputs it needs. |
71 |
*/ |
72 |
extern |
73 |
ASC_EXPORT int datareader_register(){ |
74 |
const char *help = "The is the ASCEND Data Reader, for pulling in" |
75 |
" time-series data such as weather readings for use in simulations."; |
76 |
|
77 |
int result = 0; |
78 |
|
79 |
ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Initialising data reader...\n"); |
80 |
|
81 |
/* (void)CONSOLE_DEBUG("EVALUATION FUNCTION AT %p",asc_datareader_calc); */ |
82 |
|
83 |
result += CreateUserFunctionBlackBox("datareader" |
84 |
, asc_datareader_prepare |
85 |
, asc_datareader_calc /* value */ |
86 |
, asc_datareader_calc /* deriv */ |
87 |
, NULL /* deriv2 */ |
88 |
, asc_datareader_close /* final */ |
89 |
, 1,5 /* inputs, outputs */ |
90 |
, help |
91 |
, 0.0 |
92 |
); /* returns 0 on success */ |
93 |
|
94 |
if(result){ |
95 |
ERROR_REPORTER_HERE(ASC_PROG_NOTE,"CreateUserFunction result = %d\n",result); |
96 |
} |
97 |
return result; |
98 |
} |
99 |
|
100 |
/** |
101 |
This function prepares the data that we will use before starting the solver |
102 |
process. |
103 |
*/ |
104 |
int asc_datareader_prepare(struct BBoxInterp *slv_interp, |
105 |
struct Instance *data, |
106 |
struct gl_list_t *arglist |
107 |
){ |
108 |
struct Instance *fninst, *fmtinst, *parinst; |
109 |
const char *fn, *fmt, *par; |
110 |
DataReader *d; |
111 |
char *partok = NULL; //token parser string for initialising datareader |
112 |
int noutputs; //number of outputs as per the arg file |
113 |
|
114 |
dr_symbols[0] = AddSymbol("filename"); |
115 |
dr_symbols[1] = AddSymbol("format"); |
116 |
dr_symbols[2] = AddSymbol("parameters"); |
117 |
|
118 |
/* get the data file name (we will look for this file in the ASCENDLIBRARY path) */ |
119 |
fninst = ChildByChar(data,FILENAME_SYM); |
120 |
if(!fninst){ |
121 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
122 |
,"Couldn't locate 'filename', please check Data Reader usage." |
123 |
); |
124 |
return 1; |
125 |
} |
126 |
if(InstanceKind(fninst)!=SYMBOL_CONSTANT_INST){ |
127 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"'filename' must be a symbol_constant"); |
128 |
return 1; |
129 |
} |
130 |
fn = SCP(SYMC_INST(fninst)->value); |
131 |
#if DATAREADER_DEBUG |
132 |
CONSOLE_DEBUG("FILENAME: %s",fn); |
133 |
#endif |
134 |
if(fn==NULL || strlen(fn)==0){ |
135 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"'filename' is NULL or empty"); |
136 |
return 1; |
137 |
} |
138 |
|
139 |
/* get the data reader format *//** |
140 |
This is the function called from "IMPORT extfntest" |
141 |
|
142 |
It sets up the functions in this external function library |
143 |
*/ |
144 |
|
145 |
fmtinst = ChildByChar(data,FORMAT_SYM); |
146 |
if(!fmtinst){ |
147 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
148 |
,"Couldn't locate 'format', please check Data Reader usage." |
149 |
); |
150 |
return 1; |
151 |
} |
152 |
if(InstanceKind(fmtinst)!=SYMBOL_CONSTANT_INST){ |
153 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"'format' must be a symbol_constant"); |
154 |
return 1; |
155 |
} |
156 |
fmt = SCP(SYMC_INST(fmtinst)->value); |
157 |
#if DATAREADER_DEBUG |
158 |
CONSOLE_DEBUG("FORMAT: %s",fmt); |
159 |
#endif |
160 |
if(fmt==NULL || strlen(fmt)==0){ |
161 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"'format' is NULL or empty"); |
162 |
return 1; |
163 |
} |
164 |
/* get the datareader parameters. Ascend syntax is |
165 |
parameters :== 'col1:interp1,col2,interp2,..,coln:interpn'; |
166 |
where coln is the data file column assigned to corresponding variable |
167 |
interpn is the algorithm used to interpret that data column |
168 |
*/ |
169 |
parinst = ChildByChar(data,PARAM_SYM); |
170 |
if(!fninst){ |
171 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
172 |
,"Couldn't locate 'parameters', please check Data Reader usage." |
173 |
); |
174 |
return 1; |
175 |
} |
176 |
if(InstanceKind(parinst)!=SYMBOL_CONSTANT_INST){ |
177 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"'parameters' must be a symbol_constant"); |
178 |
return 1; |
179 |
} |
180 |
par = SCP(SYMC_INST(parinst)->value); |
181 |
if(par==NULL || strlen(par)==0){ |
182 |
ERROR_REPORTER_HERE(ASC_USER_ERROR,"'parameters' is NULL or empty"); |
183 |
return 1; |
184 |
} |
185 |
|
186 |
/* obtain number of outputs from the paramater statement */ |
187 |
/* this enables to create a datareader object with the right size parameter tokens */ |
188 |
/* |
189 |
note: the reason for this string manipulations is that par is pointing directly |
190 |
to the instance of the model parinst->value. Any string operations that are not |
191 |
read only will affect this address, potentially leaving a NULL pointer in the |
192 |
instance, or the datareader structure. even if passed down it wont be passed |
193 |
by value, but by pointer, so other functions (such as datareader_set_parameters) |
194 |
might affect this address, potentially causing a seg fault. |
195 |
|
196 |
*/ |
197 |
const char *par2[strlen(par)]; //allocate enough space for a copy of par |
198 |
strcpy(par2,par); //take a copy of par an |
199 |
|
200 |
/*datareader only! in rigour nouputs has to be derived by more |
201 |
explicit methods, such as parsing or argument passing*/ |
202 |
noutputs = gl_length(arglist)-1; |
203 |
|
204 |
/* create the data reader: tell it the filename and nouputs */ |
205 |
d = datareader_new(fn,noutputs); |
206 |
//set datareader file format |
207 |
if(fmt!=NULL){ |
208 |
if(datareader_set_format(d,fmt)){ |
209 |
CONSOLE_DEBUG("Invalid 'format'"); |
210 |
return 1; |
211 |
} |
212 |
} |
213 |
//initialise datareader object |
214 |
if(datareader_init(d)){ |
215 |
CONSOLE_DEBUG("Error initialising data reader"); |
216 |
return 1; |
217 |
} |
218 |
//asign user defined parameters |
219 |
if(par2!=NULL){ |
220 |
if(datareader_set_parameters(d,par2)){ |
221 |
CONSOLE_DEBUG("failed to set parameters"); |
222 |
return 1; |
223 |
} |
224 |
} |
225 |
|
226 |
CONSOLE_DEBUG("Created data reader at %p...",d); |
227 |
/*assign the succesfully created datareader object to the |
228 |
BlackBox Cache of the relation */ |
229 |
slv_interp->user_data = (void *)d; //BROKEN AT THE MOMENT |
230 |
return 0; |
231 |
} |
232 |
|
233 |
/* return 0 on success */ |
234 |
int asc_datareader_calc(struct BBoxInterp *slv_interp, |
235 |
int ninputs, int noutputs, |
236 |
double *inputs, double *outputs, |
237 |
double *jacobian |
238 |
){ |
239 |
DataReader *d; |
240 |
d = (DataReader *)slv_interp->user_data; |
241 |
if(!d){ |
242 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
243 |
,"Datareader was not initialised successfully" |
244 |
); |
245 |
return 1; |
246 |
} |
247 |
|
248 |
if(ninputs!=datareader_num_inputs(d)){ |
249 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
250 |
,"Invalid number of inputs, expected %d but received %d" |
251 |
,datareader_num_inputs(d), ninputs |
252 |
); |
253 |
return 1; |
254 |
} |
255 |
|
256 |
if(noutputs!=datareader_num_outputs(d)){ |
257 |
ERROR_REPORTER_HERE(ASC_USER_ERROR |
258 |
,"Invalid number of outputs, expected <=%d but received %d" |
259 |
,datareader_num_outputs(d), noutputs |
260 |
); |
261 |
//return 1; warn about incompatibility but keep going ...JZap |
262 |
} |
263 |
|
264 |
#if DATAREADER_DEBUG |
265 |
for(i=0; i< ninputs; ++i){ |
266 |
CONSOLE_DEBUG("inputs[%d] = %f", i, inputs[i]); |
267 |
} |
268 |
#endif |
269 |
|
270 |
switch(slv_interp->task){ |
271 |
case bb_func_eval: |
272 |
#if DATAREADER_DEBUG |
273 |
CONSOLE_DEBUG("DATA READER EVALUATION"); |
274 |
#endif |
275 |
if(datareader_func(d,inputs,outputs)){ |
276 |
CONSOLE_DEBUG("Datareader evaluation error"); |
277 |
return 1; |
278 |
} |
279 |
#if DATAREADER_DEBUG |
280 |
for(i=0; i< noutputs; ++i){ |
281 |
CONSOLE_DEBUG("outputs[%d] = %f", i, outputs[i]); |
282 |
} |
283 |
#endif |
284 |
return 0; /* success */ |
285 |
case bb_deriv_eval: |
286 |
#if DATAREADER_DEBUG |
287 |
CONSOLE_DEBUG("DATA READER DERIVATIVE"); |
288 |
#endif |
289 |
if(datareader_deriv(d,inputs,outputs)){ |
290 |
CONSOLE_DEBUG("Datareader derivative evaluation error"); |
291 |
return 1; |
292 |
} |
293 |
return 0; /* success */ |
294 |
default: |
295 |
CONSOLE_DEBUG("UNHANDLED REQUEST"); |
296 |
return 1; |
297 |
} |
298 |
} |
299 |
|
300 |
void asc_datareader_close(struct BBoxInterp *slv_interp){ |
301 |
CONSOLE_DEBUG("NOT IMPLEMENTED"); |
302 |
} |