MoDeNa  1.0
Software framework facilitating sequential multi-scale modelling
model.c
1 /*
2 
3  ooo ooooo oooooooooo. ooooo ooo
4  `88. .888' `888' `Y8b `888b. `8'
5  888b d'888 .ooooo. 888 888 .ooooo. 8 `88b. 8 .oooo.
6  8 Y88. .P 888 d88' `88b 888 888 d88' `88b 8 `88b. 8 `P )88b
7  8 `888' 888 888 888 888 888 888ooo888 8 `88b.8 .oP"888
8  8 Y 888 888 888 888 d88' 888 .o 8 `888 d8( 888
9  o8o o888o `Y8bod8P' o888bood8P' `Y8bod8P' o8o `8 `Y888""8o
10 
11 Copyright
12  2014-2016 MoDeNa Consortium, All rights reserved.
13 
14 License
15  This file is part of Modena.
16 
17  The Modena interface library is free software; you can redistribute it
18  and/or modify it under the terms of the GNU Lesser General Public License
19  as published by the Free Software Foundation, either version 3 of the
20  License, or (at your option) any later version.
21 
22  Modena is distributed in the hope that it will be useful, but WITHOUT ANY
23  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
25  details.
26 
27  You should have received a copy of the GNU General Public License along
28  with Modena. If not, see <http://www.gnu.org/licenses/>.
29 */
30 
31 #include "model.h"
32 #include "structmember.h"
33 #include "global.h"
34 
35 PyObject *modena_SurrogateModel = NULL;
36 
37 void modena_substitute_model_calculate_maps
38 (
40  modena_model_t *parent
41 )
42 {
43  PyObject *pMaps = PyObject_CallMethod
44  (
45  parent->pModel, "calculate_maps", "(O)", sm->model->pModel
46  );
47  if(!pMaps){ Modena_PyErr_Print(); }
48 
49  PyObject *pMapOutputs = PyTuple_GET_ITEM(pMaps, 0); // Borrowed ref
50  if(!pMapOutputs){ Modena_PyErr_Print(); }
51  PyObject *pSeq = PySequence_Fast(pMapOutputs, "expected a sequence");
52  sm->map_outputs_size = PySequence_Size(pMapOutputs);
53  sm->map_outputs = malloc(sm->map_outputs_size*sizeof(double));
54 
55  size_t i;
56  for(i = 0; i < sm->map_outputs_size; i++)
57  {
58  sm->map_outputs[i] = PyInt_AsSsize_t(PyList_GET_ITEM(pSeq, i));
59  }
60  sm->map_outputs_size /= 2;
61  Py_DECREF(pSeq);
62  Py_DECREF(pMapOutputs);
63  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
64 
65  PyObject *pMapInputs = PyTuple_GET_ITEM(pMaps, 1); // Borrowed ref
66  if(!pMapInputs){ Modena_PyErr_Print(); }
67  pSeq = PySequence_Fast(pMapInputs, "expected a sequence");
68  sm->map_inputs_size = PySequence_Size(pMapInputs);
69  sm->map_inputs = malloc(sm->map_inputs_size*sizeof(double));
70  for(i = 0; i < sm->map_inputs_size; i++)
71  {
72  sm->map_inputs[i] = PyInt_AsSsize_t(PyList_GET_ITEM(pSeq, i));
73  }
74  sm->map_inputs_size /= 2;
75  Py_DECREF(pSeq);
76  Py_DECREF(pMapInputs);
77  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
78 
79  Py_DECREF(pMaps);
80 }
81 
82 bool modena_model_read_substituteModels(modena_model_t *self)
83 {
84  //Modena_Info_Print("In %s", __func__);
85 
86  PyObject *pSubstituteModels = PyObject_GetAttrString
87  (
88  self->pModel, "substituteModels"
89  );
90  if(!pSubstituteModels){ Modena_PyErr_Print(); }
91 
92  PyObject *pSeq = PySequence_Fast
93  (
94  pSubstituteModels, "expected a sequence"
95  );
96  self->substituteModels_size = PySequence_Size(pSubstituteModels);
97  self->substituteModels =
98  malloc(self->substituteModels_size*sizeof(modena_substitute_model_t));
99  size_t i;
100  for(i = 0; i < self->substituteModels_size; i++)
101  {
102  PyObject *args = PyTuple_New(0);
103  PyObject *kw = Py_BuildValue
104  (
105  "{s:O}", "model", PyList_GET_ITEM(pSeq, i)
106  );
107 
108  self->substituteModels[i].model = (modena_model_t *) PyObject_Call
109  (
110  (PyObject *) &modena_model_tType,
111  args,
112  kw
113  );
114  Py_DECREF(args);
115  Py_DECREF(kw);
116 
117  if(!self->substituteModels[i].model)
118  {
119  if
120  (
121  PyErr_ExceptionMatches(modena_DoesNotExist)
122  || PyErr_ExceptionMatches(modena_ParametersNotValid)
123  )
124  {
125  PyObject *pModelId =
126  PyObject_GetAttrString(PyList_GET_ITEM(pSeq, i), "_id");
127  if(!pModelId){ Modena_PyErr_Print(); }
128  const char* modelId = PyString_AsString(pModelId);
129  Py_DECREF(pModelId);
130 
131  PyObject *pRet = NULL;
132  if
133  (
134  PyErr_ExceptionMatches(modena_DoesNotExist)
135  )
136  {
137  fprintf
138  (
139  stderr,
140  "Loading model %s failed - Attempting automatic initialisation\n",
141  modelId
142  );
143 
144  pRet = PyObject_CallMethod
145  (
146  modena_SurrogateModel,
147  "exceptionLoad",
148  "(z)",
149  modelId
150  );
151  }
152  else
153  {
154  fprintf
155  (
156  stderr,
157  "Parameters of model %s are invalid - Trying to initialise\n",
158  modelId
159  );
160 
161  pRet = PyObject_CallMethod
162  (
163  modena_SurrogateModel,
164  "exceptionParametersNotValid",
165  "(z)",
166  modelId
167  );
168  }
169 
170  if(!pRet){ Modena_PyErr_Print(); }
171  int ret = PyInt_AsLong(pRet);
172  Py_DECREF(pRet);
173 
174  modena_error_code = ret;
175 
176  Py_DECREF(pSeq);
177  Py_DECREF(pSubstituteModels);
178 
179  return false;
180  }
181  else
182  {
183  Modena_PyErr_Print();
184  return false;
185  }
186  }
187 
188  self->substituteModels[i].inputs = modena_inputs_new
189  (
190  self->substituteModels[i].model
191  );
192 
193  self->substituteModels[i].outputs = modena_outputs_new
194  (
195  self->substituteModels[i].model
196  );
197 
198  modena_substitute_model_calculate_maps
199  (
200  &self->substituteModels[i],
201  self
202  );
203  }
204 
205  Py_DECREF(pSeq);
206  Py_DECREF(pSubstituteModels);
207  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
208 
209  return true;
210 }
211 
212 void modena_model_get_minMax
213 (
214  modena_model_t *self
215 )
216 {
217  PyObject *pObj = PyObject_CallMethod(self->pModel, "minMax", NULL);
218  if(!pObj){ Modena_PyErr_Print(); }
219 
220  PyObject *pMin = PyTuple_GET_ITEM(pObj, 0); // Borrowed ref
221  PyObject *pSeq = PySequence_Fast(pMin, "expected a sequence");
222  self->inputs_internal_size = PySequence_Size(pSeq);
223  self->inputs_min = malloc(self->inputs_internal_size*sizeof(double));
224  size_t i;
225  for(i = 0; i < self->inputs_internal_size; i++)
226  {
227  self->inputs_min[i] = PyFloat_AsDouble(PyList_GET_ITEM(pSeq, i));
228  }
229  Py_DECREF(pSeq);
230  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
231 
232  PyObject *pMax = PyTuple_GET_ITEM(pObj, 1); // Borrowed ref
233  pSeq = PySequence_Fast(pMax, "expected a sequence");
234  self->inputs_max = malloc(self->inputs_internal_size*sizeof(double));
235  for(i = 0; i < self->inputs_internal_size; i++)
236  {
237  self->inputs_max[i] = PyFloat_AsDouble(PyList_GET_ITEM(pSeq, i));
238  }
239  Py_DECREF(pSeq);
240  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
241 
242  PyObject *pinames = PyTuple_GET_ITEM(pObj, 2); // Borrowed ref
243  pSeq = PySequence_Fast(pinames, "expected a sequence");
244  self->inputs_names = malloc(self->inputs_size*sizeof(char*));
245  for(i = 0; i < self->inputs_size; i++)
246  {
247  self->inputs_names[i] =
248  strdup(PyString_AsString(PyList_GET_ITEM(pSeq, i)));
249  }
250  Py_DECREF(pSeq);
251  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
252 
253  PyObject *ponames = PyTuple_GET_ITEM(pObj, 3); // Borrowed ref
254  pSeq = PySequence_Fast(ponames, "expected a sequence");
255  self->outputs_size = PySequence_Size(pSeq);
256  self->outputs_names = malloc(self->outputs_size*sizeof(char*));
257  for(i = 0; i < PySequence_Size(pSeq); i++)
258  {
259  self->outputs_names[i] =
260  strdup(PyString_AsString(PyList_GET_ITEM(pSeq, i)));
261  }
262  Py_DECREF(pSeq);
263  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
264 
265  PyObject *ppnames = PyTuple_GET_ITEM(pObj, 4); // Borrowed ref
266  pSeq = PySequence_Fast(ppnames, "expected a sequence");
267  self->parameters_size = PySequence_Size(pSeq);
268  self->parameters_names = malloc(self->parameters_size*sizeof(char*));
269  for(i = 0; i < PySequence_Size(pSeq); i++)
270  {
271  self->parameters_names[i] =
272  strdup(PyString_AsString(PyList_GET_ITEM(pSeq, i)));
273  }
274  Py_DECREF(pSeq);
275  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
276 
277  Py_DECREF(pObj);
278 }
279 
281 (
282  const char *modelId
283 )
284 {
285  //Modena_Info_Print("In %s", __func__);
286 
287  PyObject *args = PyTuple_New(0);
288  PyObject *kw = Py_BuildValue("{s:s}", "modelId", modelId);
289 
290  PyObject *pNewObj = PyObject_Call
291  (
292  (PyObject *) &modena_model_tType,
293  args,
294  kw
295  );
296 
297  Py_DECREF(args);
298  Py_DECREF(kw);
299  if(!pNewObj)
300  {
301  if
302  (
303  PyErr_ExceptionMatches(modena_DoesNotExist)
304  || PyErr_ExceptionMatches(modena_ParametersNotValid)
305  )
306  {
307  PyErr_Clear();
308 
309  PyObject *pRet = NULL;
310  if
311  (
312  PyErr_ExceptionMatches(modena_DoesNotExist)
313  )
314  {
315  fprintf
316  (
317  stderr,
318  "Loading model %s failed - "
319  "Attempting automatic initialisation\n",
320  modelId
321  );
322 
323  pRet = PyObject_CallMethod
324  (
325  modena_SurrogateModel,
326  "exceptionLoad",
327  "(z)",
328  modelId
329  );
330  }
331  else
332  {
333  fprintf
334  (
335  stderr,
336  "Parameters of model %s are invalid - "
337  "Trying to initialise\n",
338  modelId
339  );
340 
341  pRet = PyObject_CallMethod
342  (
343  modena_SurrogateModel,
344  "exceptionParametersNotValid",
345  "(z)",
346  modelId
347  );
348  }
349 
350  if(!pRet){ Modena_PyErr_Print(); }
351  int ret = PyInt_AsLong(pRet);
352  Py_DECREF(pRet);
353 
354  modena_error_code = ret;
355  return NULL;
356  }
357  else
358  {
359  Modena_PyErr_Print();
360  }
361  }
362 
363  return (modena_model_t *) pNewObj;
364 }
365 
366 size_t modena_model_inputs_argPos(const modena_model_t *self, const char *name)
367 {
368  PyObject *pRet = PyObject_CallMethod
369  (
370  self->pModel,
371  "inputs_argPos",
372  "(z)",
373  name
374  );
375  if(!pRet){ Modena_PyErr_Print(); }
376  size_t argPos = PyInt_AsSsize_t(pRet);
377  Py_DECREF(pRet);
378 
379  if(self->argPos_used)
380  {
381  //Modena_Info_Print
382  //(
383  // "Mark argPos %zu as used from inputs_argPos\n",
384  // argPos
385  //);
386  self->argPos_used[argPos] = true;
387  }
388 
389  return argPos;
390 }
391 
392 size_t modena_model_outputs_argPos(const modena_model_t *self, const char *name)
393 {
394  PyObject *pRet = PyObject_CallMethod
395  (
396  self->pModel,
397  "outputs_argPos",
398  "(z)",
399  name
400  );
401  if(!pRet){ Modena_PyErr_Print(); }
402  size_t ret = PyInt_AsSsize_t(pRet);
403  Py_DECREF(pRet);
404 
405  return ret;
406 }
407 
409 {
410  bool allUsed = true;
411  size_t j = 0;
412 
413  for(j = 0; j < self->inputs_internal_size; j++)
414  {
415  if(!self->argPos_used[j])
416  {
417  //TODO: Replace by call into python
418  //Modena_Info_Print("argPos for %s not used", self->inputs_names[j]);
419  fprintf(stderr, "argPos %zu not used", j);
420  allUsed = false;
421  break;
422  }
423  }
424 
425  if(!allUsed)
426  {
427  fprintf(stderr, "Not all input arguments used - Exiting\n");
428  exit(1);
429  }
430 }
431 
433 {
434  return self->inputs_names;
435 }
436 
438 {
439  return self->outputs_names;
440 }
441 
443 {
444  return self->parameters_names;
445 }
446 
448 {
449  return self->inputs_size;
450 }
451 
453 {
454  return self->outputs_size;
455 }
456 
458 {
459  return self->parameters_size;
460 }
461 
462 int modena_substitute_model_call
463 (
464  const modena_substitute_model_t *sm,
465  const modena_model_t *parent,
466  modena_inputs_t *inputs
467 )
468 {
469  size_t j;
470  for(j = 0; j < sm->map_inputs_size; j++)
471  {
472  /*
473  printf
474  (
475  "i%zu <- ip%zu (%g)\n",
476  sm->map_inputs[2*j+1],
477  sm->map_inputs[2*j],
478  inputs->inputs[sm->map_inputs[2*j]]
479  );
480  */
481  sm->inputs->inputs[sm->map_inputs[2*j+1]] =
482  inputs->inputs[sm->map_inputs[2*j]];
483  }
484 
485  int ret = modena_model_call(sm->model, sm->inputs, sm->outputs);
486  if(ret){ return ret; }
487 
488  for(j = 0; j < sm->map_outputs_size; j++)
489  {
490  /*
491  printf
492  (
493  "ip%zu <- o%zu (%g)\n",
494  sm->map_outputs[2*j+1],
495  sm->map_outputs[2*j],
496  sm->outputs->outputs[sm->map_outputs[2*j]]
497  );
498  */
499  inputs->inputs[sm->map_outputs[2*j+1]] =
500  sm->outputs->outputs[sm->map_outputs[2*j]];
501  }
502 
503  return 0;
504 }
505 
506 int write_outside_point
507 (
508  modena_model_t *self,
509  modena_inputs_t *inputs
510 )
511 {
512  PyObject* pOutside = PyList_New(self->inputs_internal_size);
513 
514  size_t j;
515  for(j = 0; j < self->inputs_internal_size; j++)
516  {
517  PyList_SET_ITEM
518  (
519  pOutside, j, PyFloat_FromDouble(inputs->inputs[j])
520  );
521  }
522 
523  PyObject *pRet = PyObject_CallMethod
524  (
525  self->pModel,
526  "exceptionOutOfBounds",
527  "(O)",
528  pOutside
529  );
530  Py_DECREF(pOutside);
531  if(!pRet){ Modena_PyErr_Print(); }
532  int ret = PyInt_AsLong(pRet);
533  Py_DECREF(pRet);
534 
535  modena_error_code = ret;
536 
537  return ret;
538 }
539 
540 /*
541 modena_model_call returns:
542 
543 201: requesting exit for new DOE without Restart
544 200: requesting exit for new DOE with Restart
545 100: updated model parameters, requesting to continue this run
546 1: failure
547 0: okay
548 
549 If exit is requested, do what's necessary and exit with the same error code!
550 
551 */
553 (
554  modena_model_t *self,
555  modena_inputs_t *inputs,
556  modena_outputs_t *outputs
557 )
558 {
559  if
560  (
561  self->parameters_size == 0
562  && self->parameters_size != self->mf->parameters_size
563  )
564  {
565  return write_outside_point(self, inputs);
566  }
567 
568  size_t j;
569  for(j = 0; j < self->substituteModels_size; j++)
570  {
571  int ret = modena_substitute_model_call
572  (
573  &self->substituteModels[j],
574  self,
575  inputs
576  );
577  if(ret){ return ret; }
578  }
579 
580  for(j = 0; j < self->inputs_internal_size; j++)
581  {
582  /*
583  printf
584  (
585  "j = %zu %g < %g || %g > %g\n",
586  j,
587  inputs->inputs[j],
588  self->inputs_min[j],
589  inputs->inputs[j],
590  self->inputs_max[j]
591  );
592  */
593 
594  if
595  (
596  inputs->inputs[j] < self->inputs_min[j]
597  || inputs->inputs[j] > self->inputs_max[j]
598  )
599  {
600  return write_outside_point(self, inputs);
601  }
602  }
603 
604  self->function
605  (
606  self,
607  inputs->inputs,
608  outputs->outputs
609  );
610 
611  return 0;
612 }
613 
615 (
616  modena_model_t *self,
617  modena_inputs_t *inputs,
618  modena_outputs_t *outputs
619 )
620 {
621  //Modena_Info_Print("In %s", __func__);
622 
623  if
624  (
625  self->parameters_size == 0
626  && self->parameters_size != self->mf->parameters_size
627  )
628  {
629  write_outside_point(self, inputs);
630  }
631 
632  size_t j;
633  for(j = 0; j < self->substituteModels_size; j++)
634  {
635  modena_substitute_model_call
636  (
637  &self->substituteModels[j],
638  self,
639  inputs
640  );
641  }
642 
643  for(j = 0; j < self->inputs_internal_size; j++)
644  {
645  /*
646  printf
647  (
648  "j = %zu %g\n",
649  j,
650  inputs->inputs[j]
651  );
652  */
653  }
654 
655  self->function
656  (
657  self,
658  inputs->inputs,
659  outputs->outputs
660  );
661 }
662 
663 /* Destructor, frees the memory block occupied by a model.
664  */
666 {
667  size_t i;
668  for(i = 0; i < self->substituteModels_size; i++)
669  {
670  Py_XDECREF(self->substituteModels[i].model);
671  modena_inputs_destroy(self->substituteModels[i].inputs);
672  modena_outputs_destroy(self->substituteModels[i].outputs);
673  free(self->substituteModels[i].map_inputs);
674  free(self->substituteModels[i].map_outputs);
675  }
676  free(self->substituteModels);
677 
678  free(self->parameters);
679  free(self->inputs_min);
680  free(self->inputs_max);
681 
682  free(self->argPos_used);
683 
684  if(self->mf)
685  {
686  modena_function_destroy(self->mf);
687  }
688 
689  for(i = 0; i < self->inputs_size; i++)
690  {
691  free(self->inputs_names[i]);
692  }
693  free(self->inputs_names);
694 
695  for(i = 0; i < self->outputs_size; i++)
696  {
697  free(self->outputs_names[i]);
698  }
699  free(self->outputs_names);
700 
701  for(i = 0; i < self->parameters_size; i++)
702  {
703  free(self->parameters_names[i]);
704  }
705  free(self->parameters_names);
706 
707  Py_XDECREF(self->pModel);
708 
709  self->ob_type->tp_free((PyObject*)self);
710 }
711 
712 /* C-Python: Destructor, exposed as __del__ in Python
713  */
714 static void modena_model_t_dealloc(modena_model_t* self)
715 {
716  modena_model_destroy(self);
717 }
718 
719 /* C-Python: Member-Table
720  *
721  * Structure which describes an attribute of a type which corresponds to a C
722  * struct member. Its fields are:
723  *
724  * Field C Type Meaning
725  * ------ ---------- --------------------------------------------------------
726  * name char * name of the member
727  * type int the type of the member in the C struct
728  * offset Py_ssize_t the offset in bytes that the member is located on the
729  * type's object struct
730  * flags int flag bits indicating if the field should be read-only or
731  * writable
732  * doc char * points to the contents of the docstring
733  */
734 static PyMemberDef modena_model_t_members[] = {
735  {"outputs_size", T_PYSSIZET,
736  offsetof(modena_model_t, outputs_size), READONLY , "number of putputs"},
737  {"inputs_size", T_PYSSIZET,
738  offsetof(modena_model_t, inputs_size), READONLY , "number of inputs"},
739  {"parameters_size", T_PYSSIZET,
740  offsetof(modena_model_t, parameters_size), READONLY , "number of parameters"},
741  {NULL} /* Sentinel */
742 };
743 
744 /* C-Python: Method exposed in Python as __call__
745  *
746  * TODO: The method is also exposed as "call", but this should be deprecated
747  */
748 static PyObject *modena_model_t_call
749 (
750  modena_model_t* self,
751  PyObject *args,
752  PyObject *kwds
753 )
754 {
755  //Modena_Info_Print("In %s", __func__);
756 
757  PyObject *pI=NULL, *pCheckBounds=NULL;
758  bool checkBounds = true;
759 
760  static char *kwlist[] = { "inputs", "checkBounds", NULL };
761 
762  if
763  (
764  !PyArg_ParseTupleAndKeywords
765  (
766  args,
767  kwds,
768  "O|O",
769  kwlist,
770  &pI,
771  &pCheckBounds
772  )
773  )
774  {
775  Modena_PyErr_Print();
776  }
777 
778  if(pCheckBounds)
779  {
780  checkBounds = PyObject_IsTrue(pCheckBounds);
781  }
782 
783  if(!PyList_Check(pI))
784  {
785  printf("First argument is not a list\n");
786  return NULL;
787  }
788 
789  PyObject *pSeq = PySequence_Fast(pI, "expected a sequence");
790  size_t len = PySequence_Size(pI);
791 
792  if(len != self->inputs_internal_size)
793  {
794  Py_DECREF(pSeq);
795  printf("input array has incorrect size %zu %zu\n", len, self->inputs_internal_size);
796  return NULL;
797  }
798 
799  modena_inputs_t *inputs = modena_inputs_new(self);
800 
801  size_t j;
802  for(j = 0; j < len; j++)
803  {
804  modena_inputs_set
805  (
806  inputs, j, PyFloat_AsDouble(PyList_GET_ITEM(pSeq, j))
807  );
808  }
809  Py_DECREF(pSeq);
810  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
811 
812  modena_outputs_t *outputs = modena_outputs_new(self);
813 
814  if(checkBounds)
815  {
816  if(modena_model_call(self, inputs, outputs))
817  {
818  modena_inputs_destroy(inputs);
819  modena_outputs_destroy(outputs);
820 
821  PyErr_SetString
822  (
823  modena_OutOfBounds,
824  "Surrogate model is used out-of-bounds"
825  );
826 
827  return NULL;
828  }
829  }
830  else
831  {
832  modena_model_call_no_check(self, inputs, outputs);
833  }
834 
835  PyObject* pOutputs = PyList_New(self->outputs_size);
836  for(j = 0; j < self->outputs_size; j++)
837  {
838  PyList_SET_ITEM
839  (
840  pOutputs, j, PyFloat_FromDouble(modena_outputs_get(outputs, j))
841  );
842  }
843  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
844 
845  modena_inputs_destroy(inputs);
846  modena_outputs_destroy(outputs);
847 
848  return pOutputs;
849 }
850 
851 /* C-Python: Method-Table
852  *
853  * Structure used to describe a method of an extension type. This structure has
854  * four fields:
855  *
856  * Field C Type Meaning
857  * ------- ----------- ----------------------------------------------------
858  * ml_name char * name of the method
859  * ml_meth PyCFunction pointer to the C implementation
860  * ml_flags int flag bits indicating how the call should be
861  * constructed
862  * ml_doc char * points to the contents of the docstring
863  */
864 static PyMethodDef modena_model_t_methods[] = {
865  {"call", (PyCFunction) modena_model_t_call, METH_KEYWORDS,
866  "Call surrogate model and return outputs"
867  },
868  {NULL} /* Sentinel */
869 };
870 
871 /*
872  */
873 PyObject*
874 modena_model_t_get_parameters(modena_model_t *self, void *closure)
875 {
876  PyObject* pParams = PyList_New(self->parameters_size);
877  size_t i;
878  for(i = 0; i < self->parameters_size; i++)
879  {
880  PyList_SET_ITEM(pParams, i, PyFloat_FromDouble(self->parameters[i]) );
881  }
882  return pParams;
883 }
884 
885 /*
886  */
887 static int
888 modena_model_t_set_parameters(modena_model_t *self, PyObject *value, void *closure)
889 {
890  // TODO: Error checks for the following cases:
891  // 1. len(value) == self->parameters_size
892  // 2. type(value) == list or tuple
893  // 3. value != NULL
894 
895  if(self->parameters_size != PySequence_Size(value))
896  {
897  printf
898  (
899  "Wrong number of parameters\n"
900  );
901  exit(1);
902  }
903 
904  /*if (value == NULL)
905  {
906  PyErr_SetString(PyExc_TypeError, "Cannot delete parameter values");
907  return -1;
908  }
909  if (! PyString_Check(value)) {
910  PyErr_SetString(PyErr_TypeError, "First attribute must be a string");
911  return -1;
912  }*/
913 
914  size_t i;
915  for(i = 0; i < self->parameters_size; i++)
916  {
917  self->parameters[i] = PyFloat_AsDouble(PyList_GetItem(value, i));
918  }
919 
920  // PyErr_SetString(PyExc_TypeError, "Attribute is read-only!");
921  return 0;
922 }
923 
924 /* C-Python
925  */
926 static PyGetSetDef modena_model_t_getset[] = {
927  {"parameters",
928  (getter)modena_model_t_get_parameters,
929  (setter)modena_model_t_set_parameters,
930  "parameters",
931  NULL},
932  {NULL} /* Sentinel */
933 };
934 
935 /* C-Python: Initialiser, exposed in Python as the method: __new__
936  */
937 static int modena_model_t_init
938 (
939  modena_model_t *self,
940  PyObject *args,
941  PyObject *kwds
942 )
943 {
944  //Modena_Info_Print("In %s", __func__);
945 
946  PyObject *pParameters=NULL, *pModel=NULL;
947  char *modelId=NULL;
948  size_t i, j;
949 
950  static char *kwlist[] = {"model", "modelId", "parameters", NULL};
951 
952  if
953  (
954  !PyArg_ParseTupleAndKeywords
955  (
956  args,
957  kwds,
958  "|OsO",
959  kwlist,
960  &pModel,
961  &modelId,
962  &pParameters
963  )
964  )
965  {
966  Modena_PyErr_Print();
967  }
968 
969  if(!pModel)
970  {
971  self->pModel = PyObject_CallMethod
972  (
973  modena_SurrogateModel,
974  "load",
975  "(z)",
976  modelId
977  );
978 
979  if(!self->pModel)
980  {
981  PyErr_SetString
982  (
983  modena_DoesNotExist,
984  "Surrogate model does not exist"
985  );
986 
987  return -1;
988  }
989  }
990  else
991  {
992  Py_INCREF(pModel);
993  self->pModel = pModel;
994  }
995 
996  //PyObject_Print(self->pModel, stdout, 0);
997  //printf("\n");
998 
999  // Avoiding double indirection in modena_model_call
1000  // Use modena_function_new to construct, then copy function pointer
1001  self->mf = modena_function_new_from_model(self);
1002  self->function = self->mf->function;
1003 
1004  modena_model_get_minMax(self);
1005 
1006  PyObject *pOutputs = PyObject_GetAttrString(self->pModel, "outputs");
1007  if(!pOutputs){ Modena_PyErr_Print(); }
1008  self->outputs_size = PyDict_Size(pOutputs);
1009  Py_DECREF(pOutputs);
1010 
1011  if(!modena_model_read_substituteModels(self))
1012  {
1013  return -1;
1014  }
1015 
1016  self->argPos_used = malloc(self->inputs_internal_size*sizeof(bool));
1017 
1018  for(j = 0; j < self->inputs_internal_size; j++)
1019  {
1020  self->argPos_used[j] = false;
1021  }
1022 
1023  for(j = 0; j < self->substituteModels_size; j++)
1024  {
1025  modena_substitute_model_t *sm = &self->substituteModels[j];
1026  for(i = 0; i < sm->map_outputs_size; i++)
1027  {
1028  //printf("Mark argPos %zu as used\n", sm->map_outputs[2*i+1]);
1029  self->argPos_used[sm->map_outputs[2*i+1]] = true;
1030  }
1031  }
1032 
1033  if(!pParameters)
1034  {
1035  pParameters = PyObject_GetAttrString(self->pModel, "parameters");
1036  if(!pParameters){ Modena_PyErr_Print(); }
1037  }
1038  else
1039  {
1040  Py_INCREF(pParameters);
1041  }
1042 
1043  PyObject *pSeq = PySequence_Fast(pParameters, "expected a sequence");
1044 
1045  if
1046  (
1047  self->parameters_size == 0
1048  && self->parameters_size != self->mf->parameters_size
1049  )
1050  {
1051  PyObject *args = PyTuple_New(2);
1052  PyObject* str = PyString_FromString
1053  (
1054  "Surrogate model does not have valid parameters"
1055  );
1056  PyTuple_SET_ITEM(args, 0, str);
1057  PyTuple_SET_ITEM(args, 1, self->pModel);
1058 
1059  PyErr_SetObject
1060  (
1061  modena_ParametersNotValid,
1062  args
1063  );
1064 
1065  Py_DECREF(pSeq);
1066  Py_DECREF(pParameters);
1067  return -1;
1068  }
1069 
1070  if(self->parameters_size != PySequence_Size(pParameters))
1071  {
1072  printf
1073  (
1074  "Wrong number of parameters %zu %zu\n",
1075  self->parameters_size,
1076  PySequence_Size(pParameters)
1077  );
1078  exit(1);
1079 
1080  PyObject *args = PyTuple_New(2);
1081  PyObject* str = PyString_FromString
1082  (
1083  "Wrong number of parameters"
1084  );
1085  PyTuple_SET_ITEM(args, 0, str);
1086  PyTuple_SET_ITEM(args, 1, self->pModel);
1087 
1088  PyErr_SetObject
1089  (
1090  modena_ParametersNotValid,
1091  args
1092  );
1093 
1094  Py_DECREF(pSeq);
1095  Py_DECREF(pParameters);
1096  return -1;
1097  }
1098 
1099  self->parameters = malloc(self->parameters_size*sizeof(double));
1100  for(i = 0; i < self->parameters_size; i++)
1101  {
1102  self->parameters[i] = PyFloat_AsDouble(PyList_GET_ITEM(pSeq, i));
1103  }
1104  Py_DECREF(pSeq);
1105  Py_DECREF(pParameters);
1106  if(PyErr_Occurred()){ Modena_PyErr_Print(); }
1107 
1108  return 0;
1109 }
1110 
1111 /* C-Python: Constructor, exposed in Python as the method: __new__
1112  */
1113 static PyObject * modena_model_t_new
1114 (
1115  PyTypeObject *type,
1116  PyObject *args,
1117  PyObject *kwds
1118 )
1119 {
1120  modena_model_t *self;
1121 
1122  self = (modena_model_t *)type->tp_alloc(type, 0);
1123  if(self)
1124  {
1125  // Set everything to zero
1126  self->pModel = NULL;
1127  self->outputs_size = 0;
1128  self->inputs_size = 0;
1129  self->inputs_internal_size = 0;
1130  self->inputs_min = NULL;
1131  self->inputs_max = NULL;
1132  self->argPos_used = NULL;
1133  self->parameters_size = 0;
1134  self->parameters = NULL;
1135  self->mf = NULL;
1136  self->function = NULL;
1137  self->substituteModels_size = 0;
1138  self->substituteModels = NULL;
1139  }
1140 
1141  return (PyObject *)self;
1142 }
1143 
1144 /* C-Python: The C structure used to describe the modena_model type.
1145  */
1146 PyTypeObject modena_model_tType = {
1147  PyObject_HEAD_INIT(NULL)
1148  0, /*ob_size*/
1149  "modena.modena_model_t", /*tp_name*/
1150  sizeof(modena_model_t), /*tp_basicsize*/
1151  0, /*tp_itemsize*/
1152  (destructor)modena_model_t_dealloc, /*tp_dealloc*/
1153  0, /*tp_print*/
1154  0, /*tp_getattr*/
1155  0, /*tp_setattr*/
1156  0, /*tp_compare*/
1157  0, /*tp_repr*/
1158  0, /*tp_as_number*/
1159  0, /*tp_as_sequence*/
1160  0, /*tp_as_mapping*/
1161  0, /*tp_hash */
1162  (ternaryfunc)modena_model_t_call, /*tp_call*/
1163  0, /*tp_str*/
1164  0, /*tp_getattro*/
1165  0, /*tp_setattro*/
1166  0, /*tp_as_buffer*/
1167  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
1168  "modena_model_t objects", /* tp_doc */
1169  0, /* tp_traverse */
1170  0, /* tp_clear */
1171  0, /* tp_richcompare */
1172  0, /* tp_weaklistoffset */
1173  0, /* tp_iter */
1174  0, /* tp_iternext */
1175  modena_model_t_methods, /* tp_methods */
1176  modena_model_t_members, /* tp_members */
1177  modena_model_t_getset, /* tp_getset */
1178  0, /* tp_base */
1179  0, /* tp_dict */
1180  0, /* tp_descr_get */
1181  0, /* tp_descr_set */
1182  0, /* tp_dictoffset */
1183  (initproc)modena_model_t_init, /* tp_init */
1184  0, /* tp_alloc */
1185  modena_model_t_new, /* tp_new */
1186 };
1187 
struct modena_model_t modena_model_t
stores a surrogate model
char ** modena_model_parameters_names(const modena_model_t *self)
Function returning the names of the parameters.
Definition: model.c:442
char ** modena_model_inputs_names(const modena_model_t *self)
Function returning the names of the inputs.
Definition: model.c:432
size_t inputs_size
Definition: model.h:102
size_t modena_model_inputs_argPos(const modena_model_t *self, const char *name)
Function determining position of an argument in the input vector.
Definition: model.c:366
size_t modena_model_inputs_size(const modena_model_t *self)
Function returning the size of the input vector.
Definition: model.c:447
size_t modena_model_outputs_size(const modena_model_t *self)
Function returning the size of the output vector.
Definition: model.c:452
modena_model_t * modena_model_new(const char *modelId)
Function fetching a surrogate model from MongoDB.
Definition: model.c:281
PyObject_HEAD PyObject * pModel
Definition: model.h:98
int modena_model_call(modena_model_t *self, modena_inputs_t *inputs, modena_outputs_t *outputs)
Function calling the surrogate model and checking for errors.
Definition: model.c:553
void modena_model_call_no_check(modena_model_t *self, modena_inputs_t *inputs, modena_outputs_t *outputs)
Function calling the surrogate model w/o checking for errors.
Definition: model.c:615
stores a surrogate model
Definition: model.h:94
void modena_model_destroy(modena_model_t *self)
Function deallocating the memory allocated for the surrogate model.
Definition: model.c:665
size_t modena_model_parameters_size(const modena_model_t *self)
Function returning the size of the parameter vector.
Definition: model.c:457
size_t modena_model_outputs_argPos(const modena_model_t *self, const char *name)
Function determining position of a result in the output vector.
Definition: model.c:392
size_t parameters_size
Definition: model.h:112
size_t outputs_size
Definition: model.h:100
void modena_model_argPos_check(const modena_model_t *self)
Function checking that the user has queried all input positions.
Definition: model.c:408
stores a model and mapping for substitution
Definition: model.h:72
char ** modena_model_outputs_names(const modena_model_t *self)
Function returning the names of the outputs.
Definition: model.c:437