4. PDE#

4.1. Initialization#

To initialize/construct a PDE instance, call the method ph.pde,

pde(expression=None, interpreter=None, terms_and_signs_dict=None)[source]#

A wrapper of the __init__ method of PartialDifferentialEquations.

To make a PDE instance, you can either input

  • expression and interpreter

or input

  • terms_and_signs_dict

If you input expression and interpreter (recommended), the class will call a private method to parse expression according to interpreter and generates dictionaries of terms and signs.

Parameters:
expressionList[str]

The list of strings that represent a set of equations.

interpreterdict

The dictionary of interpreters that explain the terms in the expression.

terms_and_signs_dictdict

The dictionary that represents the terms and signs of each equation directly (instead of through expression and interpreter).

Returns:
pdePartialDifferentialEquations

The output partial differential equations instance.

For instance, if we want to solve the 2-dimensional linear port Hamitolian problem, i.e.,

\[\begin{split}\left\lbrace \begin{aligned} & \partial_t \tilde{\alpha} = \mathrm{d}\tilde{\beta} ,\\ & \partial_t \tilde{\beta} = - \mathrm{d}^\ast\tilde{\alpha}, \end{aligned} \right.\end{split}\]

for the outer 2-form \(\tilde{\alpha}\) and the outer 1-form \(\tilde{\beta}\) in the domain \(\mathcal{M}\), we make the following expression,

>>> expression = [
...     'da_dt = + d_b',
...     'db_dt = - cd_a'
... ]

where we have string terms like 'da_dt', 'd_b' and so on connected by '+', '-' and '='. Since these terms are just strings, the code needs to know what forms they are representing. Thus, we need an interpreter,

>>> interpreter = {
...     'da_dt': da_dt,
...     'd_b'  : d_b,
...     'db_dt': db_dt,
...     'cd_a' : cd_a
... }

which links the strings, i.e. the keys of interpreter, to the forms, da_dt, d_b and so on. Note that because here strings that are same to the variable names are used, this interpreter is a subset of the local variable dictionary which can be returned by the built-in function locals. Therefore, alternatively we can use

>>> interpreter = locals()

Sending expression and interpreter to ph.pde initializes a PDE instance,

>>> pde = ph.pde(expression, interpreter)

which is an instance of PartialDifferentialEquations,

class PartialDifferentialEquations(expression=None, interpreter=None, terms_and_signs_dict=None)[source]#

The Partial Differential Equations class.

property bc#

The boundary condition of the PDE.

property derive#

A wrapper all possible derivations to the PDE.

pr(indexing=True, figsize=(8, 6), vc=False, title=None)[source]#

Print the representation of the PDE.

Parameters:
indexingbool, optional

Whether to show indices of my terms. The default value is True.

figsizeTuple[float, int], optional

The figure size. It has no effect when the figure is over-sized. A tight configuration will be applied when it is the case. The default value is (8, 6).

vcbool, optional

Whether to show the vector calculus version of me. The default value is False.

title{None, str}, optional

The title of the figure. No title if it is None. The default value is None.

test_with(test_spaces, test_method='L2', sym_repr: list = None)[source]#

Test the PDE with a set of spaces to obtain a weak formulation.

Parameters:
test_spaceslist

The list of the test spaces.

test_method{'L2', }, optional

The test method. Currently, it can only be 'L2' representing the \(L^2\)-inner product. The default value is 'L2'.

sym_repr{List[str], None}, optional

The symbolic representations for the test variables. When it is None, pre-set ones will be applied. The default value is None.

Returns:
wfsrc.wf.main.WeakFormulation

The weak formulation instance.

property unknowns#

Unknowns of the PDE.

We need to set the unknowns of the pde, which is done through setting the property unknowns, i.e. PartialDifferentialEquations.unknowns,

>>> pde.unknowns = [a, b]

To visualize the PDE instnace just constructed, call the print representation method, see PartialDifferentialEquations.pr(),

>>> pde.pr()
<Figure size ...

It gives a figure of the PDE in differential forms. We can visualize the vector calculus version if we pass the requirement to pr through keyword argument vc=True like

>>> pde.pr(vc=True)
<Figure size ...

This is very handy, for example, when your reference PDE is given in vector calculus, and you want to check if you have input the correct differential form version of it, especially in 2-dimensions where the transformation between vector calculus and differential form suffers from extra minus signs here and there.

4.2. Boundary conditions#

The boundary condition setting of a PDE can be accessed through property PartialDifferentialEquations.bc. To define boundary conditions for a PDE, we first need to identify boundary sections. We can define boundary sections by calling the partition method, for example,

>>> pde.bc.partition(r"\Gamma_{\alpha}", r"\Gamma_{\beta}")

This command defines two boundary sections whose symbolic representations are '\Gamma_{\alpha}' and '\Gamma_{\beta}'. Here they are in fact two 1-dimensional sub-manifolds (recall that in this case the computational domain is a 2-dimensional manifold). They are a partition of the boundary, i.e.,

\[\Gamma_{\alpha} \cup \Gamma_{\beta} = \partial \mathcal{M}\quad\text{and}\quad \Gamma_{\alpha} \cap \Gamma_{\beta} = \emptyset,\]

where \(\partial \mathcal{M}\) is the complete boundary of the computational domain (manifold). Change the amount (\(\geq 1\)) of arguments for the partition method to define a partition of different amount of boundary sections. Since the manifold itself is abstract, the boundary sections are abstract as well; thus we can specify, for example,

\[\Gamma_{\alpha} = \partial \mathcal{M} \quad \text{and} \quad \Gamma_{\beta} = \emptyset,\]

when we invoke a particular implementation for the simulation in the future.

After we have defined boundary sections, we can specify boundary conditions on them by calling define_bc method of PartialDifferentialEquations.bc property. For example,

>>> pde.bc.define_bc(
...    {
...        r"\Gamma_{\alpha}": ph.trace(ph.Hodge(a)),   # natural boundary condition
...        r"\Gamma_{\beta}": ph.trace(b),              # essential boundary condition
...    }
... )

specifies

  • a natural boundary condition for the outer-oriented 2-form a on '\Gamma_{\alpha}',

  • an essential boundary condition for the outer-oriented 1-form b on '\Gamma_{\beta}'.

Caution

So far, only two types, essential and natural, of boundary conditions are implemented.

Now, the pr method will also list the imposed boundary conditions,

>>> pde.pr()
<Figure size ...

4.3. Derivations#

We can make changes to (for example, delete, replace or split a term in) the initialized PDE through property pde.derive which gives an instance of PDEDerive, a wrapper of all possible derivations to a PDE instance.

class PDEDerive(pde)[source]#

A wrapper all possible derivations to a PDE instance.

Todo

To be implemented.

So far, we recommend users to completely construct the PDE through initialization such that any further modification is avoided.


↩️ Back to Documentations.