Skip to content

Commit

Permalink
further work on function spaces
Browse files Browse the repository at this point in the history
  • Loading branch information
tommbendall committed Jul 26, 2023
1 parent 40e8baf commit f253414
Showing 1 changed file with 104 additions and 29 deletions.
133 changes: 104 additions & 29 deletions gusto/function_spaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,37 @@

# HDiv spaces are keys, HCurl spaces are values
hdiv_hcurl_dict = {'RT': 'RTE',
'RTF': 'RTE'
'RTF': 'RTE',
'BDM': 'BDME',
'BDMF': 'BDME',
'RTCF': 'RTCE',
'CG': 'CG'}

# HCurl spaces are keys, HDiv spaces are values
# Can't just reverse the other dictionary as values are not necessarily unique
hcurl_hdiv_dict = {'BDME': 'RT'}
hcurl_hdiv_dict = {'RT': 'RTF',
'RTE': 'RTF',
'BDM': 'BDMF',
'BDME': 'BDMF',
'RTCE': 'RTCF',
'CG': 'CG'}

# Degree to use for H1 space for a particular family
def h1_degree(family, l2_degree):
"""
Return the degree of the H1 space, for a particular de Rham complex.
Args:
family (str): the family of the HDiv or HCurl elements.
l2_degree (int): the degree of the L2 space at the bottom of the complex
Returns:
int: the degree of the H1 space at the top of the complex.
"""
if family in ['CG', 'RT', 'RTE', 'RTF', 'RTCE', 'RTCF']:
return l2_degree + 1
elif family in ['BDM', 'BDME', 'BDMF']:
return l2_degree + 2

class Spaces(object):
"""Object to create and hold the model's finite element spaces."""
Expand Down Expand Up @@ -90,7 +113,7 @@ def __call__(self, name, family=None, degree=None,

elif name == "DG1_equispaced":
# Special case as no degree arguments need providing
value = self.build_dg_space(1, 1, variant='equispaced', name='DG1_equispaced')
value = self.build_l2_space(1, 1, variant='equispaced', name='DG1_equispaced')

else:
check_degree_args('Spaces', self.mesh, degree, horizontal_degree, vertical_degree)
Expand All @@ -101,15 +124,17 @@ def __call__(self, name, family=None, degree=None,

# Loop through name and family combinations
if name == "HDiv" and family in ["BDM", "RT", "CG", "RTCF", "RTF", "BDMF"]:
value = self.build_hdiv_space(family, horizontal_degree, vertical_degree)
hdiv_family = hcurl_hdiv_dict[family]
value = self.build_hdiv_space(hdiv_family, horizontal_degree, vertical_degree)
elif name == "HCurl" and family in ["BDM", "RT", "CG", "RTCE", "RTE", "BDME"]:
value = self.build_hcurl_space(family, horizontal_degree, vertical_degree)
hcurl_family = hdiv_hcurl_dict[family]
value = self.build_hcurl_space(hcurl_family, horizontal_degree, vertical_degree)
elif name == "theta":
value = self.build_theta_space(horizontal_degree, vertical_degree)
elif family == "DG":
value = self.build_dg_space(horizontal_degree, vertical_degree, name=name)
value = self.build_l2_space(horizontal_degree, vertical_degree, name=name)
elif family == "CG":
value = self.build_cg_space(horizontal_degree, vertical_degree, name=name)
value = self.build_h1_space(horizontal_degree, vertical_degree, name=name)
else:
raise ValueError(f'There is no space corresponding to {name}')
setattr(self, name, value)
Expand All @@ -129,36 +154,57 @@ def build_compatible_spaces(self, family, horizontal_degree,
Args:
family (str): the family of the horizontal part of the HDiv space.
horizontal_degree (int): the polynomial degree of the horizontal
part of the DG space.
part of the L2 space.
vertical_degree (int, optional): the polynomial degree of the
vertical part of the DG space. Defaults to None. Must be
vertical part of the L2 space. Defaults to None. Must be
specified if the mesh is extruded.
Returns:
tuple: the created compatible :class:`FunctionSpace` objects.
"""
if self.extruded_mesh and not self._initialised_base_spaces:
# Base spaces need building, while horizontal and vertical degrees
# need specifying separately
# need specifying separately. Vtheta needs returning.
self.build_base_spaces(family, horizontal_degree, vertical_degree)
Vcg = self.build_h1_space(cg_degree(family, horizontal_degree),
cg_degree(family, vertical_degree), name='H1')
Vcg = self.build_h1_space(h1_degree(family, horizontal_degree),
h1_degree(family, vertical_degree), name='H1')
setattr(self, "H1", Vcg)
Vcurl = self.build_hcurl_space(family, horizontal_degree, vertical_degree)
hcurl_family = hdiv_hcurl_dict[family]
Vcurl = self.build_hcurl_space(hcurl_family, horizontal_degree, vertical_degree)
setattr(self, "HCurl", Vcurl)
Vu = self.build_hdiv_space(family, horizontal_degree, vertical_degree)
hdiv_family = hcurl_hdiv_dict[family]
Vu = self.build_hdiv_space(hdiv_family, horizontal_degree, vertical_degree)
setattr(self, "HDiv", Vu)
Vdg = self.build_l2_space(horizontal_degree, vertical_degree, name='L2')
setattr(self, "L2", Vdg)
Vth = self.build_theta_space(horizontal_degree, vertical_degree)
setattr(self, "theta", Vth)
return Vu, Vdg, Vth
else:
return Vcg, Vcurl, Vu, Vdg, Vth
elif self.mesh.topological_dimension() > 1:
# 2D: two de Rham complexes (hcurl or hdiv) with 3 spaces
# 3D: one de Rham complexes with 4 spaces
# either way, build all spaces
Vcg = self.build_h1_space(h1_degree(family, horizontal_degree), name='H1')
setattr(self, "H1", Vcg)
hcurl_family = hdiv_hcurl_dict[family]
Vcurl = self.build_hcurl_space(hcurl_family, horizontal_degree+1)
setattr(self, "HCurl", Vcurl)
hdiv_family = hcurl_hdiv_dict[family]
Vu = self.build_hdiv_space(family, horizontal_degree+1)
setattr(self, "HDiv", Vu)
Vdg = self.build_dg_space(horizontal_degree, vertical_degree, name='L2')
Vdg = self.build_l2_space(horizontal_degree, vertical_degree, name='L2')
setattr(self, "L2", Vdg)
return Vcg, Vcurl, Vu, Vdg
else:
# 1D domain, de Rham complex has 2 spaces
# CG, hdiv and hcurl spaces should be the same
Vcg = self.build_h1_space(horizontal_degree+1, name='H1')
setattr(self, "H1", Vcg)
setattr(self, "HCurl", Vcurl)
setattr(self, "HDiv", Vu)
Vdg = self.build_l2_space(horizontal_degree, name='L2')
setattr(self, "L2", Vdg)
return Vu, Vdg
return Vcg, Vdg

def build_base_spaces(self, family, horizontal_degree, vertical_degree):
"""
Expand All @@ -167,32 +213,61 @@ def build_base_spaces(self, family, horizontal_degree, vertical_degree):
Args:
family (str): the family of the horizontal part of the HDiv space.
horizontal_degree (int): the polynomial degree of the horizontal
part of the DG space.
part of the L2 space.
vertical_degree (int): the polynomial degree of the vertical part of
the DG space.
the L2 space.
"""
cell = self.mesh._base_mesh.ufl_cell().cellname()

# horizontal base spaces
self.S1 = FiniteElement(family, cell, horizontal_degree+1)
self.S2 = FiniteElement("DG", cell, horizontal_degree)
self.base_elt_hori_hdiv = FiniteElement(hdiv_family, cell, horizontal_degree+1)
self.base_elt_hori_hcurl = FiniteElement(hcurl_family, cell, horizontal_degree+1)
self.base_elt_hori_dg = FiniteElement("DG", cell, horizontal_degree)

# vertical base spaces
self.T0 = FiniteElement("CG", interval, vertical_degree+1)
self.T1 = FiniteElement("DG", interval, vertical_degree)
self.base_elt_vert_cg = FiniteElement("CG", interval, vertical_degree+1)
self.base_elt_vert_dg = FiniteElement("DG", interval, vertical_degree)

self._initialised_base_spaces = True

def build_hcurl_space(self, family, horizontal_degree, vertical_degree=None):
"""
Builds and returns the HCurl :class:`FunctionSpace`.
Args:
family (str): the family of the horizontal part of the HCurl space.
horizontal_degree (int): the polynomial degree of the horizontal
part of the L2 space from the de Rham complex.
vertical_degree (int, optional): the polynomial degree of the
vertical part of the L2 space from the de Rham complex.
Defaults to None. Must be specified if the mesh is extruded.
Returns:
:class:`FunctionSpace`: the HCurl space.
"""
if self.extruded_mesh:
if not self._initialised_base_spaces:
if vertical_degree is None:
raise ValueError('vertical_degree must be specified to create HCurl space on an extruded mesh')
self.build_base_spaces(family, horizontal_degree, vertical_degree)
Vh_elt = HCurl(TensorProductElement(self.S1, self.T1))
Vv_elt = HCurl(TensorProductElement(self.S2, self.T0))
V_elt = Vh_elt + Vv_elt
else:
cell = self.mesh.ufl_cell().cellname()
V_elt = FiniteElement(family, cell, horizontal_degree)
return FunctionSpace(self.mesh, V_elt, name='HCurl')

def build_hdiv_space(self, family, horizontal_degree, vertical_degree=None):
"""
Builds and returns the HDiv :class:`FunctionSpace`.
Args:
family (str): the family of the horizontal part of the HDiv space.
horizontal_degree (int): the polynomial degree of the horizontal
part of the DG space from the de Rham complex.
part of the L2 space from the de Rham complex.
vertical_degree (int, optional): the polynomial degree of the
vertical part of the the DG space from the de Rham complex.
vertical part of the L2 space from the de Rham complex.
Defaults to None. Must be specified if the mesh is extruded.
Returns:
Expand Down Expand Up @@ -284,9 +359,9 @@ def build_h1_space(self, horizontal_degree, vertical_degree=None, name='H1'):
Args:
horizontal_degree (int): the polynomial degree of the horizontal
part of the CG space.
part of the H1 space.
vertical_degree (int, optional): the polynomial degree of the
vertical part of the the CG space. Defaults to None. Must be
vertical part of the H1 space. Defaults to None. Must be
specified if the mesh is extruded.
name (str, optional): name to assign to the function space. Default
is "H1".
Expand All @@ -298,7 +373,7 @@ def build_h1_space(self, horizontal_degree, vertical_degree=None, name='H1'):

if self.extruded_mesh:
if vertical_degree is None:
raise ValueError('vertical_degree must be specified to create CG space on an extruded mesh')
raise ValueError('vertical_degree must be specified to create H1 space on an extruded mesh')
cell = self.mesh._base_mesh.ufl_cell().cellname()
CG_hori = FiniteElement("CG", cell, horizontal_degree)
CG_vert = FiniteElement("CG", interval, vertical_degree)
Expand Down

0 comments on commit f253414

Please sign in to comment.