Source code for src.spaces.main

# -*- coding: utf-8 -*-
# noinspection PyUnresolvedReferences
r"""

.. _docs-space:

Space
=====

Having abstract meshes, you can define abstract finite dimensional function spaces on them.
To do so, we first need to set the target mesh by calling

>>> ph.space.set_mesh(mesh)

Note that if there is only one mesh defined, above command can be omitted.

Then, to define a finite dimensional function space on this mesh, call function ``ph.space.new``,

    .. autofunction:: src.spaces.main.new

So far, we have implemented the following spaces.

.. admonition:: Implemented spaces

    +-------------------------+-----------------+------------------------------+-------------------------------------+
    | **description**         |**abbr.**        |    **arg**                   |    **kwarg**                        |
    +-------------------------+-----------------+------------------------------+-------------------------------------+
    | scalar-valued form      | ``'Lambda'``    | ``k`` : int.                 |   ``orientation``:                  |
    | space                   |                 | It is a :math:`k`-form       |   {``'inner'``, ``'outer'``}.       |
    |                         |                 | space.                       |   The orientation of the form space.|
    |                         |                 |                              |   The default orientation is        |
    |                         |                 |                              |   ``'outer'``.                      |
    |                         |                 |                              |                                     |
    +-------------------------+-----------------+------------------------------+-------------------------------------+

For example, to make spaces of outer orientated 1-forms and 2-forms, do

>>> Out1 = ph.space.new('Lambda', 1, orientation='outer')
>>> Out2 = ph.space.new('Lambda', 2, orientation='outer')

And we can list all existing spaces by calling ``ph.list_spaces`` method,

>>> ph.list_spaces()  # doctest: +ELLIPSIS
Implemented spaces:...

.. automodule:: src.spaces.base
    :undoc-members:

"""

import sys

if './' not in sys.path:
    sys.path.append('./')
from importlib import import_module

_config = {
    'current_mesh': '',
}
_mesh_set = dict()
_space_set = dict()

_sep = ' ->- '


# whenever new space is implemented, add it below.
_implemented_spaces = {
    # indicator: (class path                    ,  class name            , description                 , parameters),
    'Lambda':    ('src.spaces.continuous.Lambda', 'ScalarValuedFormSpace', 'scalar valued k-form space', ['k', ]),
    'bundle':    ('src.spaces.continuous.bundle', 'BundleValuedFormSpace', 'bundle valued k-form space', ['k', ]),
    'bundle-diagonal': (
        'src.spaces.continuous.bundle_diagonal',
        'DiagonalBundleValuedFormSpace',
        'diagonal bundle valued k-form space',
        ['k', ]
    ),
}


[docs] def new(abbr, *args, mesh=None, **kwargs): """Generate a spaces on the mesh. Parameters ---------- abbr : str The abbr. of the space. args : The arguments to be sent to the space. mesh : {:class:`Mesh`, None}, optional We want to generate space on this mesh. If it is ``None``, we use the current target mesh. The default value is ``None``. kwargs : The keyword arguments to be sent to the space. Returns ------- space : The finite dimensional space. """ if _config['current_mesh'] == '' and mesh is None: raise Exception(f"pls set a mesh firstly by using 'space.set_mesh' or specify 'mesh'.") else: pass if isinstance(abbr, str): # make only 1 space pass else: raise NotImplementedError() mesh_sr = _config['current_mesh'] if mesh is None: mesh = _mesh_set[mesh_sr] else: # noinspection PyUnresolvedReferences mesh_sr = mesh._sym_repr if mesh_sr in _mesh_set: pass else: _mesh_set[mesh_sr] = mesh _space_set[mesh_sr] = dict() current_spaces = _space_set[mesh_sr] assert abbr in _implemented_spaces, \ f"space abbr.={abbr} not implemented. do 'ph.space.list_()' to see all implemented spaces." space_class_path, space_class_name = _implemented_spaces[abbr][0:2] space_class = getattr(import_module(space_class_path), space_class_name) space = space_class(mesh, *args, **kwargs) srp = space._sym_repr # do not use __repr__() if srp in current_spaces: pass else: current_spaces[srp] = space space = current_spaces[srp] return space
__all__ = [ '_VarSetting_mass_matrix', # '_VarSetting_d_matrix', # '_VarSetting_d_matrix_transpose', # '_VarSetting_pi_matrix', '_VarSetting_boundary_dp_vector', # '_VarSetting_astA_x_astB_ip_tC', # '_VarSetting_astA_x_B_ip_tC', # '_VarSetting_A_x_astB_ip_tC', # '_VarSetting_A_x_B_ip_C', # nonlinear '_VarSetting_astA_x_astB__dp__tC', # vector <A x B | C>, AB are known '_VarSetting_astA_x_astB__ip__astC_x_tD', # vector (A x B, C x D), ABC known, D test. '_VarSetting_astA_x_astB__dp__astC_x_tD', # vector <A x B | C x D>, ABC known, D test. '_VarSetting_dastA_astA_tp_tC', # '_VarSetting_dastA_tB_tp_astA', '_VarSetting_dtA_astB_tp_astB', '_VarSetting_dA_B_tp_C__1Known', '_VarSetting_dA_B_tp_C__2Known', '_VarSetting_dA_B_tp_C', # nonlinear '_VarSetting_A_B_tp_C__1Known', '_VarSetting_A_B_tp_C__2Known', '_VarSetting_A_B_tp_C', # nonlinear '_VarSetting_IP_matrix_db_bf', # '_VarSetting_IP_matrix_bf_db', # # '_VarSetting_A_x_astB_ip_dC', # (A x B, dC) # '_VarSetting_astA_x_B_ip_dC', # (A x B, dC) # '_VarSetting_astA_x_astB_ip_dC', # (A x B, dC) ] # ------ basic ----------------------------------------------------------------------------------- _VarSetting_mass_matrix = [ r"\mathsf{M}", _sep.join(["Mass:Mat", "{space_pure_lin_repr}", "{d0}", "{d1}"]), ] _VarSetting_d_matrix = [ r"\mathsf{D}", _sep.join(["d:Mat", "{space_pure_lin_repr}", "{d}"]), ] _VarSetting_d_matrix_transpose = [ r"\mathbb{D}", _sep.join(["d:T:Mat", "{space_pure_lin_repr}", "{d}"]), ] _VarSetting_pi_matrix = [ r"\mathsf{P}", _sep.join([ "d:P:Mat", "{space_pure_lin_repr_from}", "{space_pure_lin_repr_to}", "{d_from}", "{d_to}" ]), ] # Natural bc ------------------------------------------------------------------------------------- _VarSetting_boundary_dp_vector = [ # once we know f0, we can find the correct basis functions it wedged with r"\boldsymbol{b}", _sep.join(["BoundaryDP:Vec", "trStar[{f0}]", "tr[{f1}]"]), # <tr star bf0 | tr f1>. ] # (w x u, u) -------------------------------------------------------------------------------------- _VarSetting_astA_x_astB_ip_tC = [ r"\mathsf{c}", _sep.join(["c_ip", "[{A}]", "[{B}]", "[{C}]"]), ] _VarSetting_astA_x_B_ip_tC = [ r"\mathsf{C}", _sep.join(["X_ip", "[{A}]", "[{B}]", "[{C}]"]), ] _VarSetting_A_x_astB_ip_tC = [ r"\boldsymbol{C}", _sep.join(["_Xip", "[{A}]", "[{B}]", "[{C}]"]), ] _VarSetting_A_x_B_ip_C = [ r"\mathsf{X}", _sep.join(["_X_:", "[{A}]", "[{B}]", "[{C}]"]), ] # --------------- <A x B | C> -------------------------------------------------------------------- _VarSetting_astA_x_astB__dp__tC = [ r"\mathsf{\left.x\right|}", _sep.join(["_X_dp", "[{A}]", "[{B}]", "[{C}]"]) ] # --------------- (A x B, C x D) ---------------------------------------------------- _VarSetting_astA_x_astB__ip__astC_x_tD = [ r"\mathsf{XX}", _sep.join(["_X__Xip", "[{A}]", "[{B}]", "[{C}]", "[{D}]"]), ] _VarSetting_astA_x_astB__dp__astC_x_tD= [ r"\mathsf{xx}", _sep.join(["_X__Xdp", "[{A}]", "[{B}]", "[{C}]", "[{D}]"]), ] # --------------- (A x B, dC) ------------------------------------------------------- # _VarSetting_A_x_astB_ip_dC = [ # r"\mathsf{C}_d", # _sep.join(["_XipD:", "[{A}]", "[{B}]", "[{C}]"]) # ] # # _VarSetting_astA_x_B_ip_dC = [ # r"\mathsf{c}_d", # _sep.join(["X_ipD:", "[{A}]", "[{B}]", "[{C}]"]) # ] # # _VarSetting_astA_x_astB_ip_dC = [ # r"\mathsf{X}_d", # _sep.join(["XipD:", "[{A}]", "[{B}]", "[{C}]"]) # ] # -----(dA, B otimes C) -------------------------------------------------------------------------- _VarSetting_dastA_astA_tp_tC = [ r"\left<\mathsf{d\cdot,\cdot\otimes\_}\right>", _sep.join(["d*A--*A-tp-tC", "[{A}]", "[{C}]"]), ] _VarSetting_dastA_tB_tp_astA = [ r"\left<\mathsf{d\cdot,\_\otimes\cdot}\right>", _sep.join(["d*A--tB-tp-*A", "[{A}]", "[{B}]"]), ] _VarSetting_dtA_astB_tp_astB = [ r"\left<\mathsf{d\_,\cdot\otimes\cdot}\right>", _sep.join(["dtA--*B-tp-*B", "[{A}]", "[{B}]"]), ] _VarSetting_dA_B_tp_C__1Known = [ # A, B, C are different; and it must have a test form r"\left<\mathsf{d\_,\_\otimes\_}\right>", _sep.join(["dA--B-tp-C:1:Known", "[{A}]", "[{B}]", "[{C}]", "[{K}]", "[{T}]", "[{U}]"]), ] _VarSetting_dA_B_tp_C__2Known = [ # A, B, C are different; two of them are known, and the rest one is the test form r"\left<\mathsf{d\_,\_\otimes\_}\right>", _sep.join(["dA--B-tp-C:2:Known", "[{A}]", "[{B}]", "[{C}]", "[{K1}]", "[{K2}]", "[{T}]"]), ] _VarSetting_dA_B_tp_C = [ # A, B, C are different; # nonlinear r"\left<\mathsf{d\cdot,\cdot\otimes\cdot}\right>", _sep.join(["dA--B-tp-C", "[{A}]", "[{B}]", "[{C}]"]), ] # (A, B otimes C) ------------------------------------------------------- _VarSetting_A_B_tp_C__1Known = [ # A, B, C are different; and it must have a test form r"\left<\mathsf{\_,\_\otimes\_}\right>", _sep.join(["A--B-tp-C:1:Known", "[{A}]", "[{B}]", "[{C}]", "[{K}]", "[{T}]", "[{U}]"]), ] _VarSetting_A_B_tp_C__2Known = [ # A, B, C are different; two of them are known, and the rest one is the test form r"\left<\mathsf{\_,\_\otimes\_}\right>", _sep.join(["A--B-tp-C:2:Known", "[{A}]", "[{B}]", "[{C}]", "[{K1}]", "[{K2}]", "[{T}]"]), ] _VarSetting_A_B_tp_C = [ # A, B, C are different; # nonlinear r"\left<\mathsf{\cdot,\cdot\otimes\cdot}\right>", _sep.join(["A--B-tp-C", "[{A}]", "[{B}]", "[{C}]"]), ] # (bundle form, special diagonal bundle form)------------------------------------------------------ _VarSetting_IP_matrix_db_bf = [ r"\mathbb{M}_{\mathcal{S}}", _sep.join( ["db-M-bf", "{db_space_pure_lin_repr}", "{bf_space_pure_lin_repr}", "{degree_db}", "{degree_bf}"] ), ] _VarSetting_IP_matrix_bf_db = [ r"\mathbb{M}^{\mathsf{T}}_{\mathcal{S}}", _sep.join( ["bf-M-db", "{bf_space_pure_lin_repr}", "{db_space_pure_lin_repr}", "{degree_bf}", "{degree_db}"] ), ] _default_space_degree_repr = ':D-' _degree_cache = {} def _degree_str_maker(degree): """""" str_degree = degree.__class__.__name__ + str(degree) _degree_cache[str_degree] = degree return str_degree def _str_degree_parser(str_degree): """""" return _degree_cache[str_degree] def set_mesh(mesh): """""" assert mesh.__class__.__name__ == 'Mesh', \ f"I need a Mesh instance." sr = mesh._sym_repr if sr in _mesh_set: pass else: _mesh_set[sr] = mesh _space_set[sr] = dict() _config['current_mesh'] = sr def _list_spaces(): """""" from src.config import RANK, MASTER_RANK if RANK != MASTER_RANK: return else: pass print('Implemented spaces:') print('{:>15} - {}'.format('abbreviation', 'description')) for abbr in _implemented_spaces: description = _implemented_spaces[abbr][2] print('{:>15} | {}'.format(abbr, description)) print('\n Existing spaces:') for mesh in _space_set: spaces = _space_set[mesh] print('{:>15} {}'.format('On mesh', mesh)) for i, sr in enumerate(spaces): space = spaces[sr] print('{:>15}: {}'.format(i, space._sym_repr)) def finite(degree, mesh=None, spaces=None): """ Parameters ---------- degree mesh spaces Returns ------- """ if mesh is None: # do it for all spaces on all meshes. for mesh_sr in _mesh_set: mesh = _mesh_set[mesh_sr] finite(degree, mesh=mesh, spaces=spaces) return else: assert mesh.__class__.__name__ == 'Mesh', f"Mesh = {mesh} is not a Mesh object." mesh_sr = mesh._sym_repr all_current_spaces = _space_set[mesh_sr] if spaces is None: spaces = all_current_spaces.values() else: if not isinstance(spaces, (list, tuple)): spaces = [spaces, ] else: pass for sp in spaces: assert sp._sym_repr in all_current_spaces, f"space: {sp} is not a space in current mesh {mesh}." for space in spaces: space.finite.specify_all(degree)