Skip to content
Bryan Lawrence edited this page May 16, 2016 · 19 revisions

Constraints

The ES-DOC meta-programming ontology schema formalism (as applied to CIM v1 & CIM v2) supports the notion of constraints. A constraint applies to a named property with in a class definition. Each declared constraint mutates either the class interface, the instance constructor, and / or instance validation.

Regular Expression

A class property value can be constrained using a regular expression. Downstream applications should enforce the constraint on instances.

'constraint':[
    ('regex', 'property-name', 'a-regular-expression') 
 ] 

For example

'property': [
     ('latitude', 'int', '1.1', "Integer latitude")     
]

could be constrained like this:

'constraint' : [
     ('regex','latitude','[0-8]?[0-9]')
]

Closed Enumerations

By default a class property value linked to an enumeration has an open enumeration:

'properties':[
    ('property-name', 'enumeration', '1.1', 'some enumerated property') 
 ] 

To constrain the the enumeration to be closed the following constraint can be defined:

'constraints':[
    ('enum-extensibilty', 'property-name', False)
]

(NB: An alternative formalism exploiting a modification of the cardinality was discussed - see the section on cardinality on the Properties page, it might be that version will be preferred.)

Cardinality

The cardinality of a class property is defined as part of the property definition:

'properties':[
    ('property-name', 'some_type', '0.N', 'some interesting property') 
 ] 

Sub-classes can constrain the cardinality definition in order to 'remove' the property:

'constraints':[
     ('cardinality','property-name', '0.0')
]

Sub-classes can constrain the cardinality definition in order to mark the property as required:

 'constraints':[
     ('cardinality', 'property-name', '1.N')
 ]

Constant Value

In some cases a sub-class can constrain a super-class by imposing a value on the property. This is most likely to happen in an extension vocabulary (which will generally use a different, more compact, syntax), so we don't expect to see this often (if at all) in the main schema. However, the logic needs to be supported since in principle any extension vocabulary could be written using the main schema formalism, so it is defined here.

Consider a property with, for example, a name which we want to constrain in the sub-class

'properties':[
    ('name','str','0.1','the name of something, e.g. a scientific process') 
 ] 

It could be constrained in the sub-class as follows:

'constraints':[
    ('constant', 'name', 'Cloud Physics')
]

When the parent property can have multiple values as in this case:

'properties':[
    ('processes','science.process','0.N','The set of simulated processes') 
 ] 

the situation is more complicated, since we might either want to fix the list of possible values, or we might still want it to be extensible. These two options are supported with the following alternate constraint declarations:

'constraints':[
    ('constant', 'process', ['cmip6.clouds','cmip6.radiation'])
]

(the default is that these are extensible), but if not

'constraints':[
    ('constant', 'process', ['cmip6.clouds','cmip6.radiation'], 'extensibility', False)
]

(NB: In this example cmip6.clouds and cmip6.radiation would have to be cmip6 extension subclasses of science.process declared and defined elsewhere.)

These last two examples could be dealt with by additional constraints on cardinality, but that seems clumsy!

Mutually Exclusive

Where there are multiple properties defined, but only one should be selected, then we use the mutually-exclusive syntax, which is best shown with an example:

 def initialisation_requirement():
    """ A requirement on how a particular simulation should be initialised """
    return {
       'type': 'class',
       'base': 'designing.numerical_requirement',
       'is_abstract': False,
       'properties': [
           ('initialise_from_experiment', 'designing.numerical_experiment', '0.1',
               'This experiment should be initialised from the output of this experiment'),
           ('initialise_from_data', 'data.dataset','0.1',
               'Initialisation should use this primary dataset'),
           ('branch_time_in_initialisation_source', 'time.datetime', '0.1',
               'If appropriate,  the time in the initialisation_source (whether observed or simulated)')
       ],
       'constraints': [
           ('mutually-exclusive', ('initialise_from_experiment', 'initialise_from_data'), '1.1')
       ]
   }

This indicates that only one of the two properties initialise_from_experiment or initialise_from_data can be selected, but one must be selected. If the 1.1 in the constraint tuple had been a 0.1 we would have been indicating that only one of them can be selected, but none could be selected. Any valid cardinality can appear in this situation.