Skip to main content

A subset of Feltor's dg library as a python implementation

Project description

pyFeltor

An implementation of feltor's discontinuous Galerkin 'dg' library in python.

LICENSE : MIT Code style: black

Rationale

In order to analyse Feltor simulations python has turned out to be an invaluable tool. However, it is currently not possible to compute a dG derivative inside python once a field is loaded from a netCDF file. All diagnostics therefore have to be written by the simulation code and an extension involves writing the new diagnostics, re-compilation of code and a re-simulation of the result. This is a too costly interruption of the workflow for a simple task. The initial goal for this package is to provide the dG derivatives as they are used inside the dg library as well as other quality of life functions like weights, grids and the evaluate functions. In fact, since many numerical packages are available through python this enables many applications beyond simple simulations diagnostics. The only downside is of course that all functions are unparallelized in python.

As a second addition, Feltor's geometries extension is available in python. However, the geometries functions and classes are not re-implemented in python, but they are bound to python via the pybind11 library. As such the corresponding C++ binding code must be compiled in order to generate the module dg.geo.

Installation

The pyfeltor.dg.geo part of the module contains python bindings for the underlying C++ feltor code using pybind11. During installation the C++ code will be compiled using cmake, which may take a minute or two.

The simplest way is to install from the python package index pypi via the package manager pip v23.0

python3 -m pip install pyfeltor

To install from github you have to clone the repository and then use the package manager pip v23.0

git clone https://github.com/feltor-dev/pyfeltor
cd pyfeltor
python3 -m pip install -e . # editable installation of the module
# ... if asked, cancel all password prompts ...
cd tests
pytest-3 -s . # run all the unittests with output

Usage

Generally, pyfeltor is built to mimic the dg library in feltor. Consider

  • pyfeltor uses 1d numpy arrays as its vector class
  • there is only one grid class dg.Grid that generalises dg::Grid1d, dg::Grid2d and dg::Grid3d
  • Grids don't hold boundary conditions, these need to be provided in each function
  • the evaluate function generates 1d (flat) numpy arrays that can be reshaped to 1d, 2d, 3d structure using reshape(grid.shape)
  • the Grids do not name dimensions, only number them; the last/rightmost dimension is the one that varies fastest in memory (contrary to the C++ library where the first dimension (x) varies fastest).
  • the equivalent of the dg::blas1 vector functions are just plain math operators with numpy arrays
  • the equivalent of dg::blas1::dot and dg::blas2::dot is np.sum
  • the derivative matrices are generated as scipy.sparse matrices
  • the equivalent of dg::blas::symv is the dot method of scipy.sparse matrices
  • The elliptic operator is a sparse matrix in pyfeltor (in contrast to a class) and is created by a function

Grid generation, evaluation and integration

import numpy as np

# import dg library
from pyfeltor import dg

n, Nx = 3, 12
grid = dg.Grid(x0=1, x1=2, n=n, N=Nx)
weights = dg.create.weights(grid)
func = dg.evaluate(lambda x: np.exp(x), grid)

sol = np.exp(2) - np.exp(1)
# the equivalent of dg::blas1::dot
num = np.sum(weights * func)
print(f"Correct integral is {sol} while numerical is {num}")

Generate and use a derivative

import numpy as np
from pyfeltor import dg

# We choose x as the second dimension here to make it vary in memory fastest
n, Nx, Ny = 3, 12, 24
g2d = dg.Grid(x0=[0.1, 0], x1=[2 * np.pi + 0.1, np.pi], n=[n, n], N=[Ny, Nx])
w2d = dg.create.weights(g2d)
f2d = dg.evaluate(lambda y, x: np.sin(x) * np.sin(y), g2d)
x2d = dg.evaluate(lambda y, x: np.cos(x) * np.sin(y), g2d)

# the x dimension is the rightmost (index 1)
dx = dg.create.dx(1, g2d, dg.bc.DIR, dg.direction.forward)
# and the y dimension is the leftmost (index 0)
dy = dg.create.dx(0, g2d, dg.bc.PER, dg.direction.centered)
error = dx.dot(f2d) - x2d
norm = np.sqrt(np.sum(w2d * error ** 2)) / np.sqrt(w2d * x2d ** 2)
print(f"Relative error to true solution: {norm}")

You can equally name x as the first dimension, then the y dimension varies fastest in memory. Just keep it consistent.

The elliptic operator

from pyfeltor import dg
import numpy as np
import scipy.sparse.linalg
import scipy.linalg

amp = 0.9
pol = lambda y, x: 1 + amp * np.sin(x) * np.sin(y)
rhs = (
    lambda y, x: 2.0 * np.sin(x) * np.sin(y) * (amp * np.sin(x) * np.sin(y) + 1)
    - amp * np.sin(x) * np.sin(x) * np.cos(y) * np.cos(y)
    - amp * np.cos(x) * np.cos(x) * np.sin(y) * np.sin(y)
)
sol = lambda y, x: np.sin(x) * np.sin(y)
lx, ly = np.pi, 2 * np.pi
bcx, bcy = dg.bc.DIR, dg.bc.PER
n, Nx, Ny = 3, 64, 64
grid = dg.Grid([0, 0], [ly, lx], [n, n], [Ny, Nx])
x = np.zeros(grid.size())
b = dg.evaluate(rhs, grid)
chi = dg.evaluate(pol, grid)
solution = dg.evaluate(sol, grid)
# here we assemble the dg elliptic operator as a sparse matrix
pol_forward = dg.create.elliptic(
    grid,
    [bcy, bcx],
    [dg.direction.forward, dg.direction.forward],
    sigma=chi,
    jumpfactor=1,
)
# use a direct solver to solve linear equation
x = scipy.sparse.linalg.spsolve(pol_forward, b)

w2d = dg.create.weights(grid)
print("Distance to true solution is ", np.sqrt(np.sum(w2d * (x - solution) ** 2)))

Interpolation

from pyfeltor import dg
import numpy as np

print("2D INTERPOLATION TEST")

n, Nx, Ny = 3, 32, 32
g = dg.Grid(x0=[-5 * np.pi, -np.pi], x1=[-4 * np.pi, 0], n=[n, n], N=[Ny, Nx])
equi = dg.Grid(
    x0=[-5 * np.pi, -np.pi], x1=[-4 * np.pi, 0], n=[1, 1], N=[n * Ny, n * Nx]
)
y = dg.evaluate(lambda y, x: y, equi)
x = dg.evaluate(lambda y, x: x, equi)

interp = dg.create.interpolation([y, x], g, [dg.bc.DIR, dg.bc.DIR])
vec = dg.evaluate(lambda y, x: np.sin(x) * np.sin(y), g)
inter = interp.dot(vec)
interE = dg.evaluate(lambda y, x: np.sin(x) * np.sin(y), equi)
error = np.sum((inter - interE) ** 2) / np.sum(inter ** 2)
print(f"Error is {np.sqrt(error)} (should be small)!")

The dg.geo module

Currently the dg.geo module binds all classes and functions available in Modules 3 and 5 in the documentation. Since the interface is now the same, the C++ documentation applies exactly to the python module as well. Only a few caveats need to be considered:

  • in all derivatives of dg::geo::aCylindricalFunctor only the 2 dimensional version of operator() is currently bound
  • in all derivatives of dg::geo::aCylindricalFunctor the operator() is vectorized, i.e. can be called on a numpy array
  • function or member parameters that are of type dg::file::WrappedJsonValue on the C++ code are simple python dictionaries on the python side (arrays need to be lists though)
  • functions or members with parameters from the original dg library (e.g. dg::Grid2d) are currently not bound (except dg::geo::createSheathRegion).
  • python does not support non-const double reference arguments (e.g. double& R) see this FAQ entry on pybind11 In the cases when it occurs we append these variables as a tuple to the return value of the function (for example in dg.geo.findOpoint, see the examples below)

Generating simple flux functions

A first application is to generate a flux function and evaluating it like so

from pyfeltor import dg
params = {c = np.array( [1,2,3,4])
params = {"R_0" : 400, "inverseaspectratio" : 20, "elongation" : 1, "triangularity" : 1,
          "PP" : 1, "PI" : 1, "description" : "standardX", "M" : 2, "N" : 2, "c" : c.tolist()}
pp = dg.geo.polynomial.Parameters(params)
psip = dg.geo.polynomial.Psip(pp)
grid = dg.Grid( x0 = (pp.R_0-pp.a, -pp.a), x1 = (pp.R_0+pp.a, pp.a), n=(3,3), N=(24, 24))
psi = dg.evaluate( psip, grid)

As an application in magneticfieldb you can see a polynomial flux function being fitted to an experimental field.

Generating the magnetic field and magnetic field functions

In this second example we look how we can use a json magnetic field file to generate the magnetic field for us:

from pyfeltor import dg
with open ("geometry_params_Xpoint.json", "r") as f:
    magparams = json.load(f)
mag = dg.geo.createMagneticField( magparams)
RO,ZO  = mag.R0(), 0
(point, RO, ZO) = dg.geo.findOpoint(mag.get_psip(), RO, ZO)
print( "O-point found at ", RO, ZO)
a = mag.params().a()
R0 = mag.R0()
grid = dg.Grid(x0=(R0-a, -a), x1=(R0+a, +a), n=(3, 3), N=(24, 24))
psi = dg.evaluate( mag.psip(), grid)
BR  = dg.evaluate( dg.geo.BFieldR(mag), grid)
BZ  = dg.evaluate( dg.geo.BFieldZ(mag), grid)
# ... the list of possible functions is large ...
# Since the functions can be evaluated using numpy arrays this also works
R = dg.evaluate( lambda R, Z: R, grid)
Z = dg.evaluate( lambda R, Z: Z, grid)
BP = dg.geo.BFieldP(mag)(R,Z)
# go on to plot with matplotlib ...

Generating q-profile

A common task is to generate a q-profile. This can be done like so:

with open ("enrx_tcv.json", "r") as f:
    magparams = json.load(f)
mag = dg.geo.createMagneticField(magparams)
qfunctor = dg.geo.SafetyFactor(mag)
RO,ZO  = mag.R0(), 0
(point, RO, ZO) = dg.geo.findOpoint(mag.get_psip(), RO, ZO)
print( "O-point found at ", RO, ZO)
psipO = mag.psip()(RO,ZO)
grid = dg.Grid( psipO, 0, 3, 64)
qprof = dg.evaluate( qfunctor, grid)
print(qprof)

Contributions

Contributions are welcome.

Authors

Matthias Wiesenberger

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyfeltor-0.1.1.tar.gz (28.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyfeltor-0.1.1-cp313-cp313-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.13Windows x86-64

pyfeltor-0.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

pyfeltor-0.1.1-cp313-cp313-macosx_14_0_arm64.whl (2.2 MB view details)

Uploaded CPython 3.13macOS 14.0+ ARM64

pyfeltor-0.1.1-cp312-cp312-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.12Windows x86-64

pyfeltor-0.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

pyfeltor-0.1.1-cp312-cp312-macosx_14_0_arm64.whl (2.2 MB view details)

Uploaded CPython 3.12macOS 14.0+ ARM64

File details

Details for the file pyfeltor-0.1.1.tar.gz.

File metadata

  • Download URL: pyfeltor-0.1.1.tar.gz
  • Upload date:
  • Size: 28.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pyfeltor-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b822f92c3023f095baee6e57bad2b91721a5c238ceffa3eb716eced0bcc1d104
MD5 08a426c19b0d9685f1cf37355681667f
BLAKE2b-256 88e618274779b482da16bb04a7283425323f44e4e6cbaf5d782cc7a74bc3f03e

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfeltor-0.1.1.tar.gz:

Publisher: python_publish.yml on feltor-dev/pyFeltor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyfeltor-0.1.1-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: pyfeltor-0.1.1-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pyfeltor-0.1.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 58e6ba02ab53e70dad2507044e54a42af5c91857aa60a135c2a71533620829bc
MD5 ddb44f10992a784e5ee9f24e897e6473
BLAKE2b-256 aa98e5ff644aff3ac54e3dee0c90a9b8398b52bc44abf6e93bed190273f44db0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfeltor-0.1.1-cp313-cp313-win_amd64.whl:

Publisher: python_publish.yml on feltor-dev/pyFeltor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyfeltor-0.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyfeltor-0.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2709b7235de5c3f12531361ccd5c88baac5ad88d5e5bb8beaac6c4b85de95969
MD5 113b08aed739403d54050768bed76e73
BLAKE2b-256 3ad9a21ac6d24943c76c242e44ae16b0cf7a492d689f361261f3d603606aba62

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfeltor-0.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: python_publish.yml on feltor-dev/pyFeltor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyfeltor-0.1.1-cp313-cp313-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for pyfeltor-0.1.1-cp313-cp313-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 5067fa6c346bb960f7a28ed1aad5e1873532e3917d8fca24c9498a35d6d9ce11
MD5 ce6f4e3fba0b0c2ea1aa39f271d5375d
BLAKE2b-256 0cf814e58efd7839280bed48df647695fadf1a61eb1dccfe417bc0c81aa5c652

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfeltor-0.1.1-cp313-cp313-macosx_14_0_arm64.whl:

Publisher: python_publish.yml on feltor-dev/pyFeltor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyfeltor-0.1.1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: pyfeltor-0.1.1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pyfeltor-0.1.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 fb219b144d56cbb093767f1594f8211cd66c627d268379dead9b594efa9734f6
MD5 4dc604e9208040c6430c4faa10f39db7
BLAKE2b-256 09e2a58c53f2905df84e34326741de18d4d13d90dc0508d7a898b9ce031f6458

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfeltor-0.1.1-cp312-cp312-win_amd64.whl:

Publisher: python_publish.yml on feltor-dev/pyFeltor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyfeltor-0.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyfeltor-0.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 37a43ff660f622b24922d9a371ddb46dbae0fb8688309d5f3e17c5df058e9e84
MD5 e9cfc2fa44f8d41f14d7b91fcb96fd2b
BLAKE2b-256 284bfe16e204f6108bdd9b8b0f119d327146c6e67974df990ebe5814fcae1660

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfeltor-0.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: python_publish.yml on feltor-dev/pyFeltor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyfeltor-0.1.1-cp312-cp312-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for pyfeltor-0.1.1-cp312-cp312-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 403aa682be294eea88293bf5b70a7b57e65e6bac01b7eb13e8112a9cf0a9ba67
MD5 ba1fe13f8bfaf17614191b1cdccb8789
BLAKE2b-256 38c18b8cadae0a2add199e8f505f4ec6839d2488d95accb535d787a4599ecf04

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyfeltor-0.1.1-cp312-cp312-macosx_14_0_arm64.whl:

Publisher: python_publish.yml on feltor-dev/pyFeltor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page