import numpy as nm
from sfepy.base.base import output, iter_dict_of_lists, Struct, basestr
[docs]def parse_approx_order(approx_order):
"""
Parse the uniform approximation order value (str or int).
"""
ao_msg = 'unsupported approximation order! (%s)'
force_bubble = False
discontinuous = False
if approx_order is None:
return 'iga', force_bubble, discontinuous
elif isinstance(approx_order, basestr):
if approx_order.startswith('iga'):
return approx_order, force_bubble, discontinuous
try:
ao = int(approx_order)
except ValueError:
mode = approx_order[-1].lower()
if mode == 'b':
ao = int(approx_order[:-1])
force_bubble = True
elif mode == 'd':
ao = int(approx_order[:-1])
discontinuous = True
else:
raise ValueError(ao_msg % approx_order)
if ao < 0:
raise ValueError(ao_msg % approx_order)
elif ao == 0:
discontinuous = True
return ao, force_bubble, discontinuous
[docs]def parse_shape(shape, dim):
if isinstance(shape, basestr):
try:
shape = {'scalar' : (1,),
'vector' : (dim,)}[shape]
except KeyError:
raise ValueError('unsupported field shape! (%s)', shape)
elif isinstance(shape, int):
shape = (shape,)
return shape
[docs]def fields_from_conf(conf, regions):
fields = {}
for key, val in conf.iteritems():
field = Field.from_conf(val, regions)
fields[field.name] = field
return fields
[docs]class Field(Struct):
"""
Base class for fields.
"""
_all = None
@staticmethod
[docs] def from_args(name, dtype, shape, region, approx_order=1,
space='H1', poly_space_base='lagrange'):
"""
Create a Field subclass instance corresponding to a given space.
Parameters
----------
name : str
The field name.
dtype : numpy.dtype
The field data type: float64 or complex128.
shape : int/tuple/str
The field shape: 1 or (1,) or 'scalar', space dimension (2, or (2,)
or 3 or (3,)) or 'vector', or a tuple. The field shape determines
the shape of the FE base functions and is related to the number of
components of variables and to the DOF per node count, depending
on the field kind.
region : Region
The region where the field is defined.
approx_order : int/str
The FE approximation order, e.g. 0, 1, 2, '1B' (1 with bubble).
space : str
The function space name.
poly_space_base : str
The name of polynomial space base.
Notes
-----
Assumes one cell type for the whole region!
"""
conf = Struct(name=name, dtype=dtype, shape=shape, region=region.name,
approx_order=approx_order, space=space,
poly_space_base=poly_space_base)
return Field.from_conf(conf, {region.name : region})
@staticmethod
[docs] def from_conf(conf, regions):
"""
Create a Field subclass instance based on the configuration.
"""
if Field._all is None:
import sfepy
from sfepy.base.base import load_classes
field_files = [ii for ii
in sfepy.get_paths('sfepy/discrete/fem/fields*.py')
if 'fields_base.py' not in ii] \
+ sfepy.get_paths('sfepy/discrete/iga/fields*.py')
Field._all = load_classes(field_files, [Field], ignore_errors=True,
name_attr='family_name')
table = Field._all
space = conf.get('space', 'H1')
poly_space_base = conf.get('poly_space_base', 'lagrange')
key = space + '_' + poly_space_base
approx_order = parse_approx_order(conf.approx_order)
ao, force_bubble, discontinuous = approx_order
region = regions[conf.region]
if region.kind == 'cell':
# Volume fields.
kind = 'volume'
if discontinuous:
cls = table[kind + '_' + key + '_discontinuous']
else:
cls = table[kind + '_' + key]
obj = cls(conf.name, conf.dtype, conf.shape, region,
approx_order=approx_order[:2])
else:
# Surface fields.
kind = 'surface'
cls = table[kind + '_' + key]
obj = cls(conf.name, conf.dtype, conf.shape, region,
approx_order=approx_order[:2])
return obj
def _setup_kind(self):
name = self.get('family_name', None,
'An abstract Field method called!')
aux = name.split('_')
self.space = aux[1]
self.poly_space_base = aux[2]
[docs] def get_dofs_in_region(self, region, merge=False, clean=False,
warn=False, igs=None):
"""
Return indices of DOFs that belong to the given region.
"""
if igs is None:
igs = region.igs
nods = []
for ig in self.igs:
if not ig in igs:
nods.append(None)
continue
nn = self.get_dofs_in_region_group(region, ig)
nods.append(nn)
if merge:
nods = [nn for nn in nods if nn is not None]
nods = nm.unique(nm.hstack(nods))
elif clean:
for nn in nods[:]:
if nn is None:
nods.remove(nn)
if warn is not None:
output(warn + ('%s' % region.name))
return nods
[docs] def clear_mappings(self, clear_all=False):
"""
Clear current reference mappings.
"""
self.mappings = {}
if clear_all:
self.mappings0 = {}
[docs] def save_mappings(self):
"""
Save current reference mappings to `mappings0` attribute.
"""
self.mappings0 = self.mappings.copy()
[docs] def get_mapping(self, ig, region, integral, integration,
get_saved=False, return_key=False):
"""
For given region, integral and integration type, get a reference
mapping, i.e. jacobians, element volumes and base function
derivatives for Volume-type geometries, and jacobians, normals
and base function derivatives for Surface-type geometries
corresponding to the field approximation.
The mappings are cached in the field instance in `mappings`
attribute. The mappings can be saved to `mappings0` using
`Field.save_mappings`. The saved mapping can be retrieved by
passing `get_saved=True`. If the required (saved) mapping
is not in cache, a new one is created.
Returns
-------
geo : CMapping instance
The reference mapping.
mapping : VolumeMapping or SurfaceMapping instance
The mapping.
key : tuple
The key of the mapping in `mappings` or `mappings0`.
"""
key = (region.name, integral.order, ig, integration)
if get_saved:
out = self.mappings0.get(key, None)
else:
out = self.mappings.get(key, None)
if out is None:
out = self.create_mapping(ig, region, integral, integration)
self.mappings[key] = out
if return_key:
out = out + (key,)
return out