Skip to content

Old data from the Discontinuous Galerkin page...

Greg Sjaardema edited this page May 23, 2024 · 1 revision

This is documentation/design information that was written previously. I don't want to lose it just yet, so stored here. Some/All of this may be invalid;

Basis Definition: HGRAD_QUAD_C2_FEM Example:

DoF DoF definition
ordinal subc dim subc ordinal subc DoF ord subc num DoF ξ η ζ
0 0 0 0 1 -1.0 -1.0 0.0
1 0 1 0 1 1.0 -1.0 0.0
2 0 2 0 1 1.0 1.0 0.0
3 0 3 0 1 -1.0 1.0 0.0
4 1 0 0 1 0.0 -1.0 0.0
5 1 1 0 1 1.0 0.0 0.0
6 1 2 0 1 0.0 1.0 0.0
7 1 3 0 1 -1.0 0.0 0.0
8 2 0 0 1 0.0 0.0 0.0
MAX maxScDim=2 maxScOrd=3 maxDfOrd=0 -
  • Based on https://docs.trilinos.org/dev/packages/intrepid/doc/html/classIntrepid_1_1Basis.html
  • DoF ordinal is not stored -- implicit numbering [Range: unlimited]
  • subc dim: dimension of the subcell associated with the specified DoF ordinal -- 0 node, 1 edge, 2 face, 3 volume [Range: 0..3]
  • subc ordinal: ordinal of the subcell relative to its parent cell -- 0..n for each ordinal with the same subc dim [Range: <= DoF ordinal]
  • subc DoF ordinal: ordinal of the DoF relative to the subcell
  • subc num DoF: cardinality of the DoF set associated with this subcell.
  • ξ, η, ζ -- Parametric coordinate location of the DoF (Only first ndim values are valid)

Potential Exodus/NetCDF CDF Representation:

  • Entity attribute on an element block:
    • Basis@name, text [HGRAD_QUAD_C2_FEM in example above]
    • Basis@cardinality, scalar [9 in example above]
    • Basis@subc_dim, vector of size cardinality integers
    • Basis@subc_ordinal, vector of size cardinality integers
    • Basis@subc_dof_ordinal, vector of size cardinality integers
    • Basis@subc_num_dof, vector of size cardinality integers
    • Basis@xi, vector of size cardinality doubles
    • Basis@eta (if 2D/3D), vector of size cardinality doubles
    • Basis@zeta (if 3D), vector of size cardinality doubles
    • Do we need type or degree or parse from name:
      • HCURL_TRI_I1_FEM -- type=FEM, degree=1
      • HGRAD_WEDGE_C2_FEM -- type=FEM, degree=2

As an example, assume HCURL_TRI_I1_FEM basis being used on an element block of triangles:

        int64 connect1(num_el_in_blk1, num_nod_per_el1) ;
                connect1:elem_type = "tri4" ; // Current Exodus
                connect1:Basis@name = "HCURL_TRI_I1_FEM" ; 
                connect1:Basis@cardinality = 3 ;
                connect1:Basis@subc_dim = 1, 1, 1 ;
                connect1:Basis@subc_ordinal = 0, 1, 2 ;
                connect1:Basis@subc_dof_ordinal = 0, 0, 0 ;
                connect1:Basis@subc_num_dof = 1, 1, 1 ;
                connect1:Basis@xi = 0.5, 0.5, 0.0 ;
                connect1:Basis@eta = 0.0, 0.5, 0.5 ;

Field Representation

  • Need to support representing fields of size Basis@cardinality by num_element_in_block at each timestep.

    • The Basis@cardinality will be consistent for a block, but can be different for different blocks.
  • Can also use this concept to store fields at quadrature points?

  • Easiest modification is to store Basis@cardinality separate fields as normal element fields.

    • This has problems of ordering -- which field corresponds to which basis point
      • can use convention of field name suffix [0..Basis@cardinality)
      • Relates back to the Basis entity attributes stored on the element block (or assembly?).
        • This will give the ordering and the parametric location of the field component within the element.
  • There is additional metadata which groups field components together and indicates how fields relate to each other.

  • For example, for a scalar field Temperature with basis cardinality 3, we would store Temperature_0, Temperature_1, and Temperature_2 as normal fields.

    • Each field would have metadata or attribute which indicates the "base name" (Temperature), cardinality, and which component this field is. E.g., Temperature_0 would have attributes base_name: Temperature, cardinality: 3, component_index: 0
      • Field probably needs a reference to the basis if it is a "basis-type" field...
      • The parametric location of this degree of freedom could be determined by referencing the Basis definition on the element block.
    • There would be an API function which would return this information for each entity.
      • Number of fields (Temperature_0, Temperature_1, Temperature_2 would be 1 field)
      • For each field, return a structure defining:
        • Base field name
        • Number of components
        • Name of each component field in correct order (Is storage allocated by callee, or by api function...)
    • How are "composite fields" handled -- 3D vector at each basis point or quadrature point:
      • A vector field Velocity with component fields Velocity_X, Velocity_Y, and Velocity_Z
      • This field exists at each of 3 basis points... Velocity_X_0, Velocity_Y_0, Velocity_Z_0, ..., Velocity_Z_2
      • Does the Velocity_X_0 field return a component count of 3 or 9 and is the base name Velocity or Velocity_X
        • If the base name is Velocity_X, then there is no place to store an attribute to recover the Velocity field unless there is additional metadata stored somewhere for that purpose.
        • Maybe there is something similar to the assembly structure which stores the field hierarchy. Disadvantage is that it needs to be done for each entity, so lots of metadata.

CDF Representation -- Basis Field

        # Temperature field on HCURL_TRI_I1_FEM Basis -- Temperature_0, Temperature_1, Temperature_2
        double vals_elem_var7eb1(time_step, num_el_in_blk1) ;
             vals_elem_var7eb1:name = "Temperature_2" ;  // Not sure this is needed since it duplicates existing element variable names...
             vals_elem_var7eb1:base_name = "Temperature" ;
             vals_elem_var7eb1:component_count = 3 ; 
             vals_elem_var7eb1:component_index = 2 ;
             vals_elem_var7eb1:storage_type = "Basis3" ;  // Tensor, SymTensor, Quaternion, Count, Quadrature, ...
             vals_elem_var7eb1:storage_type = "EX_BASIS" ; // Alternative with pre-defined types...

CDF Representation -- Composite Fields

        # Velocity_X_0, Velocity_Y_0, Velocity_Z_0, Velocity_X_1, ..., Velocity_Z_2 
        double vals_elem_var7eb1(time_step, num_el_in_blk1) ;
             vals_elem_var7eb1:name = "Velocity_X_0" ;  // Not sure this should be there since duplicates existing element variable names...
             vals_elem_var7eb1:base_name = "Velocity" ;
             vals_elem_var7eb1:component_count = 3, 3 ; // First is X,Y,Z second is the 0,1,2
             vals_elem_var7eb1:component_index = 0, 0 ; // How do we know composite cardinality (2 in this case)
             vals_elem_var7eb1:storage_type = "Vector3D, Basis3" ;  // Tensor, SymTensor, Quaternion, Count, Quadrature, ...
             vals_elem_var7eb1:storage_type = "EX_VECTOR_3D, EX_BASIS" ; // Alternative with pre-defined types...

There can be an API function which will return the number of "top-level" fields on the entity. (The current API will still return all of the low-level fields and their names). A second API function will return either an array of structures describing each top-level field or an individual top-level field. Structure will be something like:

typedef struct ex_field
{
  int   offset;  // 0-based, gives the offset to the first component in this field in all the fields on this entity
  char *field_name; // Memory provided by application or by library?
  int   component_count[MAX_FIELD_DEPTH];
  int   storage_type[MAX_FIELD_DEPTH]; // EX_VECTOR_3D, EX_TENSOR33, EX_BASIS, EX_QUADRATURE, EX_USER
} ex_field;

Internally, the exodus library will maintain a mapping which lets it efficiently access the fields corresponding to each component of the "top-level" field.

  • This could probably be provided in the ex_field struct, but then that would mean the user would have to allocate additional memory for that storage.
  • The library could build that structure internally and then return a pointer to it for use by the application which would eliminate all memory allocation issues... (This looks like a good idea; investigate further)
  • Is there a way to indicate any common fields across different entities (The same top-level field exists on element blocks 1, 3, 5, 9)...
  • API Function will permit accessing all data in the field, or a particular component.
    • Interleave would be all of each component (all X, all Y, all Z; not x1, y1, z1, x2, y2, z2, ..., xn, yn, zn)
  • Could add dimension and units to the ex_field struct. Avoids multiple API functions... (Although API function could just query the internal ex_field storage)

Internals On file open, or lazily, (or via specific API call), the Exodus library could parse all fields on all (or specified) entities and keep that storage internally for faster query from application. The library could then return a pointer to either the entire field data for a specific entity or to a specific entry on that specific entity. The internal storage would use an allocated array of ex_field structs. This would remove some issues with memory allocation ownership and might make it easier on the client as they don't need to do queries and then memory allocations... Makes it easier to modify the ex_field structure if needed... API

  int ex_get_field_param(int exoid, ex_entity_type obj_type, int obj_id, ex_field *field_data[]); // Get all
  int ex_get_field_param(int exoid, ex_entity_type obj_type, int obj_id, int entry, ex_field *field_data); // Get one
  void ex_put_field_param(int exoid, ex_entity_type obj_type, int obj_id, int num_entry, ex_field* const field_data[]);
  • Note the use of field instead of var -- Maybe use that convention to distinguish between new/old API?
  • If field_data is NULL, then just return the count; otherwise set field_data to point to exodus internal data.
  • Returns the number of "high-level" fields on this object.
  • Is it an error to mix old/new API (var vs field) on an entity type?
    • OK on read, but old api will just have access to scalar fields.

Storage Type

  • Predefined symbols EX_FIELD_VECTOR or EX_FIELD_VECTOR_3D that indicate which field is being stored.
  • Do we need the component count VECTOR_3D vs VECTOR. Can possibly get the information from the component count, but may be ambiguous in the symmetric or anti-symmetric tensor cases.
  • Probably best to fully specify field names -- EX_VECTOR_3D
  • Alternative is to just use strings. Easier to extend, but also ambiguous...

Comments/Discussion from 09/15/202 Meeting --

ALTERNATIVE

  • The above is complete, but very verbose
  • Each variable (field) has several entries:
        double vals_elem_var7eb1(time_step, num_el_in_blk1) ;
             vals_elem_var7eb1:name = "Velocity_X_0" ;  // Not sure this should be there since duplicates existing element variable names...
             vals_elem_var7eb1:base_name = "Velocity" ;
             vals_elem_var7eb1:component_count = 3, 3 ; // First is X,Y,Z second is the 0,1,2
             vals_elem_var7eb1:component_index = 0, 0 ; // How do we know composite cardinality (2 in this case)
             vals_elem_var7eb1:storage_type = "Vector3D, Basis3" ;  // Tensor, SymTensor, Quaternion, Count, Quadrature, ...
             vals_elem_var7eb1:storage_type = "EX_VECTOR_3D, EX_BASIS" ; // Alternative with pre-defined types...
  • Several of these are repeated for each component of each variable. For example, if there is a Velocity vector field of type "Basis3", there would be nine individual field components each with the above information (or slight modification)...
  • Would it be better to leave the variables alone. Keep the CDF representation as it currently exists.
  • The element block(s) (for example) would have:
    • Basis information would be stored at global level
    • Information about each Basis (or higher-order) variable:
      • Base field name "Velocity" in this case
      • Storage Type -- Either "Vector3D, Basis3" or EX_VECTOR_3D, EX_BASIS
    • From this information, it is possible to generate the names of the variable components ("Velocity_X_0", "Velocity_Y_0", "Velocity_Z_0", ..., "Velocity_Z_2")
    • A non-basis higher-order field would be something like:
      • Base field name "Stress"
      • Storage Type -- "EX_SYM_TENSOR_3D"
      • Optional COMPONENT_SEPARATOR = "_"

This has some advantages and some disadvantages over the scheme above:

Advantages

  • less verbose
  • can parse the information once on the element block instead of on each variable

Disadvantages

  • If a variable is deleted or renamed and the corresponding information on the element block is not modified, will be out of sync
    • Although, if a single variable component is deleted in original scheme, would also have problems)
  • More disconnected -- information about variables is stored somewhere else instead of on variable itself.

Nomenclature

  • A Variable is the current exodus transient data type.
  • A Field is a collection of 1 or more scalar variables. For example a Field of type EX_VECTOR_3D would be a collection of 3 variables.
  • A Composite Field is a Field consisting of 2 or more Fields. For example, a composite field representing a symmetric stress tensor at each of 8 integration points.
  • The nesting of a Field is the number of Fields in the Composite Field. In the above example, the nesting would be 2; a "normal" Field would have a nesting of 1.
  • The component count is the number of variables contained in a field.
    • For example, a EX_VECTOR_3D (three-dimensional vector) would have a component count of 3.
    • The tensor at integration point example would have a component count of [6,4] -- 6 components for each symmetric tensor (EX_SYM_TENSOR_33) and 4 for the integration points.
  • The component separator is zero-or-one characters which separate the Field name from the individual component names.
    • For example, if the separator is _, the field name is Velocity, and the field type is EX_VECTOR_3D, then the names of the variables that are collected in the field are Velocity_X, Velocity_Y, and Velocity_Z (or Velocity_x, Velocity_y, and Velocity_z)
    • If the separator is empty (char 0), then the variables would be VelocityX, VelocityY, and VelocityZ or the lower-case alternative.
  • A Sequence field type is a collection of N variables with suffices 1..N.
    • For example, a sequence field Temperature of size 4 would collect the variables Temperature_1, Temperature_2, Temperature_3, and Temperature_4.

Predefined Field Types.

The following field types are predefined:

Enum Component Count Suffices
EX_FIELD_TYPE_UNKNOWN invalid
EX_FIELD_TYPE_USER_DEFINED variable
EX_FIELD_TYPE_SEQUENCE variable 1, 2, ..., N
EX_BASIS specified by basis 1, 2, 3, ..., N ?
EX_QUADRATURE specified by quadrature 1, 2, 3, ..., N?
EX_SCALAR 1 -none-
EX_VECTOR_1D 1 x
EX_VECTOR_2D 2 x, y
EX_VECTOR_3D 3 x, y, z
EX_QUATERNION_2D 2 s, q
EX_QUATERNION_3D 4 x, y, z, q
EX_FULL_TENSOR_36 9 xx, yy, zz, xy, yz, zx, yx, zy, xz
EX_FULL_TENSOR_32 5 xx, yy, zz, xy, yx
EX_FULL_TENSOR_22 4 xx, yy, xy, yx
EX_FULL_TENSOR_16 7 xx, xy, yz, zx, yx, zy, xz
EX_FULL_TENSOR_12 3 xx, xy, yx
EX_SYM_TENSOR_33 6 xx, yy, zz, xy, yz, zx
EX_SYM_TENSOR_31 4 xx, yy, zz, xy
EX_SYM_TENSOR_21 3 xx, yy, xy
EX_SYM_TENSOR_13 4 xx, xy, yz, zx
EX_SYM_TENSOR_11 2 xx, xy
EX_SYM_TENSOR_10 1 xx
EX_ASYM_TENSOR_03 3 xy, yz, zx
EX_ASYM_TENSOR_02 2 xy, yz
EX_ASYM_TENSOR_01 1 xy
EX_MATRIX_22 4 11, 12, 21, 22
EX_MATRIX_33 9 11, 12, 13, 21, 22, 23, 31, 32, 33
EX_FIELD_TYPE_INVALID invalid

CDF

As an example, assume HCURL_TRI_I1_FEM basis being used on an element block of triangles and there are 2 basis fields Velocity and Displacement one non-basis tensor field Stress

        int64 connect1(num_el_in_blk1, num_nod_per_el1) ;
                connect1:elem_type = "tri3" ; // Current Exodus
                connect1:Basis@name = "HCURL_TRI_I1_FEM" ; 
                connect1:Basis@cardinality = 3 ;
                connect1:Basis@subc_dim = 1, 1, 1 ;
                connect1:Basis@subc_ordinal = 0, 1, 2 ;
                connect1:Basis@subc_dof_ordinal = 0, 0, 0 ;
                connect1:Basis@subc_num_dof = 1, 1, 1 ;
                connect1:Basis@xi = 0.5, 0.5, 0.0 ;
                connect1:Basis@eta = 0.0, 0.5, 0.5 ;
                connect1:Field_1@name = "Velocity" ;
                connect1:Field_1@storage_type = EX_VECTOR_3D, EX_BASIS ;
                connect1:Field_2@name = "Displacement" ;
                connect1:Field_2@component_separator = "_$" ;
                connect1:Field_2@storage_type = EX_VECTOR_3D, EX_BASIS ;
                connect1:Field_3@name = "Stress" ;
                connect1:Field_3@storage_type = EX_SYM_TENSOR_3D ;
                connect1:Field_3@component_separator = "%" ;

This would result in the client code "looking for" the following variables:

  • Field 1 -- Variables "Velocity_X_0, Velocity_Y_0, Velocity_Z_0, ..., Velocity_Z_2"
  • Field 2 -- Variables "Displacement_X$0, ... , Displacement_Z$2"
  • Field 3 -- Variables "Stress%XX, Stress%YY, Stress%ZZ, Stress%XY, Stress%YZ, Stress%ZX"
  • [Either look for both upppercase and lowercase suffices or have a default and an option to specify different than default]
  • A "non-predefined-field-type" could list the component count and optionally the suffix list.
    • Maybe a predefined type of "EX_SEQUENCE" for "field_1, field_2, field_3, ..., field_n"
    • An "EX_USER_DEFINED" which requires component count and list of that many suffices.

Simplified Basis Example...

Scalar basis field Temperature

        int64 connect1(num_el_in_blk1, num_nod_per_el1) ;
                connect1:elem_type = "tri3" ; // Current Exodus
                connect1:Basis@name = "HGRAD_TRI_C1_FEM" ; 
                connect1:Basis@cardinality = 3 ;
                connect1:Basis@subc_dim = 0, 0, 0 ;
                connect1:Basis@subc_ordinal = 0, 1, 2 ;
                connect1:Basis@subc_dof_ordinal = 0, 0, 0 ;
                connect1:Basis@subc_num_dof = 1, 1, 1 ;
                connect1:Basis@xi =  0, 1, 0 ;
                connect1:Basis@eta = 0, 0, 1 ;
                connect1:Field_1@name = "Temperature" ;
                connect1:Field_1@storage_type = EX_BASIS ;
                
  • From Basis@cardinality = 3, we know the Temperature field will have 3 components.
    • The first field component is variable Temperature_0 and is located at the node (subc_dim = 0) at location 0, 0
    • The second field component is variable Temperature_1 and is located at the node (subc_dim = 0) at location 1, 0
    • The third field component is variable Temperature_2 and is located at the node (subc_dim = 0) at location 0, 1
    • QUESTION: Current IOSS uses 1-based suffices for the "sequence" type fields. Should the "basis" fields also use 1-based, or switch to 0-based to match intrepid...

If the application does not want to store all the information about the basis, the minimal information needed would be:

        int64 connect1(num_el_in_blk1, num_nod_per_el1) ;
                connect1:elem_type = "tri3" ; // Current Exodus
                connect1:Basis@cardinality = 3 ;
                connect1:Field_1@name = "Temperature" ;
                connect1:Field_1@storage_type = EX_BASIS ;                

Which would enable a consumer of the database to map the Temperature field to its 3 variable components. The location of the variable components would be ambiguous without additional information. If producers and consumers are using a common library, for example, Intrepid; it would be possible to specify the basis name, for example connect1:Basis@name = "HGRAD_TRI_C1_FEM" ; which would give enough information to query the location information from the Intrepid library.

Extension -- Quadrature

This mechanism could also be used to specify the locaitions of quadrature points in an element. In the example below, we have an example of a beam which uses nonstandard integration point locations, as both the top and bottom surfaces have an integration point in order to immediately detect the onset of the nonlinear effects.

Scalar basis field Force

        int64 connect1(num_el_in_blk1, num_nod_per_el1) ;
                connect1:elem_type = "beam2" ; // Current Exodus
                connect1:Quadrature@Name = "Optional Name of this quadrature scheme" ;
                connect1:Quadrature@cardinality = 5 ;
                connect1:Quadrature@dimension = 1 ;
                connect1:Quadrature@xi =  -1, -0.6, 0, 0.6, 1.0 ; // Do we need to specify an extent? -1..1 or -0.5..0.5
                connect1:Quadrature@weight = 0.125, 0.5787036, 0.5925926, 0.5787036, 0.125
                connect1:Field_1@name = "Force" ;
                connect1:Field_1@storage_type = EX_QUADRATURE ;

The xi specifies the through-thickness location of the quadrature points (in a valid range of -1.0 .. 1.0) and weight specifies the weight at each quadrature point. There would be 5 scalar variables that are the components of the Force field Force_1, Force_2, Force_3, Force_4, Force_5.

Minimally, this could be specified as:

        int64 connect1(num_el_in_blk1, num_nod_per_el1) ;
                connect1:elem_type = "beam2" ; // Current Exodus
                connect1:Quadrature@cardinality = 5 ;
                connect1:Field_1@name = "Force" ;
                connect1:Field_1@storage_type = EX_QUADRATURE ;

Where only the number of components (Quadrature@cardinality) is specified. This would be enough information to map the field Force to the 5 variables that are its components.

This should hopefully solve or address some of the needs raised in #185.