SOLVCON

SOLVCON is a collection of conservation-law solvers that use the space-time Conservation Element and Solution Element (CESE) method [Chang95]. The equations to be solved are formulated as:

\[\frac{\partial\mathbf{u}}{\partial t} + \sum_{k=1}^3 \mathrm{A}^{(k)}(\mathbf{u}) \frac{\partial\mathbf{u}}{\partial x_k} = 0\]

where \(\mathbf{u}\) is the unknown vector and \(\mathrm{A}^{(1)}\), \(\mathrm{A}^{(2)}\), and \(\mathrm{A}^{(3)}\) are the Jacobian matrices.

Install

Clone from https://github.com/solvcon/solvcon:

$ git clone https://github.com/solvcon/solvcon

SOLVCON needs the following packages: A C/C++ compiler supporting C++11, cmake 3.7+, pybind11 Git master, Python 3.6+, Cython 0.16+, Numpy 1.5+, LAPACK, NetCDF 4+, SCOTCH 6.0+, Nose 1.0+, Paramiko 1.14+, boto 2.29.1+, and gmsh 3+. Support for VTK is to be enabled for conda environment.

To install the dependency, run the scripts contrib/conda.sh and contrib/build-pybind11-in-conda.sh (they use Anaconda).

The development version of SOLVCON only supports local build:

$ make; python setup.py build_ext --inplace

To build SOLVCON from source code and install it to your system:

$ make; make install

Test the build:

$ nosetests --with-doctest
$ nosetests ftests/gasplus/*

Building document requires Sphinx 1.3.1+, pstake 0.3.4+, and graphviz 2.28+. Use the following command:

$ make -C doc html

The document will be available at doc/build/html/.

Documents

Unstructured Meshes

We discretize the spatial domain of interest before solving PDEs. The discretized space is called a mesh [Mavriplis97]. When discretization is done by exploiting regularity in space, like cutting along each of the Cartesian coordinate axes, the discretized space is called a structured mesh. If the discretization does not follow any spatial order, we call the spatial domain an unstructured mesh. Both meshing strategies have their strength and weakness. Sometimes a structured mesh is also call a grid. Numerical methods that rely on spatial discretization are called mesh-based or grid-based.

To accommodate complex geometry, SOLVCON chose to use unstructured meshes of mixed elements. Because no structure is assumed for the geometry to be modeled, the mesh can be automatically generated by using computer programs. For example, the following image shows a triangular mesh of a two-dimensional irregular domain:

_images/ustmesh_2d_sample.png

Figure 1: Two-dimensional sample mesh

which is generated by using the Gmsh (see the command file ustmesh_2d_sample.geo [1]). On the other hand, creation of structured meshes often needs a large amount of manual operations and will not be discussed in this document.

In SOLVCON, we assume a mesh is fully covered by a finite number of non-overlapping sub-regions, and only composed by these sub-regions. The sub-regions are called mesh elements. In one-dimensional space, SOLVCON also defines one type of mesh elements, line, as shown in Figure One-dimensional mesh element.

_images/elm_1d.png

Figure 2: One-dimensional mesh element

SOLVCON allows two types of two-dimensional mesh elements, quadrilaterals and triangles, as shown in Figure Two-dimensional mesh elements, and four types of three-dimensional mesh elements, hexahedra, tetrahedra, prisms, and pyramids, as shown in Figure Three-dimensional mesh elements.

_images/elm_2d.png

Figure 3: Two-dimensional mesh elements

_images/elm_3d.png

Figure 4: Three-dimensional mesh elements

The numbers annotated in the figures are the order of the vertices of the elements. A SOLVCON mesh can be a mixture of elements of the same dimension, although it often only one element type.

Entities

Before explaining the data structure of the meshes, we need to introduce some basic terminologies and definitions. In SOLVCON, a cell means a mesh element. The dimensionality of a cell equals to that of the mesh it belongs to, e.g., a two-dimensional mesh is composed by two-dimensional cells. A cell is assumed to be concave, and enclosed by a set of faces. The dimensionality of a face is one less than that of a cell. A face is also assumed to be concave, and formed by connecting a sequence of nodes. The dimensionality of a node is at least one less than that of a face. Cells, faces, and nodes are the basic constructs, which we call entities, of a SOLVCON mesh.

Defining the term “entity” for SOLVCON facilitates a unified treatment of two- and three-dimensional meshes and the corresponding solvers [2]. A cell can be either two- or three-dimensional, and the associated faces become one- or two-dimensional, respectively. Because a face is either one- or two-dimensional, it can always be formed by a sequence of points, which is zero-dimensional. In this treatment, a point is equivalent to a node defined in the previous passage.

Take the two-dimensional mesh shown above as an example, triangular elements are used as the cells. The triangles are formed by three lines (one-dimensional shapes), which are the faces. Each line has two points (zero-dimensional). If we have a three-dimensional mesh composed by hexahedral cells, then the faces should be quadrilaterals (two-dimensional shapes).

All the mesh elements supported by SOLVCON are listed in the following table. The first column is the name of the element, and the second column is the type ID used in SOLVCON. The third column lists the dimensionality. The fourth, fifth, and sixth columns show the number of zero-, one-, and two-dimensional sub-entities belong to the element type, respectively. Note that the terms “point” and “line” appear in both the first row and first column, for they are the only element type in the space of the corresponding dimensionality.

Name Type Dim Point# Line# Surface#
Point 0 0 0 0 0
Line 1 1 2 0 0
Quadrilateral 2 2 4 4 0
Triangle 3 2 3 3 0
Hexahedron 4 3 8 12 6
Tetrahedron 5 3 4 4 4
Prism 6 3 6 9 5
Pyramid 7 3 5 8 5

Although SOLVCON doesn’t support one-dimensional solvers, for completeness, we define the relation between one-dimensional cells (lines) and their sub-entities as:

Shape (type) Face = Point
Line (0) 0 \(\cdot\) 0
1 \(\cdot\) 1

That is, as shown in Figure One-dimensional mesh element, a one-dimensional “cell” (line) has two “faces”, which are essentially point 0 and point 1. Symbol \(\cdot\) indicates a point.

It will be more practical to illustrate the relation between two-dimensional cells and their sub-entities in a table (see Figure Two-dimensional mesh elements for point locations):

Shape (type) Face = Line formed by points
Quadrilateral (2) 0 \(\diagup\) 0 1
1 \(\diagup\) 1 2
2 \(\diagup\) 2 3
3 \(\diagup\) 3 0
Triangle (3) 0 \(\diagup\) 0 1
1 \(\diagup\) 1 2
2 \(\diagup\) 2 0

Symbol \(\diagup\) indicates a line. The orientation of lines of each two-dimensional shape is defined to follow the right-hand rule. The shape enclosed by the lines has an area normal vector points to the direction of \(+z\) (outward paper/screen).

The relation between three-dimensional cells and their sub-entities is defined in the table (see Figure Three-dimensional mesh elements for point locations):

Shape (type) Face = Surface formed by points
Hexahedron (4) 0 \(\square\) 0 3 2 1
1 \(\square\) 1 2 6 5
2 \(\square\) 4 5 6 7
3 \(\square\) 0 4 7 3
4 \(\square\) 0 1 5 4
5 \(\square\) 2 3 7 6
Tetrahedron (5) 0 \(\triangle\) 0 2 1
1 \(\triangle\) 0 1 3
2 \(\triangle\) 0 3 2
3 \(\triangle\) 1 2 3
Prism (6) 0 \(\triangle\) 0 1 2
1 \(\triangle\) 3 5 4
2 \(\square\) 0 3 4 1
3 \(\square\) 0 2 5 3
4 \(\square\) 1 4 5 2
Pyramid (7) 0 \(\triangle\) 0 4 3
1 \(\triangle\) 1 4 0
2 \(\triangle\) 2 4 1
3 \(\triangle\) 3 4 2
4 \(\square\) 0 3 2 1

Symbol \(\square\) indicates a quadrilateral, while symbol \(\triangle\) indicates a triangle.

Because a face is associated to two adjacent cells unless it’s a boundary face, it needs to identify to which cell it belongs, and to which cell it is neighbor. The area normal vector of a face is always point from the belonging cell to neighboring cell. The same rule applies to faces of two-dimensional meshes (lines) too.

Mesh Loading

A mesh is usually built up by using a mesh generator, like Gmsh. We then feed the generated mesh file to SOLVCON, which converts the unstructured-mesh data to the internal representation format: the Block class.

There are three steps required to fully construct a Block object: (i) instantiation, (ii) definition, and (iii) build-up. First, when instantiating an object, shape information must be provided to the constructor to allocate arrays for look-up tables:

from solvcon import Block
blk = Block(ndim=2, nnode=4, ncell=3)

Second, we fill the cell definition. Node coordinates and the node lists of cells need to be provided:

# Node coordinates.
blk.ndcrd[:,:] = (0,0), (-1,-1), (1,-1), (0,1)
# Cell types.
blk.cltpn[:] = 3
# Node list of cells.
blk.clnds[:,:4] = (3, 0,1,2), (3, 0,2,3), (3, 0,3,1)

Third, build up the rest of the object by calling (they will be explained later):

blk.build_interior()
blk.build_boundary()
blk.build_ghost()
Block.build_interior()

Building up a Block object includes two steps. First, the method deduce information from the defined arrays cltpn and clnds to create the arrays clfcs, fctpn, fcnds, and fccls. If the number of extracted faces is not the same as that passed into the constructor, arrays related to faces are recreated.

The method then fills all the geometry arrays from Block.ndcrd.

Block.build_boundary()

This method iterates over each of the BC objects listed in Block.bclist to collect boundary-condition information and build boundary faces. If a face belongs to only one cell (i.e., has no neighboring cell), it is regarded as a boundary face.

Unspecified boundary faces will be collected to form an additional BC object. It sets bndfcs for later use by build_ghost().

Block.build_ghost()

This method creates the shared arrays, calculates the information for ghost cells, and reassigns interior arrays as the right portions of the shared arrays. Only after this ghost build-up process, the Block object can be used by solvers.

Block.bndfcs
Type:numpy.ndarray

The array is of shape (nbound, 2) and type int32. Each row contains the data for a boundary face. The first column is the 0-based index of the face, while the second column is the serial number of the associated solvcon.boundcond.BC object.

We then can save the block to a VTK file for viewing:

from solvcon.io.vtkxml import VtkXmlUstGridWriter
iodev = VtkXmlUstGridWriter(blk)
iodev.write('block_2d_sample.vtu')
_images/block_2d_sample.png

Figure 5: A simple Block object

class solvcon.Block(ndim=0, nnode=0, nface=0, ncell=0, nbound=0, use_incenter=False)

This class represents the unstructured meshes used in SOLVCON. As such, in SOLVCON, an unstructured mesh is also called a “block”. The following six attributes can be passed into the constructor. ndim, nnode, and ncell need to be non-zero to instantiate a valid block. nface and nbound might be different to the given value after building up the object. use_incenter is an optional flag.

ndim
Type:int

Number of dimensionalities of this mesh. Read only after instantiation.

nnode
Type:int

Total number of (non-ghost) nodes of this mesh. Read only after instantiation.

nface
Type:int

Total number of (non-ghost) faces of this mesh. Read only after instantiation.

ncell
Type:int

Total number of (non-ghost) cells of this mesh. Read only after instantiation.

nbound
Type:int

Total number of boundary faces or ghost cells of this mesh. Read only after instantiation.

use_incenter
Type:bool

Indicates calculating incenters instead of centroids for cells. Default is False (using centroids of cells).

The meshes are defined by three sets of look-up tables (arrays): (i) geometry, (ii) type, and (iii) connectivity.

Geometry Tables

ndcrd

Coordinates of nodes. It’s a two-dimensional numpy.ndarray array of shape (nnode, ndim) of type float64.

fccnd

Centroids of faces. It’s a two-dimension numpy.ndarray of shape (nface, ndim) of type float64.

fcnml

Unit normal vectors of faces. It’s a two-dimension numpy.ndarray of shape (nface, ndim) of type float64.

fcara

Areas of faces. The value should always be non-negative. It’s a one-dimension numpy.ndarray of shape (nface,) of type float64.

clcnd

Centroids of cells. It’s a two-dimension numpy.ndarray of shape (ncell, ndim) of type float64.

clvol

Volumes of cells. It’s a one-dimension numpy.ndarray of shape (ncell,) of type float64.

Type Tables

fctpn

Type ID of faces. It’s a one-dimensional numpy.ndarray of shape (nface,) of type int32.

cltpn

Type ID of cells. It’s a one-dimensional numpy.ndarray of shape (ncell,) of type int32.

clgrp

Group ID of cells. It’s a one-dimensional numpy.ndarray of shape (ncell,) of type int32. For a new Block object, it should be initialized with -1.

Connectivity Tables

fcnds

Lists of the nodes of each face. It’s a two-dimensional numpy.ndarray of shape (nface, FCMND+1) and type int32.

fccls

Lists of the cells connected by each face. It’s a two-dimensional numpy.ndarray of shape (nface, 4) and type int32.

clnds

Lists of the nodes of each cell. It’s a two-dimensional numpy.ndarray of shape (ncell, CLMND+1) and type int32.

clfcs

Lists of the faces of each cell. It’s a two-dimensional numpy.ndarray of shape (ncell, CLMFC+1) and type int32.

Constants

FCMND
Type:int

The maximum number of nodes that a face can have, which is 4.

CLMND
Type:int

The maximum number of nodes that a cell can have, which is 8.

CLMFC
Type:int

The maximum number of faces that a cell can have, which is 6.

Every look-up array has two associated arrays of different prefixes: (i) gst (denoting for “ghost”) and (ii) sh (denoting for “shared”). SOLVCON uses the technique of ghost cells to treat boundary conditions [Mavriplis97], and the gst arrays store the information for ghost cells. However, to facilitate efficient indexing in solvers, each of the ghost arrays should be put in a continuous block of memory adjacent to its interior counterpart. In SOLVCON, the sh arrays are the continuous memory blocks for both ghost and interior look-up tables, and a pair of gst and normal arrays is simply the views of two consecutive, non-overlapping sub-regions of a memory block.

Footnotes

[1]

The following command generates the mesh from the command file ustmesh_2d_sample.geo:

$ gmsh ustmesh_2d_sample.geo -3

The following command converts the mesh to a VTK file for ParaView:

$ scg mesh ustmesh_2d_sample.msh ustmesh_2d_sample.vtk
[2]SOLVCON focuses on two- and three-dimensional meshes. But if we put an additional constraint on the mesh elements: Requiring them to be simplices, it wouldn’t be difficult to extend the data structure of SOLVCON meshes into higher-dimensional space.

Nested Looping

The whole numerical simulation is controlled by an Outer Loop and many Inner Loops. SOLVCON materializes them with MeshCase and MeshSolver, respectively.

Outer Loop

SOLVCON simulation is orchestrated by MeshCase, which should be subclassed to implement control logic for a specific application. The application can be a concrete model for a certain physical process, or an abstraction of a group of related physical processes, which can be further subclassed.

Because a case controls the whole process of a simulation run, for parallel execution, there can be only one MeshCase object residing in the controller (head) node.

class solvcon.MeshCase(**kw)

init() and run() are the two primary methods responsible for the execution of the simulation case object. Both methods accept a keyword parameter “level”:

  • run level 0: fresh run (default),
  • run level 1: restart run,
  • run level 2: initialization only.
Initialize
MeshCase.init(level=0)
Parameters:level (int) – Run level; higher level does less work.
Returns:Nothing

Load a block and initialize the solver from the geometry information in the block and conditions in the self case. If parallel run is specified (through domaintype), split the domain and perform corresponding tasks.

For a MeshCase to be initialized, some information needs to be supplied to the constructor:

>>> cse = MeshCase()
>>> cse.info.muted = True
>>> cse.init() 
Traceback (most recent call last):
    ...
TypeError: ...
  1. Mesh information. We can provide meshfn that specifying the path of a valid mesh file, or provide mesher, which is a function that generates the mesh and returns the solvcon.block.Block object, like the following code:

    >>> from solvcon.testing import create_trivial_2d_blk
    >>> blk = create_trivial_2d_blk()
    >>> cse = MeshCase(mesher=lambda *arg: blk)
    >>> cse.info.muted = True
    >>> cse.init() 
    Traceback (most recent call last):
        ...
    TypeError: isinstance() arg 2 must be ...
    
  2. Type of the spatial domain. This information is used for detemining sequential or parallel execution, and performing related operations:

    >>> cse = MeshCase(mesher=lambda *arg: blk, domaintype=domain.Domain)
    >>> cse.info.muted = True
    >>> cse.init()
    Traceback (most recent call last):
        ...
    TypeError: 'NoneType' object is not callable
    
  3. The type of solver. It is used to specify the underlying numerical method:

    >>> from solvcon.solver import MeshSolver
    >>> cse = MeshCase(mesher=lambda *arg: blk,
    ...                domaintype=domain.Domain,
    ...                solvertype=MeshSolver)
    >>> cse.info.muted = True
    >>> cse.init() 
    Traceback (most recent call last):
        ...
    TypeError: ...
    
  4. The base name. It is used to name its output files:

    >>> cse = MeshCase(
    ...     mesher=lambda *arg: blk, domaintype=domain.Domain,
    ...     solvertype=MeshSolver, basefn='meshcase')
    >>> cse.info.muted = True
    >>> cse.init()
    
Time-March
MeshCase.run(level=0)
Parameters:level (int) – Run level; higher level does less work.
Returns:Nothing

Temporal loop for the incorporated solver. A simple example:

>>> from .testing import create_trivial_2d_blk
>>> from .solver import MeshSolver
>>> blk = create_trivial_2d_blk()
>>> cse = MeshCase(basefn='meshcase', mesher=lambda *arg: blk,
...                domaintype=domain.Domain, solvertype=MeshSolver)
>>> cse.info.muted = True
>>> cse.init()
>>> cse.run()
Arrangement
MeshCase.arrangements

The class-level registry for arrangements.

classmethod MeshCase.register_arrangement(func, casename=None)
Returns:Simulation function.
Return type:callable

This class method is a decorator that creates a closure (internal function) that turns the decorated function to an arrangement, and registers the arrangement into the module-level registry and the class-level registry. The decorator function should return a MeshCase object cse, and the closure performs a simulation run by the following code:

try:
    signal.signal(signal.SIGTERM, cse.cleanup)
    signal.signal(signal.SIGINT, cse.cleanup)
    cse.init(level=runlevel)
    cse.run(level=runlevel)
    cse.cleanup()
except:
    cse.cleanup()
    raise

The usage of this decorator can be exemplified by the following code, which creates four arrangements (although the first three are erroneous):

>>> @MeshCase.register_arrangement
... def arg1():
...     return None
>>> @MeshCase.register_arrangement
... def arg2(wrongname):
...     return None
>>> @MeshCase.register_arrangement
... def arg3(casename):
...     return None
>>> @MeshCase.register_arrangement
... def arg4(casename):
...     from .testing import create_trivial_2d_blk
...     from .solver import MeshSolver
...     blk = create_trivial_2d_blk()
...     cse = MeshCase(basefn='meshcase', mesher=lambda *arg: blk,
...                    domaintype=domain.Domain, solvertype=MeshSolver)
...     cse.info.muted = True
...     return cse

The created arrangements are collected to a class attribute arrangements, i.e., the class-level registry:

>>> sorted(MeshCase.arrangements.keys())
['arg1', 'arg2', 'arg3', 'arg4']

The arrangements in the class attribute arrangements are also put into a module-level attribute solvcon.case.arrangements:

>>> arrangements == MeshCase.arrangements
True

The first example arrangement is a bad one, because it allows no argument:

>>> arrangements.arg1() 
Traceback (most recent call last):
  ...
TypeError: arg1() ...

The second example arrangement is still a bad one, because although it has an argument, the name of the argument is incorrect:

>>> arrangements.arg2()
Traceback (most recent call last):
  ...
TypeError: arg2() got an unexpected keyword argument 'casename'

The third example arrangement is a bad one for another reason. It doesn’t return a MeshCase:

>>> arrangements.arg3()
Traceback (most recent call last):
  ...
AttributeError: 'NoneType' object has no attribute 'cleanup'

The fourth example arrangement is finally good:

>>> arrangements.arg4()
Hooks on Cases

MeshHook performs custom operations at certain pre-defined stages.

class solvcon.MeshHook(cse, **kw)

Base type for hooks needing a MeshCase.

MeshCase.defer(delayed, replace=False, **kw)
Parameters:
Returns:

Nothing.

Insert (append or replace) hooks.

>>> import solvcon as sc
>>> from solvcon.testing import create_trivial_2d_blk
>>> cse = MeshCase() # No arguments because of demonstration.
>>> len(cse.runhooks)
0
>>> # Insert a hook.
>>> cse.defer(sc.MeshHook, dummy='name1')
>>> cse.runhooks[0].kws['dummy']
'name1'
>>> # Insert the second hook to replace the first one.
>>> cse.defer(sc.MeshHook, replace=True, dummy='name2')
>>> cse.runhooks[0].kws['dummy'] # Got replaced.
'name2'
>>> len(cse.runhooks) # Still only one hook in the list.
1
>>> # Insert the third hook without replace.
>>> cse.defer(sc.MeshHook, dummy='name3')
>>> cse.runhooks[1].kws['dummy'] # Got replaced.
'name3'

Inner Loops

Numerical methods should be implemented by subclassing MeshSolver. The base class is defined as:

class solvcon.MeshSolver(blk, time=0.0, time_increment=0.0, enable_mesg=False, debug=False, **kw)

Base class for all solving code that take Mesh, which is usually needed to write efficient C/C++ code for implementing numerical methods.

Here’re some examples about using MeshSolver. The first example shows that we can’t directly use it. A vanilla MeshSolver can’t march:

>>> from . import testing
>>> svr = MeshSolver(testing.create_trivial_2d_blk())
>>> svr.march(0.0, 0.1, 1) 
Traceback (most recent call last):
    ...
TypeError: 'NoneType' object ...

Of course the above solver does nothing. Let’s see another example for a non-trivial solver:

>>> class ExampleSolver(MeshSolver):
...     @MeshSolver.register_marcher
...     def calcsomething(self, worker=None):
...         self.marchret['key'] = 'value'
>>> svr = ExampleSolver(testing.create_trivial_2d_blk())
>>> svr.march(0.0, 0.1, 1)
{'key': 'value'}

Two instance attributes are used to record the temporal information:

time = None
time_increment = None

Four instance attributes are used to track the status of time-marching:

step_current = None
step_global = None
substep_run = None
substep_current = None

Time-marchers:

static register_marcher(func)

Any time-marching method in a derived class of MeshSolver should be decorated by this function.

mmnames

Generator of time-marcher names.

Useful entities are attached to the class MeshSolver:

MeshSolver.ALMOST_ZERO

A positive floating-point number close to zero. The value is not DBL_MIN, which can be accessed through sys.float_info.

Time-Marching
MeshSolver.march(time_current, time_increment, steps_run, worker=None)
Parameters:
  • time_current (float) – Starting time of this set of marching steps.
  • time_increment (float) – Temporal interval \(\Delta t\) of the time step.
  • steps_run (int) – The count of time steps to run.
Returns:

marchret

This method performs time-marching. The parameters time_current and time_increment are used to reset the instance attributes time and time_increment, respectively. In each invokation step_current is reset to 0.

There is a nested two-level loop in this method for time-marching. The outer loop iterates for time steps, and the inner loop iterates for sub time steps. The outer loop runs steps_run times, while the inner loop runs substep_run times. In total, the inner loop runs steps_run * substep_run times. In each sub time step (in the inner loop), the increment of the attribute time is time_increment/substep_run. The temporal increment per time step is effectively time_increment, with a slight error because of round-off.

Before entering and after leaving the outer loop, premarch and postmarch anchors will be run (through the attribute runanchors). Similarly, before entering and after leaving the inner loop, prefull and postfull anchors will be run. Inside the inner loop of sub steps, before and after executing all the marching methods, presub and postsub anchors will be run. Lastly, before and after invoking every marching method, a pair of anchors will be run. The anchors for a marching method are related to the name of the marching method itself. For example, if a marching method is named “calcsome”, anchor precalcsome will be run before the invocation, and anchor postcalcsome will be run afterward.

Derived classes can set marchret dictionary, and march() will return the dictionary at the end of execution. The dictionary is reset to empty at the begninning of the execution.

MeshSolver.marchret

Values to be returned by this solver. It will be set to a dict in march().

MeshSolver.runanchors

This attribute is of type MeshAnchorList, and the foundation of the anchor mechanism of SOLVCON. An MeshAnchorList object like this collects a set of MeshAnchor objects, and is callable. When being called, runanchors iterates the contained MeshAnchor objects and invokes the corresponding methods of the individual MeshAnchor.

MeshSolver.der

Derived data container as a dict.

Parallel Computing
MeshSolver.svrn

This member indicates the serial number (0-based) of the MeshSolver object.

MeshSolver.nsvr

The total number of collaborative solvers in the parallel run, and is initialized to None.

Anchors on Solvers
class solvcon.MeshAnchor(svr, **kw)

Callback class to be invoked by MeshSolver objects at various stages.

svr

The associated MeshSolver instance.

class solvcon.MeshAnchorList(svr, *args, **kw)

Sequence container for MeshAnchor instances.

svr

The associated MeshSolver instance.

Boundary-Condition Treatments

class solvcon.BC(bc=None, fpdtype=None)

Generic boundary condition abstract class; the base class that all boundary condition classes should subclass.

FIXME: provide doctests as examples.

facn = None
value = None
nvalue

Return the length of vnames as number of values per boundary face. It should be equivalent to the second shape element of value.

FIXME: provide doctests.

__len__()

Return the first shape element of facn.

FIXME: provide doctests.

cloneTo(another)
Parameters:another (solvcon.boundcond.BC) – Another BC object.
Returns:Nothing.

Clone self to another BC object.

create_bcd()
Returns:An object contains the sc_bound_t variable for C interfacing.
Return type:solvcon.mesh.Bound

The following code shows how and when to use this method:

>>> import numpy as np
>>> # craft some face numbers for testing.
>>> bndfcs = [0,1,2]
>>> # craft the BC object for testing.
>>> bc = BC()
>>> bc.name = 'some_name'
>>> bc.facn = np.empty((len(bndfcs), BC.BFREL), dtype='int32')
>>> bc.facn.fill(-1)
>>> bc.facn[:,0] = bndfcs
>>> bc.sern = 0
>>> bc.blk = None # should be set to a block.
>>> # test for this method.
>>> bcd = bc.create_bcd()
BC.vnames = []

Settable value names.

BC.vdefaults = {}

Default values.

sc_bound_t

This struct contains essential information of a BC object in C.

int nbound

Number of boundary faces. It’s equivalent to what BC.__len__() returns.

int nvalue

Number of values per boundary face.

int* facn

Pointer to the data storage of BC.facn.

double* value

Pointer to the data storage of BC.value.

class solvcon.mesh.Bound

This class associates the C functions for mesh operations to the mesh data and exposes the functions to Python.

_bcd

This attribute holds a C struct sc_bound_t for internal use.

setup_bound(bc)
Parameters:bc – The BC object that supplies information.