NcmModel tutorial part I

Overview

One of the main objectives in NumCosmo is to provide a complete framework for model fitting. In this tutorial we discuss the framework for model definition. When modeling a given phenomenon we need first to identify which are the quantities concerning the model and to decide how to describe them. In the NumCosmo framework we translate these two aspects into an Object Oriented (OO) way.

The abstract model must describe the fundamental quantities only, the specific descriptions of these quantities are left to the implementations of this abstract model. This split may appear cumbersome but it has many advantages and it potentially decreases the code complexity in the long run. Some of the advantages are:

  • When implementing functions/objects that depend on the abstract model, one can write his code using the abstract interface without worrying about any specific implementation.
  • It forces the programmer/scientist to identify which parts of the model represents its fundamental quantities and which are result of a given choice of implementation.
  • Creates naturally a modular framework, i.e., one can change the specific implementation of a model without changing the overall code.
  • It simplifies collaborations, once the abstract model is agreed on, different groups may work in their implementations independently.

This tutorial teaches how to write an abstract model in C. It is also possible to write models in any language bound by the GObject introspection framework, click here for a list of languages that already support it. We already tested abstract objects in both Python and Perl, and we have planned a tutorial on how to write them in Python.

Our abstract model

In this tutorial we consider the case where we are modeling a phenomenon described by a curve $f(x)$. In this simple example it is pretty clear that the only fundamental quantities are the curve $f(x)$ itself and its domain $[x_l,\; x_u]$. Note that we are not including any specific functional form for $f(x)$ nor any parametrization, these choices are left to specific implementations of our model.

If you are not familiar to the GObject framework, take a look at this tutorial.

To translate this to an OO frame, we will define an abstract object NcCurve (child of NcmModel) to represent the fundamental quantities. The function $f(x)$ is represented by the virtual method called nc_curve_f, and the interval by two properties called “xl” and “xu” used to represent respectively $x_l$ and $x_u$.

Abstract model header

We include the full header below, however, first we are going to highlight the main steps:

Virtual method type

This line determines the prototype of our virtual function. This is not required since one could used this prototype directly at the class struct, however, defining a type improve the readability.

1
 typedef gdouble (*NcCurveF) (NcCurve *curve, const gdouble x); 

Object class

In the GObject framework the virtual functions are defined as function pointers at the class struct, in this simple example there is a single virtual function which we call “f”.

1
2
3
4
5
6
struct _NcCurveClass
{
  /*< private >*/
  NcmModelClass parent_class;
  NcCurveF f;
}; 

Implementation enumerator

The NcmModel) framework is designed to support complex models that may involve several virtual methods. Some of these methods may not be required by every implementation. For this reason in introduced the implementation enumerator, this way, the object that use the model may ask if that model implements a specific virtual method. In our example we have a single virtual method, so the enumerator is given by:

1
2
3
4
typedef enum _NcCurveImpl
{
  NC_CURVE_IMPL_f = 0,
} NcCurveImpl; 

The enumerator above states that the first bit in the implementation enumerator controls if the child implements this method.

Defining the class id prototype

In order to be able to analyze different models, we need an object to aggregate all necessary models. In NumCosmo this task is performed by the NcmMSet (model set) object. We use the NCM_MSET_MODEL_DECLARE_ID macro to declare the id function nc_curve_id in the NcmMSet framework.

1
NCM_MSET_MODEL_DECLARE_ID (nc_curve);

Method prototypes

Finally we declare the two methods necessary to close the implementation (apart from the default ones).

1
2
void nc_curve_set_f_impl (NcCurveClass *curve_class, NcCurveF f);
gdouble nc_curve_f (NcCurve *curve, const gdouble x);

The first one just declare the class method that will be used to set the virtual method “f”. It is necessary because it has two jobs, it sets the function pointer to the member “f” of the class structure and updates the implementation flags to assert that child implements this method.

Abstract model source

We include the full C file below, however, first we are going to highlight the main steps:

Private properties

The first declaration in our code is the definition of the private struct

1
2
3
4
5
struct _NcCurvePrivate
{
  gdouble xl;
  gdouble xu;
};

In principle we could have added these members directly to the NcCurve struct, however, this would allow users to modify it directly. By hiding it in the compilation unit we avoid this, the only way to modify these values will be through the exported methods. This is important since these methods will perform consistency checks and call all necessary hooks that update the model.

As a rule of thumb, if the access to the struct member is not part of computationally heavy part of the code (here these members are likely to be set just once in the beginning and stay constant throughout all computations), then it just me hidden in the private struct for the reasons described above. Nevertheless, if the member will be accessed constantly during the computations we would attribute it to the object struct and include a inline method to access it, this technique will be discussed in detail in a future tutorial.

Property enumerator

The property enumerator is the part of the GObject framework used to define properties. Here the PROP_0 member is required by the GObject implementation and do not correspond to any actual property. The following members PROP_XL and PROP_XU correspond respectively to $x_l$ and $x_u$. Finally, PROP_SIZE is a convenience member (that we use by convention in NumCosmo) used to count the number of properties, in this case PROP_SIZE == 3 (an enumerator always assign 0 to its first member and +1 to the subsequent, this can be changes by explicitly assigning values).

1
2
3
4
5
6
7
enum
{
  PROP_0,
  PROP_XL,
  PROP_XU,
  PROP_SIZE,
};

Object defining macro

The first step in creating the object is to call the GObject macro G_DEFINE_ABSTRACT_TYPE. This macro creates an abstract object (that cannot be instantiated) that is a child of NcmModel).

1
G_DEFINE_ABSTRACT_TYPE (NcCurve, nc_curve, NCM_TYPE_MODEL);

This macro assumes that two functions will be implemented:

  • nc_curve_init: this function should make all necessary initialization to the object that happens before any property is set. This usually consists of assign null or zero value for all struct members.
  • nc_curve_class_init: the class method must define all object’s properties and virtual methods.

Object initialization

In this example, the initialization function nc_curve_init has two tasks, getting the pointer to its private struct and assign the initial values for the properties. Our convention is that if both xl and xu are equal to zero then the object is in the initial state.

1
2
3
4
5
6
7
static void
nc_curve_init (NcCurve *curve)
{
  curve->priv = G_TYPE_INSTANCE_GET_PRIVATE (curve, NC_TYPE_CURVE, NcCurvePrivate);
  curve->priv->xl = 0.0;
  curve->priv->xu = 0.0;
}

Set/Get hooks

In the GObject framework all properties are modified through the set/get hooks. Here we have a two properties, for both we delegate the set/get job to methods implemented below. We could implement it directly inside the enumerators below, however, implementing it in separated functions improve readability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
static void
_nc_curve_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  NcCurve *curve = NC_CURVE (object);
  g_return_if_fail (NC_IS_CURVE (object));

  switch (prop_id)
  {
    case PROP_XL:
      nc_curve_set_xl (curve, g_value_get_double (value));
      break;
    case PROP_XU:
      nc_curve_set_xu (curve, g_value_get_double (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
_nc_curve_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
  NcCurve *curve = NC_CURVE (object);
  g_return_if_fail (NC_IS_CURVE (object));

  switch (prop_id)
  {
    case PROP_XL:
      g_value_set_double (value, nc_curve_get_xl (curve));
      break;
    case PROP_XU:
      g_value_set_double (value, nc_curve_get_xu (curve));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Defining the id function

The NcmMSet framework provides the macro NCM_MSET_MODEL_REGISTER_ID that should be used in the object code to create the nc_curve_id function. This function will return the unique ID defined in the NcmMSet type list.

1
NCM_MSET_MODEL_REGISTER_ID (nc_curve, NC_TYPE_CURVE);

The class initialization hook

Attributing the Set/Get hooks

In the class initialization hook one must define the object related hooks, as the set/get functions. Since we are using the NcmModel) framework, we need to attribute the set/get hooks to the NcmModelClass struct. This is necessary since the framework takes care in organizing all model parameters and only the additional properties are handled by the set/get hooks defined here.

1
2
3
4
5
6
7
  GObjectClass* object_class = G_OBJECT_CLASS (klass);
  NcmModelClass *model_class = NCM_MODEL_CLASS (klass);

  ...
  
  model_class->set_property = &_nc_curve_set_property;
  model_class->get_property = &_nc_curve_get_property;

The next step is to initialize the model parameters.

1
2
ncm_model_class_set_name_nick (model_class, "Curve-f", "NcCurve");
ncm_model_class_add_params (model_class, 0, 0, PROP_SIZE);

These functions set the model name and nick and tells the framework that it requires 0 scalar parameters and 0 vector parameters and it has additionally PROP_SIZE properties. It is usually the case that the abstract model has no parameters, this happens because the parameters are usually connected to the specific implementations and not to the abstract definitions.

Registering the ID in NcmMSet

1
2
3
4
5
6
  ncm_mset_model_register_id (model_class,
                              "NcCurve",
                              "Curve model.",
                              NULL,
                              FALSE,
                              NCM_MSET_MODEL_MAIN);

This functions call takes care in registering the NcCurve type in the NcmMSet type list. This is necessary to set/get objects of this type from a NcmMSet.

Registering the properties

As discussed above, the properties $x_l$ and $x_u$ will not be part of the model parameters and will be taken as fixed numbers, in this case, we include them as usual GObject properties.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  g_object_class_install_property (object_class,
                                   PROP_XL,
                                   g_param_spec_double ("xl",
                                                        NULL,
                                                        "x lower bound",
                                                        -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
  g_object_class_install_property (object_class,
                                   PROP_XU,
                                   g_param_spec_double ("xu",
                                                        NULL,
                                                        "x upper bound",
                                                        -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));

Checking parameters implementation

The function call below asserts that all required scalar and vector parameters were implemented correctly. In this case, none were required.

1
  ncm_model_class_check_params_info (model_class);

Setting a default implementation for the virtual function

This line sets the default implementation for the virtual function “f”.

1
  curve_class = &_nc_curve_f;

Methods

Our implementation of the virtual function “f” here is just a placeholder that raises an error if called.

1
2
3
4
5
6
7
static gdouble 
_nc_curve_f (NcCurve *curve, const gdouble x)
{
  g_error ("nc_curve_f: model `%s' does not implement this function.", 
           G_OBJECT_TYPE_NAME (curve)); 
  return 0.0;
}

We use the NCM_MODEL_SET_IMPL_FUNC to generate the code for the implementation function nc_curve_set_f_impl. This class method, when called by the child implementations will attribute the function to the class struct and enable the implementation flag for this method.

1
NCM_MODEL_SET_IMPL_FUNC (NC_CURVE, NcCurve, nc_curve, NcCurveF, f)

Example sources

Click here to download the header. Click here to download the source.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/***************************************************************************
 *            nc_curve.h
 *
 *  Tue July 11 16:30:36 2017
 *  Copyright  2017  Sandro Dias Pinto Vitenti
 *  <sandro@isoftware.com.br>
 ****************************************************************************/
/*
 * nc_curve.h
 * Copyright (C) 2017 Sandro Dias Pinto Vitenti <sandro@isoftware.com.br>
 *
 * numcosmo is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * numcosmo is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _NC_CURVE_H_
#define _NC_CURVE_H_

#include <glib.h>
#include <glib-object.h>
#include <numcosmo/build_cfg.h>

/*
 * Here we are writing an object external to NumCosmo so we must use
 * the full header numcosmo/numcosmo.h. Otherwise we could include
 * only the actual header we need, i.e.,
 * #include <numcosmo/math/ncm_model.h>
 * 
 */
#include <numcosmo/numcosmo.h>

G_BEGIN_DECLS

/* These are the basic macros useful for the GObject framework */
#define NC_TYPE_CURVE             (nc_curve_get_type ())
#define NC_CURVE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), NC_TYPE_CURVE, NcCurve))
#define NC_CURVE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), NC_TYPE_CURVE, NcCurveClass))
#define NC_IS_CURVE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NC_TYPE_CURVE))
#define NC_IS_CURVE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), NC_TYPE_CURVE))
#define NC_CURVE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), NC_TYPE_CURVE, NcCurveClass))

/* 
 * This is the class struct, it will contains everything that is
 * that is defined across instances.
 * 
 */
typedef struct _NcCurveClass NcCurveClass;

/* 
 * This is the instance struct, it will contains everything pertaining 
 * to a given instance.
 * 
 */
typedef struct _NcCurve NcCurve;

/* 
 * This is the instance private struct, it will contains everything 
 * pertaining that should never be seen outside of the compilation unit.
 * 
 */
typedef struct _NcCurvePrivate NcCurvePrivate;

/*
 * Here we define a type to describe the virtual function which represets
 * the actual f(x).
 * 
 */
typedef gdouble (*NcCurveF) (NcCurve *curve, const gdouble x);

/*
 * The class struct contains just one element `f' that must be assigned
 * by implementations. The first item is just the parent class structure.
 * 
 */
struct _NcCurveClass
{
  /*< private >*/
  NcmModelClass parent_class;
  NcCurveF f;
};

/*
 * No elements needed apart from our private struct.
 * The first item is just the parent object structure.
 * 
 */
struct _NcCurve
{
  /*< private >*/
  NcmModel parent_instance;
  NcCurvePrivate *priv;
};

/**
 * NcCurveImpl:
 * @NC_CURVE_F: The curve function $f(x)$.
 *
 * These flags are used to control which functions each child 
 * implement. In this case the object is very simple and has
 * only one function that may be implement. In more complex cases
 * a child could implement only partially the abstract model
 * and it would the enumerator to inform which functions are
 * actually implement.
 * 
 */
typedef enum _NcCurveImpl
{
  NC_CURVE_IMPL_f = 0,
} NcCurveImpl;

#define NC_CURVE_IMPL_ALL NCM_MODEL_CLASS_IMPL_ALL

GType nc_curve_get_type (void) G_GNUC_CONST;

/*
 * Since this is a abstract model we need to define its ID
 * in order to allow it to be found inside of a #NcmMSet
 * object.
 * 
 */
NCM_MSET_MODEL_DECLARE_ID (nc_curve);

/*
 * This function must be used by implementations to set the virtual function f.
 * 
 */
void nc_curve_set_f_impl (NcCurveClass *curve_class, NcCurveF f);

/* 
 * The default methods that all objects should implement.
 * We also chose to implement a constructor nc_curve_new_from_name()
 * to allow creating any child directly.
 * 
 */
NcCurve *nc_curve_new_from_name (const gchar *curve_name);
NcCurve *nc_curve_ref (NcCurve *curve);
void nc_curve_free (NcCurve *curve);
void nc_curve_clear (NcCurve **curve);

/*
 * Property accessors prototypes.
 * 
 */
void nc_curve_set_xl (NcCurve *curve, const gdouble xl);
void nc_curve_set_xu (NcCurve *curve, const gdouble xu);
gdouble nc_curve_get_xl (NcCurve *curve);
gdouble nc_curve_get_xu (NcCurve *curve);

/*
 * The only quantity calculated by this model is the value
 * of $f(x)$, which is represented by the following virtual 
 * method.
 * 
 */
gdouble nc_curve_f (NcCurve *curve, const gdouble x);

G_END_DECLS

#endif /* _NC_CURVE_H_ */

Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
/***************************************************************************
 *            nc_curve.c
 *
 *  Tue July 11 16:30:26 2017
 *  Copyright  2017  Sandro Dias Pinto Vitenti
 *  <sandro@isoftware.com.br>
 ****************************************************************************/
/*
 * nc_curve.c
 * Copyright (C) 2017 Sandro Dias Pinto Vitenti <sandro@isoftware.com.br>
 *
 * numcosmo is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * numcosmo is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * SECTION:nc_curve
 * @title: NcCurve
 * @short_description: Abstract class for curves!
 *
 * NcCurve is the abstract class designed to include the functions
 * that any simple curve should implement, see NcCurveImpl.
 * Its parent_class is NcmModel.
 * 
 * (This is a gtk-doc comment, which always start with two **)
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif /* HAVE_CONFIG_H */
#include "build_cfg.h"

#include "nc_curve.h"

/*
 * All implementations of NcCurve will required the function interval.
 * We could have let the interval itself as model parameters in order
 * to allow it to be fit too, this approach will be presented in a more
 * advanced example.
 * 
 */
struct _NcCurvePrivate
{
  gdouble xl;
  gdouble xu;
};

/*
 * Properties enumerator, note that we added a last item PROP_SIZE,
 * this is useful to obtain the actual number of properties in the
 * object without hard coding it. PROP_0 is a special property of
 * the GObject system we should always be there.
 * 
 */
enum
{
  PROP_0,
  PROP_XL,
  PROP_XU,
  PROP_SIZE,
};

/*
 * Here we define the object GType using the macro G_DEFINE_ABSTRACT_TYPE.
 * This macro basically defines the function nc_curve_get_type (void) and
 * everything necessary to define an object in the GLib type system. The
 * ABSTRACT version of the macro creates a GType that cannot be instantiated
 * this means that to use this object we *must* define a child. 
 * 
 * The last argument provides the GType of the parent object.
 * 
 */
G_DEFINE_ABSTRACT_TYPE (NcCurve, nc_curve, NCM_TYPE_MODEL);

static void
nc_curve_init (NcCurve *curve)
{
  /* 
   * The first step is the creation of the private structure, all allocation and 
   * de-allocation is automatically performed by the GObject framework.
   */
  curve->priv = G_TYPE_INSTANCE_GET_PRIVATE (curve, NC_TYPE_CURVE, NcCurvePrivate);

  /*
   * Here we initialize all structure member to null/zero.
   * 
   */
  curve->priv->xl = 0.0;
  curve->priv->xu = 0.0;  
}

/*
 * Here we call all property accessors in order to set property values.
 * 
 */
static void
_nc_curve_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  NcCurve *curve = NC_CURVE (object);
  g_return_if_fail (NC_IS_CURVE (object));

  switch (prop_id)
  {
    case PROP_XL:
      nc_curve_set_xl (curve, g_value_get_double (value));
      break;
    case PROP_XU:
      nc_curve_set_xu (curve, g_value_get_double (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

/*
 * Here we call all property accessors in order to get property values.
 * 
 */
static void
_nc_curve_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
  NcCurve *curve = NC_CURVE (object);
  g_return_if_fail (NC_IS_CURVE (object));

  switch (prop_id)
  {
    case PROP_XL:
      g_value_set_double (value, nc_curve_get_xl (curve));
      break;
    case PROP_XU:
      g_value_set_double (value, nc_curve_get_xu (curve));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

/*
 * Here we must de-allocate any memory allocated *inside* gobject framework,
 * i.e., we must unref any outside object contained in our object.
 * 
 * Nothing to do!
 * 
 */
static void
_nc_curve_dispose (GObject *object)
{

  /* 
   * The following comment is always included to remark that at this point the parent
   * method must be called, chaining down the function call, i.e., first we finalize
   * the properties of the child, if any, then the parent and parent's parent, etc.
   * 
   */
  /* Chain up : end */
  G_OBJECT_CLASS (nc_curve_parent_class)->dispose (object);
}

/*
 * Here we must de-allocate any memory allocated *outside* gobject framework.
 * Nothing to do!
 * 
 */
static void
_nc_curve_finalize (GObject *object)
{

  /* 
   * The following comment is always included to remark that at this point the parent
   * method must be called, chaining down the function call, i.e., first we finalize
   * the properties of the child, if any, then the parent and parent's parent, etc.
   * 
   */
  /* Chain up : end */
  G_OBJECT_CLASS (nc_curve_parent_class)->finalize (object);
}

/*
 * Registry the ID in NumCosmo model system.
 * 
 */
NCM_MSET_MODEL_REGISTER_ID (nc_curve, NC_TYPE_CURVE);

/*
 * Prototype of the default implementation.
 * 
 */
static gdouble _nc_curve_f (NcCurve *curve, const gdouble x);

/*
 * At _class_init we will define all properties and parameters we should
 * also include a default implementation for our virtual function `f'.
 * 
 */
static void
nc_curve_class_init (NcCurveClass *klass)
{
  GObjectClass* object_class = G_OBJECT_CLASS (klass);
  NcmModelClass *model_class = NCM_MODEL_CLASS (klass);

  /* 
   * Tells the type system that we have a private struct
   * 
   */
  g_type_class_add_private (klass, sizeof (NcCurvePrivate));

  /*
   * The NcmModel class takes cares of the parameters, thus,
   * the set/get functions above must be set in NcmModelClass
   * structure.
   * 
   */
  model_class->set_property = &_nc_curve_set_property;
  model_class->get_property = &_nc_curve_get_property;

  /*
   * The other functions are the usual GObject hooks and
   * must be assigned to GObjectClass.
   * 
   */  
  object_class->dispose     = &_nc_curve_dispose;
  object_class->finalize    = &_nc_curve_finalize;

  /*
   * First we set the model nick `Curve-f' and its name 
   * 
   */
  ncm_model_class_set_name_nick (model_class, "Curve-f", "NcCurve");

  /*
   * Now we inform that we have 0 scalar parameters and 0 vector parameters.
   * Note that here we should include parameters that *all* implementations
   * would share, here we have none. Finallt, with the last argument we assert
   * that we have PROP_SIZE == 2 additional properties. 
   * 
   */
  ncm_model_class_add_params (model_class, 0, 0, PROP_SIZE);

  /*
   * Next step is to register the object in NumCosmo's model set object class.
   * We don't include a long description (first NULL below). The last arguments
   * FALSE and NCM_MSET_MODEL_MAIN determines that this object in non-stackable
   * and it is a MAIN model, both concepts will be discussed in an advanced example.
   * 
   */
  ncm_mset_model_register_id (model_class,
                              "NcCurve",
                              "Curve model.",
                              NULL,
                              FALSE,
                              NCM_MSET_MODEL_MAIN);

  
  /*
   * The property PROP_XL is allowed in range [-G_MAXDOUBLE, G_MAXDOUBLE]
   * and its default value is 0.0. This property can be set only during
   * the object construction.
   * 
   */
  g_object_class_install_property (object_class,
                                   PROP_XL,
                                   g_param_spec_double ("xl",
                                                        NULL,
                                                        "x lower bound",
                                                        -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
  /*
   * The property PROP_XU is allowed in range [-G_MAXDOUBLE, G_MAXDOUBLE]
   * and its default value is 1.0. This property can be set only during
   * the object construction.
   * 
   */
  g_object_class_install_property (object_class,
                                   PROP_XU,
                                   g_param_spec_double ("xu",
                                                        NULL,
                                                        "x upper bound",
                                                        -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));

  /* 
   * Check for errors in parameters initialization.
   * 
   */
  ncm_model_class_check_params_info (model_class);

  curve_class = &_nc_curve_f;
}

/*
 * Default implementation. It raises an error if the method is called 
 * and the child didn't implement it.
 * 
 */
static gdouble 
_nc_curve_f (NcCurve *curve, const gdouble x)
{
  g_error ("nc_curve_f: model `%s' does not implement this function.", 
           G_OBJECT_TYPE_NAME (curve)); 
  return 0.0;
}

/*
 * This creates a function to be used by the children to
 * implement this method.
 * 
 */
/**
 * nc_curve_set_f_impl: (skip)
 * @curve_class: a #NcCurveClass
 * @f: function $f(x)$
 *
 * Sets the implementation of the curve $f(x)$.
 *
 */
NCM_MODEL_SET_IMPL_FUNC (NC_CURVE, NcCurve, nc_curve, NcCurveF, f)

/*
 * This defines a generic constructor for the subclasses 
 * 
 */ 
/**
 * nc_curve_new_from_name:
 * @curve_name: #NcCurve child type name
 * 
 * Creates a new #NcCurve of the type described by @curve_name.
 * 
 * Returns: (transfer full): a new #NcCurve
 */ 
NcCurve *
nc_curve_new_from_name (const gchar *curve_name)
{
  /* 
   * We use the serialization object to transform a string into an instance.
   * This also allows us to chose the parameters through the string.
   * 
   */
  GObject *obj     = ncm_serialize_global_from_string (curve_name);
  /*
   * This gets the GType of this new instance.
   * 
   */
  GType curve_type = G_OBJECT_TYPE (obj);

  /*
   * Check if the string represent an actual child of #NcCurve.
   * 
   */
  if (!g_type_is_a (curve_type, NC_TYPE_CLUSTER_MASS))
    g_error ("nc_curve_new_from_name: NcCurve `%s' do not descend from `%s'.", 
             curve_name, g_type_name (NC_TYPE_CURVE));

  /*
   * Returns the correct cast.
   * 
   */
  return NC_CURVE (obj);
}

/*
 * The reference increasing function
 * 
 */
/**
 * nc_curve_ref:
 * @curve: a #NcCurve
 * 
 * Increase reference count by one.
 * 
 * Returns: (transfer full): @curve.
 */
NcCurve *
nc_curve_ref (NcCurve *curve)
{
  return g_object_ref (curve);
}

/*
 * The reference decreasing function
 * 
 */
/**
 * nc_curve_free:
 * @curve: a #NcCurve
 * 
 * Decrease reference count by one.
 * 
 */
void 
nc_curve_free (NcCurve *curve)
{
  g_object_unref (curve);
}

/*
 * This function decreases the reference count
 * by one only if *curve != NULL, and in that case
 * it sets *curve to NULL after decreasing the 
 * reference count. It is useful to use these 
 * functions in the dispose hooks.
 * 
 * 
 */
/**
 * nc_curve_clear:
 * @curve: a #NcCurve
 * 
 * Decrease reference count by one if *@curve != NULL
 * and sets @curve to NULL. 
 * 
 */
void 
nc_curve_clear (NcCurve **curve)
{
  g_clear_object (curve);
}

/*
 * Below we implement the accessor functions.
 * 
 */

/**
 * nc_curve_set_xl:
 * @curve: a #NcCurve
 * @xl: new $x$ lower bound
 * 
 * Sets $x$ lower bound to @xl. 
 * 
 */
void 
nc_curve_set_xl (NcCurve *curve, const gdouble xl)
{
  /*
   * Checking if the object is still in the unintitalized state,
   * if that's the case just assign the value to the private
   * struct.
   * 
   */
  if ((curve->priv->xl == 0.0) && (curve->priv->xl == curve->priv->xu))
    curve->priv->xl = xl;
  else
  {
    /*
     * Otherwise assert that xl will be less than xu.
     * 
     */
    g_assert_cmpfloat (xl, <, curve->priv->xu);
    curve->priv->xl = xl;
  }
}

/**
 * nc_curve_set_xu:
 * @curve: a #NcCurve
 * @xu: new $x$ upper bound
 * 
 * Sets $x$ upper bound to @xu. 
 * 
 */
void 
nc_curve_set_xu (NcCurve *curve, const gdouble xu)
{
  /*
   * Checking if the object is still in the unintitalized state,
   * if that's the case just assign the value to the private
   * struct.
   * 
   */
  if ((curve->priv->xl == 0.0) && (curve->priv->xl == curve->priv->xu))
    curve->priv->xu = xu;
  else
  {
    /*
     * Otherwise assert that xl will be less than xu.
     * 
     */
    g_assert_cmpfloat (curve->priv->xl, <, xu);
    curve->priv->xu = xu;
  }
}

/**
 * nc_curve_get_xl:
 * @curve: a #NcCurve
 * 
 * Sets $x$ lower bound. 
 * 
 * Returns: $x_l$.
 */
gdouble 
nc_curve_get_xl (NcCurve *curve)
{
  return curve->priv->xl;
}

/**
 * nc_curve_get_xu:
 * @curve: a #NcCurve
 * 
 * Sets $x$ lower bound. 
 * 
 * Returns: $x_l$.
 */
gdouble 
nc_curve_get_xu (NcCurve *curve)
{
  return curve->priv->xu;
}

/*
 * Finally, we implement the generic caller for the
 * virtual function `f'.
 * 
 */
/**
 * nc_curve_f: (virtual f)
 * @curve: a #NcCurve
 * @x: $x$
 * 
 * Computes $f(x)$.
 * 
 * Returns: the value of $f(x)$.
 */
gdouble 
nc_curve_f (NcCurve *curve, const gdouble x)
{
  /*
   * This function call by pointer guarantees that
   * the correct virtual function will be called.
   * 
   */
  return NC_CURVE_GET_CLASS (curve)->f (curve, x);
}

Tutorial Directory