Skip to content

Commit a7e8350

Browse files
committed
updates
1 parent e339099 commit a7e8350

25 files changed

+826
-359
lines changed

README.rst

+1-5
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,8 @@ After cloning the source code, PyNucleus is installed via
133133
134134
make
135135
136-
The compilation of PyNucleus can be configured by modifying the file `config.yaml` in the root folder.
136+
The compilation of PyNucleus can be configured by modifying the file `config.yaml <https://github.com/sandialabs/PyNucleus/blob/master/config.yaml>`_ in the root folder.
137137
This allows for example to set paths for libraries that are installed in non-standard directories.
138-
The defaults are as follows:
139-
140-
.. literalinclude:: ../config.yaml
141-
:language: yaml
142138

143139
If you want to easily modify the source code without re-installing the package every time, and editable install is available as
144140

base/PyNucleus_base/linear_operators.pyx

+10
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,16 @@ cdef class blockOperator(LinearOperator):
882882
self.blockInptrRight[j]:self.blockInptrRight[j+1]] = lo.toarray()
883883
return B
884884

885+
def isSparse(self):
886+
cdef:
887+
BOOL_t sparse = True
888+
INDEX_t i, j
889+
for i in range(self.blockShape[0]):
890+
for j in range(self.blockShape[1]):
891+
lo = self.subblocks[i][j]
892+
sparse &= lo.isSparse()
893+
return sparse
894+
885895

886896
cdef class blockDiagonalOperator(blockOperator):
887897
def __init__(self, list diagonalBlocks):

base/PyNucleus_base/utilsFem.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ def process(self, override={}):
10281028
doTerminate = False
10291029
if self.isMaster:
10301030
if 'plots' in self.argGroups:
1031-
io = self.addGroup('input/output')
1031+
io = self.addGroup('plots')
10321032
io.add('plotFolder', '', help='folder for saving plots')
10331033
io.add('plotFormat', acceptedValues=['pdf', 'png', 'jpeg', 'eps', 'ps', 'svg'], help='File format for saving plots')
10341034
try:
@@ -1450,6 +1450,12 @@ def interpret(self, s):
14501450
m = self.regexp.match(s)
14511451
return [p(v) for v, p in zip(m.groups(), self.params)]
14521452

1453+
def __repr__(self):
1454+
params = []
1455+
for p in self.params:
1456+
params.append(p.__name__)
1457+
return "{}({})".format(self.name, ','.join(params))
1458+
14531459

14541460
class propertyBuilder:
14551461
def __init__(self, baseObj, fun):
@@ -1782,7 +1788,7 @@ def interpreter(v):
17821788
for p in parametrizedArgs:
17831789
if self.parametrizedArg(p).match(v):
17841790
return v
1785-
raise ArgumentTypeError()
1791+
raise ArgumentTypeError("\"{}\" is not in list of accepted values {} or cannot be interpreted as parametrized arg {}.".format(v, acceptedValues, [repr(self.parametrizedArg(p)) for p in parametrizedArgs]))
17861792

17871793
return interpreter
17881794

docs/drivers.rst

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
Drivers
3+
=======
4+
5+
The code repository contains several sample problems that can be found in the `drivers <https://github.com/sandialabs/PyNucleus/blob/master/drivers>`_ subfolder.
6+
The drivers take several command line parameters that can be used to change the problem and the type of outputs.
7+
A listing of parameters can be displayed by passing the `--help` flag:
8+
9+
.. code-block:: shell
10+
11+
drivers/runFractional.py --help
12+
13+
.. program-output:: python3 ../drivers/runFractional.py --help
14+
15+
Some of the drivers can be run in parallel using MPI, e.g.
16+
17+
.. code-block:: shell
18+
19+
mpiexec -n 4 drivers/runFractional.py --domain=disc
20+
21+
22+
runFractional.py
23+
----------------
24+
25+
Assembles and solves fractional Poisson problems with infinite horizon.
26+
27+
runNonlocal.py
28+
----------------
29+
30+
Assembles and solves nonlocal Poisson problems with finite horizon.
31+
32+
runFractionalHeat.py
33+
--------------------
34+
35+
Solves a fractional heat equation with infinite horizon.
36+
37+
runNonlocalInterface.py
38+
-----------------------
39+
40+
A two domain interface problem with jumps in solution and flux for finite horizon kernels.
41+
42+
brusselator.py
43+
--------------
44+
45+
Solves a fractional-order Brusselator system.
46+
47+
runParallelGMG.py
48+
-----------------
49+
50+
Assembles and solves a classical local Poisson problem using geometric multigrid.
51+
52+
runHelmholtz.py
53+
-----------------
54+
55+
Assembles and solves a classical local Helmholtz problem.

docs/example1.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Example 1 - A simple PDE problem
33
================================
44

55
In this first example, we will construct a finite element discretization of a classical PDE problem and solve it.
6-
The full code of this example can be found in ``examples/example1.py``.
6+
The full code of this example can be found in `examples/example1.py <https://github.com/sandialabs/PyNucleus/blob/master/examples/example1.py>`_.
77

88
Factories
99
---------

docs/example2.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Example 2 - Nonlocal problems
33
=============================
44

55
I this second example, we will assemble and solve several nonlocal equations.
6-
The full code of this example can be found in ``examples/example2.py``.
6+
The full code of this example can be found in `examples/example2.py <https://github.com/sandialabs/PyNucleus/blob/master/examples/example2.py>`_.
77

88
PyNucleus can assemble operators of the form
99

docs/example3.py

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env python3
2+
###################################################################################
3+
# Copyright 2021 National Technology & Engineering Solutions of Sandia, #
4+
# LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the #
5+
# U.S. Government retains certain rights in this software. #
6+
# If you want to use this code, please refer to the README.rst and LICENSE files. #
7+
###################################################################################
8+
9+
from PyNucleus.packageTools.sphinxTools import codeRegionManager
10+
11+
mgr = codeRegionManager()
12+
13+
with mgr.add('preamble'):
14+
######################################################################
15+
# preamble
16+
import logging
17+
from PyNucleus_base.utilsFem import TimerManager
18+
from PyNucleus import meshFactory, dofmapFactory, kernelFactory, functionFactory, solverFactory
19+
from PyNucleus_nl.operatorInterpolation import admissibleSet
20+
import h5py
21+
from PyNucleus_base.linear_operators import LinearOperator
22+
from PyNucleus_fem.DoFMaps import DoFMap
23+
24+
fmt = '{message}'
25+
logging.basicConfig(level=logging.INFO,
26+
format=fmt,
27+
style='{',
28+
datefmt="%Y-%m-%d %H:%M:%S")
29+
logger = logging.getLogger('__main__')
30+
timer = TimerManager(logger)
31+
32+
with mgr.add('setup'):
33+
# Set up a mesh and a dofmap on it.
34+
mesh = meshFactory('interval', hTarget=2e-3, a=-1, b=1)
35+
dm = dofmapFactory('P1', mesh)
36+
37+
# Construct a RHS vector and a vector for the solution.
38+
f = functionFactory('constant', 1.)
39+
b = dm.assembleRHS(f)
40+
u = dm.zeros()
41+
42+
# construct a fractional kernel with fractional order in S = [0.05, 0.95]
43+
s = admissibleSet([0.05, 0.95])
44+
kernel = kernelFactory('fractional', s=s, dim=mesh.dim)
45+
46+
with mgr.add('operator'):
47+
######################################################################
48+
# The operator is set up to be constructed on-demand.
49+
# We partition the interval S into several sub-interval and construct a Chebyshev interpolant on each sub-interval.
50+
# Therefore this operation is fast.
51+
with timer('operator creation'):
52+
A = dm.assembleNonlocal(kernel, matrixFormat='H2')
53+
54+
with mgr.add('firstSolve'):
55+
######################################################################
56+
# Set s = 0.75.
57+
A.set(0.75)
58+
59+
# Let's solve a system.
60+
# This triggers the assembly of the operators for the matrices at the interpolation nodes of the interval that contains s.
61+
# The required matrices are constructed on-demand and then stay in memory.
62+
with timer('solve 1 (slow)'):
63+
solver = solverFactory('cg-jacobi', A=A, setup=True)
64+
solver.maxIter = 1000
65+
numIter = solver(b, u)
66+
logger.info('Solved problem for s={} in {} iterations (residual norm {})'.format(A.get(), numIter, solver.residuals[-1]))
67+
68+
with mgr.add('secondSolve'):
69+
######################################################################
70+
# Let's solve a second sytem for a closeby value of s.
71+
# This should be faster since we no longer need to assemble any matrices.
72+
with timer('solve 2 (fast)'):
73+
A.set(0.76)
74+
solver = solverFactory('cg-jacobi', A=A, setup=True)
75+
solver.maxIter = 1000
76+
numIter = solver(b, u)
77+
logger.info('Solved problem for s={} in {} iterations (residual norm {})'.format(A.get(), numIter, solver.residuals[-1]))
78+
79+
with mgr.add('saveToFile'):
80+
######################################################################
81+
# We can save the operator and the DoFMap to a file.
82+
# This will trigger the assembly of all matrices.
83+
with timer('save operator'):
84+
h5_file = h5py.File('test.hdf5', 'w')
85+
A.HDF5write(h5_file.create_group('A'))
86+
dm.HDF5write(h5_file.create_group('dm'))
87+
h5_file.close()
88+
89+
with mgr.add('readFromFile'):
90+
######################################################################
91+
# Now we can read them back in.
92+
with timer('load operator'):
93+
h5_file = h5py.File('test.hdf5', 'r')
94+
A_2 = LinearOperator.HDF5read(h5_file['A'])
95+
dm_2 = DoFMap.HDF5read(h5_file['dm'])
96+
h5_file.close()
97+
98+
with mgr.add('thirdSolve'):
99+
# Set up and solve a system with the operator we loaded.
100+
f_2 = functionFactory('constant', 2.)
101+
b_2 = dm_2.assembleRHS(f_2)
102+
u_2 = dm_2.zeros()
103+
with timer('solve 3 (fast)'):
104+
A_2.set(0.8)
105+
solver = solverFactory('cg-jacobi', A=A_2, setup=True)
106+
solver.maxIter = 1000
107+
numIter = solver(b_2, u_2)
108+
logger.info('Solved problem for s={} in {} iterations (residual norm {})'.format(A_2.get(), numIter, solver.residuals[-1]))
109+
######################################################################

docs/example3.rst

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
2+
Example 3 - Operator interpolation
3+
==================================
4+
5+
This example demostrates the construction of a family of fractional
6+
Laplacians parametrized by the fractional order using operator
7+
interpolation. This can reduce the cost compared to assembling a new
8+
matrix for each value.
9+
10+
The fractional Laplacian
11+
12+
.. math::
13+
14+
(-\Delta)^{s} \text{ for } s \in [s_{\min}, s_{\max}] \subset (0, 1)
15+
16+
is approximated by
17+
18+
.. math::
19+
20+
(-\Delta)^{s} \approx \sum_{m=0}^{M} \Theta_{k,m}(s) (-\Delta)^{s_{k,m}} \text{ for } s \in \mathcal{S}_{k}
21+
22+
for a sequence of intervals :math:`\mathcal{S}_{k}` that cover :math:`[s_{\min}, s_{\max}]` and scalar coefficients :math:`\Theta_{k,m}(s)`.
23+
The number of intervals and interpolation nodes is picked so that the interpolation error is dominated by the discretization error.
24+
25+
The following example can be found at `examples/example3.py <https://github.com/sandialabs/PyNucleus/blob/master/examples/example3.py>`_.
26+
27+
We set up a mesh, a dofmap and a fractional kernel.
28+
Instead of specifying a single value for the fractional order, we allow a range of values :math:`[s_{\min}, s_{\max}]=[0.05, 0.95]`.
29+
30+
.. literalinclude:: ../examples/example3.py
31+
:start-after: preamble
32+
:end-before: #################
33+
:lineno-match:
34+
35+
Next, we call the assembly of a nonlocal operator as before.
36+
Since the operator for a particular value of the fractional order will be constructed on demand, this operation is fast.
37+
38+
.. literalinclude:: ../examples/example3.py
39+
:start-after: The operator is set up to be constructed on-demand
40+
:end-before: #################
41+
:lineno-match:
42+
43+
.. program-output:: python3 example3.py --finalTarget operator
44+
45+
Next, we choose the value of the fractional order. This needs to be within the range that we specified earlier.
46+
We then solve a linear system involving the operator.
47+
48+
.. literalinclude:: ../examples/example3.py
49+
:start-after: Set s = 0.75
50+
:end-before: #################
51+
:lineno-match:
52+
53+
.. program-output:: python3 example3.py --finalTarget firstSolve
54+
55+
This solve is relatively slow, as it involves the assembly of the nonlocal operators that are needed for the interpolation.
56+
We select a different value for the fractional order that is close to the first.
57+
Solving a linear system with this value is faster as we have already assembled the operator needed for the interpolation.
58+
59+
.. literalinclude:: ../examples/example3.py
60+
:start-after: This should be faster
61+
:end-before: #################
62+
:lineno-match:
63+
64+
.. program-output:: python3 example3.py --finalTarget secondSolve
65+
66+
Next, we save the operator to file.
67+
This first triggers the assembly of all operators nescessary to represent every value in :math:`s\in[0.05,0.95]`.
68+
69+
.. literalinclude:: ../examples/example3.py
70+
:start-after: We can save the operator
71+
:end-before: #################
72+
:lineno-match:
73+
74+
.. program-output:: python3 example3.py --finalTarget saveToFile
75+
76+
Next, we read the operator back in and solve another linear system.
77+
78+
.. literalinclude:: ../examples/example3.py
79+
:start-after: Now we can read them
80+
:lineno-match:
81+
82+
.. program-output:: python3 example3.py --finalTarget thirdSolve

docs/index.rst

+11
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Features
3636

3737
* Nonlocal assembly (1D and 2D) into dense, sparse and hierarchical matrices
3838

39+
* Operator interpolation
40+
3941
* Solvers/preconditioners:
4042

4143
* LU,
@@ -70,6 +72,15 @@ Examples
7072

7173
example1
7274
example2
75+
example3
76+
77+
Drivers
78+
-------
79+
80+
.. toctree::
81+
:maxdepth: 1
82+
83+
drivers
7384

7485

7586
Funding

drivers/runNonlocal.py

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
##################################################
3333

3434
vectors = d.addOutputGroup('vectors')
35+
vectors.add('dm', mS.u.dm)
3536
vectors.add('u', mS.u)
3637
if mS.u_interp is not None:
3738
vectors.add('uEx', mS.u_interp)

drivers/runParallelGMG.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@
159159

160160
rate = d.addOutputGroup('rates', tested=True, aTol=1e-2)
161161
its = d.addOutputGroup('iterations', tested=True)
162-
res = d.addOutputGroup('residuals', tested=True, rTol=4e-1)
162+
res = d.addOutputGroup('residuals', tested=True, rTol=2.)
163163
resHist = d.addOutputGroup('resHist', tested=True, aTol=5e-8)
164-
errs = d.addOutputGroup('errors', tested=True, rTol=2.)
164+
errs = d.addOutputGroup('errors', tested=True, rTol=4.)
165165

166166
for cycle, label in [(V, 'MG'),
167167
(FMG_V, 'FMG')]:

0 commit comments

Comments
 (0)