Skip to content
Distributed Computation
=======================
.. automodule:: boxtree.distributed
FMM driver
==========
.. module:: boxtree.fmm
.. autofunction:: drive_fmm
.. autoclass:: ExpansionWranglerInterface
:members:
:undoc-members:
:member-order: bysource
Integration with PyFMMLib
-------------------------
.. automodule:: boxtree.fmm
.. automodule:: boxtree.pyfmmlib_integration
.. autoclass:: Helmholtz2DExpansionWrangler
.. vim: sw=4
......@@ -8,8 +8,8 @@ generates FMM interaction lists.
Other places on the web to find boxtree stuff:
* `wiki home page <http://wiki.tiker.net/BoxTree>`_
* `github (source code, bug tracker) <http://github.com/inducer/boxtree>`_
* `documentation page <https://documen.tician.de/boxtree>`__
* `Github (source code, bug tracker) <https://github.com/inducer/boxtree>`__
Now you obviously want to watch the library do something (at least mildly)
cool? Well, sit back and watch:
......@@ -25,7 +25,7 @@ included in the demo file), you can see what's going on:
.. image:: images/tree.png
More importantly, perhaps, than being able to draw the tree, the :class:`boxtree.Tree`
data structure is now accesible via the ``tree`` variable above, and the connecitivity
data structure is now accessible via the ``tree`` variable above, and the connectivity
information needed for an FMM-like traversal is available in ``trav`` as
a :class:`boxtree.traversal.FMMTraversalInfo`.
......@@ -37,10 +37,16 @@ Overview
intro
tree
tree-build
traversal
fmm
lookup
cost
distributed
tools
misc
🚀 Github <https://github.com/inducer/boxtree>
💾 Download Releases <https://pypi.python.org/pypi/boxtree>
Indices and tables
------------------
......@@ -48,5 +54,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. vim: sw=4
......@@ -2,5 +2,5 @@ Introduction
------------
.. automodule:: boxtree
.. moduleauthor:: Andreas Kloeckner <inform@tiker.net>
.. moduleauthor:: Andreas Kloeckner <inform@tiker.net>
\def\sout{0.25}
\def\pr{0.55}
\begin{tikzpicture}[scale=1.5,baseline={(0,0)}]
\def\sout{0.25}
\draw (-1,-1) rectangle (1,1);
\draw [dashed] (-1-\sout,-1-\sout) rectangle (1+\sout,1+\sout);
\node [anchor=south] at (0,-1) {Box};
\node [anchor=north] at (0,-1-\sout) {`Stick-out' region };
\node [anchor=north] at (0,-1.85) {\texttt{'linf'}};
\draw [|<->|] (0,0) -- (1+\sout,0) node [pos=0.5, anchor=north] {$(1+\alpha)R$};
\coordinate (pc) at (-0.25, 0.9);
\fill [red] (pc) circle (1pt);
\draw [red] (pc) ++(-\pr,-\pr) rectangle ++(2*\pr,2*\pr);
\draw [red,|<->|] (pc) -- ++(0,-\pr) node [pos=0.5,anchor=east] {$r$};
\node [anchor=north] at ($ (pc) + (0,-\pr)$) {Particle not in box};
\end{tikzpicture}
\begin{tikzpicture}[scale=1.5,baseline={(0,0)}]
\def\sqrttwo{1.4145}
\draw (-1,-1) rectangle (1,1);
\draw [dashed] (0,0) circle ((\sqrttwo+\sout*\sqrttwo);
\draw [|<->|] (0,0) -- (-1-\sout,-1-\sout) node [pos=0.3, anchor=west] {$\sqrt2(1+\alpha)R$};
\node [anchor=south] at (0,-1) {Box};
\node [anchor=north] at (0,-1-\sout) {`Stick-out' region };
\node [anchor=north] at (0,-1.85) {\texttt{'l2'}};
\coordinate (pc) at (-0.25, 0.9);
\fill [green] (pc) circle (1pt);
\draw [green] (pc) circle (\pr);
\draw [green,|<->|] (pc) -- ++(0,-\pr) node [pos=0.5,anchor=east] {$r$};
\node [anchor=north] at ($ (pc) + (0,-\pr)$) {Particle in box};
\end{tikzpicture}
Tree-based geometric lookup
===========================
.. automodule:: boxtree.geo_lookup
.. automodule:: boxtree.area_query
.. vim: sw=4
#! /bin/bash
tikz2png linf-l2.tikz images/linf-l2.png -density 200
Installation
============
This command should install :mod:`boxtree`::
This command should install the latest release of :mod:`boxtree` directly from PyPI::
pip install boxtree
You may need to run this with :command:`sudo`.
If you don't already have `pip <https://pypi.python.org/pypi/pip>`_,
You may need to run this with :command:`sudo` if you are not in a virtual environment
(not recommended). If you don't already have `pip <https://pypi.org/project/pip>`__,
run this beforehand::
curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
python get-pip.py
python -m ensurepip
For a more manual installation, download the source, unpack it,
and say::
For a more manual installation, download the source, unpack it, and run::
python setup.py install
pip install .
In addition, you need to have PyOpenCL installed. See the
`PyOpenCL Wiki <http://wiki.tiker.net/PyOpenCL/Installation>`_
for instructions.
This should also install all the required dependencies (see ``pyproject.toml``
for a complete list). The main one is PyOpenCL, which has extensive installation
instructions on the `PyOpenCL Wiki <https://wiki.tiker.net/PyOpenCL/Installation>`__.
For development, you may want to install in `editable mode
<https://setuptools.pypa.io/en/latest/userguide/development_mode.html>`__::
pip install --no-build-isolation --editable .[test]
User-visible Changes
====================
Version 2013.1
Version 2019.1
--------------
.. note::
This version is currently under development. You can get snapshots from
boxtree's `git repository <https://github.com/inducer/boxtree>`_
boxtree's `git repository <https://github.com/inducer/boxtree>`__
* Faster M2Ls in the FMMLIB backend using precomputed rotation matrices. This
change adds an optional *rotation_data* parameter to the FMMLIB geometry wrangler
constructor.
Version 2018.2
--------------
* Changed index style of the *from_sep_close_bigger_starts* interaction list.
Version 2018.1
--------------
* Added *timing_data* parameter to FMM driver.
Version 2013.1
--------------
* Initial release.
......@@ -63,8 +84,28 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Frequently Asked Questions
==========================
Acknowledgments
===============
Work on meshmode was supported in part by
* the US National Science Foundation under grant numbers DMS-1418961,
DMS-1654756, SHF-1911019, and OAC-1931577.
AK also gratefully acknowledges a hardware gift from Nvidia Corporation.
The views and opinions expressed herein do not necessarily reflect those of the
funding agencies.
Cross-References to Other Documentation
=======================================
.. currentmodule:: numpy
.. class:: int8
See :class:`numpy.generic`.
.. class:: int32
The FAQ is maintained collaboratively on the
`Wiki FAQ page <http://wiki.tiker.net/BoxTree/FrequentlyAskedQuestions>`_.
See :class:`numpy.generic`.
#! /bin/bash
set -e
set -x
if test "$1" = "" || test "$2" = ""; then
echo "Usage: $0 pic.tikz pic.png [CONVERT FLAGS ...]"
exit 1
fi
TIKZ="$1"
OUTF="$2"
shift
shift
TMPDIR=$(mktemp -d)
TEX="$TMPDIR/source.tex"
cat <<END > $TEX
\documentclass[preview]{standalone}
\nonstopmode
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}
\usetikzlibrary{fadings}
\usetikzlibrary{chains}
\usetikzlibrary{scopes}
\usetikzlibrary{shadows}
\usetikzlibrary{arrows}
\usetikzlibrary{snakes}
\usetikzlibrary{shapes.misc}
\usetikzlibrary{shapes.symbols}
\usetikzlibrary{shapes.multipart}
\usetikzlibrary{fit}
\usetikzlibrary{shapes.arrows}
\usetikzlibrary{shapes.geometric}
\usetikzlibrary{shapes.callouts}
\usetikzlibrary{decorations.text}
\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfsetlayers{background,main,foreground}
\renewcommand*\familydefault{\sfdefault}
\begin{document}
END
cat $TIKZ >> $TEX
cat <<END >> $TEX
\end{document}
END
(cd "$TMPDIR"; pdflatex source)
(cd "$TMPDIR"; convert "$@" source.pdf source.png)
cp "$TMPDIR/source.png" "$OUTF"
Utility Functionality
=====================
.. automodule:: boxtree.timing
.. automodule:: boxtree.constant_one
Traversal building
==================
Building interaction lists
==========================
.. automodule:: boxtree.traversal
Traversal data structure
------------------------
.. autoclass:: FMMTraversalInfo()
.. automethod:: get
.. automethod:: merge_close_lists
Build Entrypoint
----------------
.. autoclass:: FMMTraversalBuilder
.. automethod:: __call__
.. vim: sw=4
.. automodule:: boxtree.rotation_classes
.. automodule:: boxtree.translation_classes
Building Trees
==============
.. automodule:: boxtree.tree_of_boxes
.. automodule:: boxtree.tree_build
Tree building
=============
Tree Data Structures
====================
.. currentmodule:: boxtree
Tree data structure
-------------------
.. autoclass:: box_flags_enum
:members:
:undoc-members:
.. autoclass:: Tree()
.. rubric:: Methods
.. automethod:: get
Tree with linked point sources
------------------------------
.. currentmodule:: boxtree.tree
.. autoclass:: TreeWithLinkedPointSources
.. rubric:: Methods
.. automethod:: get
.. autofunction:: link_point_sources
Filtering the lists of targets
------------------------------
.. currentmodule:: boxtree.tree
Data structures
^^^^^^^^^^^^^^^
.. autoclass:: FilteredTargetListsInUserOrder()
.. rubric:: Methods
.. automethod:: get
.. autoclass:: FilteredTargetListsInTreeOrder()
.. rubric:: Methods
.. automethod:: get
Functions
^^^^^^^^^
.. autofunction:: filter_target_lists_in_user_order
.. autofunction:: filter_target_lists_in_tree_order
Build Entrypoint
----------------
.. currentmodule:: boxtree
.. autoclass:: TreeBuilder
.. automethod:: __call__
.. vim: sw=4
.. automodule:: boxtree.tree
#! /bin/sh
rsync --progress --verbose --archive --delete _build/html/* doc-upload:doc/boxtree
rsync --verbose --archive --delete _build/html/ doc-upload:doc/boxtree
import logging
import os
import sys
import numpy as np
import pyopencl as cl
# Configure the root logger
logging.basicConfig(level=os.environ.get("LOGLEVEL", "WARNING"))
logger = logging.getLogger(__name__)
# Set the logger level of this module to INFO so that logging outputs of this module
# are shown
logger.setLevel(logging.INFO)
# `process_elapsed` in `ProcessTimer` is only supported for Python >= 3.3
SUPPORTS_PROCESS_TIME = (sys.version_info >= (3, 3))
def demo_cost_model():
if not SUPPORTS_PROCESS_TIME:
raise NotImplementedError(
"Currently this script uses process time which only works on Python>=3.3"
)
from boxtree.pyfmmlib_integration import (
FMMLibExpansionWrangler,
FMMLibTreeIndependentDataForWrangler,
Kernel,
)
rng = np.random.default_rng(seed=42)
nsources_list = [1000, 2000, 3000, 4000, 5000]
ntargets_list = [1000, 2000, 3000, 4000, 5000]
dims = 3
dtype = np.float64
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
traversals = []
traversals_dev = []
level_orders_list = []
timing_results = []
def fmm_level_to_order(tree, ilevel):
return 10
for nsources, ntargets in zip(nsources_list, ntargets_list, strict=True):
# {{{ Generate sources, targets and target_radii
from boxtree.tools import make_normal_particle_array as p_normal
sources = p_normal(queue, nsources, dims, dtype, seed=15)
targets = p_normal(queue, ntargets, dims, dtype, seed=18)
from pyopencl.clrandom import PhiloxGenerator
clrng = PhiloxGenerator(queue.context, seed=22)
target_radii = clrng.uniform(
queue, ntargets, a=0, b=0.05, dtype=dtype
).get()
# }}}
# {{{ Generate tree and traversal
from boxtree import TreeBuilder
tb = TreeBuilder(ctx)
tree, _ = tb(
queue, sources, targets=targets, target_radii=target_radii,
stick_out_factor=0.15, max_particles_in_box=30, debug=True
)
from boxtree.traversal import FMMTraversalBuilder
tg = FMMTraversalBuilder(ctx, well_sep_is_n_away=2)
trav_dev, _ = tg(queue, tree, debug=True)
trav = trav_dev.get(queue=queue)
traversals.append(trav)
traversals_dev.append(trav_dev)
# }}}
tree_indep = FMMLibTreeIndependentDataForWrangler(
trav.tree.dimensions, Kernel.LAPLACE)
wrangler = FMMLibExpansionWrangler(tree_indep, trav,
fmm_level_to_order=fmm_level_to_order)
level_orders_list.append(wrangler.level_orders)
timing_data = {}
from boxtree.fmm import drive_fmm
src_weights = rng.random(size=tree.nsources, dtype=tree.coord_dtype)
drive_fmm(wrangler, (src_weights,), timing_data=timing_data)
timing_results.append(timing_data)
time_field_name = "process_elapsed"
from boxtree.cost import FMMCostModel, make_pde_aware_translation_cost_model
cost_model = FMMCostModel(make_pde_aware_translation_cost_model)
model_results = []
for icase in range(len(traversals)-1):
traversal = traversals_dev[icase]
model_results.append(
cost_model.cost_per_stage(
queue, traversal, level_orders_list[icase],
FMMCostModel.get_unit_calibration_params(),
)
)
queue.finish()
params = cost_model.estimate_calibration_params(
model_results, timing_results[:-1], time_field_name=time_field_name
)
predicted_time = cost_model.cost_per_stage(
queue, traversals_dev[-1], level_orders_list[-1], params,
)
queue.finish()
for field in ["form_multipoles", "eval_direct", "multipole_to_local",
"eval_multipoles", "form_locals", "eval_locals",
"coarsen_multipoles", "refine_locals"]:
measured = timing_results[-1][field]["process_elapsed"]
pred_err = (
(measured - predicted_time[field])
/ measured)
logger.info("actual/predicted time for %s: %.3g/%.3g -> %g %% error",
field,
measured,
predicted_time[field],
abs(100*pred_err))
if __name__ == "__main__":
demo_cost_model()
from __future__ import absolute_import
# STARTEXAMPLE
import pyopencl as cl
import logging
import numpy as np
from six.moves import range
import pyopencl as cl
logging.basicConfig(level="INFO")
ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)
dims = 2
nparticles = 10**4
nparticles = 500
# -----------------------------------------------------------------------------
# generate some random particle positions
# -----------------------------------------------------------------------------
from pyopencl.clrandom import RanluxGenerator
rng = RanluxGenerator(queue, seed=15)
from pyopencl.clrandom import PhiloxGenerator
rng = PhiloxGenerator(ctx, seed=15)
from pytools.obj_array import make_obj_array
particles = make_obj_array([
rng.normal(queue, nparticles, dtype=np.float64)
for i in range(dims)])
......@@ -25,10 +33,14 @@ particles = make_obj_array([
# build tree and traversals (lists)
# -----------------------------------------------------------------------------
from boxtree import TreeBuilder
tb = TreeBuilder(ctx)
tree, _ = tb(queue, particles, max_particles_in_box=30)
tree, _ = tb(queue, particles, max_particles_in_box=5)
from boxtree.traversal import FMMTraversalBuilder
tg = FMMTraversalBuilder(ctx)
trav, _ = tg(queue, tree)
......@@ -40,12 +52,28 @@ trav, _ = tg(queue, tree)
import matplotlib.pyplot as pt
pt.plot(particles[0].get(), particles[1].get(), "x")
pt.plot(particles[0].get(), particles[1].get(), "+")
from boxtree.visualization import TreePlotter
plotter = TreePlotter(tree.get(queue=queue))
plotter.draw_tree(fill=False, edgecolor="black")
plotter.draw_box_numbers()
# plotter.draw_box_numbers()
plotter.set_bounding_box()
pt.gca().set_aspect("equal")
pt.savefig("tree.png")
pt.tight_layout()
pt.tick_params(
axis="x", # changes apply to the x-axis
which="both", # both major and minor ticks are affected
bottom="off", # ticks along the bottom edge are off
top="off", # ticks along the top edge are off
labelbottom="off")
pt.tick_params(
axis="y",
which="both",
left="off",
top="off",
labelleft="off")
pt.savefig("tree.pdf")
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "boxtree"
version = "2024.10"
description = "Quadtree/octree building in Python and OpenCL"
readme = "README.rst"
license = { text = "MIT" }
authors = [
{ name = "Andreas Kloeckner", email = "inform@tiker.net" },
]
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Other Audience",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Information Analysis",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Scientific/Engineering :: Visualization",
"Topic :: Software Development :: Libraries",
"Topic :: Utilities",
]
dependencies = [
"arraycontext>=2021.1",
"cgen>=2020.1",
"mako",
"pymbolic>=2022.2",
"pyopencl>=2022.1",
"pytools>=2022.1",
]
[project.optional-dependencies]
doc = [
"furo",
"sphinx-copybutton",
"sphinx>=4",
]
fmmlib = [
"pyfmmlib>=2023.1",
]
meshmode = [
"loopy>=2024.1",
"meshmode>=2021.2",
"modepy>=2021.1",
]
test = [
"pylint",
"pytest",
"ruff",
]
[project.urls]
Documentation = "https://documen.tician.de/boxtree"
Repository = "https://github.com/inducer/boxtree"
[tool.ruff]
preview = true
[tool.ruff.lint]
extend-select = [
"B", # flake8-bugbear
"C", # flake8-comprehensions
"E", # pycodestyle
"F", # pyflakes
"G", # flake8-logging-format
"I", # flake8-isort
"N", # pep8-naming
"NPY", # numpy
"Q", # flake8-quotes
"RUF", # ruff
"SIM", # flake8-simplify
"UP", # pyupgrade
"W", # pycodestyle
]
extend-ignore = [
"C90", # McCabe complexity
"E221", # multiple spaces before operator
"E226", # missing whitespace around arithmetic operator
"E402", # module-level import not at top of file
"SIM223", # simplify `False and ...` conditional
]
[tool.ruff.lint.flake8-quotes]
docstring-quotes = "double"
inline-quotes = "double"
multiline-quotes = "double"
[tool.ruff.lint.isort]
combine-as-imports = true
known-first-party = [
"pytools",
"pymbolic",
"loopy",
"pyopencl",
"meshmode",
"modepy",
"cgen"
]
known-local-folder = [
"boxtree",
]
lines-after-imports = 2
# TODO: enable postponed annotations at some point
# required-imports = ["from __future__ import annotations"]
# [tool.ruff.lint.per-file-ignores]
# "doc/**/*.py" = ["I002"]
# "examples/**/*.py" = ["I002"]
[tool.typos.default]
extend-ignore-re = [
"(?Rm)^.*(#|//)\\s*spellchecker:\\s*disable-line$"
]
[tool.typos.default.extend-words]
# arange like np.arange
arange = "arange"
# as in mis-implements
mis = "mis"
[tool.typos.files]
extend-exclude = [
"contrib/*/*.ipynb",
"notes/*/*.eps",
]
[tool.pytest.ini_options]
markers = [
"opencl: uses OpenCL",
"geo_lookup: tests geometric lookups",
"area_query: tests area queries",
"mpi: tests distributed FMM",
]
numpy
git+git://github.com/pyopencl/pyopencl
git+git://github.com/inducer/islpy
git+git://github.com/inducer/loopy
git+git://github.com/inducer/pyfmmlib
git+https://github.com/inducer/pyopencl#egg=pyopencl
git+https://github.com/inducer/pymbolic#egg=pymbolic
git+https://github.com/inducer/islpy#egg=islpy
git+https://github.com/inducer/loopy#egg=loopy
git+https://github.com/inducer/modepy#egg=modepy
git+https://github.com/inducer/meshmode#egg=meshmode
git+https://github.com/inducer/pyfmmlib#egg=pyfmmlib
git+https://github.com/inducer/arraycontext#egg=arraycontext
# only for reference values for the fmmlib test
# (unable to use---circular dep)
# git+https://github.com/inducer/sumpy