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()andrun()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
MeshCaseto 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: ...
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.Blockobject, 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 ...
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
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: ...
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
MeshCaseobjectcse, 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
arrangementsare also put into a module-level attributesolvcon.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: - delayed (
solvcon.MeshHookorsolvcon.MeshAnchor.) – The delayed construct. - replace (bool) – True if existing object should be replaced.
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'
- delayed (
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 vanillaMeshSolvercan’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
MeshSolvershould 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 throughsys.float_info.
Time-Marching¶
-
MeshSolver.march(time_current, time_increment, steps_run, worker=None)¶ Parameters: Returns: This method performs time-marching. The parameters time_current and time_increment are used to reset the instance attributes
timeandtime_increment, respectively. In each invokationstep_currentis 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_runtimes. In total, the inner loop runs steps_run *substep_runtimes. In each sub time step (in the inner loop), the increment of the attributetimeistime_increment/substep_run. The temporal increment per time step is effectivelytime_increment, with a slight error because of round-off.Before entering and after leaving the outer loop,
premarchandpostmarchanchors will be run (through the attributerunanchors). Similarly, before entering and after leaving the inner loop,prefullandpostfullanchors will be run. Inside the inner loop of sub steps, before and after executing all the marching methods,presubandpostsubanchors 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”, anchorprecalcsomewill be run before the invocation, and anchorpostcalcsomewill be run afterward.Derived classes can set
marchretdictionary, andmarch()will return the dictionary at the end of execution. The dictionary is reset to empty at the begninning of the execution.
-
MeshSolver.runanchors¶ This attribute is of type
MeshAnchorList, and the foundation of the anchor mechanism of SOLVCON. AnMeshAnchorListobject like this collects a set ofMeshAnchorobjects, and is callable. When being called,runanchorsiterates the containedMeshAnchorobjects and invokes the corresponding methods of the individualMeshAnchor.
Parallel Computing¶
-
MeshSolver.svrn¶ This member indicates the serial number (0-based) of the
MeshSolverobject.
-
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
MeshSolverobjects at various stages.-
svr¶ The associated
MeshSolverinstance.
-
-
class
solvcon.MeshAnchorList(svr, *args, **kw)¶ Sequence container for
MeshAnchorinstances.-
svr¶ The associated
MeshSolverinstance.
-
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
vnamesas number of values per boundary face. It should be equivalent to the second shape element ofvalue.FIXME: provide doctests.
-
cloneTo(another)¶ Parameters: another (solvcon.boundcond.BC) – Another BC object. Returns: Nothing. Clone self to another
BCobject.
-
create_bcd()¶ Returns: An object contains the sc_bound_tvariable for C interfacing.Return type: solvcon.mesh.BoundThe 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
structcontains essential information of aBCobject 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.
-
int
-
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
structsc_bound_tfor internal use.
-
setup_bound(bc)¶ Parameters: bc – The BCobject that supplies information.
-