POSC Specifications
Version 3.0
Epicentre Modeling Methodology

Epicentre Types

This is the non-normative specification of the Epicentre data type. A normative text version is included. The Epicentre data types are defined as meta types. In order to minimize behavioral differences with standard express, the meta path syntax is not used and is not allowed in a conformant Epicentre model. The meta derived element syntax is not used because derived attributes are not used in a compliant Epicentre model.

Changes from DAE V2.2

Type Parameters

The meta type descriptions in the V2.2 DAE Appendix F "Summary of Data Types" document specified a variant of the parameter specification defined here because it allowed optional components to the actual parameter list. For example:

     LOCATION `(' [ constraint ] `,' precision_spec `)'

It was inconsistent for MONEY because it allowed the whole parameter list to be optional but did not allow the individual parameter to be optional.

     MONEY [`(' currency_type `)' ] 

It was also inconsistent for UNSTRUCTURED_3D_TOPOLOGY because it specified an empty parameter list.

     UNSTRUCTURED_3D_TOPOLOGY`(' `)'  

Fortunately, the epicentre_extensions file specifies an empty string for all optional arguments. For example:

     TYPE ndt_location = location('', 10);
     END_TYPE;

Thus, the only effect this will have on our NDT specifications will be to fix ndt_money and ndt_unstructured_grid_3d (not counting any modifications required by other changes).

Interface Changes

The following interface changes have been made with respect to the V2.2 DAE specification. They do not represent a semantic change to the meta type and do not require a change to the DAE.

  1. The daeEtype has been split into components.
  2. Not all daeItypes have been supported.
  3. Units of measure are referenced by relationships rather than by value.
  4. The statistical values have not been included in the leaf frame because they are an interface concept.
  5. The coordinate_system_axis element has been eliminated from geometry leaves in favor of a geometry frame having a LIST of geometry leaves. The list aligns with the order of axes on a coordinate system. This eliminates the requirement to make sure that the specified axis actually corresponds to the coordinate system.
  6. Control frames are not supported because they are an interface concept.
  7. All references to a single index scheme (i.e., ijk = (k-1)*(NI*NJ) + (j-1)*(NI) + i) have been eliminated because the single indexing scheme does not appear to be used anywhere.
  8. The frame type FTK_ARRAY is not supported. This was added to support things (like "list of simple") that are inherently supported by EXPRESS.
  9. The MONEY type has been constrained to a maximum of 10 subvalues and the LOCATION type has been constrained to a maximum of 20 ordinal values. These constraints are not considered to be semantic changes. Rather, they exceed all practical real world constraints.

Semantic Changes

The following semantic changes have been made with respect to the V2.2 DAE specification. These represent a logical change in capability.

  1. SPARSE geometries are not supported nor are NULL values allowed for geometries. Coordinate values are required for all nodes of a grid. Technically, the DAE allowed a subset (via start and count) but the geometry is inherently a mandatory part of the specification. Those special cases where it might have been made some sense to not specify values can now be handled by specifying a special property (e.g., preferred coordinate).
  2. Coordinate_system_axis has been eliminated from POINT, SAMPLE and ELEMENT.
  3. String properties within frames have been constrained to 80 characters.
  4. A mandatory coordinate system element has been added to SAMPLE. That is, SAMPLE is logically a collection of unordered POINT. This change is consistent with existing usages of this type.
  5. The abstract supertype GEOMETRY (unconstrained dimensionality) has been allowed. This is needed for seismic structures that may be either 1D or 2D depending on the acquisition. Along with this, a constraint of "grid1to2d" (i.e., either 1D or 2D) has been added.
  6. All Epicentre spatial meta types assign geometry to nodes and may assign properties to those nodes.
  7. The DAE constraint that prevents MONEY, TIME and TIMESTAMP from being represented in frames has been extended to POINT in order to be consistent.
  8. The content of a frame has been made consistent with the other types in that a type such as integer and real are distinguished from a quantity type. That is, a quantity cannot have alternative representations (e.g., INTEGER, RATIO).
  9. The leaf formula string has been omitted in favor of a simple start/step capability.
  10. A mandatory coordinate system has been added to the CONTOUR representation (i.e., isoline_set). The coordinate system has been constrained to a compound system. The property and unit have been constrained to match a 1D component system and the isolines have been constrained to use the other component system. This change makes the contour representation entirely consistent with the other geometry representations (i.e., you must specify a coherent coordinate system).
  11. LOCATION and GEOMETRY require that the coordinate system utilize the ref_coordinate_sys_constraint instance specified by the meta constraint parameter. This allows the constraint to categorize a broad class of systems (e.g., earth system, capillary pressure system, compaction factor system) with particular dimensionality. The advantage of this is that the coordinate system must only be checked once.

Semantic Additions

The following capabilities have been added with respect to the V2.2 specification:

  1. An imprecise timestamp capability has been added as an alternative to the precise timestamp. Geologic time is one of the imprecise alternatives. Quarters, weeks, day of week and day of year are also allowed. Note that you now have to inspect the values to understand how precise it is.
  2. A degrees/minutes/seconds capability has been added to LOCATION and POINT. This was a major inconsistency (at least to the EPSG) in previous versions because SAMPLE/LINE/SURFACE/VOLUME could utilize a DMS functionality but LOCATION could not.
  3. A grid_1d_equal/unequal capability has been added to geometry. Most knowledge of the grid is retained in the meta type. The exception is refining/coarsening of grids. The refining/coarsening data was not pulled into the meta structures because grid_modification is an association (which implies a network). The meta type structures can support a hierarchy but not a network. Thus, refining/coarsening is considered to be an application concept rather than a data type concept.
  4. A lower bound has been added to the grid axis in order to constrain the allowed index values.
  5. HYPERCELL (4D+) has been added as a new non-abstract subtype in addition to LINE/SURFACE/VOLUME. Note that the DAE has always supported this but there was no type that utilized it.
  6. VOLUME has new constraint values of "unstructured" and "cornerpoint".

Epicentre Model

This section defines parts of a conformant Epicentre model that are required to exist in order to use all of the Epicentre quantity and geometry data types. The simple pattern types are not dependent on the Epicentre Data Model.

 ENTITY COORDINATE_SYSTEM
 ABSTRACT SUPERTYPE OF 
 (
   ONEOF
   (
   simple_coordinate_system,
   compound_coordinate_system
   )
 );
   ref_coordinate_sys_constraint : ref_coordinate_sys_constraint;
 END_ENTITY;

 ENTITY ref_coordinate_sys_constraint;
   identifier : STRING(40);
   axis_type  : SET [0:?] OF coordinate_axis_constraint;
 END_ENTITY;

 ENTITY SIMPLE_COORDINATE_SYSTEM 
 SUBTYPE OF
 (
   coordinate_system
 );
   coordinate_system_axis : SET [0:?] OF coordinate_system_axis;
 END_ENTITY; 

 ENTITY COMPOUND_COORDINATE_SYSTEM 
 SUBTYPE OF
 (
   coordinate_system
 );
   first_system  : simple_coordinate_system;
   second_system : simple_coordinate_system;
 END_ENTITY;

 ENTITY coordinate_system_axis;
   axis_order          : INTEGER;
   constrained_to_unit : OPTIONAL ref_unit_of_measure;
   property_kind       : property_kind;
 END_ENTITY;

 ENTITY VERTEX;
 END_ENTITY; 

 ENTITY coordinate_axis_constraint;
 END_ENTITY;

 ENTITY PROPERTY_KIND;
   quantity_constraint : OPTIONAL ref_quantity_type;
 END_ENTITY;

 ENTITY REF_QUANTITY_TYPE;
   identifier : STRING(40);
   alternative_unit_of_measure : SET [0:?] OF quantity_type_alternative;
 END_ENTITY;

 ENTITY quantity_type_alternative;
   unit_of_measure : ref_unit_of_measure;
 END_ENTITY;

 ENTITY REF_UNIT_OF_MEASURE;
   acronym : STRING(40);
 END_ENTITY;

 ENTITY REF_PROPERTY_SET;
   identifier : STRING(40);
   ref_property_set_type : ref_property_set_type;
   use_limit : OPTIONAL INTEGER;
   component_kind : SET [0:?] OF property_set_composition;
 END_ENTITY;

 ENTITY property_set_composition;
   property_kind : property_kind;
 END_ENTITY;

 ENTITY ref_property_set_type;
   identifier : STRING(40);
 END_ENTITY;

 ENTITY GRID_OR_MESH;
 END_ENTITY; 

 ENTITY GRID_GEOMETRY_BEHAVIOR;
 END_ENTITY; 

 ENTITY REF_CURRENCY_UNIT;
   acronym           : STRING(15);
   smaller_unit      : OPTIONAL ref_currency_unit;
   sub_unit_per_unit : OPTIONAL INTEGER;
 END_ENTITY; 

Conformance Notes:

  1. The only entity names that are required to exist are the ones that are explicitly specified as element types or are explicitly specified within rules and functions that are invoked by the meta type. The other entity names are informative rather than normative. For convenience, entity names that are normative are listed as bold upper case.
  2. No semantic distinction has been made between explicit and inverse relationships. Only the attribute path is normative (i.e., the attribute names are normative).
  3. If a model does not use a particular meta type then any entities or paths that are required exclusively by that meta type are not required.
  4. String attributes that are compared against string constraint parameters may have a length that is smaller than the parameter length. The attribute length must be greater than or equal to the longest actual constraint string for a model but all constraint strings must be less than or equal to the parameter length.
  5. Named defined types can be used instead of the simple types for the entity attributes but the underlying types must be compatible.

Simple Pattern Types

The following simple pattern types define the interrelationship of multiple components of what is logically a single value: complex, rational, ratio, date, time, timestamp, yearmonthinterval, daytimeinterval. These types have a single where rule to test the validity of the type. This is intended to facilitate projecting the logical model to an EXPRESS Version 1 syntax using the attribute replication technique. That is, the validity function calling parameters could be adjusted to conform to the projection technique with the function itself being hand modified to conform to the changes.

 META_TYPE COMPLEX ( precision : INTEGER );
   real_part      : REAL (precision);
   imaginary_part : REAL (precision); 
 WHERE
   is_valid: complex_valid ( precision, real_part, imaginary_part );
 END_META_TYPE;

 META_TYPE RATIONAL;
   numerator   : INTEGER;
   denominator : INTEGER;
 WHERE
   is_valid: rational_valid ( numerator, denominator );
 END_META_TYPE;

 META_TYPE RATIO ( precision : INTEGER );
   numerator    : REAL (precision);
   denominator  : REAL (precision); 
 WHERE
   is_valid: ratio_valid ( precision, numerator, denominator );
 END_META_TYPE;

 META_TYPE DATE
   (*This is intended to conform to the SQL92 specification for DATE.*);
   year  : INTEGER;
   month : INTEGER;
   day   : INTEGER;
 WHERE
   is_valid: date_valid ( year, month, day );
 END_META_TYPE;

 META_TYPE TIME ( precision : INTEGER )
   (*This is intended to conform to the SQL92 specification for TIME.
     The Offset of the time-zone is from Universal Coordinated Time.*);
   hour          : INTEGER;
   minute        : INTEGER;
   second        : REAL ( precision );
   offset_hour   : INTEGER;
   offset_minute : INTEGER;
 WHERE
   is_valid: time_valid ( precision, hour, minute, second, 
                          offset_hour, offset_minute );
 END_META_TYPE;

 META_TYPE TIMESTAMP ( precision : INTEGER )
 (*This is intended to support the reduced precision of the ISO 8601 
 specification for Dates and Dates with Time-of-Day. In addition, it adds 
 support for quarter and geologic age. Not specifying a full precision 
 timestamp (e.g., a quarter or no hour) indicates an imprecise point-in-time 
 rather than a period-of-time.  Information should be specified to the 
 greatest degree of precision that is known. A content_code of "M" with
 all elements specified is intended to conform to the SQL92 specification for 
 TIMESTAMP. The Offset of the time-zone is from Universal Coordinated Time.
 The allowed imprecise combinations for each code are:
  Code  Combination
   G     geologic-age
   C     century-only
   Q     year         quarter-of-year
   M     year         month-of-year   [day-of-month [time-of-day]]
   W     year         week-of-year    [day-of-week  [time-of-day]]
   D     year                         [day-of-year  [time-of-day]]
 where time-of-day = hour [minute [second]] [offset_hour [offset_second]].
 *);
   content_code  : emi_timestamp_content
    (*Indicates the semantic content of elements year, year_part and day.
      G = Year is geologic age in millions of years ago. 
          No other option is allowed.
      C = Year is the whole number Gregorian century with no year 
          (e.g., 19 = somewhere in 1900's).  No other option is allowed.
      D = Year is whole number Gregorian year including century. 
          Day is day-of-year. Year_part is not allowed.
      M = Year is whole number Gregorian year including century.
          Year_part is month-of-year (1 = January, 12 = December).
          Day is day-of-month.
      W = Year is whole number Gregorian year including century.
          Year_part is week-of-year (1 = first week with a Thursday).
          Day is day-of-week (1 = Monday, 7 = Sunday).
      Q = Year is whole number Gregorian year including century.
          Year_part is quarter-of-year (1 = somewhere in Jan, Feb or Mar). *);
   year          : REAL(6);
   year_part     : OPTIONAL INTEGER;
   day           : OPTIONAL INTEGER;
   hour          : OPTIONAL INTEGER; -- 0 or 24 = midnight
   minute        : OPTIONAL INTEGER;
   second        : OPTIONAL REAL ( precision );
   offset_hour   : OPTIONAL INTEGER;
   offset_minute : OPTIONAL INTEGER;
 WHERE
   is_valid: timestamp_valid ( precision, content_code, year,
                               year_part, day, hour, minute,
                               second,  offset_hour, offset_minute );
 END_META_TYPE;

 TYPE emi_timestamp_content = ENUMERATION OF ( G, C, D, M, W, Q );
 END_TYPE;

 META_TYPE YEARMONTHINTERVAL
 (*This is intended to conform to the SQL92 specification of 
   year-month intervals.*);
   years   : INTEGER;
   months  : INTEGER;
 WHERE
   is_valid: yearmonthinterval_valid ( years, months );
 END_META_TYPE;

 META_TYPE DAYTIMEINTERVAL ( precision : INTEGER )
 (*This is intended to conform to the SQL92 specification for 
   day-time intervals.*);
   day    : INTEGER;
   hour   : INTEGER;
   minute : INTEGER;
   second : REAL ( precision );
 WHERE
   is_valid: daytimeinterval_valid (precision, day ,hour, minute, second); 
 END_META_TYPE;

Measured Quantity Types

The following measured quantity types also define the interrelationship of multiple components of what is logically a single value: money, location, quantity, anyquantity, angle. They differ from the simple pattern types in that they require unit of measure information in addition to the numerical values. See the Data Type Summary for a summary of the parameters. These types also have a single where rule to test the validity of the type.

 META_TYPE MONEY ( money_constraint : STRING(80) );
   currency_value : INTEGER;
   currency_unit  : ref_currency_unit;
   subvalue       : OPTIONAL LIST[1:10] OF INTEGER;
 WHERE
   is_valid: money_valid ( money_constraint, currency_value, 
                           currency_unit, subvalue );
 END_META_TYPE;

 META_TYPE LOCATION ( coordinate_constraint : STRING(80); 
                      precision : INTEGER );
   system  : coordinate_system
     (*This is the coordinate system of the coordinates except when Origin is 
       specified. If origin is specified then this is the coordinate system 
       from which the actual coordinate system is created by transformation.*);
   origin  : OPTIONAL vertex
     (*The origin of the coordinates if different from the origin of the 
       coordinate system. Note that specifying this value is a shortcut to 
       creating a new coordinate system with this origin as a transformation 
       parameter from the existing coordinate system to the new system.*);
   coords  : LIST[1:20] OF emi_ordinal (precision)
     (*The list refers to the order of the axes on the coordinate system.*);
 WHERE
   is_valid: location_valid ( coordinate_constraint, precision, 
                              system, origin, coords );
 END_META_TYPE;

 META_TYPE emi_ordinal ( precision : INTEGER );
   ordinal_value : REAL (precision);
   ordinal_unit  : ref_unit_of_measure;
   minute_value  : OPTIONAL INTEGER;
   second_value  : OPTIONAL REAL(precision-5);
 END_META_TYPE;

 META_TYPE QUANTITY ( quantity_type : STRING(80); 
                      precision     : INTEGER );
   quantity_value : REAL (precision);
   quantity_unit  : ref_unit_of_measure; 
 WHERE
   is_valid: quantity_valid ( quantity_type, precision, 
                              quantity_value, quantity_unit );
 END_META_TYPE;

 META_TYPE ANYQUANTITY ( precision : INTEGER );
   quantity_value : REAL ( precision );
   quantity_unit  : ref_unit_of_measure;
   quantity_type  : ref_quantity_type;
 WHERE
   is_valid: anyquantity_valid ( precision, quantity_value, 
                                 quantity_unit, quantity_type );
 END_META_TYPE;

 META_TYPE ANGLE ( quantity_type : STRING(80); 
                   precision     : INTEGER ); 
   quantity_value : REAL (precision);
   quantity_unit  : ref_unit_of_measure;
   minute_value   : OPTIONAL INTEGER;
   second_value   : OPTIONAL REAL(precision-5);
 WHERE
   is_valid: angle_valid ( quantity_type, precision , quantity_value, 
                           quantity_unit, minute_value, second_value ); 
 END_META_TYPE;

Conformance Notes:

  1. Meta types with the prefix emi_ (Epicentre Meta Internal) are intended for internal use to describe the Epicentre types and must not be directly invoked by a conformant model.

Geometry Types

The following geometry types define a somewhat complex interaction of arrays of data that is used to define the geometry and other properties of an object: geometry, point, sample, line, surface, volume, hypercell, . element. The abstract geometry type allows a meta type instance to be any of its subtypes; however, parameters may be used to constrain an instance to be one of a subset of the subtypes. See the Data Type Summary for a summary of the parameters. The following table describes some of the main aggregation concepts used by the geometry types.

Concept Description
Descriptor The descriptors provide a mechanism to implicitly describe the grid indexes of the grid nodes where information will be defined. Basically, the descriptors map the N dimensional grid indexes to the one dimensional list index. The first descriptor specifies the grid index that varies the fastest. For a 2x3 grid with INDEX_J being specified by the first descriptor, the list order will correspond to the following grid nodes: (1,1), (1,2), (1,3), (2,1), (2,1), (2,3). If INDEX_I was specified in the first descriptor then the following list ordered grid nodes would be implied: (1,1), (2,1), (1,2), (2,2), (1,3, (2,3). The descriptor mechanism is mutually exclusive with the index leaf mechanism and is generally used for a dense sampling of the grid.
Index Leaf The index leaves provide a mechanism to explicitly describe the grid indices of the grid nodes where information will be defined. For each index leaf, there can only one index value at each node. The index leaf mechanism is generally used for a sparse sampling of the grid.
Geometry Leaf The geometry leaves define the geometrical coordinates at each specified node of the grid. There will be one leaf for each axis of the coordinate system. The number of axes should normally be greater than or equal to the number of dimensions of the grid. For each geometry leaf, there can only be one ordinate value at each node.
Property Leaf The property leaves define the property values at each specified node of the grid. For each property leaf, there can only one property value at each node.

The unstructured_3d_topology type is a special case that allows an unstructured grid to be defined independent of a volume instance.

 META_TYPE GEOMETRY ( representation_constraint : STRING(80);
                      position_constraint       : STRING(80);
                      coordinate_constraint     : STRING(80); 
                      coordinate_precision      : INTEGER;  
                      property_constraint       : STRING(80);  
                      property_precision        : INTEGER )
 ABSTRACT SUPERTYPE OF 
 (
   ONEOF
   (
   point,
   sample,
   line,
   surface,
   volume,
   hypercell,
   element
   )
 );
 WHERE
   coordinate_precision_valid : coordinate_precision > 0;
   property_precision_valid   : (property_constraint='none') OR 
                                ((property_constraint<>'none') AND 
                                 (property_precision>0));
 END_META_TYPE;

 META_TYPE POINT
 SUBTYPE OF 
 (
   geometry
 );
   location_value : location (coordinate_constraint, coordinate_precision);
   property_value : OPTIONAL SET[1:?] OF emi_point_property (property_precision);
 WHERE
   is_valid: point_property_valid ( representation_constraint, 
                position_constraint, property_constraint, property_value );
 END_META_TYPE;

 META_TYPE emi_point_property ( prop_precision : INTEGER )
 ABSTRACT SUPERTYPE OF 
 (
   ONEOF
   (
   emi_point_integer_value,
   emi_point_real_value,
   emi_point_string_value,
   emi_point_boolean_value,
   emi_point_logical_value,
   emi_point_complex_value, 
   emi_point_rational_value,
   emi_point_ratio_value,
   emi_point_dms_value,
   emi_point_quantity_value,
   emi_point_date_value,
   emi_point_yearmonthinterval_value,
   emi_point_daytimeinterval_value
   )
 );
   kind           : property_kind;
 UNIQUE
   pk : kind;
 END_META_TYPE;

 META_TYPE emi_point_integer_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   integer_value : INTEGER;
 END_META_TYPE;

 META_TYPE emi_point_real_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   real_value : REAL(prop_precision);
 END_META_TYPE;

 META_TYPE emi_point_string_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   string_value : STRING(80);
 END_META_TYPE;

 META_TYPE emi_point_boolean_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   boolean_value : BOOLEAN;
 END_META_TYPE;

 META_TYPE emi_point_logical_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   logical_value : LOGICAL;
 END_META_TYPE; 

 META_TYPE emi_point_complex_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   complex_value : complex(prop_precision);
 END_META_TYPE;  

 META_TYPE emi_point_rational_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   rational_value : rational;
 END_META_TYPE;   

 META_TYPE emi_point_ratio_value
 SUBTYPE OF 
 (
   emi_point_property
 );
   ratio_value : ratio(prop_precision);
 END_META_TYPE;    

 META_TYPE emi_point_dms_value 
 SUBTYPE OF
 (
   emi_point_property
 );
   angle_value : emi_angle_dms(prop_precision)
     (*The value is assumed to be defined in units of angular degree.*);
 END_META_TYPE;   

 META_TYPE emi_point_quantity_value 
 SUBTYPE OF
 (
   emi_point_property
 );
   quantity_value : REAL (prop_precision);
   quantity_unit  : ref_unit_of_measure; 
 WHERE
   is_valid: quantity_valid ( kind.quantity_constraint.identifier, 
             prop_precision, quantity_value, quantity_unit );
 END_META_TYPE;

 META_TYPE emi_point_date_value 
 SUBTYPE OF
 (
   emi_point_property
 );
   date_value : date;
 END_META_TYPE;    

 META_TYPE emi_point_yearmonthinterval_value 
 SUBTYPE OF
 (
   emi_point_property
 );
   yearmonthinterval_value : yearmonthinterval;
 END_META_TYPE;     

 META_TYPE emi_point_daytimeinterval_value 
 SUBTYPE OF
 (
   emi_point_property
 );
   daytimeinterval_value : daytimeinterval(prop_precision);
 END_META_TYPE;

 META_TYPE SAMPLE 
 SUBTYPE OF
 (
   geometry
 );
   tuple_count     : INTEGER;
   system          : coordinate_system
     (*This is the coordinate system of the coordinates except when Origin is 
       specified. If origin is specified then this is the coordinate system 
       from which the actual coordinate system is created by transformation.*);
   origin          : OPTIONAL vertex
     (*The origin of the coordinates if different from the origin of the 
       coordinate system. Note that specifying this value is a shortcut to 
       creating a new coordinate system with this origin as a transformation 
       parameter from the existing coordinate system to the new system.*);
   coordinate_leaf : LIST[1:?] OF emi_geometry_leaf (coordinate_precision)
     (*The list order aligns with the coordinate system axis.
       The underlying LIST aligns the tuples.*);
   property_leaf   : OPTIONAL SET[1:?] OF emi_property_leaf (property_precision)
     (*The underlying LIST aligns the tuples.*);
 WHERE
   is_valid: sample_valid ( representation_constraint, position_constraint,
                            coordinate_constraint, coordinate_precision,
                            property_constraint, property_precision,
                            tuple_count, system, coordinate_leaf, 
                            property_leaf );
 END_META_TYPE;


 META_TYPE LINE 
 SUBTYPE OF
 (
   geometry,
   emi_geometry_grid
 );
   system          : coordinate_system
     (*This is the coordinate system of the coordinates except when Origin is 
       specified. If origin is specified then this is the coordinate system 
       from which the actual coordinate system is created by transformation.*);
   origin          : OPTIONAL vertex
     (*The origin of the coordinates if different from the origin of the 
       coordinate system. Note that specifying this value is a shortcut to 
       creating a new coordinate system with this origin as a transformation 
       parameter from the existing coordinate system to the new system.*);
   coordinate_leaf : LIST[1:?] OF emi_geometry_leaf (coordinate_precision)
     (*The list order aligns with the coordinate system axis.*);
   property_leaf   : OPTIONAL SET[1:?] OF emi_property_leaf (property_precision)
     (*The underlying LIST aligns the tuples.*);
 WHERE
   is_valid: line_valid ( representation_constraint, position_constraint,
                          coordinate_constraint, coordinate_precision,
                          property_constraint, property_precision,
                          tuple_count, grid1, descriptor, index_leaf,
                          system, coordinate_leaf, property_leaf );
 END_META_TYPE;

 META_TYPE SURFACE
 ABSTRACT SUPERTYPE OF 
 (
   ONEOF
   (
   emi_surface_grid,
   emi_isoline_set
   )
 )
 SUBTYPE OF
 (
   geometry
 );
 END_META_TYPE;

 META_TYPE emi_surface_grid 
 SUBTYPE OF
 (
   surface,
   emi_geometry_grid
 );
   system          : coordinate_system
     (*This is the coordinate system of the coordinates except when Origin is 
       specified. If origin is specified then this is the coordinate system 
       from which the actual coordinate system is created by transformation.*);
   origin          : OPTIONAL vertex
     (*The origin of the coordinates if different from the origin of the 
       coordinate system. Note that specifying this value is a shortcut to 
       creating a new coordinate system with this origin as a transformation 
       parameter from the existing coordinate system to the new system.*);
   coordinate_leaf : LIST[1:?] OF emi_geometry_leaf (coordinate_precision)
     (*The list order aligns with the coordinate system axis.*);
   property_leaf   : OPTIONAL SET[1:?] OF emi_property_leaf (property_precision)
     (*The underlying LIST aligns the tuples.*);
 WHERE
   is_valid: surface_grid_valid ( representation_constraint, position_constraint,
                                  coordinate_constraint, coordinate_precision,
                                  property_constraint, property_precision,
                                  tuple_count, grid1, descriptor, index_leaf,
                                  system, coordinate_leaf, property_leaf );
 END_META_TYPE;

 META_TYPE emi_isoline_set 
 SUBTYPE OF
 (
   surface
 );
   system     : coordinate_system
     (*This is the coordinate system of the coordinates. This must be a 
       compound system with the 1D component applying to the isovalue.*);
   kind       : property_kind;
   value_unit : ref_unit_of_measure;
   isoline    : SET [1:?] OF emi_isoline (coordinate_precision, 
                                         property_constraint,property_precision);
 WHERE
   is_valid: isoline_set_valid ( representation_constraint, position_constraint,
                                 coordinate_constraint, system, 
                                 kind, value_unit, isoline );
 END_META_TYPE;

 META_TYPE emi_isoline ( coord_precision : INTEGER;  
                         prop_constraint : STRING(80);  
                         prop_precision  : INTEGER );
   isovalue      : REAL(coord_precision);
   line_geometry : line('grid1d','node','',
                        coord_precision,prop_constraint,
                        prop_precision);
 UNIQUE
   pk : isovalue;
 END_META_TYPE;

 META_TYPE VOLUME 
 SUBTYPE OF
 (
   geometry,
   emi_geometry_grid
 );
   system          : coordinate_system
     (*This is the coordinate system of the coordinates except when Origin is 
       specified. If origin is specified then this is the coordinate system 
       from which the actual coordinate system is created by transformation.*);
   origin          : OPTIONAL vertex
     (*The origin of the coordinates if different from the origin of the 
       coordinate system. Note that specifying this value is a shortcut to 
       creating a new coordinate system with this origin as a transformation 
       parameter from the existing coordinate system to the new system.*);
   coordinate_leaf : LIST[1:?] OF emi_geometry_leaf (coordinate_precision)
     (*The list order aligns with the coordinate system axis.*);
   property_leaf   : OPTIONAL SET[1:?] OF emi_property_leaf (property_precision)
     (*The underlying LIST aligns the tuples.*);
 WHERE
 is_valid: volume_valid ( representation_constraint, position_constraint,
                          coordinate_constraint, coordinate_precision,
                          property_constraint, property_precision,
                          tuple_count, grid1, descriptor, index_leaf,
                          system , coordinate_leaf, property_leaf );
 END_META_TYPE;

 META_TYPE HYPERCELL 
 SUBTYPE OF
 (
   geometry,
   emi_geometry_grid
 );
   system          : coordinate_system
     (*This is the coordinate system of the coordinates except when Origin is 
       specified. If origin is specified then this is the coordinate system 
       from which the actual coordinate system is created by transformation.*);
   origin          : OPTIONAL vertex
     (*The origin of the coordinates if different from the origin of the 
       coordinate system. Note that specifying this value is a shortcut to 
       creating a new coordinate system with this origin as a transformation 
       parameter from the existing coordinate system to the new system.*);
   coordinate_leaf : LIST[1:?] OF emi_geometry_leaf (coordinate_precision)
     (*The list order aligns with the coordinate system axis.*);
   property_leaf   : OPTIONAL SET[1:?] OF emi_property_leaf (property_precision)
     (*The underlying LIST aligns the tuples.*);
 WHERE
   is_valid: hypercell_valid ( representation_constraint, position_constraint,
                               coordinate_constraint, coordinate_precision,
                               property_constraint, property_precision,
                               tuple_count, grid1, descriptor, index_leaf,
                               system , coordinate_leaf, property_leaf );
 END_META_TYPE;

 META_TYPE emi_geometry_leaf ( coord_precision : INTEGER )
 ABSTRACT SUPERTYPE OF
 (
   ONEOF
   (
   emi_geometry_leaf_variable,
   emi_geometry_leaf_equal,
   emi_geometry_leaf_unequal
   )
 );
   ordinal_unit   : ref_unit_of_measure;
 END_META_TYPE;

 META_TYPE emi_geometry_leaf_variable 
 SUBTYPE OF
 (
   emi_geometry_leaf
 );
   ordinal_values : LIST[1:?] OF REAL(coord_precision)
    (*One value per tuple. This is used when the geometry is irregular.*);
 END_META_TYPE;

 META_TYPE emi_geometry_leaf_unequal 
 SUBTYPE OF
 (
   (emi_geometry_leaf
 );
   ordinal_values : LIST[1:?] OF REAL(coord_precision)
     (*One value per index value of one dimension.*);
   unequal_index  : emi_index_type
     (*Specifies the dimension to which these values are held constant.
       For example, INDEX_J would indicate that ordinal_value(1) is held constant
       for all nodes with j=1.*);
 END_META_TYPE;

 META_TYPE emi_geometry_leaf_equal 
 SUBTYPE OF
 (
   emi_geometry_leaf
 )
   (*Defines a simple formula for calculating the values based on the grid index.
     The list corresponds to the grid dimension.
     That is, the ordinal value at a node is 
     "start +step(1)*i +step(2)*j +step(3)*k, etc". Note that specifying a 
     zero step for all but one dimension is a variation of an unequal 
     geometry leaf.*);
   start : REAL(coord_precision);
   step  : LIST[1:15] OF REAL(coord_precision)
   (*Missing values are assumed to be zero.*);
 END_META_TYPE;

 META_TYPE UNSTRUCTURED_3D_TOPOLOGY
 (*This is a special type whose only purpose is to allow the grid 
 to be defined independent of an instance of a VOLUME type. 
 The only real use for this is for grid modification which is currently
 out of scope for a data type.*);
   u3_topology : emi_grid_unstructured_3d;
 END_META_TYPE;

 META_TYPE ELEMENT 
 SUBTYPE OF
 (
   geometry,
   emi_geometry_grid
 );
   geom1             : emi_geom_relationship
     (*The instance that contains the geometrical values associated with grid1.*);
   geom2            : OPTIONAL emi_geom_relationship
     (*The instance that contains the geometrical values associated with grid2.*); 
   grid2            : OPTIONAL emi_grid;
   sparse_flag      : BOOLEAN;
   value_connection : emi_index_type
   (*This is constrained to an element of a grid (e.g., node, edge)*);
   property_leaf    : SET[1:?] OF emi_property_leaf (property_precision);
 WHERE
   is_valid: element_valid ( representation_constraint, position_constraint,
                             coordinate_constraint, coordinate_precision, 
                             property_constraint, property_precision,
                             tuple_count, grid1, geom1, grid2, geom2,
                             sparse_flag , value_connection,
                             descriptor, index_leaf, property_leaf );
 END_META_TYPE;

 META_TYPE emi_geom_relationship;
 (*The three geom_xxxx elements represent a direct relationship to the geometry 
 meta type instance that contains the geometrical values associated with the grid 
 in an element meta type instance.
 Grid_geometry_behavior is intended to be the abstract supertype of all 
 entities that have an attribute of type geometry whose contents may be shared 
 with an attribute of type element.*)
   geom_instance    : grid_geometry_behavior
     (*The instance that contains the attribute of type geometry.*);
   geom_subtype     : STRING(80)
     (*The name of the subtype in geom_instance that contains the attribute.*);
   geom_attribute   : STRING(80)
     (*The name of the geometrical attribute in geom_subtype.*);
 END_META_TYPE;

 META_TYPE emi_property_leaf ( prop_precision : INTEGER )
 ABSTRACT SUPERTYPE OF 
 (
   ONEOF
   (
   emi_leaf_equal_step_values,
   emi_leaf_integer_values,
   emi_leaf_real_values,
   emi_leaf_string_values,
   emi_leaf_boolean_values,
   emi_leaf_logical_values,
   emi_leaf_complex_values, 
   emi_leaf_rational_values,
   emi_leaf_ratio_values,
   emi_leaf_dms_values, 
   emi_leaf_date_values,
   emi_leaf_yearmonthinterval_values, 
   emi_leaf_daytimeinterval_values,
   emi_leaf_quantity_values
   )
 );
   kind            : property_kind;
 UNIQUE
   pk : kind;
 END_META_TYPE;

 META_TYPE emi_leaf_quantity_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   property_unit : ref_unit_of_measure;
   values        : LIST[1:?] OF REAL(prop_precision);
   null_value    : OPTIONAL REAL(prop_precision);
 END_META_TYPE; 

 META_TYPE emi_leaf_equal_step_values 
 SUBTYPE OF
 (
   emi_property_leaf
 )
   (*Defines a simple formula for calculating the values based on the 
     grid index (or list index for SAMPLE).
     The list corresponds to the grid dimension.
     That is, the value at a node is 
     "start +step(1)*i +step(2)*j +step(3)*k, etc".*);
   start : REAL(prop_precision);
   step  : LIST[1:15] OF REAL(prop_precision);
 END_META_TYPE;

 META_TYPE emi_leaf_integer_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF INTEGER;
   null_value : OPTIONAL INTEGER;
 END_META_TYPE;

 META_TYPE emi_leaf_real_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF REAL(prop_precision);
   null_value : OPTIONAL REAL(prop_precision);
 END_META_TYPE;

 META_TYPE emi_leaf_string_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF STRING(80);
   null_value : OPTIONAL STRING(80);
 END_META_TYPE;

 META_TYPE emi_leaf_boolean_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF BOOLEAN;
 END_META_TYPE;

 META_TYPE emi_leaf_logical_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF LOGICAL;
 END_META_TYPE; 

 META_TYPE emi_leaf_complex_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF complex(prop_precision);
   null_value : OPTIONAL complex(prop_precision);
 END_META_TYPE;  

 META_TYPE emi_leaf_rational_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF rational;
   null_value : OPTIONAL rational;
 END_META_TYPE;   

 META_TYPE emi_leaf_ratio_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF ratio(prop_precision);
   null_value : OPTIONAL ratio(prop_precision);
 END_META_TYPE;    

 META_TYPE emi_leaf_dms_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF emi_angle_dms(prop_precision);
   null_value : OPTIONAL emi_angle_dms(prop_precision);
 END_META_TYPE;

 META_TYPE emi_angle_dms ( precision : INTEGER )
  (*This is a compound-quantity with a fixed unit of measure (i.e., degrees).*);
   degree_value : INTEGER;
   minute_value : INTEGER;
   second_value : REAL(precision-5);
 WHERE
   is_valid : dms_valid (degree_value, minute_value, second_value);
 END_META_TYPE;

 META_TYPE emi_leaf_date_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF date;
   null_value : OPTIONAL date;
 END_META_TYPE;     

 META_TYPE emi_leaf_yearmonthinterval_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF yearmonthinterval;
   null_value : OPTIONAL yearmonthinterval;
 END_META_TYPE;     

 META_TYPE emi_leaf_daytimeinterval_values 
 SUBTYPE OF
 (
   emi_property_leaf
 );
   values     : LIST[1:?] OF daytimeinterval(prop_precision);
   null_value : OPTIONAL daytimeinterval(prop_precision);
 END_META_TYPE;

 META_TYPE emi_geometry_grid
 ABSTRACT SUPERTYPE OF
 (
   ONEOF
   (
   line,
   emi_surface_grid,
   volume,
   hypercell,
   element
   )
 );
   grid1       : emi_grid;
   tuple_count : INTEGER;
   descriptor  : OPTIONAL LIST[1:16] OF UNIQUE emi_descriptor
    (*This implicitly defines the grid index values of each tuple in the list. 
    The maximum bound must allow for an element index.*);
   index_leaf  : OPTIONAL SET[1:?] OF emi_index_leaf
    (*This explicitly defines the grid index values of each tuple in the list.*);
 WHERE
   mse         : EXISTS(descriptor) XOR EXISTS(index_leaf);
 END_META_TYPE;

 TYPE emi_index_type = ENUMERATION OF (
         -- Axis indices (I = first dimension)
          INDEX_I, INDEX_J, INDEX_K, INDEX_L, INDEX_M, INDEX_N, INDEX_O, INDEX_P,
          INDEX_Q, INDEX_R, INDEX_S, INDEX_T, INDEX_U, INDEX_V, INDEX_W,
         -- Element indices (node = 0 dimensional)
          INDEX_NODE, INDEX_EDGE, INDEX_FACE, INDEX_CELL, INDEX_4,  INDEX_5,  
          INDEX_6,    INDEX_7,    INDEX_8,    INDEX_9,    INDEX_10, INDEX_11,
          INDEX_12,   INDEX_13,   INDEX_14,   INDEX_15 );
 END_TYPE;

 META_TYPE emi_descriptor
 (*This describes the implicit indexing for one dimension of the grid.*);
   dimension   : emi_index_type;
   lower_bound : INTEGER
     (*The beginning index value.*);
   count       : INTEGER;
 UNIQUE
   pk : dimension;
 WHERE 
   lower_bound_valid : lower_bound > 0;
   count_valid       : count > 0;
 END_META_TYPE;

 META_TYPE emi_index_leaf
 (*This provides and explicit grid index value for each tuple.*);
   index_name  : emi_index_type;
   grid_number : INTEGER;
   data_values : LIST[1:?] OF INTEGER;
 UNIQUE
   pk : index_name, grid_number;
 WHERE
   grid_number_valid : (grid_number=1) OR (grid_number=2);
 END_META_TYPE; 

 TYPE emi_grid_type = ENUMERATION OF ( regular, cornerpoint, 
                                       trimesh, unstructured_3d);
 END_TYPE;

 META_TYPE emi_grid
 ABSTRACT SUPERTYPE OF 
 (
   ONEOF
   (
   emi_grid_regular,
   emi_grid_trimesh,
   emi_grid_unstructured_3d
   )
 );
   grid_type       : emi_grid_type;
   dimension_count : INTEGER;
   node_count      : INTEGER
     (*The total number of nodes on the grid.*);
   grid_modified   : OPTIONAL grid_or_mesh
     (*This refers to an equivalent grid description that "somehow" contains 
     knowledge about how this grid has been refined or coarsened 
     by another grid.*);
 WHERE
   dimension_count_valid : dimension_count<=15;
 END_META_TYPE;

 META_TYPE emi_grid_regular 
 SUBTYPE OF
 (
   emi_grid
 );
   grid_axis_point_count : LIST[1:15] OF INTEGER
    (*Specifies the node count on each axis. 
      The list corresponds to the axis order.*);
   grid_axis_lower_bound : LIST[1:15] OF INTEGER
    (*Specifies the starting index value on each axis. 
      The list corresponds to the axis order.*);
   closed_flag           : OPTIONAL LIST[1:15] OF BOOLEAN
    (*TRUE indicates that the last node is topologically connected 
      to the first node. The list corresponds to the axis order.*);
 WHERE
   axis_count_valid   : SIZEOF(grid_axis_point_count) = dimension_count;
   axis_bound_valid   : SIZEOF(grid_axis_lower_bound) = dimension_count;
   flag_count_valid   : NOT (EXISTS(closed_flag)) OR 
                        (SIZEOF(closed_flag) <= dimension_count);
   regular_type_valid : (grid_type=emi_grid_type.regular) OR 
                        (grid_type=emi_grid_type.cornerpoint);
 END_META_TYPE;

 META_TYPE emi_grid_trimesh 
 SUBTYPE OF
 (
   emi_grid
 );
   mesh_edge : SET[1:?] OF emi_mesh_edge;
   mesh_face : SET[1:?] OF emi_mesh_face;
 WHERE
   trimesh_count_valid : dimension_count=2;
   mesh_type_valid     : grid_type=emi_grid_type.trimesh;
 END_META_TYPE;

 META_TYPE emi_mesh_edge;
   edge_index : INTEGER;
   start_node : INTEGER;
   end_node   : INTEGER;
 UNIQUE
   pk : edge_index;
   sk : start_node, end_node;
 END_META_TYPE;

 META_TYPE emi_mesh_face;
   face_index : INTEGER;
   node1      : INTEGER;
   node2      : INTEGER;
   node3      : INTEGER;
 UNIQUE
   pk : face_index;
 END_META_TYPE;

 META_TYPE emi_grid_unstructured_3d 
 SUBTYPE OF
 (
   emi_grid
 );
   u3_cell : OPTIONAL SET[1:?] OF emi_u3_cell;
   u3_face : OPTIONAL SET[1:?] OF emi_u3_face;
   u3_edge : OPTIONAL SET[1:?] OF emi_u3_edge;
 WHERE
   unstruct_count_valid : dimension_count=3;
   unstruct_type_valid  : grid_type=emi_grid_type.unstructured_3d;
 END_META_TYPE;

 META_TYPE emi_u3_cell;
   cell_index    : INTEGER;
   bounding_face : OPTIONAL SET[1:?] OF INTEGER
   (*This specifies the face indices of the faces that bound the cell.*);
   center_node   : OPTIONAL INTEGER
   (*This specifies the node index of the node at the center of the cell.*);
 UNIQUE
   pk : cell_index;
 END_META_TYPE;

 META_TYPE emi_u3_face;
   face_index    : INTEGER;
   bounding_edge : OPTIONAL SET[1:?] OF INTEGER
   (*This specifies the edge indices of the edges that bound the face.*);
   adjacent_face : OPTIONAL SET[1:?] OF INTEGER
   (*This specifies the faces indices of the faces that are adjacent 
     to this face.*);
   center_node   : OPTIONAL INTEGER
   (*This specifies the node index of the node at the center of the face.*);
 UNIQUE
   pk : face_index;
 END_META_TYPE;

 META_TYPE emi_u3_edge;
   edge_index    : INTEGER;
   bounding_node : OPTIONAL LIST[1:?] OF UNIQUE INTEGER
   (*This specifies the node indices of the nodes that define the edge.*);
 UNIQUE
   pk : edge_index;
 END_META_TYPE;

Conformance Notes:

  1. Meta types with the prefix emi_ (Epicentre Meta Internal) are intended for internal use to describe the Epicentre types and must not be directly invoked by a conformant model.

Simple Pattern Validity Functions

These are the functions that are required in order to check the validity of the Epicentre Simple Pattern and Quantity (i.e., non-geometry) types.

 FUNCTION complex_valid ( precision       : INTEGER; 
                          real_part       : REAL;
                          imaginary_part  : REAL ) : BOOLEAN;
 -- Return FALSE if the complex data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(real_part) <> EXISTS(imaginary_part) ) THEN
     RETURN (FALSE);
   END_IF;
 *)
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION rational_valid (numerator   : INTEGER; 
                          denominator : INTEGER ) : BOOLEAN;
 -- Return FALSE if the rational data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(numerator) <> EXISTS(denominator) ) THEN
     RETURN (FALSE);
   END_IF;
 *)
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION ratio_valid ( precision   : INTEGER; 
                        numerator   : INTEGER; 
                        denominator : INTEGER ) : BOOLEAN;
 -- Return FALSE if the ratio data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(numerator) <> EXISTS(denominator) ) THEN
     RETURN (FALSE);
   END_IF;
 *)
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION money_valid ( money_constraint : STRING;
                        currency_value   : INTEGER;
                        currency_unit    : ref_currency_unit; 
                        subvalue         : LIST OF INTEGER ) : BOOLEAN;
 -- Return FALSE if the currency data is not valid.
   LOCAL
     val         : INTEGER;
     parent_unit : ref_currency_unit;
   END_LOCAL;
 (*The following are enforced by the meta type.
   IF ( EXISTS(currency_value) <> EXISTS(currency_unit) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(subvalue) AND NOT EXISTS(currency_value) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the unit constraint.
   IF ( money_constraint<>'' ) THEN
     IF ( money_constraint<>currency_unit.acronym ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;

 -- Check the sign.
   IF (SIZEOF(subvalue)=0 ) THEN
      RETURN (TRUE);
   END_IF;
   val := 0;
   IF (currency_value<>0) THEN
     val := currency_value;
   ELSE
     REPEAT i := LOINDEX(subvalue) TO HIINDEX(subvalue);
       IF (subvalue[i]<>0) THEN
         val := subvalue[i];
         ESCAPE;
       END_IF;
     END_REPEAT;
   END_IF;
   IF ( val>0 ) THEN
   -- All values must be positive or zero.
     IF ( SIZEOF(QUERY( tmp <* subvalue | tmp<0 ))<>0 ) THEN
       RETURN (FALSE);
     END_IF;
   ELSE
     IF ( val<0 ) THEN
     -- All values must be negative or zero.
       IF ( SIZEOF(QUERY( tmp <* subvalue | tmp>0 ))<>0 ) THEN
         RETURN (FALSE);
       END_IF;
     END_IF;
   END_IF;

 -- Insure that there are not more subunits than are allowed.
   IF ( EXISTS(subvalue) ) THEN
     IF ( SIZEOF(subvalue) > subcurrency_depth(currency_units) ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;

 -- Insure that the subvalues are less than the allowed subunit per unit.
   parent_unit := currency_unit;
   REPEAT i := LOINDEX(subvalue) TO HIINDEX(subvalue);
     IF NOT (subvalue[i] < parent_unit.sub_unit_per_unit) THEN
       RETURN (FALSE);
     END_IF;
     parent_unit := parent_unit.smaller_unit;
   END_REPEAT;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION subcurrency_depth ( unit : ref_currency_unit ) : INTEGER;
 -- Recursive function that returns the count of currency subunits.
   IF NOT EXISTS(unit.sub_unit_per_unit) THEN
     RETURN ( 0 );
   ELSE
     RETURN ( 1 + subunit_depth(unit.sub_unit_per_unit) );
   END_IF;
 END_FUNCTION;


 FUNCTION location_valid ( coord_constraint : STRING; 
                           precision        : INTEGER;
                           system           : coordinate_system;
                           origin           : vertex; 
                           coords           : LIST OF emi_ordinal 
                         ) : BOOLEAN;
 -- Return FALSE if the location values are not valid.

   LOCAL
     uom : LIST[0:?] OF ref_unit_of_measure;
   END_LOCAL;

 -- Check the precision.
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the coordinate system constraint.
   IF ( coord_constraint<>'' ) THEN
     IF ( coord_constraint<>system.ref_coordinate_sys_constraint.identifier ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;

 -- Check the coordinate values.
   REPEAT i := LOINDEX(coords) TO HIINDEX(coords);
   -- Check any DMS values.
     IF ( NOT quantity_dms_valid ( coords[i].ordinal_value, 
                  coords[i].ordinal_unit, coords[i].minute_value, 
                  coords[i].second_value) ) THEN
       RETURN (FALSE);
     END_IF;
   -- Collect the units into a local variable.
     uom[i] := coords[i].ordinal_unit;
   END_REPEAT;

 -- Check the coordinate units against what is allowed by the system.
   IF ( NOT coordinate_units_valid (system,uom) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION coordinate_units_valid ( system : coordinate_system; 
                                   uom    : LIST OF ref_unit_of_measure 
                                 ) : BOOLEAN;
 -- Return FALSE if the units do not match what is allowed by the axes 
 -- of the coordinate system.

   IF (SIZEOF(uom) <> SIZEOF(system.ref_coordinate_sys_constraint.axis_type)) THEN
     RETURN (FALSE);
   END_IF;
   IF ( SIZEOF(QUERY( typ <* TYPEOF(system)  | 
                      typ LIKE '*SIMPLE_COORDINATE_SYSTEM*'))>0 ) THEN
   -- This is not a compound system.
     IF (NOT(simple_system_unit_valid(system\simple_coordinate_system,uom,0))) THEN
       RETURN (FALSE);
     END_IF;
     RETURN (TRUE);
   END_IF;
   IF ( SIZEOF(QUERY( typ <* TYPEOF(system)  | 
                      typ LIKE '*COMPOUND_COORDINATE_SYSTEM*'))>0 ) THEN
   -- This is a compound system that is composed of two ordered simple 
   -- systems (e.g., a 2d horizontal plus a 1d vertical).
     ALIAS first FOR system.first_system;
     -- Check each component system in order. The first N axes belong 
     -- to the first system.
       IF ( NOT (simple_system_unit_valid (first, uom, 0)) ) THEN
         RETURN (FALSE);
       END_IF;
     -- The next M axes belong to the second system.
       IF ( NOT (simple_system_unit_valid (
                        system.second_system, 
                        uom, SIZEOF(first.coordinate_system_axis))) ) THEN
         RETURN (FALSE);
       END_IF;
     END_ALIAS;
     RETURN (TRUE);
   END_IF;
   -- bad model.
   RETURN (FALSE);
 END_FUNCTION;


 FUNCTION simple_system_unit_valid ( system : simple_coordinate_system ; 
                                     uom    : LIST OF ref_unit_of_measure; 
                                     ix     : INTEGER ) : BOOLEAN;
   -- Return FALSE if the units do not match those allowed by the simple system.
   -- Parameter "ix" specifies the number of axes utilized in a previous 
   -- component of a compound system.
   ALIAS csa FOR system.coordinate_system_axis;
     REPEAT i := LOINDEX(csa) TO HIINDEX(csa);
       IF ( EXISTS(csa[i].constrained_to_unit) ) THEN
       -- This axis is constrained to use a particular unit of measure.
         IF (csa[i].constrained_to_unit <> uom[ix+csa[i].axis_order]) THEN
           RETURN (FALSE);
         END_IF;
       ELSE
       -- Check against the units allowed by the underlying quantity type.
         IF ( SIZEOF(QUERY( alt <* 
              csa[i].property_kind.quantity_constraint.alternative_unit_of_measure | 
              alt.unit_of_measure = uom[ix+csa[i].axis_order]))=0 ) THEN
           RETURN (FALSE);
         END_IF;
       END_IF;
     END_REPEAT;
   END_ALIAS;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION whole_number ( real_value : REAL ) : BOOLEAN;
 -- Return TRUE if the number has no fractional part.
   LOCAL
     int : INTEGER;
   END_LOCAL;
   int := real_value;
   IF ( (real_value -int) <> 0 ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION date_valid ( year  : INTEGER;
                       month : INTEGER;
                       day   : INTEGER ) : BOOLEAN;
 -- Return FALSE if the date data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(year) <> EXISTS(month) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(year) <> EXISTS(day) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the year.
   IF ( ( year<1 ) OR ( year>9999 ) ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the month.
   IF ( ( month<1 ) OR ( month>12 ) ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the day.
   IF ( NOT( ((month IN [1,3,5,7,8,10,12]) AND { 1 <= day <= 31 }) XOR
             (((month IN [4,6,9,11])       AND { 1 <= day <= 30 }) XOR
              ((month = 2) AND
               ((leap_year(year) AND { 1 <= day <= 29 }) OR
                { 1 <= day <= 28 }))) ) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION leap_year ( year : INTEGER ) : BOOLEAN;
   IF ( (((year mod 4) = 0) AND 
         ((year mod 100) <> 0)) OR
        ((year mod 400) = 0) ) THEN
      RETURN (TRUE);
   END_IF;
   RETURN (FALSE);
 END_FUNCTION;


 FUNCTION time_valid ( precision     : INTEGER;
                       hour          : INTEGER;
                       minute        : INTEGER;
                       second        : REAL;
                       offset_hour   : INTEGER;
                       offset_minute : INTEGER) : BOOLEAN;
 -- Return FALSE if the time data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(hour) <> EXISTS(minute) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(hour) <> EXISTS(second) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(hour) <> EXISTS(offset_hour) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(hour) <> EXISTS(offset_minute) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the precision.
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the hour.
   IF ( ( hour<0 ) OR ( hour>23 ) ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the minute.
   IF ( ( minute<0 ) OR ( minute>59 ) ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the second.
   IF ( ( second<0 ) OR ( second>=62 ) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( ( second>=60 ) AND (( hour<>23 ) OR ( minute<>59 )) ) THEN
   -- Bad leap second.
     RETURN (FALSE);
   END_IF;
 -- Check the offset.
   IF ( ABS(offset_hour) > 13 ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( ( offset_hour=13 ) AND ( offset_minute<>0 ) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( ABS(offset_minute) >= 60 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the SIGN.
   IF NOT (((offset_hour>=0) AND (offset_minute>=0)) OR
           ((offset_hour<=0) AND (offset_minute<=0))) THEN
   -- Everything must be positive or negative.
       RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION timestamp_valid ( precision     : INTEGER;
                            content_code  : emi_timestamp_content;
                            year          : REAL;
                            year_part     : INTEGER;
                            day           : INTEGER;
                            hour          : INTEGER;
                            minute        : INTEGER;
                            second        : REAL;
                            offset_hour   : INTEGER;
                            offset_minute : INTEGER) : BOOLEAN;
 -- Return FALSE if the timestamp data is not valid.

 (*The following is checked by the meta syntax. 
   IF ( NOT EXISTS(year) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( NOT EXISTS(content_code) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the precision.
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check for geologic age only.
   IF ( content_code=emi_timestamp_content.G ) THEN
   -- We are in geologic time.
     IF ( year<0  ) THEN
     -- Must be positive.
       RETURN (FALSE);
     END_IF;
     IF ( EXISTS(year_part) OR EXISTS(day)  OR EXISTS(hour) OR EXISTS(minute) OR 
          EXISTS(second) OR EXISTS(offset_hour) OR EXISTS(offset_minute) ) THEN
     -- This is mutually exclusive with the other components.
       RETURN (FALSE);
     END_IF;
     RETURN (TRUE);
   END_IF;

 -- We are in modern times.
   IF ( NOT whole_number(year)  ) THEN
   -- Fractional parts are not allowed.
     RETURN (FALSE);
   END_IF;

 -- Check for century only.
   IF ( content_code=emi_timestamp_content.C ) THEN
   -- Only the century is being specified.
     IF ( abs(year)>99  ) THEN
     -- Must be maximum of two digits.
       RETURN (FALSE);
     END_IF;
     IF ( EXISTS(year_part) OR EXISTS(day) OR EXISTS(hour) OR EXISTS(minute) OR 
          EXISTS(second) OR EXISTS(offset_hour) OR EXISTS(offset_minute) ) THEN
     -- This is mutually exclusive with the other components.
       RETURN (FALSE);
     END_IF;
     RETURN (TRUE);
   END_IF;

 -- Check the year.
   IF ( year=0 ) THEN -- Negative values indicate "Before Christian Era".
     RETURN (FALSE);
   END_IF;
 -- Check the quarter of the year.
   IF ( content_code=emi_timestamp_content.Q ) THEN
     IF ( (year_part<1) OR (year_part>4) ) THEN
       RETURN (FALSE);
     END_IF;
   -- This is mutually exclusive with the other components.
     IF ( EXISTS(day)    OR EXISTS(hour)        OR EXISTS(minute) OR 
          EXISTS(second) OR EXISTS(offset_hour) OR EXISTS(offset_minute) ) THEN
       RETURN (FALSE);
     END_IF;
     RETURN (TRUE);
   END_IF;
 -- Check the month of the year.
   IF ( content_code=emi_timestamp_content.M ) THEN
     IF ( (year_part<1) OR (year_part>12) ) THEN
       RETURN (FALSE);
     END_IF;
   -- Check the day of the month.
     IF ( EXISTS(day) ) THEN
       IF ( year_part=2 ) THEN
       -- February.
         IF ( NOT( (leap_year(year) AND { 1 <= day <= 29 }) OR
                   { 1 <= day <= 28 } ) ) THEN
           RETURN (FALSE);
         END_IF;
       ELSE
       -- Not February.
         IF ( NOT( 
               ((year_part IN [1,3,5,7,8,10,12]) AND { 1 <= day <= 31 }) XOR
               ((year_part IN [4,6,9,11])        AND { 1 <= day <= 30 }) ) ) THEN
           RETURN (FALSE);
         END_IF;
       END_IF;
     END_IF;
   END_IF;
 -- Check the week of the year.
   IF ( content_code=emi_timestamp_content.W ) THEN
     IF ( (year_part<1) OR (year_part>53) ) THEN
       RETURN (FALSE);
     END_IF;
   -- Check the day of the week. 1=Monday.
     IF EXISTS(day) THEN
       IF ( (day<1) OR (day>7) ) THEN
         RETURN (FALSE);
       END_IF;
     END_IF;
   END_IF;
 -- Check the day of the year.
   IF ( content_code=emi_timestamp_content.D ) THEN
     IF EXISTS(day) THEN
       IF ( (day>366) OR ((NOT leap_year(year)) AND (day>365)) ) THEN
         RETURN (FALSE);
       END_IF;
     END_IF;
   END_IF;
 -- Check the hour of the day.
   IF ( EXISTS(hour) ) THEN
     IF ( NOT EXISTS(day)  ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( (hour<0) OR (hour>24) ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;
 -- Check the minute of the hour.
   IF ( EXISTS(minute)  ) THEN
     IF ( NOT EXISTS(hour) ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( (minute<0) OR (minute>59) ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( (hour=24) AND (minute>0) ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;
 -- Check the second.
   IF ( EXISTS(second)  ) THEN
     IF ( NOT EXISTS(minute) ) THEN
       RETURN (FALSE);
     END_IF;
   -- Check the low side.
     IF ( (second<0)  ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( (hour=24) AND (second>0) ) THEN
       RETURN (FALSE);
     END_IF;
   -- Check the high side.
     IF ( content_code=emi_timestamp_content.M ) THEN
     -- Month given. Check for a bad leap year second.
       IF ( second>=60 ) THEN
         IF ( NOT (leap_year(year) AND 
                   (year_part=12) AND
                   (day=31) AND 
                   (hour=23) AND 
                   (minute=59) AND 
                   (second<62)) ) THEN
           RETURN (FALSE);
         END_IF;
       END_IF;
     ELSE
     -- Month not given.
       IF ( content_code=emi_timestamp_content.D ) THEN
       -- Day of year. 
       -- Check for a bad leap year second.
         IF ( second>=60 ) THEN
           IF ( NOT (leap_year(year) AND
                     (day=366) AND 
                     (hour=23) AND 
                     (minute=59) AND 
                     (second<62)) ) THEN
             RETURN (FALSE);
           END_IF;
         END_IF;
       ELSE
       -- Day of week.
       -- Too complicated to check for a leap second.
         IF ( (second>=62) ) THEN -- Allow for leap seconds.
           RETURN (FALSE);
         END_IF;
       END_IF;
     END_IF;
   END_IF;
 -- Check the offset hour.
   IF ( EXISTS(offset_hour) ) THEN
     IF ( NOT EXISTS(hour) ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( (offset_hour<-12) OR (offset_hour>13) ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;
 -- Check the offset minute.
   IF ( EXISTS(offset_minute) ) THEN 
     IF ( NOT EXISTS(offset_hour) ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( (offset_hour=13) AND (offset_minute<>0) ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( ABS(offset_minute)>59 ) THEN
       RETURN (FALSE);
     END_IF;
   -- Check the SIGN.
     IF ( ((offset_hour>0)  AND (offset_minute<0)) OR
          ((offset_hour<0)  AND (offset_minute>0)) ) THEN
     -- Both must be positive or negative.
       RETURN (FALSE);
     END_IF;
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION yearmonthinterval_valid ( years  : INTEGER;
                                    months : INTEGER) : BOOLEAN;
 -- Return FALSE if the yearmonthinterval data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(years) <> EXISTS(months) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the years.
   IF ( ABS(years)>9999 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the months.
   IF ( ABS(months)>12 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the sign.
   IF ( ((years>0)  AND (months<0)) OR
        ((years<0)  AND (months>0)) ) THEN
   -- Both must be positive or negative.
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION daytimeinterval_valid ( precision : INTEGER;
                                  day       : INTEGER;
                                  hour      : INTEGER;
                                  minute    : INTEGER;
                                  second    : REAL) : BOOLEAN;
 -- Return FALSE if the daytimeinterval data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(day) <> EXISTS(hour) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(day) <> EXISTS(minute) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(day) <> EXISTS(second) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the precision.
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the day.
   IF ( ABS(day)>9999 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the hour.
   IF ( ABS(hour)>23 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the minute.
   IF ( ABS(minute)>59 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the second.
   IF ( ABS(second)>=60 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the sign.
   IF ( (NOT ((day>=0) AND (hour>=0) AND (minute>=0) AND (second>=0))) AND
        (NOT ((day<=0) AND (hour<=0) AND (minute<=0) AND (second<=0))) ) THEN
   -- All must be positive or negative.
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION quantity_valid ( quantity_type  : STRING(80); 
                           precision      : INTEGER;
                           quantity_value : REAL;
                           quantity_unit  : ref_unit_of_measure) : BOOLEAN;
 -- Return FALSE if the quantity data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(quantity_value) <> EXISTS(quantity_unit) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the precision.
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the constraint.
   IF ( quantity_type='' ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the unit of measure against those allowed by the type.
   IF ( SIZEOF(QUERY( tmp <* USEDIN ( quantity_unit, 
            'REF_QUANTITY_TYPE.ALTERNATIVE_UNIT_OF_MEASURE.UNIT_OF_MEASURE') |  
            tmp.identifier=quantity_type ))<>1 ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION anyquantity_valid ( precision     : INTEGER;
                              quantity_value: REAL;
                              quantity_unit : ref_unit_of_measure;
                              quantity_type : ref_quantity_type) : BOOLEAN;
 -- Return FALSE if the anyquantity data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(quantity_value) <> EXISTS(quantity_unit) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(quantity_value) <> EXISTS(quantity_type) ) THEN
     RETURN (FALSE);
   END_IF;
 *)

 -- Check the precision.
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the unit of measure against those allowed by the type.
   IF ( SIZEOF(QUERY( tmp <* quantity_type.alternative_unit_of_measure |  
            tmp.unit_of_measure :=: quantity_unit ))<>1 ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION angle_valid ( quantity_type  : STRING(80); 
                        precision      : INTEGER; 
                        quantity_value : REAL;
                        quantity_unit  : ref_unit_of_measure;
                        minute_value   : INTEGER;
                        second_value   : REAL ) : BOOLEAN;
 -- Return FALSE if the angle data is not valid.
 (*The following are enforced by the meta type.
   IF ( EXISTS(quantity_value) <> EXISTS(quantity_unit) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(minute_value) AND NOT EXISTS(minute_value) ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( EXISTS(minute_value) AND NOT EXISTS(second_value) ) THEN
     RETURN (FALSE);
   END_IF;
 *)


 -- Check the precision.
   IF ( precision <=  0 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the constraint.
   IF ( quantity_type='' ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the unit of measure against those allowed by the type.
   IF ( SIZEOF(QUERY( tmp <* USEDIN ( quantity_unit, 
               'REF_QUANTITY_TYPE.ALTERNATIVE_UNIT_OF_MEASURE.UNIT_OF_MEASURE') |  
               tmp.identifier=quantity_type ))<>1 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Check the value.
   IF ( NOT quantity_dms_valid ( quantity_value, quantity_unit, 
                                 minute_value, second_value ) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION quantity_dms_valid ( quantity_value : REAL;
                               quantity_unit  : ref_unit_of_measure;
                               minute_value   : INTEGER;
                               second_value   : REAL ) : BOOLEAN;
 -- Return FALSE if the dms data is not valid.
 -- The minute and second values may be null.

   IF ( EXISTS(minute_value) OR EXISTS(second_value) ) THEN
   -- This is a DMS value.
     IF (EXISTS(minute_value) <> EXISTS(second_value) ) THEN
     -- If either is given then both must be given.
       RETURN (FALSE);
     END_IF;
     IF ( quantity_unit.acronym<>'dega' ) THEN
     -- The unit must specify degrees of angle.
       RETURN (FALSE);
     END_IF;
     IF ( NOT whole_number(quantity_value) ) THEN
     -- The degree component must be a whole number.
       RETURN (FALSE);
     END_IF;
     IF (ABS(minute_value) > 59) THEN
       RETURN (FALSE);
     END_IF;
     IF (ABS(second_value) >= 60) THEN
       RETURN (FALSE);
     END_IF;
     IF ( (NOT ((quantity_value>=0) AND 
                (minute_value  >=0) AND 
                (second_value  >=0))) AND
          (NOT ((quantity_value<=0) AND 
                (minute_value  <=0) AND 
                (second_value  <=0))) ) THEN
     -- All must be positive or negative.
       RETURN (FALSE);
     END_IF;
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;

Geometry Validity Functions

These are the functions that are required in order to check the validity of the complex Epicentre Geometry types. Some of the simple pattern validity functions may also be utilized.

 FUNCTION point_property_valid ( rep_constraint      : STRING;
                                 position_constraint : STRING;
                                 property_constraint : STRING;  
                                 property            : SET OF emi_point_property 
                      ) : BOOLEAN;
 -- Return FALSE if the point property data is not valid.

   LOCAL
     prp : SET[0:?] OF property_kind;
   END_LOCAL;

 -- Check the representation constraints.
   IF ( (rep_constraint<>'') AND (rep_constraint<>'grid0d') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   IF ( (position_constraint<>'') AND (position_constraint<>'node') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the property constraint and the properties.
   IF EXISTS(property) THEN
     REPEAT i := LOINDEX(property) TO HIINDEX(property);
     -- Note that the SET constrains the values to be unique.
       prp[i] := property[i].kind;
     END_REPEAT;
   END_IF;
   IF ( NOT property_set_valid (property_constraint,prp) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION property_set_valid ( property_constraint : STRING ; 
                               property            : SET OF property_kind 
                             ) : BOOLEAN;
 -- Return FALSE if the properties are not allowed by the constraint.
   LOCAL
     type_set   : SET[0:?] OF ref_property_set;
     constraint : ref_property_set;
   END_LOCAL;

   IF ( (NOT EXISTS(property)) AND ((property_constraint<>'none') AND 
                                    (property_constraint<>'')) ) THEN
   -- A property is required.
     RETURN (FALSE);
   END_IF;
   IF ( NOT EXISTS(property) ) THEN
   -- Nothing else to check.
     RETURN (TRUE);
   END_IF;
   IF ( property_constraint='none' )  THEN
   -- Properties are not allowed.
     RETURN (FALSE);
   END_IF;
   IF ( (property_constraint<>'') AND 
        (property_constraint<>'required') )  THEN
   -- Find all sets that use the first property kind.
     type_set := USEDIN ( property[1], 
                         'REF_PROPERTY_SET.COMPONENT_KIND.PROPERTY_KIND');
   -- Find the kind that matches the constraint.
     REPEAT i := LOINDEX(type_set) TO HIINDEX(type_set);
       IF ( type_set[i].identifier = property_constraint ) THEN
         constraint := type_set[i];
         ESCAPE;
       END_IF;
     END_REPEAT;
     IF ( NOT EXISTS(constraint) ) THEN
     -- The constraint instance was not found.
       RETURN (FALSE);
     END_IF;
   -- Make sure the number of properties is valid.
     IF ( constraint.use_limit > 0 ) THEN
       IF ( NOT (((constraint.use_limit=SIZEOF(property)) AND 
                  (constraint.ref_property_set_type.identifier='EQ')) OR
                 ((constraint.use_limit>SIZEOF(property)) AND 
                  (constraint.ref_property_set_type.identifier='LE'))) ) THEN
         RETURN (FALSE);
       END_IF;
     END_IF;
   -- Make sure each property is allowed by the constraint.
     REPEAT i := LOINDEX(property) TO HIINDEX(property);
       IF ( SIZEOF(QUERY( comp <* constraint.component_kind |
                   property[i] = comp.property_kind 
             ))=0 ) THEN
         RETURN (FALSE);
       END_IF;
     END_REPEAT;
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


FUNCTION sample_valid ( rep_constraint      : STRING(80);
                        position_constraint : STRING(80);
                        coord_constraint    : STRING(80); 
                        coord_precision     : INTEGER;  
                        property_constraint : STRING(80);  
                        property_precision  : INTEGER;
                        tuple_count         : INTEGER;
                        system              : coordinate_system;
                        coordinate_leaf     : LIST OF emi_geometry_leaf;
                        property_leaf       : SET OF emi_property_leaf
                      ) : BOOLEAN;
-- Return false if the sample values are not valid.

 -- Check the validity of the property leaf.
   IF ( NOT leaf_property_valid(1,tuple_count,property_constraint,
              property_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the units match what is allowed by the CS axes.
   IF ( NOT geometry_coord_valid( system, coordinate_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   IF ( (position_constraint<>'') AND (position_constraint<>'node') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the representation constraint.
   IF ( (rep_constraint<>'') AND (rep_constraint<>'random') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the coordinate system constraint.
   IF ( (coord_constraint<>'') AND 
        (coord_constraint<>system.ref_coordinate_sys_constraint.identifier) ) THEN
     RETURN (FALSE);
   END_IF;

   RETURN (TRUE);
 END_FUNCTION;


FUNCTION line_valid ( rep_constraint      : STRING(80);
                      position_constraint : STRING(80);
                      coord_constraint    : STRING(80); 
                      coord_precision     : INTEGER;  
                      property_constraint : STRING(80);  
                      property_precision  : INTEGER;
                      tuple_count         : INTEGER;
                      grid1               : emi_grid;
                      descriptor          : LIST OF emi_descriptor;
                      index_leaf          : SET OF emi_index_leaf;
                      system              : coordinate_system;
                      coord_leaf          : LIST OF emi_geometry_leaf;
                      property_leaf       : SET OF emi_property_leaf
                    ) : BOOLEAN;
-- Return false if the line values are not valid.

 -- Check the validity of the property leaf.
   IF ( NOT leaf_property_valid(grid1.dimension_count,tuple_count, 
                 property_constraint,property_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the descriptor and index_leaf are valid.
   IF ( NOT geometry_descriptor_valid (tuple_count,grid1,descriptor, 
                    index_leaf,coord_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the units match what is allowed by the CS axes.
   IF ( NOT geometry_coord_valid (system,coordinate_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the dimensionality of the grid.
   IF ( grid1.dimension_count<>1 ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the instantiated representation.
   IF ( grid1.grid_type<>emi_grid_type.regular ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the representation constraint.
   IF ( (rep_constraint<>'') AND
        (rep_constraint<>'grid1d') AND
        (rep_constraint<>'grid1to2d') AND
        (rep_constraint<>'grid') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   IF ( (position_constraint<>'') AND
        (position_constraint<>'node') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the coordinate system constraint.
   IF ( (coord_constraint<>'') AND 
        (coord_constraint<>system.ref_coordinate_sys_constraint.identifier) ) THEN
     RETURN (FALSE);
   END_IF;

   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION isoline_set_valid ( rep_constraint      : STRING;
                              position_constraint : STRING(80);
                              coord_constraint    : STRING(80);
                              system              : coordinate_system ;
                              prop_kind           : property_kind;
                              value_unit          : ref_unit_of_measure;
                              isoline             : SET OF emi_isoline ) : BOOLEAN;
 -- Return FALSE if the isoline set is not valid.
   LOCAL
     uom    : LIST[0:?] OF ref_unit_of_measure;
     sys_1d : simple_coordinate_system;
     sys_nd : simple_coordinate_system;
     first  : INTEGER;
     found  : BOOLEAN;
   END_LOCAL;

 -- Check the representation constraint.
   IF ( (rep_constraint<>'') AND (rep_constraint<>'contour')) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   IF ( (position_constraint<>'') AND
        (position_constraint<>'node') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the coordinate system constraint.
   IF ( (coord_constraint<>'') AND 
        (coord_constraint<>system.ref_coordinate_sys_constraint.identifier) ) THEN
     RETURN (FALSE);
   END_IF;

 -- The system must be compound.
   IF ( SIZEOF(QUERY( tmp <* TYPEOF(system) | 
                      tmp LIKE '*COMPOUND_COORDINATE_SYSTEM*' ))<>1 ) THEN
     RETURN (FALSE);
   END_IF;

 -- The dimensionality of the compound system must be greater than or equal to 3D.
   IF ( (SIZEOF(system.first_system.coordinate_system_axis) +
         SIZEOF(system.second_system.coordinate_system_axis)) < 3 ) THEN
     RETURN (FALSE);
   END_IF;
 -- Find each component system.
   IF (  SIZEOF(system.second_system.coordinate_system_axis)=1) THEN
     sys_nd := system.first_system;
     sys_1d := system.second_system;
   ELSE
     IF (  SIZEOF(system.first_system.coordinate_system_axis)=1) THEN
       sys_1d := system.first_system;
       sys_nd := system.second_system;
     ELSE
     -- At least one component must be 1D.
       RETURN (FALSE);
     END_IF;
   END_IF;

 -- Each isoline must use the Nd component of the compound system.
   REPEAT i := LOINDEX(isoline) TO HIINDEX(isoline);
     IF (isoline[i].line_geometry.system <> sys_nd) THEN
       RETURN (FALSE);
     END_IF;
   END_REPEAT;

 -- Each isoline must specify the same properties.
   first := LOINDEX(isoline);
   REPEAT i := first+1 TO HIINDEX(isoline);
   -- Iterate over the properties for this isoline.
     REPEAT j := LOINDEX(isoline[i].property_leaf) TO 
                 HIINDEX(isoline[i].property_leaf);
     -- Compare the property to those for the first isoline.
       found := FALSE;
       REPEAT k := LOINDEX(isoline[first].property_leaf) TO 
                   HIINDEX(isoline[first].property_leaf);
         IF ( isoline[i].line_geometry.property_leaf[j].kind = 
              isoline[first].line_geometry.property_leaf[k].kind ) THEN
           found := TRUE;
           ESCAPE;
         END_IF;
       END_REPEAT;
       IF (NOT found) THEN
         RETURN (FALSE);
       END_IF;
     END_REPEAT;
   END_REPEAT;

 -- The property must match the 1D component of the compound system.
   IF ( prop_kind <> sys_1d.coordinate_system_axis.property_kind ) THEN
     RETURN (FALSE);
   END_IF;

 -- The unit must conform to the 1D component of the compound system.
   uom[1] := value_unit;
   IF ( NOT coordinate_units_valid (sys_1d,uom) ) THEN
     RETURN (FALSE);
   END_IF;

   RETURN (TRUE);
 END_FUNCTION;


FUNCTION surface_grid_valid ( rep_constraint      : STRING(80);
                              position_constraint : STRING(80);
                              coord_constraint    : STRING(80); 
                              coord_precision     : INTEGER;  
                              property_constraint : STRING(80);  
                              property_precision  : INTEGER;
                              tuple_count         : INTEGER;
                              grid1               : emi_grid;
                              descriptor          : LIST OF emi_descriptor;
                              index_leaf          : SET OF emi_index_leaf;
                              system              : coordinate_system;
                              coord_leaf          : LIST OF emi_geometry_leaf;
                              property_leaf       : SET OF emi_property_leaf
                            ) : BOOLEAN;
-- Return false if the (gridded) surface values are not valid.

 -- Check the validity of the property leaf.
   IF ( NOT leaf_property_valid  (grid1.dimension_count,tuple_count, 
                         property_constraint,property_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the descriptor and index_leaf are valid.
   IF ( NOT geometry_descriptor_valid (tuple_count,grid1,descriptor,
                    index_leaf,coord_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the units match what is allowed by the CS axes.
   IF ( NOT geometry_coord_valid (system,coord_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the dimensionality of the grid.
   IF ( (grid1.dimension_count<>2) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the instantiated representation.
   IF ( (grid1.grid_type<>emi_grid_type.regular) AND
        (grid1.grid_type<>emi_grid_type.trimesh) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the representation constraint.
   IF ( (rep_constraint<>'') AND
        (rep_constraint<>'grid2d') AND
        (rep_constraint<>'trimesh') AND 
        (rep_constraint<>'grid1to2d') AND 
        (rep_constraint<>'grid') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the instantiated representation.
   IF ( ((rep_constraint='grid2d') AND 
                            (grid1.grid_type<>emi_grid_type.regular)) OR 
        ((rep_constraint='trimesh') AND 
                            (grid1.grid_type<>emi_grid_type.trimesh)) OR
        ((rep_constraint='grid1to2d') AND 
                            (grid1.grid_type<>emi_grid_type.regular)) OR 
        ((rep_constraint='grid') AND 
                            (grid1.grid_type<>emi_grid_type.regular)) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   IF ( (position_constraint<>'') AND
        (position_constraint<>'node') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the coordinate system constraint.
   IF ( (coord_constraint<>'') AND 
        (coord_constraint<>system.ref_coordinate_sys_constraint.identifier) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


FUNCTION volume_valid ( rep_constraint      : STRING(80);
                        position_constraint : STRING(80);
                        coord_constraint    : STRING(80); 
                        coord_precision     : INTEGER;  
                        property_constraint : STRING(80);  
                        property_precision  : INTEGER;
                        tuple_count         : INTEGER;
                        grid1               : emi_grid;
                        descriptor          : LIST OF emi_descriptor;
                        index_leaf          : SET OF emi_index_leaf;
                        system              : coordinate_system;
                        coord_leaf          : LIST OF emi_geometry_leaf;
                        property_leaf       : SET OF emi_property_leaf
                       ) : BOOLEAN;
-- Return false if the volume values are not valid.

 -- Check the validity of the property leaf.
   IF ( NOT leaf_property_valid (grid1.dimension_count,tuple_count, 
                property_constraint,property_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the descriptor and index_leaf are valid.
   IF ( NOT geometry_descriptor_valid (tuple_count,grid1,descriptor,  
                    index_leaf, coord_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the units match what is allowed by the CS axes.
   IF ( NOT geometry_coord_valid (system,coord_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the dimensionality of the grid.
   IF ( (grid1.dimension_count<>3)  ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the instantiated representation.
   IF ( (grid1.grid_type<>emi_grid_type.regular) AND
        (grid1.grid_type<>emi_grid_type.unstructured_3d) AND
        (grid1.grid_type<>emi_grid_type.cornerpoint) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the representation constraint.
   IF ( (rep_constraint<>'') AND
        (rep_constraint<>'grid3d') AND 
        (rep_constraint<>'grid') AND 
        (rep_constraint<>'unstructured_3d') AND 
        (rep_constraint<>'cornerpoint') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the instantiated representation.
   IF ( ((rep_constraint='grid3d') AND 
                            (grid1.grid_type<>emi_grid_type.regular)) OR 
        ((rep_constraint='grid') AND 
                            (grid1.grid_type<>emi_grid_type.regular)) OR 
        ((rep_constraint='unstructured_3d') AND 
                            (grid1.grid_type<>emi_grid_type.unstructured_3d)) OR 
        ((rep_constraint='cornerpoint') AND 
                            (grid1.grid_type<>emi_grid_type.cornerpoint)) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   IF ( (position_constraint<>'') AND
        (position_constraint<>'node') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the coordinate system constraint.
   IF ( (coord_constraint<>'') AND 
        (coord_constraint<>system.ref_coordinate_sys_constraint.identifier) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


FUNCTION hypercell_valid ( rep_constraint      : STRING(80);
                           position_constraint : STRING(80);
                           coord_constraint    : STRING(80); 
                           coord_precision     : INTEGER;  
                           property_constraint : STRING(80);  
                           property_precision  : INTEGER;
                           tuple_count         : INTEGER;
                           grid1               : emi_grid;
                           descriptor          : LIST OF emi_descriptor;
                           index_leaf          : SET OF emi_index_leaf;
                           system              : coordinate_system;
                           coord_leaf          : LIST OF emi_geometry_leaf;
                           property_leaf       : SET OF emi_property_leaf
                          ) : BOOLEAN;
-- Return false if the hypercell values are not valid.

 -- Check the validity of the property leaf.
   IF ( NOT leaf_property_valid (grid1.dimension_count,tuple_count, 
                property_constraint,property_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the descriptor and index_leaf are valid.
   IF ( NOT geometry_descriptor_valid (tuple_count,grid1, 
                    descriptor,index_leaf,coord_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Insure that the units match what is allowed by the CS axes.
   IF ( NOT geometry_coord_valid (system,coord_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the dimensionality of the grid.
   IF ( grid1.dimension_count>3 ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the instantiated representation.
   IF ( grid1.grid_type<>emi_grid_type.regular ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   IF ( (position_constraint<>'') AND 
        (position_constraint<>'node') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the coordinate system constraint.
   IF ( (coord_constraint<>'') AND 
        (coord_constraint<>system.ref_coordinate_sys_constraint.identifier) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the representation constraint.
   IF ( (rep_constraint<>'')          AND
        (rep_constraint<>'grid')      AND 
        (rep_constraint<>'gridhyper') AND
        (rep_constraint<>'grid4D')    AND
        (rep_constraint<>'grid5D')    AND
        (rep_constraint<>'grid6D')    AND
        (rep_constraint<>'grid7D')    AND
        (rep_constraint<>'grid8D')    AND
        (rep_constraint<>'grid9D')    AND
        (rep_constraint<>'grid10D')   AND 
        (rep_constraint<>'grid11D')   AND
        (rep_constraint<>'grid12D')   AND
        (rep_constraint<>'grid13D')   AND
        (rep_constraint<>'grid14D')   AND
        (rep_constraint<>'grid15D') ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the dimensionality of the grid.
   IF ( ((rep_constraint='grid')      AND (grid1.dimension_count<4)) OR 
        ((rep_constraint='gridhyper') AND (grid1.dimension_count<4)) OR 
        ((rep_constraint='grid4D')    AND (grid1.dimension_count<>4)) OR 
        ((rep_constraint='grid5D')    AND (grid1.dimension_count<>5)) OR 
        ((rep_constraint='grid6D')    AND (grid1.dimension_count<>6)) OR 
        ((rep_constraint='grid7D')    AND (grid1.dimension_count<>7)) OR 
        ((rep_constraint='grid8D')    AND (grid1.dimension_count<>8)) OR 
        ((rep_constraint='grid9D')    AND (grid1.dimension_count<>9)) OR 
        ((rep_constraint='grid10D')   AND (grid1.dimension_count<>10)) OR 
        ((rep_constraint='grid11D')   AND (grid1.dimension_count<>11)) OR 
        ((rep_constraint='grid12D')   AND (grid1.dimension_count<>12)) OR 
        ((rep_constraint='grid13D')   AND (grid1.dimension_count<>13)) OR 
        ((rep_constraint='grid14D')   AND (grid1.dimension_count<>14)) OR 
        ((rep_constraint='grid15D')   AND (grid1.dimension_count<>15)) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;

FUNCTION element_valid ( rep_constraint      : STRING(80);
                         position_constraint : STRING(80);
                         coord_constraint    : STRING(80); 
                         coord_precision     : INTEGER;  
                         property_constraint : STRING(80);  
                         property_precision  : INTEGER;
                         tuple_count         : INTEGER;
                         grid1               : emi_grid;
                         geom1               : emi_geom_relationship;
                         grid2               : emi_grid;
                         geom2               : emi_geom_relationship;
                         sparse_flag         : BOOLEAN;
                         value_connection    : emi_index_type;
                         descriptor          : LIST OF emi_descriptor;
                         index_leaf          : SET OF emi_index_leaf;
                         property_leaf       : SET OF emi_property_leaf
                        ) : BOOLEAN;
-- Return false if the element values are not valid.
   LOCAL
     cnt : INTEGER;
     pos : STRING;
   END_LOCAL;

 -- If one is given, then both must be given.
   IF ( EXISTS(geom2) <> EXISTS(grid2) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the property constraint.
   IF ( property_constraint='none' ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the validity of the property leaf.
   IF ( NOT leaf_property_valid (grid1.dimension_count,tuple_count,
                 property_constraint,property_leaf )) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the validity of the descriptor and the index leaf.
   IF ( NOT element_descriptor_valid (tuple_count,sparse_flag, 
                   grid1,grid2,value_connection,descriptor,index_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the dimensionality of the grid.
   cnt := grid1.dimension_count; 
   IF ( ((cnt=1) AND (grid1.grid_type<>emi_grid_type.regular)) OR
        ((cnt=2) AND ((grid1.grid_type<>emi_grid_type.regular) AND
                      (grid1.grid_type<>emi_grid_type.trimesh))) OR
        ((cnt=3) AND ((grid1.grid_type<>emi_grid_type.regular) AND
                      (grid1.grid_type<>emi_grid_type.unstructured_3d) AND
                      (grid1.grid_type<>emi_grid_type.cornerpoint))) OR
        ((cnt>3) AND (grid1.grid_type<>emi_grid_type.regular)) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the position constraint.
   pos := position_constraint;
   IF ( (pos<>'')        AND
        (pos<>'node')    AND 
        (pos<>'edge')    AND
        (pos<>'face')    AND 
        (pos<>'cell')    AND 
        (pos<>'hyper4')  AND 
        (pos<>'hyper5')  AND 
        (pos<>'hyper6')  AND  
        (pos<>'hyper7')  AND 
        (pos<>'hyper8')  AND 
        (pos<>'hyper9')  AND 
        (pos<>'hyper10') AND 
        (pos<>'hyper11') AND 
        (pos<>'hyper12') AND 
        (pos<>'hyper13') AND 
        (pos<>'hyper14') AND 
        (pos<>'hyper15') ) THEN
     RETURN (FALSE);
   END_IF;
   IF ( ((pos='node')    AND (value_connection<>emi_index_type.INDEX_NODE)) OR 
        ((pos='edge')    AND (value_connection<>emi_index_type.INDEX_EDGE)) OR
        ((pos='face')    AND (value_connection<>emi_index_type.INDEX_FACE)) OR 
        ((pos='cell')    AND (value_connection<>emi_index_type.INDEX_CELL)) OR 
        ((pos='hyper4')  AND (value_connection<>emi_index_type.INDEX_4)) OR 
        ((pos='hyper5')  AND (value_connection<>emi_index_type.INDEX_5)) OR 
        ((pos='hyper6')  AND (value_connection<>emi_index_type.INDEX_6)) OR 
        ((pos='hyper7')  AND (value_connection<>emi_index_type.INDEX_7)) OR 
        ((pos='hyper8')  AND (value_connection<>emi_index_type.INDEX_8)) OR 
        ((pos='hyper9')  AND (value_connection<>emi_index_type.INDEX_9)) OR 
        ((pos='hyper10') AND (value_connection<>emi_index_type.INDEX_10)) OR 
        ((pos='hyper11') AND (value_connection<>emi_index_type.INDEX_11)) OR 
        ((pos='hyper12') AND (value_connection<>emi_index_type.INDEX_12)) OR 
        ((pos='hyper13') AND (value_connection<>emi_index_type.INDEX_13)) OR 
        ((pos='hyper14') AND (value_connection<>emi_index_type.INDEX_14)) OR 
        ((pos='hyper15') AND (value_connection<>emi_index_type.INDEX_15)) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the representation constraint.
   IF ( (rep_constraint<>'')                AND
        (rep_constraint<>'trimesh')         AND 
        (rep_constraint<>'unstructured_3d') AND 
        (rep_constraint<>'cornerpoint')     AND
        (rep_constraint<>'grid1to2d') AND 
        (rep_constraint<>'grid1to2d') AND
        (rep_constraint<>'grid')      AND 
        (rep_constraint<>'gridhyper') AND
        (rep_constraint<>'grid1d')    AND 
        (rep_constraint<>'grid2d')    AND 
        (rep_constraint<>'grid3d')    AND 
        (rep_constraint<>'grid4d')    AND 
        (rep_constraint<>'grid5d')    AND 
        (rep_constraint<>'grid6d')    AND
        (rep_constraint<>'grid7d')    AND
        (rep_constraint<>'grid8d')    AND
        (rep_constraint<>'grid9d')    AND
        (rep_constraint<>'grid10d')   AND
        (rep_constraint<>'grid11d')   AND
        (rep_constraint<>'grid12d')   AND
        (rep_constraint<>'grid13d')   AND
        (rep_constraint<>'grid14d')   AND
        (rep_constraint<>'grid15d')   ) THEN
     RETURN (FALSE);
   END_IF;
 
 -- Check the instantiated representation.
   IF ( ((rep_constraint='trimesh') AND 
                             (grid1.grid_type=emi_grid_type.trimesh)) OR
        ((rep_constraint='unstructured_3d') AND 
                             (grid1.grid_type=emi_grid_type.unstructured_3d)) OR
        ((rep_constraint='cornerpoint') AND 
                             (grid1.grid_type=emi_grid_type.cornerpoint)) OR
        ((rep_constraint LIKE 'grid*') AND
                             (grid1.grid_type=emi_grid_type.regular)) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check the dimensionality of the grid.
   cnt := grid1.dimension_count; 
   IF ( (cnt<1) OR
        ((rep_constraint='trimesh')         AND (cnt<>2)) OR
        ((rep_constraint='unstructured_3d') AND (cnt<>3)) OR
        ((rep_constraint='cornerpoint')     AND (cnt<>3)) OR
        ((rep_constraint='grid1to2d')       AND (cnt>2)) OR 
        ((rep_constraint='grid1to2d')       AND (cnt>2)) OR 
        ((rep_constraint='grid')            AND (cnt<1)) OR 
        ((rep_constraint='gridhyper')       AND (cnt<4)) OR
        ((rep_constraint='grid1d')          AND (cnt<>1)) OR 
        ((rep_constraint='grid2d')          AND (cnt<>2)) OR 
        ((rep_constraint='grid3d')          AND (cnt<>3)) OR 
        ((rep_constraint='grid4d')          AND (cnt<>4)) OR 
        ((rep_constraint='grid5d')          AND (cnt<>5)) OR 
        ((rep_constraint='grid6d')          AND (cnt<>6)) OR 
        ((rep_constraint='grid7d')          AND (cnt<>7)) OR 
        ((rep_constraint='grid8d')          AND (cnt<>8)) OR 
        ((rep_constraint='grid9d')          AND (cnt<>9)) OR 
        ((rep_constraint='grid10d')         AND (cnt<>10)) OR 
        ((rep_constraint='grid11d')         AND (cnt<>11)) OR 
        ((rep_constraint='grid12d')         AND (cnt<>12)) OR 
        ((rep_constraint='grid13d')         AND (cnt<>13)) OR 
        ((rep_constraint='grid14d')         AND (cnt<>14)) OR 
        ((rep_constraint='grid15d')         AND (cnt<>15)) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION geometry_coord_valid ( system : coordinate_system ; 
                                 coords : LIST OF emi_geometry_leaf 
                               ) : BOOLEAN;
 -- Return FALSE if the units do not match what is allowed by the axes.
   LOCAL
     uom : LIST[0:?] OF ref_unit_of_measure;
   END_LOCAL;
   REPEAT i := LOINDEX(coords) TO HIINDEX(coords);
     uom[i] := coords[i].ordinal_unit;
   END_REPEAT;
   IF ( NOT coordinate_units_valid (system,uom) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION geometry_descriptor_valid (tuple_count     : INTEGER;
                                     grid1           : emi_grid;
                                     descriptor      : LIST OF emi_descriptor;
                                     index_leaf      : SET OF emi_index_leaf;
                                     coordinate_leaf : LIST OF emi_geometry_leaf
                                    ) : BOOLEAN;
 -- Return TRUE if the descriptor and index_leaf appear to be valid.

   LOCAL
     sparse_flag     : boolean := FALSE;
     partial_allowed : boolean := FALSE;
     grid2           : emi_grid;      -- Always NULL for everything but element.
   END_LOCAL;


 -- Check the validity of all non-geometry aspects.
   IF ( NOT descriptor_valid (tuple_count, emi_index_type.INDEX_NODE, 
                              sparse_flag, partial_allowed, grid, grid2, 
                              descriptor, index_leaf) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Only one grid is allowed for geometries.
   REPEAT i := LOINDEX(index_leaf) TO HIINDEX(index_leaf);
     IF ( index_leaf[i].grid_number<>1 ) THEN
       RETURN (FALSE);
     END_IF;
   END_REPEAT;

 -- The number of coordinate system axes must be greater than or equal
 -- to the number of dimensions of the grid.
   IF  ( SIZEOF(coordinate_leaf) < grid1.dimension_count ) THEN
     RETURN (FALSE);
   END_IF;

 -- The number of coordinate values must equal the expected value count.
   REPEAT i := LOINDEX(coordinate_leaf) TO HIINDEX(coordinate_leaf);
     IF ( SIZEOF(QUERY( tmp <* TYPEOF(coordinate_leaf[i]) | 
               tmp LIKE '*EMI_GEOMETRY_LEAF_VARIABLE*' ))<>0 ) THEN
     -- Each node gets a value.
       IF ( SIZEOF(coordinate_leaf[i].ordinal_values) <> tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
     ELSE
       IF ( SIZEOF(QUERY( tmp <* TYPEOF(coordinate_leaf[i]) | 
                 tmp LIKE '*EMI_GEOMETRY_LEAF_UNEQUAL*' ))<>0 ) THEN
       -- Unequal step based on grid index.
         REPEAT j := LOINDEX(descriptor) TO HIINDEX(descriptor);
         -- Check the value count against the appropriate grid axis.
           IF ( descriptor[j].dimension=coordinate_leaf[i].unequal_index ) THEN
             IF ( SIZEOF(coordinate_leaf[i].ordinal_values) <> 
                         descriptor[J].count ) THEN
               RETURN (FALSE);
             END_IF;
             ESCAPE;
           END_IF;
         END_REPEAT;
       ELSE
       -- Equal step based on grid index.
         IF ( SIZEOF(coordinate_leaf[i].step)>grid1.dimension_count ) THEN
           RETURN (FALSE);
         END_IF;
       END_IF;
     END_IF;
   END_REPEAT;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION descriptor_valid (tuple_count     : INTEGER;
                            connection_type : emi_index_type;
                            sparse_flag     : BOOLEAN; 
                            partial_allowed : BOOLEAN;
                            grid1           : emi_grid;
                            grid2           : emi_grid;
                            descriptor      : LIST OF emi_descriptor;
                            index_leaf      : SET OF emi_index_leaf ) : BOOLEAN;
 -- Return TRUE if the descriptor or index leaf is valid.
 -- Parameter partial_allowed indicates whether the value count must 
 -- match the grid count. TRUE indicates no.

   LOCAL
     idx         : INTEGER;
     index_type  : SET[1:?] OF emi_index_type;
     index2_type : SET[1:?] OF emi_index_type;
   -- Correspondence of index type to axis order. 
   -- That is, the first axis is index "i".
     axis_index : LIST[1:15] OF UNIQUE emi_index_type
        := [emi_index_type.INDEX_I, emi_index_type.INDEX_J, emi_index_type.INDEX_K, 
            emi_index_type.INDEX_L, emi_index_type.INDEX_M, emi_index_type.INDEX_N,
            emi_index_type.INDEX_O, emi_index_type.INDEX_P, emi_index_type.INDEX_Q, 
            emi_index_type.INDEX_R, emi_index_type.INDEX_S, emi_index_type.INDEX_T, 
            emi_index_type.INDEX_U, emi_index_type.INDEX_V, emi_index_type.INDEX_W];
   -- Correspondence of index type to element dimensionality. 
   -- That is, the lowest dimensional element is "node".
     elem_index : LIST[1:16] OF UNIQUE emi_index_type 
        := [emi_index_type.INDEX_NODE, emi_index_type.INDEX_EDGE, 
            emi_index_type.INDEX_FACE, emi_index_type.INDEX_CELL, 
            emi_index_type.INDEX_4,    emi_index_type.INDEX_5,  
            emi_index_type.INDEX_6,    emi_index_type.INDEX_7,    
            emi_index_type.INDEX_8,    emi_index_type.INDEX_9,    
            emi_index_type.INDEX_10,   emi_index_type.INDEX_11,
            emi_index_type.INDEX_12,   emi_index_type.INDEX_13, 
            emi_index_type.INDEX_14,   emi_index_type.INDEX_15];
   END_LOCAL;

   IF ( (NOT sparse_flag) AND EXISTS(grid2) ) THEN
   -- A second grid requires a sparse representation.
     RETURN (FALSE);
   END_IF;

 -- Compare the tuple count against the number of values allowed by the grid.
   IF ( NOT tuple_count_valid (tuple_count,connection_type,
                               partial_allowed,grid,grid2,elem_index) ) THEN
     RETURN (FALSE);
   END_IF;

 -- Distinguish between the two different scenarios.
   IF ( EXISTS(descriptor) ) THEN
   -- Dense population in one regular grid.
     IF ( sparse_flag OR 
          ((grid1.grid_type<>emi_grid_type.regular) AND 
           (grid1.grid_type<>emi_grid_type.cornerpoint)) ) THEN
       RETURN (FALSE);
     END_IF;
     REPEAT i := LOINDEX(descriptor) TO LOINDEX(descriptor);
     -- Needed for next function call.
       index_type[i] := descriptor[i].dimension;
     END_REPEAT;
     IF ( NOT index_type_valid (grid1.grid_type, connection_type, 
                 grid1.dimension_count, index_type, axis_index, elem_index) ) THEN
       RETURN (FALSE);
     END_IF;
   -- Compare the number of tuples specified by the descriptor against 
   -- the number of tuples allowed by the grid and the connection element.
     REPEAT i := LOINDEX(descriptor) TO HIINDEX(descriptor);
     -- The index count cannot be greater than the number of points on the 
     -- corresponding grid axis. 
     -- This check excludes the grid-element indices.
       REPEAT j := LOINDEX(axis_index) TO 
                   (LOINDEX(axis_index)+grid1.dimension_count-1);
         IF ( descriptor[i].dimension=axis_index[j] ) THEN
         -- Found the corresponding axis.
           IF ( partial_allowed ) THEN
             IF ( descriptor[i].count > grid1.grid_axis_point_count[j] ) THEN
               RETURN (FALSE);
             END_IF;
             IF ( descriptor[i].lower_bound < grid1.grid_axis_lower_bound[j] ) THEN
               RETURN (FALSE);
             END_IF;
           ELSE
             IF ( descriptor[i].count <> grid1.grid_axis_point_count[j] ) THEN
               RETURN (FALSE);
             END_IF;
             IF ( descriptor[i].lower_bound <> grid1.grid_axis_lower_bound[j] ) THEN
               RETURN (FALSE);
             END_IF;
           END_IF;
           ESCAPE;
         END_IF;
       END_REPEAT;
     -- The number of values cannot be greater than the maximum count 
     -- of any "grid element" for this dimension.
       REPEAT j := LOINDEX(elem_index) TO 
                   (LOINDEX(elem_index)+grid1.dimension_count);
         IF ( descriptor[i].dimension=elem_index[j] ) THEN
         -- Found a connection grid-element.
           IF ( partial_allowed ) THEN
             IF ( descriptor[i].count > 
                max_count(grid1.grid_type,descriptor[i].dimension, 
                          grid1.dimension_count, elem_index) ) THEN
               RETURN (FALSE);
             END_IF;
           ELSE
             IF ( descriptor[i].count <> 
                max_count(grid1.grid_type,descriptor[i].dimension, 
                          grid1.dimension_count, elem_index) ) THEN
               RETURN (FALSE);
             END_IF;
           END_IF;
           ESCAPE;
         END_IF;
       END_REPEAT;
     END_REPEAT;
   ELSE
   -- Irregular grid or sparse population of regular grid.
     IF ( NOT EXISTS(index_leaf) ) THEN   
       RETURN (FALSE);
     END_IF;
     idx := 0;
     REPEAT i := LOINDEX(index_leaf) TO HIINDEX(index_leaf);
     -- The number of index values must equal the expected value count.
       IF ( SIZEOF(index_leaf[i].data_values) <> tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
     -- Specific to first grid.
       IF ( index_leaf[i].grid_number=1 ) THEN
       -- Check the leaf index values against what is allowed by the regular grid.
         IF ( NOT index_leaf_valid(grid1,index_leaf[i],
                                   axis_index,elem_index) ) THEN
           RETURN (FALSE);
         END_IF;
       -- Needed for next function call.
         idx := idx +1;
         index_type[idx] := index_leaf[i].index_name;
       END_IF;
     END_REPEAT;
   -- Insure that the index types are valid for the first grid.
     IF ( NOT index_type_valid (grid1.grid_type, connection_type, 
                 grid1.dimension_count, index_type, axis_index, elem_index) ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( EXISTS (grid2) ) THEN
       idx := 0;
       REPEAT i := LOINDEX(index_leaf) TO HIINDEX(index_leaf);
         IF ( index_leaf[i].grid_number=2 ) THEN
        -- Check the index values against what is allowed by the grid.
           IF ( NOT index_leaf_valid(grid2,index_leaf[i],
                                     axis_index,elem_index) ) THEN
             RETURN (FALSE);
           END_IF;
         -- Needed for next function call.
           idx := idx +1;
           index2_type[idx] := index_leaf[i].index_name;
         END_IF;
       END_REPEAT;
     -- Insure that the index types are valid for the second grid.
       IF ( NOT index_type_valid (grid2.grid_type, connection_type, 
                 grid2.dimension_count, index2_type, axis_index, elem_index) ) THEN
         RETURN (FALSE);
       END_IF;
     END_IF;
   -- Insure that the index tuples are all unique.
   -- Note that this check also works for the combination of grid1 and grid2.
   -- That is, either the grid1 tuple is unique or the combination of
   -- grid1 and grid2 tuples are unique.
     IF ( NOT index_tuples_valid (index_leaf) ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION index_leaf_valid (grid       : emi_grid;
                            index_leaf : emi_index_leaf;
                            axis_index : LIST OF emi_index_type;
                            elem_index : LIST OF emi_index_type  ) : BOOLEAN;
 -- Return TRUE if the index leaf "index values" are valid.
 -- axis_index is the correspondence of index type to axis order. 
 -- That is, the first axis is index "i".
 -- elem_index is the correspondence of index type to element dimensionality. 
 -- That is, the lowest dimensional element is "node".
   LOCAL
     max : INTEGER;
   END_LOCAL;

   IF ( (grid.grid_type<>emi_grid_type.regular) AND 
        (grid.grid_type<>emi_grid_type.cornerpoint) ) THEN;
   -- Not a regular grid.
     RETURN(TRUE);
   END_IF;

 -- Check against grid axis first.
   REPEAT i := LOINDEX(axis_index) TO LOINDEX(axis_index)+grid.dimension_count-1;
     IF ( index_leaf.index_name=axis_index[i] ) THEN
     -- Found the corresponding axis.
       max :=  grid.grid_axis_lower_bound[i] +grid_axis_point_count[i] -1;
       REPEAT j := LOINDEX(index_leaf.data_values) TO 
                   HIINDEX(index_leaf.data_values);
         IF ( index_leaf.data_values[j] > max ) THEN
           RETURN(FALSE);
         END_IF;
       END_REPEAT;
       RETURN(TRUE);
     END_IF;
   END_REPEAT;

 -- Does not represent a grid axis.
 -- Check against the possible grid elements.
   REPEAT i := LOINDEX(elem_index) TO LOINDEX(elem_index)+grid.dimension_count;
     IF ( index_leaf.index_name=elem_index[i] ) THEN
     -- This is a valid grid element.
       max :=  max_count(grid.grid_type,index_leaf.index_name,
                         grid.dimension_count, elem_index);
       REPEAT j := LOINDEX(index_leaf.data_values) TO 
                   HIINDEX(index_leaf.data_values);
         IF ( index_leaf.data_values[j] > max ) THEN
           RETURN(FALSE);
         END_IF;
       END_REPEAT;
       RETURN(TRUE);
     END_IF;
   END_REPEAT;

 -- Neither a valid grid axis index nor a valid grid element index.
   RETURN (FALSE);
 END_FUNCTION;


 FUNCTION index_tuples_valid (index_leaf : SET OF emi_index_leaf ) : BOOLEAN;
 -- Return TRUE if the index tuples are unique.
 -- This assumes that the bounds of index aggregates are the same.
   LOCAL
     tuple : LIST OF INTEGER;
     start : INTEGER;
     stop  : INTEGER;
   END_LOCAL;

   start := LOINDEX(index_leaf[LOINDEX(index_leaf)].data_values);
   stop  := LOINDEX(index_leaf[LOINDEX(index_leaf)].data_values);
   REPEAT i := start TO stop;
     tuple := next_tuple(i,index_leaf);
     REPEAT j := start TO stop;
       IF ( j<>i ) THEN
         IF ( tuple = next_tuple(j,index_leaf) ) THEN
           RETURN(FALSE);
         END_IF;
       END_IF;
     END_REPEAT;
   END_REPEAT;

   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION next_tuple (ix          : INTEGER;
                      index_leaf  : SET OF emi_index_leaf ) : LIST of INTEGER;
 -- Returns ix'th tuple from the set of demultiplexed leaf values.
 -- This assumes that all aggregates have the same lower index.
   LOCAL
     tuple : LIST OF INTEGER;
   END_LOCAL;

   REPEAT i := LOINDEX(index_leaf) TO HIINDEX(index_leaf);
     tuple[i] := index_leaf[i].data_values[ix];
   END_REPEAT;
   
   RETURN (tuple);
 END_FUNCTION;


 FUNCTION tuple_count_valid (tuple_count     : INTEGER;
                             connection_type : emi_index_type;
                             partial_allowed : BOOLEAN;
                             grid1           : emi_grid;
                             grid2           : emi_grid;
                             elem_index      : LIST OF emi_index_type 
                            ) : BOOLEAN;
 -- Return TRUE if the tuple count is valid when compared against the grid.
 -- Parameter partial_allowed indicates whether the value count must 
 -- match the grid count. TRUE indicates no.
 -- elem_index is the correspondence of index type to element dimensionality. 
 -- That is, the lowest dimensional element is "node".
   LOCAL
     grid_cnt    : INTEGER;
     grid2_cnt   : INTEGER;
   END_LOCAL;

 -- Determine the maximum number of tuples allowed by the grid 
 -- and the grid connection.
   IF ( (grid1.grid_type=emi_grid_type.regular) OR 
        (grid1.grid_type=emi_grid_type.cornerpoint) ) THEN
   -- regular grid.
   -- First check the grid.
     grid_cnt := 1;
     REPEAT i := LOINDEX(grid1.grid_axis_point_count) TO 
                 HIINDEX(grid1.grid_axis_point_count);
       grid_cnt := grid_cnt * grid1.grid_axis_point_count[i];
     END_REPEAT;
     IF ( grid_cnt<>grid1.node_count ) THEN
       RETURN (FALSE);
     END_IF;
   -- Add any space for grid elements.
     IF ( ((grid1.grid_type=emi_grid_type.cornerpoint) AND 
           (connection_type=elem_index[1])) OR
          ((grid1.grid_type<>emi_grid_type.cornerpoint) AND 
           ((connection_type<>elem_index[1]) AND
            (connection_type<>elem_index[grid1.dimension_count+1]))) ) THEN
     -- Need space for the grid-element index.
       grid_cnt := grid_cnt * max_count(grid1.grid_type,connection_type,
                                        grid1.dimension_count, elem_index);
     END_IF;
   ELSE
   -- Irregular grid.
     grid_cnt := grid1.node_count;
   END_IF;
   IF ( EXISTS (grid2) ) THEN
   -- Determine the total number of points on the second grid.
     IF ( grid2.grid_type=emi_grid_type.regular ) THEN
     -- regular grid.
     -- First check the grid.
       grid2_cnt := 1;
       REPEAT i := LOINDEX(grid2.grid_axis_point_count) TO 
                   HIINDEX(grid2.grid_axis_point_count);
         grid2_cnt := grid2_cnt * grid2.grid_axis_point_count[i];
       END_REPEAT;
       IF ( grid2_cnt<>grid2.node_count ) THEN
         RETURN (FALSE);
       END_IF;
     -- Add any space for grid elements.
       IF ( ((grid2.grid_type=emi_grid_type.cornerpoint) AND 
             (connection_type=elem_index[1])) OR
            ((grid2.grid_type<>emi_grid_type.cornerpoint) AND 
             ((connection_type<>elem_index[1]) AND
              (connection_type<>elem_index[grid1.dimension_count+1]))) ) THEN
       -- Need space for the grid-element index.
         grid2_cnt := grid2_cnt * max_count(grid2.grid_type,connection_type,
                                            grid2.dimension_count, elem_index);
       END_IF;
     ELSE
     -- Irregular grid.
       grid2_cnt := grid1.node_count;
     END_IF;
     IF ( grid2_cnt < grid_count) THEN
     -- We need the minimum size of a grid.
       grid_cnt := grid2_count;
     END_IF;
   END_IF;

   IF ( partial_allowed ) THEN
   -- The number of values cannot be greater than the number of points on the grid. 
     IF ( tuple_count > grid_cnt ) THEN
       RETURN (FALSE);
     END_IF;
   ELSE
   -- The number of values must equal the number of points on the grid. 
     IF ( tuple_count <> grid_cnt ) THEN
       RETURN (FALSE);
     END_IF;
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION index_type_valid (grid_type       : emi_grid_type;
                            connection_type : emi_index_type;
                            dimension_count : INTEGER;
                            index_type      : SET  OF emi_index_type;
                            axis_index      : LIST OF emi_index_type;
                            elem_index      : LIST OF emi_index_type 
                           ) : BOOLEAN;
 -- Return TRUE if the index type is valid.
 -- It is assumed that the parameters all apply to one grid.
 -- axis_index = Correspondence of index type to regular grid axis order. 
 --              For example, the first axis is INDEX_I.
 -- elem_index = Correspondence of index type to element dimensionality. 
 --              For example, the lowest dimensional element is INDEX_NODE.
   LOCAL
     found      : BOOLEAN;
     idx        : INTEGER;
     index_type : SET[1:?] OF emi_index_type;
   END_LOCAL;

 -- Check the number of index types and the allowed types. 
 -- This combination inherently results in a failure for types 
 -- which are not allowed.


 -- Make sure the connection type is valid for this dimension.
   REPEAT i := LOINDEX(elem_index) TO LOINDEX(elem_index)+dimension_count;
     IF ( connection_type=elem_index[i] ) THEN
       found := TRUE;
       ESCAPE;
     END_IF;
   END_REPEAT;
   IF ( NOT found ) THEN
     RETURN (FALSE);
   END_IF;

 -- Check for the irregular grids.
   IF ( (grid_type = emi_grid_type.trimesh) OR
        (grid_type = emi_grid_type.unstructured_3d) ) THEN
   -- These irregular grids only utilize one index.
     IF ( SIZEOF(index_type)<>1 ) THEN
       RETURN (FALSE);
     END_IF;
     IF ( connection_type<>index_type[LOINDEX(index_type)] ) THEN
       RETURN (FALSE);
     END_IF;
     RETURN (TRUE);
   END_IF;

 -- Regular grid. In general, the node and the highest dimensional element is 
 -- referenced by the i,j,k indexes with an additional index if connecting to 
 -- the other "elements" (e.g., edge or face).
   IF ( grid_type=emi_grid_type.cornerpoint ) THEN
   -- Corner point is different because the node element is required 
   -- when the connection type is node.
     IF (connection_type=emi_index_type.INDEX_FACE) THEN
       IF ( SIZEOF(index_type)<>dimension_count ) THEN
         RETURN (FALSE);
       END_IF;
     ELSE
       IF ( SIZEOF(index_type)<>(dimension_count+1) ) THEN
         RETURN (FALSE);
       END_IF;
       IF ( (SIZEOF(QUERY ( tmp <* index_type | tmp=connection_type ))<>1) ) THEN
         RETURN (FALSE);
       END_IF;
     END_IF;
   ELSE
   -- Not cornerpoint.
     IF ( (connection_type=emi_index_type.INDEX_NODE) or 
          (connection_type=elem_index[LOINDEX(elem_index)+dimension_count]) ) THEN
       IF ( SIZEOF(index_type)<>(dimension_count) ) THEN
         RETURN (FALSE);
       END_IF;
     ELSE
       IF ( SIZEOF(index_type)<>dimension_count+1 ) THEN
       -- Need an index for the "other" element.
         RETURN (FALSE);
       END_IF;
       IF ( (SIZEOF(QUERY ( tmp <* index_type | tmp=connection_type ))<>1) ) THEN
         RETURN (FALSE);
       END_IF;
     END_IF;
   END_IF;

 -- For a regular grid, there must be one and only one unique index 
 -- type for each axis.
   REPEAT i := LOINDEX(axis_index) TO (LOINDEX(axis_index)+dimension_count-1);
     IF ( (SIZEOF(QUERY ( tmp <* index_type | tmp=axis_index[i] ))<>1) ) THEN
       RETURN (FALSE);
     END_IF;
   END_REPEAT;

   RETURN (TRUE);
 END_FUNCTION;

   
 FUNCTION dms_valid (degree_value : INTEGER;
                     minute_value : INTEGER;
                     second_value : REAL ) : BOOLEAN;
 -- Return false if the dms values are not valid.

 (*The following are enforced by the meta type.
   IF ( EXISTS(degree_value)<>EXISTS(minute_value) THEN
     RETURN(FALSE);
   END_IF;
   IF ( EXISTS(degree_value)<>EXISTS(second_value) THEN
     RETURN(FALSE);
   END_IF;
 *)
   IF NOT( ((degree_value <= 0) AND 
            (minute_value <= 0) AND 
            (second_value <= 0)) OR 
           ((degree_value >= 0) AND 
            (minute_value >= 0) AND 
            (second_value >= 0)) ) THEN
   -- Must be same sign.
     RETURN(FALSE);
   END_IF;
   IF ( ABS(minute_value) > 59 ) THEN
     RETURN(FALSE);
   END_IF;
   IF ( ABS(second_value) >= 60 ) THEN
     RETURN(FALSE);
   END_IF;
   RETURN(TRUE);
 END_FUNCTION;


 FUNCTION max_count (grid_type       : emi_grid_type;
                     index_type      : emi_index_type;
                     dimension_count : INTEGER;
                     elem_index      : LIST OF emi_index_type  
                    ) : INTEGER;
 -- Return the upper bound for indices specified for the index type and dimension.
 -- The upper bound will always be 1 for node and for the highest dimensional
 -- element. The exception is that corner point node will be 8.
 -- elem_index = Correspondence of index type to element dimensionality. 
 --              For example, the lowest dimensional element is INDEX_NODE.
   LOCAL
     ele_dim  : INTEGER; -- The dimensionality of the element (index_type).
     count    : INTEGER;
   END_LOCAL;

   IF ( grid_type=emi_grid_type.cornerpoint ) THEN
     IF ( index_type=emi_index_type.INDEX_NODE ) THEN
       RETURN (8);
     END_IF;
     IF ( index_type=emi_index_type.INDEX_EDGE ) THEN
       RETURN (12);
     END_IF;
     IF ( index_type=emi_index_type.INDEX_FACE ) THEN
       RETURN (6);
     END_IF;
     IF ( index_type=emi_index_type.INDEX_CELL ) THEN
       RETURN (1);
     END_IF;
     RETURN (0);
   END_IF;

   REPEAT i := LOINDEX(elem_index) TO (LOINDEX(elem_index)+dimension_count);
     IF ( index_type=elem_index[i] ) THEN
       -- The first element in the array has dimensionality of zero.
       ele_dim := i -LOINDEX(elem_index);
       count := (factorial(dimension_count)) / 
                (factorial(dimension_count-ele_dim) * factorial(ele_dim));
       RETURN (count);
     END_IF;
   END_REPEAT;

   RETURN (0);
 END_FUNCTION;


 FUNCTION factorial (int_value : INTEGER ) : INTEGER;
 -- Return the factorial of the specified positive integer.
   LOCAL
     i_fact  : INTEGER;
   END_LOCAL;
   i_fact := 1;
   REPEAT i :=2 TO int_value BY 1;
     i_fact := i_fact * i;
   END_REPEAT;
   RETURN (i_fact);
 END_FUNCTION;


 FUNCTION element_descriptor_valid (tuple_count      : INTEGER;
                                    sparse_flag      : BOOLEAN; 
                                    grid1            : emi_grid;
                                    grid2            : emi_grid;
                                    value_connection : emi_index_type;
                                    descriptor       : LIST OF emi_descriptor;
                                    index_leaf       : SET OF emi_index_leaf
                                   ) : BOOLEAN;
 -- Return TRUE if the descriptor and index_leaf appear to be valid.

   LOCAL
     partial_allowed : BOOLEAN := TRUE;
   END_LOCAL;

 -- Check the validity of all non-element aspects.
   IF ( NOT descriptor_valid (tuple_count, value_connection, 
                              sparse_flag, partial_allowed, grid, grid2, 
                              descriptor, index_leaf) ) THEN
     RETURN (FALSE);
   END_IF;
   RETURN (TRUE);
 END_FUNCTION;


 FUNCTION leaf_property_valid ( dimension_count     : INTEGER;
                                tuple_count         : INTEGER;
                                property_constraint : STRING ; 
                                property_leaf       : SET OF emi_property_leaf 
                              ) : BOOLEAN;
 -- Return FALSE if the properties are not allowed by the constraint 
 -- or the value count is not valid.
   LOCAL
     prp         : SET[0:?] OF property_kind;
     value_type  : SET[0:?] OF STRING;
   END_LOCAL;

   IF ( EXISTS(property_leaf) ) THEN
     REPEAT i := LOINDEX(property_leaf) TO HIINDEX(property_leaf);
       prp[i] := property_leaf[i].kind;
     END_REPEAT;
   END_IF;
   IF ( NOT property_set_valid (property_constraint,prp) ) THEN
     RETURN (FALSE);
   END_IF;

   IF ( SIZEOF(property_leaf)=0 ) THEN
   -- Nothing to check.
     RETURN (TRUE);
   END_IF;

 -- The number of property values must equal the expected value count.
   REPEAT i := LOINDEX(property_leaf) TO 
               HIINDEX(property_leaf);
     value_type := TYPEOF(property_leaf[i]);
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_QUANTITY_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_quantity_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
     -- Check the unit of measure against those allowed by the property kind.
       IF ( SIZEOF(QUERY( tmp <* 
                          kind.quantity_constraint.alternative_unit_of_measure |  
                          tmp.unit_of_measure = property_unit ))<>1 ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_REAL_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_real_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_INTEGER_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_integer_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_EQUAL_STEP_VALUES*' ))>0 ) THEN
       -- Equal step based on grid/list index.
       IF ( SIZEOF(coordinate_leaf.step)>dimension_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_STRING_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_string_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_BOOLEAN_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_boolean_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_LOGICAL_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_logical_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_COMPLEX_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_complex_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_RATIONAL_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_rational_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_RATIO_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_ratio_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_DMS_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_dms_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_DATE_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_date_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_YEARMONTHINTERVAL_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_yearmonthinterval_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
     IF ( SIZEOF(QUERY( tmp <* value_type | 
                        tmp LIKE '*EMI_LEAF_DAYTIMEINTERVAL_VALUES*' ))>0 ) THEN
       IF ( SIZEOF(property_leaf[i]\emi_leaf_yearmonthinterval_values.values) <> 
            tuple_count ) THEN
         RETURN (FALSE);
       END_IF;
       ESCAPE;
     END_IF;
   END_REPEAT;

   RETURN (TRUE);
 END_FUNCTION;

© Copyright 1994-2001 POSC. All rights reserved.