Full Code of JuliaParallel/PETSc.jl for AI

main 7fe119ea52d8 cached
221 files
7.6 MB
2.0M tokens
7 symbols
1 requests
Download .txt
Showing preview only (8,032K chars total). Download the full file or copy to clipboard to get everything.
Repository: JuliaParallel/PETSc.jl
Branch: main
Commit: 7fe119ea52d8
Files: 221
Total size: 7.6 MB

Directory structure:
gitextract_n_mpc822/

├── .JuliaFormatter.toml
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── CompatHelper.yml
│       ├── TagBot.yml
│       ├── ci.yml
│       └── documentation.yml
├── .gitignore
├── .zenodo.json
├── CITATION.bib
├── LICENSE
├── Project.toml
├── README.md
├── docs/
│   ├── .documenter.enc
│   ├── Project.toml
│   ├── make.jl
│   └── src/
│       ├── index.md
│       └── man/
│           ├── FAQ.md
│           ├── ao_lowlevel.md
│           ├── contributing.md
│           ├── design.md
│           ├── dm.md
│           ├── dm_lowlevel.md
│           ├── dmda.md
│           ├── dmda_lowlevel.md
│           ├── dmforest_lowlevel.md
│           ├── dmnetwork_lowlevel.md
│           ├── dmplex_lowlevel.md
│           ├── dmshell_lowlevel.md
│           ├── dmstag.md
│           ├── dmstag_lowlevel.md
│           ├── dmswarm_lowlevel.md
│           ├── funding.md
│           ├── getting_started.md
│           ├── hpc.md
│           ├── installation.md
│           ├── is_lowlevel.md
│           ├── ksp.md
│           ├── ksp_lowlevel.md
│           ├── lowlevel_intro.md
│           ├── mat.md
│           ├── mat_lowlevel.md
│           ├── pc_lowlevel.md
│           ├── petscsection_lowlevel.md
│           ├── petscsf_lowlevel.md
│           ├── petscviewer_lowlevel.md
│           ├── snes.md
│           ├── snes_lowlevel.md
│           ├── tao_lowlevel.md
│           ├── ts_lowlevel.md
│           ├── utilities.md
│           ├── vec.md
│           └── vec_lowlevel.md
├── examples/
│   ├── Liouville_Bratu_Gelfand.jl
│   ├── SNES_ex2.jl
│   ├── SNES_ex2b.jl
│   ├── convergence_test.jl
│   ├── dmda_laplacian.jl
│   ├── dmda_laplacian_convergence.jl
│   ├── dmstag_ex8.jl
│   ├── ex1.jl
│   ├── ex16.jl
│   ├── ex45.jl
│   ├── ex50.jl
│   ├── ex51.jl
│   ├── ex51_implicit.jl
│   ├── laplacian.jl
│   ├── porosity_waves.jl
│   ├── scalability_tests/
│   │   ├── ex45_julia.c
│   │   ├── job.sh
│   │   ├── makefile
│   │   ├── parse_scaling.jl
│   │   ├── submit_scaling.sh
│   │   └── weak_scaling_plot.jl
│   └── stokes2d_linear_dmstag.jl
├── src/
│   ├── LibPETSc.jl
│   ├── LibPETSc_const.jl
│   ├── LibPETSc_lib.jl
│   ├── LibPETSc_startup.jl
│   ├── PETSc.jl
│   ├── audit.jl
│   ├── autowrapped/
│   │   ├── AO_wrappers.jl
│   │   ├── Characteristic_wrappers.jl
│   │   ├── DM_wrappers.jl
│   │   ├── DMaddons_wrappers.jl
│   │   ├── IS_wrappers.jl
│   │   ├── ISaddons_wrappers.jl
│   │   ├── KSPGuess_wrappers.jl
│   │   ├── KSP_wrappers.jl
│   │   ├── Mat_wrappers.jl
│   │   ├── Mataddons_wrappers.jl
│   │   ├── PC_wrappers.jl
│   │   ├── PF_wrappers.jl
│   │   ├── PetscBag_wrappers.jl
│   │   ├── PetscBench_wrappers.jl
│   │   ├── PetscContainer_wrappers.jl
│   │   ├── PetscConvEst_wrappers.jl
│   │   ├── PetscDLLibrary_wrappers.jl
│   │   ├── PetscDS_wrappers.jl
│   │   ├── PetscDevice_wrappers.jl
│   │   ├── PetscDraw_wrappers.jl
│   │   ├── PetscDualSpace_wrappers.jl
│   │   ├── PetscFE_wrappers.jl
│   │   ├── PetscFV_wrappers.jl
│   │   ├── PetscFunctionList_wrappers.jl
│   │   ├── PetscGridHash_wrappers.jl
│   │   ├── PetscHeap_wrappers.jl
│   │   ├── PetscIntStack_wrappers.jl
│   │   ├── PetscKDTree_wrappers.jl
│   │   ├── PetscLayout_wrappers.jl
│   │   ├── PetscLimiter_wrappers.jl
│   │   ├── PetscLog_wrappers.jl
│   │   ├── PetscMatlabEngine_wrappers.jl
│   │   ├── PetscObject_wrappers.jl
│   │   ├── PetscOmpCtrl_wrappers.jl
│   │   ├── PetscOptions_addons_wrappers.jl
│   │   ├── PetscOptions_wrappers.jl
│   │   ├── PetscPartitioner_wrappers.jl
│   │   ├── PetscRandom_wrappers.jl
│   │   ├── PetscRegressor_wrappers.jl
│   │   ├── PetscSF_wrappers.jl
│   │   ├── PetscSection_wrappers.jl
│   │   ├── PetscSegBuffer_wrappers.jl
│   │   ├── PetscSpace_wrappers.jl
│   │   ├── PetscToken_wrappers.jl
│   │   ├── PetscViennaCLIndices_wrappers.jl
│   │   ├── PetscViewer_wrappers.jl
│   │   ├── Petsccomm_wrappers.jl
│   │   ├── SNESLineSearch_wrappers.jl
│   │   ├── SNES_wrappers.jl
│   │   ├── Sys_wrappers.jl
│   │   ├── TS_wrappers.jl
│   │   ├── TSaddons_wrappers.jl
│   │   ├── Tao_addons_wrappers.jl
│   │   ├── Tao_wrappers.jl
│   │   ├── VecTagger_wrappers.jl
│   │   ├── Vec_wrappers.jl
│   │   ├── Vecs_wrappers.jl
│   │   ├── enums_wrappers.jl
│   │   ├── petsc_library.jl
│   │   ├── petsc_wrappers_version.jl
│   │   ├── petscarray.jl
│   │   ├── senums_wrappers.jl
│   │   ├── struct_wrappers.jl
│   │   └── typedefs_wrappers.jl
│   ├── deprecated/
│   │   ├── const.jl
│   │   ├── dm.jl
│   │   ├── dm_wrapped.jl
│   │   ├── dmda.jl
│   │   ├── dmstag.jl
│   │   ├── dmstag_wrapped.jl
│   │   ├── ksp.jl
│   │   ├── ksp_wrapped.jl
│   │   ├── lib.jl
│   │   ├── mat.jl
│   │   ├── matshell.jl
│   │   ├── options.jl
│   │   ├── pc.jl
│   │   ├── ref.jl
│   │   ├── snes.jl
│   │   ├── sys.jl
│   │   ├── utils.jl
│   │   ├── vec.jl
│   │   └── viewer.jl
│   ├── dm.jl
│   ├── dmda.jl
│   ├── dmstag.jl
│   ├── init.jl
│   ├── ksp.jl
│   ├── mat.jl
│   ├── options.jl
│   ├── snes.jl
│   ├── startup.jl
│   ├── string_wrappers.jl
│   ├── string_wrappers_extra.jl
│   ├── sys.jl
│   ├── ts.jl
│   └── vec.jl
├── test/
│   ├── 01-hello.jl
│   ├── dmda.jl
│   ├── dmnetwork.jl
│   ├── dmproduct.jl
│   ├── dmshell.jl
│   ├── dmstag.jl
│   ├── doc_examples_is.jl
│   ├── doc_examples_tao.jl
│   ├── doc_examples_ts.jl
│   ├── doc_examples_viewer.jl
│   ├── examples.jl
│   ├── init.jl
│   ├── ksp.jl
│   ├── lib.jl
│   ├── low_level_is.jl
│   ├── low_level_petscsection.jl
│   ├── low_level_tao.jl
│   ├── low_level_ts.jl
│   ├── low_level_viewer.jl
│   ├── mat.jl
│   ├── matshell.jl
│   ├── mpi_examples.jl
│   ├── mpimat.jl
│   ├── mpivec.jl
│   ├── old_test.jl
│   ├── options.jl
│   ├── runtests.jl
│   ├── snes.jl
│   ├── snes_helpers.jl
│   ├── tao_helpers.jl
│   ├── test_dmstag.jl
│   ├── test_snes.jl
│   ├── testutils.jl
│   ├── ts_ex16.jl
│   ├── ts_ex51.jl
│   ├── ts_ex51_implicit.jl
│   └── vec.jl
└── wrapping/
    ├── Project.toml
    ├── currently_wrapped_classes.md
    ├── find_doc_strings.jl
    ├── generatejuliabindings.jl
    ├── local_types.jl
    ├── prologue.jl
    └── test.jl

================================================
FILE CONTENTS
================================================

================================================
FILE: .JuliaFormatter.toml
================================================
indent = 4
margin = 80
always_for_in = true
whitespace_typedefs = true
whitespace_ops_in_indices = true
remove_extra_newlines = true


================================================
FILE: .github/dependabot.yml
================================================
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/" # Location of package manifests
    schedule:
      interval: "monthly"


================================================
FILE: .github/workflows/CompatHelper.yml
================================================
name: CompatHelper
on:
  schedule:
    - cron: 0 0 * * *
  workflow_dispatch:
jobs:
  CompatHelper:
    runs-on: ubuntu-latest
    steps:
      - name: "Add the General registry via Git"
        run: |
          import Pkg
          ENV["JULIA_PKG_SERVER"] = ""
          Pkg.Registry.add("General")
        shell: julia --color=yes {0}
      - name: "Install CompatHelper"
        run: |
          import Pkg
          name = "CompatHelper"
          uuid = "aa819f21-2bde-4658-8897-bab36330d9b7"
          version = "3"
          Pkg.add(; name, uuid, version)
        shell: julia --color=yes {0}
      - name: "Run CompatHelper"
        run: |
          import CompatHelper
          CompatHelper.main()
        shell: julia --color=yes {0}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
          # COMPATHELPER_PRIV: ${{ secrets.COMPATHELPER_PRIV }}


================================================
FILE: .github/workflows/TagBot.yml
================================================
name: TagBot
on:
  issue_comment:
    types:
      - created
  workflow_dispatch:
    inputs:
      lookback:
        default: "3"
permissions:
  actions: read
  checks: read
  contents: write
  deployments: read
  issues: read
  discussions: read
  packages: read
  pages: read
  pull-requests: read
  repository-projects: read
  security-events: read
  statuses: read
jobs:
  TagBot:
    if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
    runs-on: ubuntu-latest
    steps:
      - uses: JuliaRegistries/TagBot@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          # Edit the following line to reflect the actual name of the GitHub Secret containing your private key
          ssh: ${{ secrets.DOCUMENTER_KEY }}
          # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }}


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
  push:
    branches:
      - main
      - staging
      - trying
    tags: '*'
  pull_request:

concurrency:
  # Skip intermediate builds: always.
  # Cancel intermediate builds: always.
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
  
jobs:
  test:
    name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ github.event_name }}
    runs-on: ${{ matrix.os }}
    timeout-minutes: 45
    strategy:
      fail-fast: false
      matrix:
        version:
          - '1'
        os:
          - macOS-15-intel        # with intel processors 
          - ubuntu-latest
          # - windows-latest  # Disabled: Windows PETSc_jll built without MPI, requires WSL for testing
        arch:
          - x64 
        include:
          - os: macOS-latest
            arch: aarch64
            version: 1
          - os: ubuntu-latest
            arch: x64
            version: 'lts'
            
    steps:
      - uses: actions/checkout@v6
      - uses: julia-actions/setup-julia@v3
        with:
          version: ${{ matrix.version }}
          arch: ${{ matrix.arch }}
      - run: julia -e 'using InteractiveUtils; versioninfo(verbose=true)'
      - uses: julia-actions/cache@v3
      - uses: julia-actions/julia-buildpkg@latest
      - uses: julia-actions/julia-runtest@latest
        


================================================
FILE: .github/workflows/documentation.yml
================================================
name: Documentation

on:
  push:
    branches:
      - main
      - staging
      - trying
    tags: '*'
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: julia-actions/setup-julia@latest
        with:
          version: '1'
      - name: Install dependencies
        run: julia --project=docs/ -e 'using Pkg; Pkg.build(); Pkg.develop(PackageSpec(path=pwd()));  Pkg.instantiate()'
      - name: Build and deploy
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token
          DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key
        run: julia --project=docs/ docs/make.jl


================================================
FILE: .gitignore
================================================
deps/ComplexDouble/
deps/RealDouble/
deps/RealSingle/
deps/deps.jl
deps/petsc-*.tar.gz
*.jl.cov
*.jl.mem
docs/build/
docs/site/
Manifest.toml
wrapping/.CondaPkg
**/.vscode
**/run/



================================================
FILE: .zenodo.json
================================================
{
  "title": "PETSc.jl: Julia bindings for PETSc",
  "description": "PETSc.jl provides an interface to the Portable, Extensible Toolkit for Scientific Computation (PETSc) library, allowing the combination of Julia features (such as automatic differentiation) with PETSc's infrastructure, including linear, nonlinear, and optimization solvers, timesteppers, domain management (DM), and more, in a distributed-memory (MPI) environment.",
  "creators": [
    {
      "name": "Kaus, Boris J.P.",
      "affiliation": "Johannes Gutenberg University Mainz, Germany",
      "orcid": "https://orcid.org/0000-0002-0247-8660"
    },
    {
      "name": "Shah, Viral B.",
      "affiliation": "Julia Computing, USA",
      "orcid": "https://orcid.org/0000-0001-9602-4012"
    },
    {
      "name": "Kozdon, Jeremy E.",
      "affiliation": "NextSilicon, USA",
      "orcid": "https://orcid.org/0000-0002-2493-4292"
    },
    {
      "name": "Churavy, Valentin",
      "affiliation": "Johannes Gutenberg University Mainz, Germany",
      "orcid": "https://orcid.org/0000-0002-9033-165X"
    },
    {
      "name": "Schnetter, Erik",
      "affiliation": "Perimeter Institute, Canada",
      "orcid": "https://orcid.org/0000-0002-4518-9017"
    },
    {
      "name": "Byrne, Simon",
      "affiliation": "NVIDIA, USA",
      "orcid": "https://orcid.org/0000-0001-8048-6810"
    }
  ],
  "keywords": [
    "Julia",
    "PETSc",
    "scientific computing",
    "linear algebra",
    "nonlinear solvers",
    "optimization",
    "MPI",
    "parallel computing"
  ],
  "grants": [
    {"id": "10.13039/501100000780::771143"} 
  ],
  "license": {
    "id": "MIT"
  },
  "upload_type": "software",
  "access_right": "open",
  "communities": [
    {
      "identifier": "juliapackage"
    }
  ]
}


================================================
FILE: CITATION.bib
================================================
@software{petsc_jl,
  author       = {Kaus, Boris and
                  Shah, Viral B. and
                  Kozdon, Jeremy E. and
                  Churavy, Valentin and
                  Schnetter, Erik and
                  Byrne, Simon},
  title        = {PETSc.jl: Julia bindings for PETSc},
  year         = 2026,
  publisher    = {Zenodo},
  doi          = {10.5281/zenodo.18274810},
  url          = {https://github.com/JuliaParallel/PETSc.jl}
}

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2026 Boris Kaus, Viral B. Shah, Valentin Churavy, Erik Schnetter, Jeremy E. Kozdon, Simon Byrne 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, 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.


================================================
FILE: Project.toml
================================================
name = "PETSc"
uuid = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0"
version = "0.4.9"
authors = ["Boris Kaus <kaus@uni-mainz.de>", "Viral B. Shah <virals@gmail.com>", "Valentin Churavy <v.churavy@gmail.com>", "Erik Schnetter <eschnetter@perimeterinstitute.ca>", "Jeremy E. Kozdon <jeremy@kozdon.net>", "Simon Byrne <simonbyrne@gmail.com>"]

[deps]
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
MPIPreferences = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
PETSc_jll = "8fa3689e-f0b9-5420-9873-adf6ccf46f2d"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"

[compat]
ForwardDiff = "0.10, 1"
Libdl = "^1.10"
LinearAlgebra = "^1.10"
MPI = "0.20"
MPIPreferences = "0.1"
Preferences = "1"
OffsetArrays = "1.0"
PETSc_jll = "3.22"
Pkg = "^1.10"
SparseArrays = "1.10"
Statistics = "^1.10"
UnicodePlots = "3.0"
julia = "^1.10"

[extras]
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseDiffTools = "47a9eef4-7e08-11e9-0b38-333d64bd3804"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228"

[targets]
test = ["ForwardDiff", "UnicodePlots", "Test", "Plots", "SparseDiffTools", "Printf", "Random", "CairoMakie"]


================================================
FILE: README.md
================================================
# PETSc.jl

[![Build Status](https://github.com/JuliaParallel/PETSc.jl/workflows/CI/badge.svg)](https://github.com/JuliaParallel/PETSc.jl/actions/workflows/ci.yml)
[![doc stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliaparallel.github.io/PETSc.jl/stable/)
[![doc dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliaparallel.github.io/PETSc.jl/dev/)
[![DOI](https://zenodo.org/badge/38933145.svg)](https://doi.org/10.5281/zenodo.18274809)


`PETSc.jl` provides an interface to the Portable, Extensible Toolkit for Scientific Computation ([PETSc](https://petsc.org)) library, allowing the combination of Julia features (such as automatic differentiation) with the PETSc's infrastructure, including linear, nonlinear, and optimization solvers, timesteppers, domain management (DM), and more, in a distributed-memory (MPI) environment. 

This package comprises two main components:

1. An automatically generated, low-level interface for large parts of the PETSc API (see `PETSc.LibPETSc`).
2. A curated, high-level, more Julianic interface for selected functionality.

The low-level interface covers nearly the entire PETSc API, but may be awkward to work with and likely requires previous experience with PETSc to use effectively. The high level interface is designed to be more familiar and convenient for Julia users, and allows, for example, to set matrix entries with `A[1,2] = 3.0`, rather than having to call `LibPETSc.MatSetValue`. It, however, exposes only a small portion of the functionality of the underlying library. 

## Installation
This package can be added with the julia command:
```julia
julia>]add PETSc
```
The installation can be tested with
```julia
julia>]test PETSc
```

## PETSc binaries

By default, the package uses a pre-built binary of PETSc (see [PETSc_jll](https://github.com/JuliaBinaryWrappers/PETSc_jll.jl)) along with a default installation of `MPI.jl`, so you don't have to install it on your machine.

If you want to use the package with a custom PETSc build, use `set_library!` to configure it once — the path is stored persistently in `LocalPreferences.toml` and no environment variables are needed:

```julia
using PETSc
PETSc.set_library!("/path/to/custom/libpetsc.so"; PetscScalar=Float64, PetscInt=Int64)
# Restart Julia — PETSc_jll is not loaded and your library is used automatically.
```

To revert to the bundled binaries: `PETSc.unset_library!()`. To check the current configuration: `PETSc.library_info()`.

To get an overview of available precompiled libraries:
```julia
julia>using PETSc
julia>[PETSc.petsclibs...]
```

## Windows users 
The package currently does not work on windows, mainly because `MicrosoftMPI_jll` does not function when used along with the precompiled version used in `PETSc_jll`. Windows users are therefore advised to install the [Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux) (WSL) and run PETSc through there. 

## Getting started
Have a look at the [documentation](https://juliaparallel.org/PETSc.jl/stable/), at the [examples](./examples/) directory or at the tests in the [test](./test) directory. We do keep the tests up to date, so that is a good starting point. 

Note, that we do not have tests in place for the whole library at this stage. The best supported parts are `DMDA`,`DMStag`, `KSP`,`SNES`,`Vec` and `Mat` interfaces, while other parts such as `DMPlex` do not have a high-level interface or tests yet. Users will thus have to rely on the low-level interface.


================================================
FILE: docs/Project.toml
================================================
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0"


================================================
FILE: docs/make.jl
================================================
using Documenter, PETSc

makedocs(;
    modules=[PETSc],
    sitename="PETSc.jl",
    checkdocs=:exports,  # Only check exported functions, skip LibPETSc internals
    warnonly=true,  # Warn but don't error for any documentation issues
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        size_threshold_warn = nothing,  # Disable size warnings for large low-level API pages
        size_threshold = nothing,  # Disable size errors for large low-level API pages
    ),
    pages=[
        "Home" => "index.md",
        "Installation" => "man/installation.md",
        "Getting Started" => "man/getting_started.md",
        "High-level interface" => Any[
            "Vec" =>  "man/vec.md",
            "Mat" =>  "man/mat.md",
            "DM" =>  "man/dm.md",
            "DMDA" =>  "man/dmda.md",
            "DMStag" =>  "man/dmstag.md",
            "KSP" =>  "man/ksp.md",
            "SNES" =>  "man/snes.md",
        ],
        "Low-level interface (LibPETSc)" => Any[
            "Introduction" =>  "man/lowlevel_intro.md",
            "Vec" =>  "man/vec_lowlevel.md",
            "Mat" =>  "man/mat_lowlevel.md",
            "DM" => Any[
                "DM" =>  "man/dm_lowlevel.md",
                "DMDA" =>  "man/dmda_lowlevel.md",
                "DMPlex" =>  "man/dmplex_lowlevel.md",
                "DMStag" =>  "man/dmstag_lowlevel.md",
                "DMSwarm" =>  "man/dmswarm_lowlevel.md",
                "DMForest" =>  "man/dmforest_lowlevel.md",
                "DMNetwork" =>  "man/dmnetwork_lowlevel.md",
                "DMShell and others" =>  "man/dmshell_lowlevel.md",
            ],
            "KSP" =>  "man/ksp_lowlevel.md",
            "SNES" =>  "man/snes_lowlevel.md",
            "TS (Time Stepping)" =>  "man/ts_lowlevel.md",
            "Tao (Optimization)" =>  "man/tao_lowlevel.md",
            "IS (Index Sets)" =>  "man/is_lowlevel.md",
            "PetscViewer (I/O)" =>  "man/petscviewer_lowlevel.md",
            "PetscSection (DOF Layout)" =>  "man/petscsection_lowlevel.md",
            "PetscSF (Communication)" =>  "man/petscsf_lowlevel.md",
            "AO (Application Ordering)" =>  "man/ao_lowlevel.md",
        ],
        "Utilities" => "man/utilities.md",
        "Running on HPC Systems" => "man/hpc.md",
        "FAQ"  => "man/FAQ.md",
        "Contributing"  => "man/contributing.md",
        "Funding" => "man/funding.md",
    ],
)

deploydocs(;
    repo="github.com/JuliaParallel/PETSc.jl.git",
    branch = "gh-pages",
    target = "build",
    devbranch = "main",
    devurl = "dev",
    forcepush=true,
    push_preview = true
)


================================================
FILE: docs/src/index.md
================================================
# PETSc.jl

[PETSc.jl](https://github.com/JuliaParallel/PETSc.jl) is a Julia wrapper for the Portable, Extensible Toolkit for Scientific Computation [PETSc](https://petsc.org/) package, which allows solving ordinary and partial differential equations in parallel on laptops or massively parallel high-performance systems.

The use of Julia greatly simplifies the code that developers have to write, while allowing them to employ Julia features such as automatic differentiation. The Julia wrapper also comes with a pre-built library, which greatly simplifies the process of getting your first code working in parallel on different operating systems. In many cases, the Julia code is significantly shorter than its C counterpart.

This wrapper mimics the PETSc functionality as closely as possible, but remains work in progress. We have (semi-automatically) translated most of the functionality of PETSc, along with help docstrings. Yet, the higher-level interface is only available for part of the library. Likewise, the tests currently only cover part of the library, so we can only guarantee that this part works.

See the official [user guide](https://petsc.org/release/overview/) if you want to learn more about PETSc in general. For Julia-specific examples, have a look at our [examples](https://github.com/JuliaParallel/PETSc.jl/tree/main/examples) or [tests](https://github.com/JuliaParallel/PETSc.jl/tree/main/test).

## The High-Level Interface

The high-level interface is designed to be familiar and convenient for Julia users, but exposes only a small portion of the functionality of the underlying PETSc library.

For example, with this interface, PETSc's [KSP](https://petsc.org/release/docs/manual/ksp) linear solvers (including Krylov methods) can be used in a way similar to solvers from other Julia packages. See the example in [Getting started](@ref) and the API in [KSP](@ref).

For nonlinear problems, [NonlinearSolve.jl](https://github.com/SciML/NonlinearSolve.jl) provides a high-level interface to PETSc's [SNES](https://petsc.org/release/docs/manual/snes) nonlinear solvers. This integration offers automatic sparsity detection, efficient sparse Jacobian computation, and a unified API that works across multiple solver backends.

## The Low-Level Interface

The low-level interface covers most of the PETSc API, but may be awkward to work with and likely requires previous experience with PETSc to use effectively. It is (mostly) automatically generated by borrowing the Python code that creates Fortran wrappers for PETSc.

The high-level interface described in [KSP](@ref) creates a [KSP](https://petsc.org/release/docs/manual/ksp) linear solver object via the low-level interface to [`KSPSolve()`](https://petsc.org/release/docs/manualpages/KSP/KSPCreate.html), with the use of constructs which require knowledge of PETSc's nature as a [C library](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/). Expert users are of course free to directly use the low-level interface, as in this simple example which directly calls [`PetscGetVersionNumber()`](https://petsc.org/release/docs/manualpages/PetscGetVersionNumber.html).

```julia
using MPI
MPI.Initialized() || MPI.Init()
using PETSc

# Select the appropriate library
petsclib = PETSc.petsclibs[1]

# Initialize PETSc (with logging turned off; the default)
PETSc.initialize(petsclib, log_view=false)

# Call the low-level interface (which always starts with petsclib)
version = LibPETSc.PetscGetVersionNumber(petsclib)

# Print result
println("PETSc $(version[1]).$(version[2]).$(version[3])")

# Finalize
PETSc.finalize(petsclib)
```


================================================
FILE: docs/src/man/FAQ.md
================================================
# Frequently Asked Questions


## 1. Can I use my own PETSc library?
Yes. You do need to compile PETSc as a dynamic (shared) library, and `MPI.jl` must be configured to be compatible with the MPI used to compile PETSc. If you run this on an HPC system and don't know the configuration options, compile one of the PETSc examples and run it with `-log_view`; the output lists all configuration options used on that machine.

Please note that the version of PETSc should be compatible with the version used for the wrappers.

The recommended approach is `set_library!`, which stores the path persistently in `LocalPreferences.toml` — no environment variables needed:

```julia
using PETSc
PETSc.set_library!("/path/to/libpetsc.so"; PetscScalar=Float64, PetscInt=Int64)
# Restart Julia — the custom library is used automatically from here on.
```

To revert to the bundled `PETSc_jll` binaries:
```julia
PETSc.unset_library!()
```

For a one-off session without changing the persistent preference, use `set_petsclib` directly:
```julia
petsclib = PETSc.set_petsclib("/path/to/libpetsc.so"; PetscScalar=Float64, PetscInt=Int64)
```

## 2. Help, my code crashes?
That is very possible. If you provide a *short* minimum working example (MWE), feel free to open an issue on the github repo, so we can check it out.

## 3. What about the garbage collector in Julia?
Using the GC in combination with MPI code is a tricky business. The users are therefore responsible to free PETSc objects in the code, as shown in the various examples. We have some help for that:
1. The julia function `PETSc.audit_petsc_file("path/to/your/file.jl")` which scans your julia file and tries to guess whether objects are destroyed.
2. You can initialize the PETSc library with `log_view=true`. At the end of the code, it will give an  

## 4. Is it compatible with GPUs?
The precompiled `PETSc_jll` binaries are currently not compatible with GPUs. You can, however, use your own PETSc compilation and things should be fine. 

## 5. I really like this package!
Thanks a lot, we appreciate if you give us a star on github!

## 6. How do I cite this in publications?
We are planning to submit a JOSS paper once the current release is sufficiently stable. Meanwhile, you can cite the [zenodo release](https://doi.org/10.5281/zenodo.18274809).

================================================
FILE: docs/src/man/ao_lowlevel.md
================================================
# AO (Application Ordering) - Low-level Interface

The AO (Application Ordering) component provides mappings between the natural "application" ordering of unknowns and the PETSc parallel ordering, which is optimized for parallel sparse matrix operations.

## Overview

Application orderings are used when:
- **Natural ordering differs from parallel ordering**: Your application uses a different numbering scheme
- **I/O operations**: Reading/writing data in application order
- **User interaction**: Displaying results in familiar ordering
- **Legacy code integration**: Interfacing with existing applications

The AO object maintains bidirectional mappings:
- **Application → PETSc**: Convert from your numbering to PETSc's
- **PETSc → Application**: Convert from PETSc's numbering to yours

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Define the mapping
# application[i] is the application index for PETSc index i
n = 10
application = Int32[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]  # Reverse ordering
petsc = Int32[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]        # Natural PETSc ordering

# Create AO object
ao = LibPETSc.AOCreateBasic(petsclib, LibPETSc.PETSC_COMM_SELF, n, application, petsc)

# Convert application indices to PETSc indices
app_indices = Int32[0, 5, 9]
LibPETSc.AOApplicationToPetsc(petsclib, ao, length(app_indices), app_indices)
# app_indices now contains corresponding PETSc indices

# Convert PETSc indices to application indices  
petsc_indices = Int32[0, 1, 2]
LibPETSc.AOPetscToApplication(petsclib, ao, length(petsc_indices), petsc_indices)
# petsc_indices now contains corresponding application indices

# Cleanup
LibPETSc.AODestroy(petsclib, ao)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Creating AO Objects

### Basic AO

Most common: explicit mapping arrays:

```julia
# Create from application and PETSc index arrays
ao = LibPETSc.AOCreateBasic(petsclib, comm, n, app_indices, petsc_indices)
```

### Identity Mapping

When application and PETSc orderings are the same:

```julia
# Create identity mapping (no conversion needed)
ao = LibPETSc.AOCreateIdentity(petsclib, comm, n)
```

### Memory Scalable

For large problems where storing full mapping is expensive:

```julia
# Create memory-scalable AO (uses hash table)
ao = LibPETSc.AOCreateMemoryScalable(petsclib, comm, n, app_indices, petsc_indices)
```

## Conversion Operations

### Forward Conversion (Application → PETSc)

```julia
# Convert array of application indices to PETSc indices
indices = Int32[10, 20, 30, 40]
# Create forward conversion example
LibPETSc.AOApplicationToPetsc(petsclib, ao, length(indices), indices)
# indices are now in PETSc ordering (modified in-place)
```

### Reverse Conversion (PETSc → Application)

```julia
# Convert array of PETSc indices to application indices
indices = Int32[0, 5, 10, 15]
# Reverse conversion example
LibPETSc.AOPetscToApplication(petsclib, ao, length(indices), indices)
# indices are now in application ordering (modified in-place)
```

### Index Set Conversion

```julia
# Convert IS (index set) from application to PETSc ordering
is_app = Ref{LibPETSc.IS}()
# ... create IS with application indices ...

LibPETSc.AOApplicationToPetscIS(petsclib, ao, is_app[])
# is_app now contains PETSc indices
```

## Parallel Considerations

- Each processor has its own local application ordering
- AO objects handle parallel communication automatically
- Indices not owned locally are mapped via MPI communication

```julia
# Create parallel AO
ao = LibPETSc.AOCreateBasic(petsclib, MPI.COMM_WORLD, local_n, 
                           local_app_indices, local_petsc_indices)
```

## Common Use Cases

### 1. Reading Data in Application Order

```julia
# Read matrix entries in application ordering
# Then convert to PETSc ordering for assembly

# Application-ordered rows/cols
app_rows = Int32[...]
app_cols = Int32[...]

# Convert to PETSc ordering
LibPETSc.AOApplicationToPetsc(petsclib, ao, length(app_rows), app_rows)
LibPETSc.AOApplicationToPetsc(petsclib, ao, length(app_cols), app_cols)

# Now use app_rows, app_cols (which are now in PETSc ordering) for MatSetValues
```

### 2. Displaying Results

```julia
# After solve, convert solution indices for output
solution_indices = Int32[0, 1, 2, 3, 4]  # PETSc ordering

# Convert to application ordering for display
LibPETSc.AOPetscToApplication(petsclib, ao, length(solution_indices), solution_indices)

# Display in application order
# for i in solution_indices
#     println("Application DOF $i: value = ", solution[i])
# end
```

### 3. Integration with DM

```julia
# Get AO from DM
dm_ao = Ref{LibPETSc.AO}()
# LibPETSc.DMGetAO(petsclib, dm, dm_ao)

# Use for converting between natural and distributed orderings
```

## Viewing and Debugging

```julia
# View the mapping (for debugging)
viewer = LibPETSc.PETSC_VIEWER_STDOUT_SELF(petsclib)
LibPETSc.AOView(petsclib, ao, viewer)
```

## AO Types

Available types (set with `AOSetType`):
- **AOBASIC**: Hash table based, good for general use
- **AOMEMSCALABLE**: Memory efficient for large problems
- **AOMAPPING1TO1**: Optimized for one-to-one mappings

## Performance Tips

- **Reuse AO objects**: Creation can be expensive for large problems
- **Batch conversions**: Convert arrays rather than individual indices
- **Use identity when possible**: Skip AO entirely if orderings match
- **Choose appropriate type**: `AOMEMSCALABLE` for very large problems

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/AO_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/contributing.md
================================================
# Contributing

Contributions are highly welcome, in particular since only part of the PETSc functionality is currently being tested and has high-level interfaces. 

You can thus help in many ways:
1) Add more examples
2) Add new tests
3) Update documentation
4) Report bugs
5) Add a high-level interface for unsupported features
6) Keep the routines up to date with future PETSc versions
7) Keep the precompiled binaries in [PETSc_jll](https://github.com/JuliaBinaryWrappers/PETSc_jll.jl) up to date.


#### Autowrappers
We originally used the function `/wrapping/generatejuliabindings.jl` to wrap the whole PETSc library, which borrows python routines by Barry Smith. The PETSc version for which we generated these original wrappers was 3.23.6.

Note, however, that a range of additional changes were necessary and we thus had manually fix a number of things. It is therefore *not* recommended to rerun these autowrappers for newer versions of PETSc. 
Since there are usually only a limited number of new or updated functions between PETSc releases, it is recommended to run the a wrapper only for these new functions and replace those affected accordingly.   

Make sure that the tests work!

#### Adding new functionality
Please open a pull request to add any of the above contributions.

================================================
FILE: docs/src/man/design.md
================================================
# Design notes



- PETSc can only be built for a single `PetscScalar` type. A workaround is to build multiple PETSc libraries for all supported scalar types (`Float32`, `Float64`, `Complex{Float64}`)
  - Appears that `Complex{Float32}` not supported (https://github.com/JuliaParallel/PETSc.jl/blob/old/deps/build_petscs.jl#L128).
    * TODO: check if still the case
      - should be supported.
  - Need JLL support for this (https://github.com/JuliaPackaging/Yggdrasil/issues/1527)
  - Define macro to `@eval` all functions which use `ccall` for different scalar types.
  - All PETSc types and methods which involve `ccall` need a `PetscScalar` parameter.
  * TODO: GPU support: need separate ones for CUDA as well?


- We lazily initialize each library if an object of that parameter is constructed.
  - Also initialize MPI if not already initialized
    * What MPI thread level is required?
      - none.
  - Add `atexit` hook to finalize PETSc (this should be okay with MPI `atexit`, due to LIFO)
  - Disable the PETSc signal handler


- A Julia object matching each PETSc object (`Vec`, `Mat`, `KSP`, etc.).
  - These will typically have a `ptr` as the first field, which lets us use the `cconvert`/`unsafe_convert` trick to pass pointer by value/reference.
  - Most (all?) objects will have a `comm` field, for the MPI communicator
  - Objects which wrap Julia objects will also need a reference to those objects to prevent GC.


- For convenience, attach finalizers to call `destroy` for single-process ("sequential") objects (`VecSeq`, `MatSeqXXX`, or any others where `comm = MPI.COMM_SELF`).
  - We can't attach finalizers for distributed objects (i.e. `VecMPI`), as `destroy` needs to be called collectively on all MPI ranks.
  - Safe for users to call `destroy` manually if finalizer already defined
    * TODO: check this with PETSc devs
      - yes.
  - Unclear how to handle objects that are contained within others, e.g. `PC` from `KSPGetPC`, `KSP` from `SNESGetKSP`, etc.
    * There appears to be some sort of reference counting, unclear if this is valid.
      `PetscObjectReference` / `PetscObjectDereference`
      - just need to manually increment reference counter for these.

- For PETSc objects which are equivalent to Julia objects (e.g. `VecSeq` : `Vector{PetscScalar}`, `MatSeqDense` : `Matrix{PetscScalar}`), use `XXXCreateSeqWithArray` methods so that they can share same memory.
    * TODO: check PETSc guarantees on accessing the Julia objects directly.
  - For other objects (`MatSeqAIJ`), for now we let PETSc manage memory (may want to re-evaluate this later)
  - Define conversion routines to wrap with `Seq` objects where possible.
  - Define convenience versions of functions which take/return Julia `Vector`s, e.g. `y = KSP(M) \ x` where `y` and `x` are `Vector`s.

  - Can annotate PETSc objects with Julia ones via `PetscContainerCreate` & `PetscObjectCompose`

- For specifying object options, there are 2 possible approaches:
    (a) use `PetscOptions` objects (key-value pairs) to capture keyword args, which can be pushed and popped to the global options, then use `XXXSetFromOption` methods, e.g. `KSP(mat, ksp_atol=1e-8)`
    (b) use C setter functions (e.g. `KSPSetTolerances`)
  - for now, we go with (a).
    - it's easier
    - not all options are available via C setters, e.g. `mg_coarse_XXX`/`mg_levels_XXX` options
  - ideally we would create a more "object-oriented" interface: e.g. each preconditioner would be a different Julia type, but this doesn't yet seem possible.

- can use `PetscFunctionListGet` to get runtime list of PC etc.


- For cases where PETSc needs to call Julia functions (`MatShell`, `SNES`), PETSc provides a mechanism to pass through a context pointer. We can use this to pass through a pointer to the object itself via `pointer_from_objref`.
  * Can we pass `NULL` to vec/matrix args? What does that do?
  - What should the callback interface look like?
  - How to handle errors from within callbacks?


- TODO: Error handling:
  - https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Sys/PetscPushErrorHandler.html

================================================
FILE: docs/src/man/dm.md
================================================
# DM

The `DM` module provides the base functionality for managing distributed data structures in PETSc. It serves as a foundation for various grid managers.

## Overview

A `DM` object encapsulates the topology and data layout of a computational grid, enabling:
- Parallel data distribution across MPI processes
- Ghost point management for communication
- Creation of vectors and matrices with appropriate parallel layouts
- Multigrid hierarchy management

## DM Types in PETSc

PETSc provides several DM implementations for different mesh types:

### High-Level Interface Available in PETSc.jl

| DM Type | Description | Status |
|---------|-------------|--------|
| **DMDA** | Distributed arrays for structured grids (1D/2D/3D) | ✅ Full support |
| **DMStag** | Staggered grids for finite volume/difference methods | ✅ Full support |

### Low-Level Interface Only (via LibPETSc)

The following DM types are available through the low-level `LibPETSc` wrapper but do not yet have a convenient high-level Julia interface:

| DM Type | Description | Use Case |
|---------|-------------|----------|
| **DMPlex** | Unstructured meshes with arbitrary topology | Finite elements, complex geometries |
| **DMForest** | Adaptive mesh refinement (AMR) via p4est/p8est | Octree-based adaptivity |
| **DMNetwork** | Graph/network structures | Power grids, pipe networks |
| **DMSwarm** | Particle data management | PIC methods, Lagrangian particles |
| **DMProduct** | Tensor product of DMs | Semi-structured problems |
| **DMSliced** | Sliced representation | Legacy, specialized uses |
| **DMShell** | User-defined DM | Custom implementations |
| **DMComposite** | Composition of multiple DMs | Multi-physics coupling |
| **DMRedundant** | Redundant storage on all ranks | Small coupled systems |

### Using Low-Level DM Types

For DM types without high-level wrappers, you can use the `LibPETSc` module directly:

```julia
using PETSc
using PETSc.LibPETSc

# Example: Create a DMPlex (low-level)
petsclib = PETSc.petsclibs[1]
dm = LibPETSc.DMPlexCreate(petsclib, MPI.COMM_WORLD)
# ... configure using LibPETSc functions ...
LibPETSc.DMDestroy(petsclib, dm)
```

!!! note "Contributing"
    Contributions to add high-level interfaces for additional DM types are welcome! 
    See the [Contributing](@ref) page for guidelines.

## Functions

```@autodocs
Modules = [PETSc]
Pages   = ["dm.jl"]
```


================================================
FILE: docs/src/man/dm_lowlevel.md
================================================
# DM (Domain Management)

The DM (Domain Management) object encapsulates the relationship between a mesh data structure and the algebraic objects (vectors, matrices). It provides a common interface for structured and unstructured meshes, particles, networks, and other geometric/topological structures.

## Overview

The DM abstraction in PETSc manages:
- Topology and geometry of computational domains
- Mapping between geometric entities and degrees of freedom
- Distribution of data across MPI processes
- Multi-level representations for multigrid
- Field definitions and discretizations

## Basic Usage Pattern

```julia
using PETSc, MPI

# Initialize
petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

# Create a DM (specific type created via DMDACreate, DMPlexCreate, etc.)
# See subtype-specific pages for creation functions

# Setup the DM
LibPETSc.DMSetFromOptions(petsclib, dm)
LibPETSc.DMSetUp(petsclib, dm)

# Create global vectors
x = LibPETSc.DMCreateGlobalVector(petsclib, dm)

# Create matrices
A = LibPETSc.DMCreateMatrix(petsclib, dm)

# Use the DM...

# Cleanup
LibPETSc.VecDestroy(petsclib, x)
LibPETSc.MatDestroy(petsclib, A)
LibPETSc.DMDestroy(petsclib, dm)
PETSc.finalize(petsclib)
```

## DM Subtypes

PETSc provides several DM implementations for different problem types:

- **[DMDA](dmda_lowlevel.md)**: Structured grids (1D, 2D, 3D) with regular topology
- **[DMPlex](dmplex_lowlevel.md)**: Unstructured meshes using Sieve/DMPlex topology
- **[DMStag](dmstag_lowlevel.md)**: Staggered grids for staggered finite differences
- **[DMSwarm](dmswarm_lowlevel.md)**: Particle methods and particle-in-cell
- **[DMForest](dmforest_lowlevel.md)**: Adaptive mesh refinement with forest-of-octrees
- **[DMNetwork](dmnetwork_lowlevel.md)**: Network/graph-based problems
- **[DMShell and others](dmshell_lowlevel.md)**: User-defined and composite DM types

## General DM Functions

The following functions work across all DM types:

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl", "DMaddons_wrappers.jl"]
Order   = [:function]
Filter = t -> begin
    name = string(nameof(t))
    # Include only general DM functions, exclude subtype-specific ones
    startswith(name, "DM") && 
    !startswith(name, "DMDA") &&
    !startswith(name, "DMPlex") &&
    !startswith(name, "DMStag") &&
    !startswith(name, "DMSwarm") &&
    !startswith(name, "DMForest") &&
    !startswith(name, "DMNetwork") &&
    !startswith(name, "DMShell") &&
    !startswith(name, "DMProduct") &&
    !startswith(name, "DMRedundant") &&
    !startswith(name, "DMComposite")
end
```


================================================
FILE: docs/src/man/dmda.md
================================================
# DMDA

The `DMDA` (Distributed Array) module provides functionality for creating and managing structured grids in 1D, 2D, or 3D.

## Overview

`DMDA` is ideal for problems on regular structured grids where:
- The grid is logically rectangular
- Each grid point has the same number of degrees of freedom
- Stencil operations follow a regular pattern (star or box stencils)

## Creating a DMDA

```julia
# 2D grid example
da = DMDA(
    petsclib,
    MPI.COMM_WORLD,
    (DM_BOUNDARY_NONE, DM_BOUNDARY_NONE),  # boundary types
    (nx, ny),                               # global dimensions
    1,                                      # degrees of freedom per node
    1,                                      # stencil width
    DMDA_STENCIL_STAR                      # stencil type
)
```

## Functions

```@autodocs
Modules = [PETSc]
Pages   = ["dmda.jl"]
```


================================================
FILE: docs/src/man/dmda_lowlevel.md
================================================
# DMDA (Structured Grids)

DMDA manages structured grids with regular topology in 1D, 2D, and 3D. It's designed for finite difference and finite volume methods on Cartesian grids.

## Overview

DMDA provides:
- Regular grid topology in 1D, 2D, or 3D
- Efficient stencil-based communication
- Natural ordering and ghost point management
- Support for multiple degrees of freedom per grid point
- Boundary type specification (periodic, ghosted, none)

## Basic Usage Pattern

```julia
using PETSc, MPI

petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

# Get PETSc types
PetscInt = petsclib.PetscInt

# Create a 2D structured grid: 10x10 global size, 1 dof per point
dm = LibPETSc.DMDACreate2d(
    petsclib,
    MPI.COMM_WORLD,
    LibPETSc.DM_BOUNDARY_NONE,  # x boundary
    LibPETSc.DM_BOUNDARY_NONE,  # y boundary
    LibPETSc.DMDA_STENCIL_STAR, # stencil type
    PetscInt(10), PetscInt(10),  # global dimensions
    PetscInt(LibPETSc.PETSC_DECIDE), # processes in x
    PetscInt(LibPETSc.PETSC_DECIDE), # processes in y
    PetscInt(1),                 # dof per node
    PetscInt(1),                 # stencil width
    C_NULL, C_NULL               # nodes per process
)

# Set up and use
LibPETSc.DMSetFromOptions(petsclib, dm)
LibPETSc.DMSetUp(petsclib, dm)

# Get local vector with ghost points
local_vec = LibPETSc.DMCreateLocalVector(petsclib, dm)

# Get global vector
global_vec = LibPETSc.DMCreateGlobalVector(petsclib, dm)

# Cleanup
LibPETSc.VecDestroy(petsclib, local_vec)
LibPETSc.VecDestroy(petsclib, global_vec)
LibPETSc.DMDestroy(petsclib, dm)
```

## DMDA Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMDA")
```


================================================
FILE: docs/src/man/dmforest_lowlevel.md
================================================
# DMForest (Adaptive Mesh Refinement)

DMForest manages adaptive mesh refinement (AMR) using forest-of-octrees (2D: quadtrees, 3D: octrees). It provides hierarchical mesh adaptation with efficient parallel implementation.

## Overview

DMForest provides:
- Adaptive mesh refinement and coarsening
- Forest-of-octrees topology
- Integration with p4est library
- Multi-level mesh hierarchies for multigrid
- Metric-based adaptation
- Label-based adaptation

## Basic Usage Pattern

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a DMForest
forest = LibPETSc.DMCreate(petsclib, MPI.COMM_WORLD)
LibPETSc.DMSetType(petsclib, forest, "forest")  # String convenience wrapper
# Set^ the geometric/topological dimension (e.g., 2 for a surface)
LibPETSc.DMSetDimension(petsclib, forest, 2)

# Set forest topology (e.g., unit square/cube)
# Set topology by name (e.g., "brick")
LibPETSc.DMForestSetTopology(petsclib, forest, "brick")

# Set base DM (initial coarse mesh) using a simple DMPlex box mesh
base_dm = LibPETSc.DMPlexCreateBoxMesh(
    petsclib, MPI.COMM_WORLD, 2, LibPETSc.PETSC_FALSE,
    [2, 2], [0.0, 0.0], [1.0, 1.0], [LibPETSc.DM_BOUNDARY_NONE, LibPETSc.DM_BOUNDARY_NONE], LibPETSc.PETSC_TRUE, 0, LibPETSc.PETSC_FALSE
)
LibPETSc.DMForestSetBaseDM(petsclib, forest, base_dm)

# Set initial refinement level
LibPETSc.DMForestSetInitialRefinement(petsclib, forest, 2)

# Ensure adjacency dimension and partition overlap are non-negative (some builds may leave them unset)
LibPETSc.DMForestSetAdjacencyDimension(petsclib, forest, 0)
LibPETSc.DMForestSetPartitionOverlap(petsclib, forest, 0)

# Set up
# (Skip DMSetFromOptions in this simple example to avoid parsing unexpected runtime options)
LibPETSc.DMSetUp(petsclib, forest)

# Adapt based on some criterion
# Create adapted forest (returns via out-parameter)
tdm = LibPETSc.PetscDM(C_NULL, petsclib)
LibPETSc.DMForestTemplate(petsclib, forest, MPI.COMM_WORLD, tdm)
adapted = tdm
# `adapted` now points to the adapted DM (if any) and should be checked before use.

# Cleanup
LibPETSc.DMDestroy(petsclib, adapted)
LibPETSc.DMDestroy(petsclib, forest)
LibPETSc.DMDestroy(petsclib, base_dm)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## DMForest Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMForest")
```


================================================
FILE: docs/src/man/dmnetwork_lowlevel.md
================================================
# DMNetwork (Network/Graph Problems)

DMNetwork manages network and graph-based problems, such as power grids, transportation networks, and general graph computations with edge and vertex data.

## Overview

DMNetwork provides:
- Network topology with edges and vertices
- Component registration for edges and vertices
- Coupling of network problems with other DM types
- Distribution across MPI processes
- Support for algebraic constraints at vertices

## Basic Usage Pattern

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a DMNetwork (returns a PetscDM)
network = LibPETSc.DMNetworkCreate(petsclib, MPI.COMM_WORLD)

# Set network sizes
# For simple examples, provide a name and an explicit edge list vector
nedges = 2
nvertices = 3
LibPETSc.DMNetworkSetNumSubNetworks(petsclib, network, 1, 1)
edgelist = [1, 2, 2, 3]  # pairs of vertex global indices
LibPETSc.DMNetworkAddSubnetwork(petsclib, network, "main", nedges, edgelist)

# Register components (size in bytes; use appropriate struct size in real examples)
# Register a component; the function returns a component key (PetscInt)
compkey = LibPETSc.DMNetworkRegisterComponent(petsclib, network, "bus", Csize_t(0))

# Add components to vertices/edges using DMNetworkAddComponent (omitted here)

# Finalize network and set up
LibPETSc.DMNetworkLayoutSetUp(petsclib, network)
LibPETSc.DMSetUp(petsclib, network)

# Create vectors
x = LibPETSc.DMCreateGlobalVector(petsclib, network)

# Cleanup
LibPETSc.VecDestroy(petsclib, x)
LibPETSc.DMDestroy(petsclib, network)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## DMNetwork Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMNetwork")
```


================================================
FILE: docs/src/man/dmplex_lowlevel.md
================================================
# DMPlex (Unstructured Meshes)

DMPlex manages unstructured meshes using a flexible topology representation. It supports finite element methods, finite volume methods, and general mesh-based computations.

## Overview

DMPlex provides:
- General unstructured mesh topology (simplices, tensor products, polyhedra)
- Point-based topological representation (vertices, edges, faces, cells)
- Mesh partitioning and distribution
- Overlap and ghost cell management
- Section-based field layout
- Support for FEM assembly via PetscDS and PetscFE
- Mesh refinement and adaptation

## Basic Usage Pattern

```julia
using PETSc, MPI

MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a simple 2D quadrilateral mesh
dm = LibPETSc.DMPlexCreateBoxMesh(
    petsclib,
    MPI.COMM_WORLD,
    2,                           # dimension
    LibPETSc.PETSC_FALSE,        # simplex (false = tensor/quad)
    [5, 5],                      # faces per dimension
    [0.0, 0.0],                  # lower bounds
    [1.0, 1.0],                  # upper bounds
    [LibPETSc.DM_BOUNDARY_NONE, LibPETSc.DM_BOUNDARY_NONE],  # boundary types
    LibPETSc.PETSC_TRUE,         # interpolate
    0,                           # localizationHeight
    LibPETSc.PETSC_FALSE         # sparseLocalize
)

# Distribute mesh across processes (for serial, this is a no-op)
dmParallel = LibPETSc.PetscDM(C_NULL, petsclib)
LibPETSc.DMPlexDistribute(petsclib, dm, 0, C_NULL, dmParallel)
if dmParallel.ptr != C_NULL
    LibPETSc.DMDestroy(petsclib, dm)
    dm = dmParallel
end

# Set up
LibPETSc.DMSetFromOptions(petsclib, dm)
LibPETSc.DMSetUp(petsclib, dm)

# Create section to define field layout
section = Ref{LibPETSc.PetscSection}()
LibPETSc.DMGetLocalSection(petsclib, dm, section)

# Create vectors and matrices
x = LibPETSc.DMCreateGlobalVector(petsclib, dm)

# Cleanup
LibPETSc.VecDestroy(petsclib, x)
LibPETSc.DMDestroy(petsclib, dm)
PETSc.finalize(petsclib)
MPI.Finalize()
```

## DMPlex Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMPlex")
```


================================================
FILE: docs/src/man/dmshell_lowlevel.md
================================================
# DMShell and Other DM Types

This page documents DMShell (user-defined DM), DMProduct (Cartesian product of DMs), DMRedundant (redundantly-stored DM), and DMComposite (deprecated composite DM).

## DMShell

DMShell allows users to define custom DM implementations by providing callback functions for operations like creating vectors, matrices, and managing topology.

### Basic DMShell Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a DMShell (returns a PetscDM) and set callbacks
shell = LibPETSc.DMShellCreate(petsclib, MPI.COMM_WORLD)
# Set callbacks on the returned DM, for example:
# LibPETSc.DMShellSetCreateGlobalVector(petsclib, shell, ...)
# LibPETSc.DMShellSetCreateLocalVector(petsclib, shell, ...)
# LibPETSc.DMShellSetCreateMatrix(petsclib, shell, ...)

# Set up
LibPETSc.DMSetUp(petsclib, shell)

# Cleanup
LibPETSc.DMDestroy(petsclib, shell)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## DMProduct

DMProduct represents a Cartesian product of multiple DMs, useful for coupled multi-physics problems.

### Basic DMProduct Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create component DMs
dm1 = Ref{LibPETSc.CDM}()
dm2 = Ref{LibPETSc.CDM}()
# ... create dm1 and dm2 ...

# Create DMProduct
# Create two simple component DMs (here using small box DMPlex meshes)
dm1 = LibPETSc.DMPlexCreateBoxMesh(
    petsclib, MPI.COMM_WORLD, 2, LibPETSc.PETSC_FALSE,
    [1,1], [0.0,0.0], [1.0,1.0], [LibPETSc.DM_BOUNDARY_NONE, LibPETSc.DM_BOUNDARY_NONE], LibPETSc.PETSC_TRUE, 0, LibPETSc.PETSC_FALSE
)
dm2 = LibPETSc.DMPlexCreateBoxMesh(
    petsclib, MPI.COMM_WORLD, 2, LibPETSc.PETSC_FALSE,
    [1,1], [0.0,0.0], [1.0,1.0], [LibPETSc.DM_BOUNDARY_NONE, LibPETSc.DM_BOUNDARY_NONE], LibPETSc.PETSC_TRUE, 0, LibPETSc.PETSC_FALSE
)

# Create a DMProduct and attach component DMs
product = LibPETSc.DMCreate(petsclib, MPI.COMM_WORLD)
LibPETSc.DMSetType(petsclib, product, "product")
# Set the product 'topological' dimension to the number of component DMs
LibPETSc.DMSetDimension(petsclib, product, 2)
# Map product slots to sub-DM dimensions (index mapping); then attach sub-DMs
LibPETSc.DMProductSetDimensionIndex(petsclib, product, 0, 0)
LibPETSc.DMProductSetDimensionIndex(petsclib, product, 1, 0)
LibPETSc.DMProductSetDM(petsclib, product, 0, dm1)
LibPETSc.DMProductSetDM(petsclib, product, 1, dm2)

# Set up
LibPETSc.DMSetUp(petsclib, product)

# Cleanup
LibPETSc.DMDestroy(petsclib, product)
LibPETSc.DMDestroy(petsclib, dm1)
LibPETSc.DMDestroy(petsclib, dm2)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## DMRedundant

DMRedundant stores data redundantly across all processes, useful for small shared data.

## DMComposite

DMComposite (deprecated, use DMProduct instead) manages multiple DMs as separate fields.

## Functions

### DMShell Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMShell")
```

### DMProduct Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMProduct")
```

### DMRedundant Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMRedundant")
```

### DMComposite Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMComposite")
```


================================================
FILE: docs/src/man/dmstag.md
================================================
# DMStag

The DMStag (Staggered Grid DM) module provides data management for staggered grids, commonly used in finite difference/volume methods for fluid dynamics and similar applications.

## Overview

DMStag is designed for problems where:
- Variables live at different grid locations (vertices, edges, faces, cell centers)
- Staggered grids provide better stability for incompressible flow
- Multiple degrees of freedom per grid location are needed

### Staggered Grid Layout

In a staggered grid, different physical quantities are stored at different locations:

**1D**: 
- Vertices: Scalar quantities (pressure, temperature)
- Elements: Flux quantities

**2D**:
- Vertices: Corner values
- Edges: Face-normal velocities (u on vertical edges, v on horizontal edges)  
- Elements: Cell-centered values (pressure)

**3D**:
- Vertices: Corner values
- Edges: Edge-centered values
- Faces: Face-normal quantities
- Elements: Cell-centered values

## Creating a DMStag

```julia
# 2D staggered grid
dm = DMStag(
    petsclib,
    MPI.COMM_WORLD,
    (DM_BOUNDARY_NONE, DM_BOUNDARY_NONE),  # boundary types
    (nx, ny),                               # global dimensions
    (dof_vertex, dof_edge, dof_element),   # DOF at each location
    1,                                      # stencil width
    DMSTAG_STENCIL_BOX                     # stencil type
)

# 3D staggered grid
dm = DMStag(
    petsclib,
    MPI.COMM_WORLD,
    (DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE),
    (nx, ny, nz),
    (dof_vertex, dof_edge, dof_face, dof_element),
    1,
    DMSTAG_STENCIL_BOX
)
```

## Accessing Data

### Grid Corners and Sizes

```julia
# Get local grid extent (without ghost points)
corners = getcorners_dmstag(dm)
# Returns (lower=CartesianIndex, upper=CartesianIndex, size=Tuple)

# Get local grid extent (with ghost points)  
ghost_corners = getghostcorners_dmstag(dm)
```

### Working with Vectors

```julia
# Create global and local vectors
global_vec = DMGlobalVec(dm)
local_vec = DMLocalVec(dm)

# Transfer data between global and local
dm_global_to_local!(dm, global_vec, INSERT_VALUES, local_vec)
dm_local_to_global!(dm, local_vec, ADD_VALUES, global_vec)
```

### Getting Location Indices

```julia
# Get indices for accessing specific DOF locations
indices = DMStagGetIndices(dm)
# Use indices to access vertex, edge, face, or element DOFs
```

## Setting Coordinates

```julia
# Set uniform coordinates
setuniformcoordinates_stag!(dm, xmin, xmax)           # 1D
setuniformcoordinates_stag!(dm, xmin, xmax, ymin, ymax)  # 2D
setuniformcoordinates_stag!(dm, xmin, xmax, ymin, ymax, zmin, zmax)  # 3D

# Get local coordinate array
coords = getlocalcoordinatearray(dm)
```

## Stencil Types

- `DMSTAG_STENCIL_BOX` - Full box stencil (includes diagonals)
- `DMSTAG_STENCIL_STAR` - Star stencil (axis-aligned neighbors only)

## Example: 2D Stokes Flow Setup

```julia
# Create staggered grid for Stokes: velocity on edges, pressure in cells
dm = DMStag(
    petsclib,
    MPI.COMM_WORLD,
    (DM_BOUNDARY_NONE, DM_BOUNDARY_NONE),
    (64, 64),      # 64x64 grid
    (0, 1, 1),     # 0 DOF at vertices, 1 at edges (velocity), 1 in elements (pressure)
    1,
    DMSTAG_STENCIL_BOX
)

setuniformcoordinates_stag!(dm, 0.0, 1.0, 0.0, 1.0)

# Create vectors and matrix
x = DMGlobalVec(dm)
b = DMGlobalVec(dm)
A = DMCreateMatrix(dm)
```

## Functions

```@autodocs
Modules = [PETSc]
Pages   = ["dmstag.jl"]
```


================================================
FILE: docs/src/man/dmstag_lowlevel.md
================================================
# DMStag (Staggered Grids)

DMStag manages staggered grid discretizations, commonly used in computational fluid dynamics and geophysics. It handles variables defined at different locations (vertices, edges, faces, element centers).

## Overview

DMStag provides:
- Staggered grid support for MAC/marker-and-cell schemes
- Multiple degrees of freedom at vertices, edges, faces, and element centers
- Efficient communication for staggered variables
- 1D, 2D, and 3D support
- Boundary type specification

## Basic Usage Pattern

```julia
using PETSc, MPI

petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

# Get PETSc types
PetscInt = petsclib.PetscInt

# Create a 2D staggered grid
# Velocity components on edges, pressure at cell centers
dm = LibPETSc.DMStagCreate2d(
    petsclib,
    MPI.COMM_WORLD,
    LibPETSc.DM_BOUNDARY_NONE,
    LibPETSc.DM_BOUNDARY_NONE,
    PetscInt(10), PetscInt(10),  # global dimensions
    PetscInt(LibPETSc.PETSC_DECIDE),
    PetscInt(LibPETSc.PETSC_DECIDE),
    PetscInt(0),                 # dof per vertex
    PetscInt(1),                 # dof per edge (velocity)
    PetscInt(1),                 # dof per element (pressure)
    LibPETSc.DMSTAG_STENCIL_BOX,
    PetscInt(1),                 # stencil width
    C_NULL, C_NULL
)

# Set up
LibPETSc.DMSetFromOptions(petsclib, dm)
LibPETSc.DMSetUp(petsclib, dm)

# Create vectors
x = LibPETSc.DMCreateGlobalVector(petsclib, dm)

# Access staggered components
# Use DMStagGetLocationSlot to get indices for each component

# Cleanup
LibPETSc.VecDestroy(petsclib, x)
LibPETSc.DMDestroy(petsclib, dm)
PETSc.finalize(petsclib)
```

## DMStag Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMStag")
```


================================================
FILE: docs/src/man/dmswarm_lowlevel.md
================================================
# DMSwarm (Particle Methods)

DMSwarm manages particle-based methods including particle-in-cell (PIC), smoothed particle hydrodynamics (SPH), and general particle simulations.

## Overview

DMSwarm provides:
- Dynamic particle creation and migration
- Field registration for particle data
- Particle-mesh coupling
- Particle migration across MPI processes
- Support for PIC and general particle methods
- Cell-based particle management

## Basic Usage Pattern

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a DMSwarm in PIC mode
swarm = LibPETSc.DMCreate(petsclib, MPI.COMM_WORLD)
LibPETSc.DMSetType(petsclib, swarm, "swarm")  # String convenience wrapper
# Set the geometric/topological dimension for the swarm (required)
LibPETSc.DMSetDimension(petsclib, swarm, 1)

# Set swarm type to PIC
LibPETSc.DMSwarmSetType(petsclib, swarm, LibPETSc.DMSWARM_PIC)

# Create background mesh (here we use a 1D DMDA for simplicity)
da = LibPETSc.DMDACreate1d(petsclib, MPI.COMM_WORLD, LibPETSc.DM_BOUNDARY_NONE, 10, 1, 1, C_NULL)

# Set the cell DM for PIC
LibPETSc.DMSwarmSetCellDM(petsclib, swarm, da)

# Register a particle field `velocity` with blocksize 3 (x,y,z components)
LibPETSc.DMSwarmRegisterPetscDatatypeField(
    petsclib, swarm,
    "velocity", 3, LibPETSc.PETSC_DOUBLE
)

# Finalize field registration
LibPETSc.DMSwarmFinalizeFieldRegister(petsclib, swarm)

# Set number of particles
nparticles = 100
LibPETSc.DMSwarmSetLocalSizes(petsclib, swarm, nparticles, 0)

# Access and set particle data using DMSwarmGetField / DMSwarmRestoreField
# `DMSwarmGetField` returns the blocksize and fills a pointer to the underlying
# data array. In Julia, pass a `Vector{Ptr{Cvoid}}(undef,1)` and a `Ref{PetscDataType}`
# to receive the out parameters and then wrap the returned pointer with `unsafe_wrap`.
ptr_store = Vector{Ptr{Cvoid}}(undef, 1)
type_store = Ref{LibPETSc.PetscDataType}()
blocksize = LibPETSc.DMSwarmGetField(petsclib, swarm, "velocity", type_store, pointer(ptr_store))
# `ptr_store[1]` is a pointer to `PetscReal` (Float64 by default) array of length blocksize * nparticles
@assert type_store[] == LibPETSc.PETSC_DOUBLE
data = unsafe_wrap(Array, Ptr{Float64}(ptr_store[1]), (blocksize * nparticles,))
# Initialize velocity values
for i in 1:length(data)
    data[i] = 0.1 * i
end
# Restore the field when done (unlocks internal storage)
LibPETSc.DMSwarmRestoreField(petsclib, swarm, "velocity", type_store, pointer(ptr_store))

# Cleanup
LibPETSc.DMDestroy(petsclib, swarm)
LibPETSc.DMDestroy(petsclib, da)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## DMSwarm Functions

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["DM_wrappers.jl"]
Order   = [:function]
Filter = t -> startswith(string(nameof(t)), "DMSwarm")
```


================================================
FILE: docs/src/man/funding.md
================================================
# Funding

The most recent release of PETSc.jl was developed by Boris Kaus and Jeremy Kozdon, and Boris was funded by:

- The GPU4GEO and dGPU4GEO projects of the Platform of Advanced Scientific Computing (PASC) in Switzerland, with Paul Tackley and Ludovic Räss as PIs and Kaus as co-PI.
- European Research Council Consolidator Grant MAGMA (Melting and Geodynamic models of Ascent) #771149 awarded to Kaus.
- A Gutenberg Research Council Fellowship by the Johannes Gutenberg University Mainz.
- DFG project MOSAIC - A modular, parallel, 3D seismo-thermo-mechanical approach to simulate the feedback between geodynamic & seismic processes.
- The project DEGREE by the German Ministry of Science, Technology and Space (BMFTR).

For more information on these projects, please visit the respective websites or contact the investigators.


================================================
FILE: docs/src/man/getting_started.md
================================================
# Getting started

- [1. Solving a linear system of equations](#1-solving-a-linear-system-of-equations)
- [2. Nonlinear example](#2-nonlinear-example)
- [3. Next steps](#3-next-steps)

### 1. Solving a linear system of equations

Lets consider the following elliptic equation:
```math
\begin{aligned}
{\partial^2 T \over \partial x^2}  = 0, T(0) = 1, T(1) = 11
\end{aligned}
```

```julia
julia> using PETSc
julia> petsclib = PETSc.petsclibs[1]
julia> PETSc.initialize(petsclib, log_view=false)
```
Note that if you initialize the PETSc library with the option `log_view=true` you will get a detailed overview of your simulation once you close the library with `PETSc.finalize(petsclib)` (note that it's set to `false` by default). 
Next, lets define the number of gridpoints and the spacing between them with:
```julia
julia> n   =  11
julia> Δx  =  1. / (n - 1)
```

Let's first define the matrix with coefficients:
```julia
julia> nnz =  ones(Int64,n); nnz[2:n-1] .= 3;
julia> A   =  PETSc.MatSeqAIJ(petsclib,n,n,nnz);
julia> for i=2:n-1
            A[i,i-1] =  1/Δx^2
            A[i,i  ] = -2/Δx^2
            A[i,i+1] =  1/Δx^2
       end;
julia> A[1,1]=1; A[n,n]=1;  # boundary conditions (Dirichlet)
julia> PETSc.assemble!(A)   # Assemble the matrix
```
This creates a sequential matrix (on one processor):
```julia
julia> A
PETSc seqaij Mat of size (11, 11)
```
Now, lets define the right-hand-size vector `rhs` as a julia vector:
```julia
julia> rhs = zeros(n); rhs[1]=1; rhs[11]=11;
```

Next, we define the linear solver for the matrix `A`, which is done by setting a `KSP` solver: 
```julia
julia> ksp = PETSc.KSP(A; ksp_rtol=1e-8, pc_type="jacobi", ksp_monitor=true)
```
Note that you can specify all PETSc command-line options as keywords here.

Solving the system of equations is simple:
```julia
julia> sol = ksp\rhs
  0 KSP Residual norm 1.104536101719e+01 
  1 KSP Residual norm 4.939635614091e+00 
  2 KSP Residual norm 2.410295378065e+00 
  3 KSP Residual norm 1.462993806273e+00 
  4 KSP Residual norm 1.004123728835e+00 
  5 KSP Residual norm 7.700861485629e-01 
  6 KSP Residual norm 6.165623662013e-01 
  7 KSP Residual norm 4.972507567923e-01 
  8 KSP Residual norm 4.074986825669e-01 
  9 KSP Residual norm 3.398492183940e-01 
 10 KSP Residual norm 3.283015493450e-15 
11-element Vector{Float64}:
  1.0
  2.000000000000001
  3.0000000000000013
  4.000000000000001
  5.000000000000002
  6.0
  7.0000000000000036
  8.000000000000002
  9.000000000000004
 10.000000000000002
 11.0
```
This solves the system of equations with the default (iterative) solver of PETSc. You can find out what is being used by adding the `ksp_view=true` option to the `ksp` object:

```julia
julia> ksp = PETSc.KSP(A; ksp_rtol=1e-8, pc_type="jacobi", ksp_monitor=true, ksp_view=true)
```
which will tell you:
```julia
KSP Object: 1 MPI process
  type: gmres
    restart=30, using Classical (unmodified) Gram-Schmidt Orthogonalization with no iterative refinement
    happy breakdown tolerance 1e-30
  maximum iterations=10000, initial guess is zero
  tolerances: relative=1e-08, absolute=1e-50, divergence=10000.
  left preconditioning
  using PRECONDITIONED norm type for convergence test
PC Object: 1 MPI process
  type: jacobi
    type DIAGONAL
  linear system matrix = precond matrix:
  Mat Object: 1 MPI process
    type: seqaij
    rows=11, cols=11
    total: nonzeros=29, allocated nonzeros=29
    total number of mallocs used during MatSetValues calls=0
      not using I-node routines
```      
So we are using a `gmres` solver with a `jacobi` preconditioner.
The power of PETSc is that you can change the solver on the fly.
We can solve the same system with a direct solver, using:
```julia
julia> ksp = PETSc.KSP(A; ksp_rtol=1e-8, pc_type="lu", ksp_monitor=true, ksp_view=true, ksp_type="preonly");
julia> sol = ksp\rhs;
  0 KSP Residual norm 1.104536101719e+01
  1 KSP Residual norm 2.846442092393e-13
KSP Object: 1 MPI process
  type: preonly
  maximum iterations=10000, initial guess is zero
  tolerances: relative=1e-05, absolute=1e-50, divergence=10000.
  left preconditioning
  using NONE norm type for convergence test
PC Object: 1 MPI process
  type: lu
    out-of-place factorization
    tolerance for zero pivot 2.22045e-14
    matrix ordering: nd
    factor fill ratio given 5., needed 1.31034
      Factored matrix follows:
        Mat Object: 1 MPI process
          type: seqaij
          rows=11, cols=11
          package used to perform factorization: petsc
          total: nonzeros=38, allocated nonzeros=38
            not using I-node routines
  linear system matrix = precond matrix:
  Mat Object: 1 MPI process
    type: seqaij
    rows=11, cols=11
    total: nonzeros=29, allocated nonzeros=29
    total number of mallocs used during MatSetValues calls=0
      not using I-node routines
```
which converges, as expected, in 1 iteration.

And since we are using julia, plotting the solution can be done with
```julia
julia> using Plots
julia> plot(0:Δx:1,sol, ylabel="solution",xlabel="x")
```

![linear_solution](../assets/img/linear_KSP_solution.png)


Note that in general, one knows more about the equations than just the matrix entries. In this case, for example, we use a finite difference discretization to solve the equation. We could also have used a finite element code to do the same. 
This is important information that helps PETSc to distribute the problem over several processors, or to setup multigrid preconditioners etc.
PETSc uses the `DM` infrastructure for such cases. `DMDA` is for (collocated) finite differences, `DMStag` for staggered finite differences, `DMPlex` for finite element/finite volume discretisations and `DMForest` for adaptive mesh refinement problems.
If possible, use this infrastructure as it simplifies your life. Have a look at the [examples](https://github.com/JuliaParallel/PETSc.jl/tree/main/examples) or [tests](https://github.com/JuliaParallel/PETSc.jl/tree/main/test) of `PETSc.jl`.

### 2. Nonlinear example
Let's solve the coupled system of nonlinear equations: 
```math
\begin{aligned}
x^2 + x y  &= 3 \\
x y + y^2  &= 6
\end{aligned}
```
for ``x`` and ``y``, which can be written in terms of a residual vector ``r``:
```math
r = \binom{ x^2 + x y  - 3} {x y + y^2  - 6}
```

We start by initializing PETSc:
```julia
julia> using PETSc, MPI
julia> petsclib = PETSc.petsclibs[1]
julia> PETSc.initialize(petsclib, log_view=false)
julia> PetscScalar = petsclib.PetscScalar
julia> PetscInt = petsclib.PetscInt
```
In order to solve this, we need to provide a residual function that computes ``r``:
```julia
julia> function Residual!(rx, snes, x)
            rx[1] = x[1]^2 + x[1] * x[2] - 3
            rx[2] = x[1] * x[2] + x[2]^2 - 6
            return PetscInt(0)  # petsc success
        end
```

In addition, we need to provide the Jacobian:
```math
J = 
\begin{pmatrix}
\frac{\partial r_1}{ \partial x} & \frac{\partial r_1}{ \partial y}  \\
\frac{\partial r_2}{ \partial x} & \frac{\partial r_2}{ \partial y}  \\
\end{pmatrix}
= 
\begin{pmatrix}
2x + y & x  \\
y & x + 2y  \\
\end{pmatrix}
```
In Julia, this is:
```julia
julia> function updateJ!(J, snes, x)
            J[1, 1] = 2x[1] + x[2]
            J[1, 2] = x[1]
            J[2, 1] = x[2]
            J[2, 2] = x[1] + 2x[2]

            PETSc.assemble!(J)
            return PetscInt(0)
        end
```
In order to solve this using the PETSc nonlinear equation solvers, you first define the `SNES` solver together with the jacobian and residual functions as 
```julia
julia> snes = PETSc.SNES(petsclib,MPI.COMM_SELF; ksp_rtol=1e-4, pc_type="none")
julia> r = PETSc.VecSeq(petsclib, zeros(PetscScalar, 2))
julia> PETSc.setfunction!(snes, Residual!, r)
julia> J = zeros(2,2)
julia> PJ = PETSc.MatSeqDense(petsclib,J)
julia> PETSc.setjacobian!(updateJ!, snes, PJ)
```

You can solve this as:
```julia
julia> x = PETSc.VecSeq(petsclib, [2.0, 3.0])
julia> b = PETSc.VecSeq(petsclib, [0.0, 0.0])
julia> PETSc.solve!(x, snes, b)
julia> x[:]
2-element Vector{Float64}:
 1.000000003259629
 1.999999998137355
```
which indeed recovers the analytical solution ``(x=1, y=2)``.

If we are done, finalize it with:
```julia
julia> PETSc.finalize(petsclib)
```


### 3. Next steps 
Now that you have the basics, you can start playing with some more complete examples.
Here some suggestions:
1. [laplacian.jl](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/laplacian.jl) - simple (non-parallel) laplacian example using Julia sparse matrixes
2. [dmda_laplacian.jl](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/dmda_laplacian.jl) - 2D laplacian example with Dirichlet boundary conditions using the `DMDA` framework. Examples are given on how to run it with various (multigrid) solvers.
3. [ex50.jl](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/ex50.jl) - 2D parallel `DMDA` laplacian example with Neumann boundary conditions, which requires the nullspace to be removed.  
4. [SNES_ex2.jl](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/SNES_ex2.jl) - 1D laplacian with nonlinear terms where the hand-derived jacobian is hardcoded
5. [SNES_ex2b.jl](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/SNES_ex2b.jl) - as `SNES_ex2.jl` but using automatic differentiation to derive the jacobian.
6. [Liouville_Bratu_Gelfand.jl](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/Liouville\_Bratu\_Gelfand.jl) - 1D/2D/3D poisson equation with nonlinear terms. Shows how to combine `DMDA` with `SNES` solvers and solve them in parallel, if you have a jacobian.
7. [porosity_waves.jl](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/porosity_waves.jl) nD MPI-parallel example of 2 coupled nonlinear PDE's using the `DMDA` framework along with automatic differentiation to derive the jacobian.

Working through those examples should give you a fair idea of how to use PETSc. 

If you have further questions, please have a look at the [test](https://github.com/JuliaParallel/PETSc.jl/tree/main/test) directory; this is run automatically every time we make a new commit, and we do our best to keep it working.

Furthermore, the left menu gives additional instructions on how to use the low-level interface.


================================================
FILE: docs/src/man/hpc.md
================================================
# Running on HPC Systems

PETSc.jl can be used on HPC clusters in two main configurations: using precompiled binaries via MPITrampoline, or by pointing to a locally installed PETSc build.

## 1. Use precompiled binaries via MPITrampoline

The main reason that it is challenging to run applications on HPC systems is that MPI is implemented in a different way by different vendors. This will change in the future as there is now the MPI ABI (application Binary Interface) and our precompiled PETSc binaries are already compatible with that.

Yet, until all MPI implementations fully support this, we recommend using [MPITrampoline](https://github.com/eschnett/MPITrampoline) instead, which is a MPI wrapper layer that lets MPI-linked binaries be redirected to any system MPI at runtime. The `PETSc_jll` binaries distributed with PETSc.jl are built against MPITrampoline, which means they can be used on clusters by simply configuring `MPI.jl` to use the system MPI.
Doing this requires you to compile a small code on the HPC system that is linked versus the local MPI implementation.

Here step-by-step instructions (for Linux, as that is what essentially all HPC systems use):

#### 1.1 Install MPIwrapper 

* Download [MPIwrapper](https://github.com/eschnett/MPIwrapper): 
```bash
git clone https://github.com/eschnett/MPIwrapper.git 
cd MPIwrapper
```

* Install it after making sure that `mpiexec` points to the one you want (you may have to load some modules, depending on your system):
```bash
cmake -S . -B build -DMPIEXEC_EXECUTABLE=/full/path/to/mpiexec -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=$HOME/mpiwrapper
cmake --build build
cmake --install build
```
> [!IMPORTANT]  
> You need to specify the full path to `mpiexec` (or equivalent, such as `srun` or `mpirun`, depending oin your system) and not just the name. If you don't know that, you can determine this with
> `which mpiexec`
 
At this stage, `MPIwrapper` is installed in `$HOME/mpiwrapper`

#### 1.2 Set the correct wrappers
Next, you need to specify these environmental variables:
```
export MPITRAMPOLINE_LIB=$HOME/mpiwrapper/lib64/libmpiwrapper.so
export MPITRAMPOLINE_MPIEXEC=$HOME/MPIwrapper/mpiwrapper/bin/mpiwrapperexec 
```
Depending on the system it may be called `lib` instead of `lib64` (check!).


#### 1.3 Install the `MPI` and `MPIPreferences` packages:
Install packages the usual way:
```julia
julia
julia> ]
pkg>add MPI, MPIPreferences
```

Set the preference to use `MPItrampoline`:
```julia
julia> using MPIPreferences; MPIPreferences.use_jll_binary("MPItrampoline_jll")
┌ Info: MPIPreferences unchanged
└   binary = "MPItrampoline_jll"
```

Load `MPI` and verify it is the correct one:
```julia
julia> using MPI
julia> MPI.Get_library_version()
"MPIwrapper 2.10.3, using MPIABI 2.9.0, wrapping:\nOpen MPI v4.1.4, package: Open MPI boris@Pluton Distribution, ident: 4.1.4, repo rev: v4.1.4, May 26, 2022"
```
After this, restart julia (this only needs to be done once, next time all is fine).

#### 1.4 Test `MPI`:

If you want you can run a test case with:
```julia
julia> using MPI
julia> mpiexec(cmd -> run(`$cmd -n 3 echo hello world`));
hello world
hello world
hello world
```

#### 1.5 Install and use `PETSc.jl`:
Now install `PETSc.jl`:
```julia
julia> using MPI,PETSc
```
At this stage you can use PETSc.jl as normal — no changes to your script needed:
```julia
using PETSc, MPI
petsclib = PETSc.getlib(; PetscScalar = Float64, PetscInt = Int64)
PETSc.initialize(petsclib)
# ...
```

1. Launch via the cluster's MPI:
```bash
mpiexec -n 128 julia --project myScript.jl
```
This is the easiest path for most clusters and requires no custom PETSc compilation.

## 2. Use a locally installed PETSc build

If you need a PETSc build with specific options (external packages, GPU support, custom BLAS, etc.), you can point `PETSc.jl` directly to your local installation.
The local PETSc must be compiled as a **shared library** (`--with-shared-libraries=1`) and linked against the **same MPI** that `MPI.jl` is configured to use (i.e. the one that you should use on your HPC machine).

#### 2.1 Link `PETSc` to the local library  

Use `set_library!` to configure the path once — it is stored in `LocalPreferences.toml` and no environment variables are needed afterwards:

```julia
using PETSc
PETSc.set_library!(
    "/path/to/custom/libpetsc.so";
    PetscScalar = Float64,
    PetscInt    = Int64,
)
# Restart Julia — PETSc_jll is not loaded and your library is used automatically.
```

#### 2.2 Link `MPI.jl` to the local mpi
You *must* use the same MPI implementation as the one versus which you compiled PETSc (otherwise you'll get heaps of problems).

Check with:
```julia
julia> using MPI

julia> MPI.Get_library_version()
```



### 3. Typical HPC job script

A typical slurm submissions script to run PETSc code using `MPITrampoline` binaries can look like:

```bash
#!/bin/bash
#SBATCH --nodes=8
#SBATCH --ntasks-per-node=128


export MPITRAMPOLINE_LIB=/users/kausbori/mpiwrapper/lib64/libmpiwrapper.so
export JULIA_CPU_TARGET="generic"

srun julia --project ex45.jl \
  -N 257 \
  -ksp_type cg \
  -pc_type mg \
  -pc_mg_levels 5 \
  -mg_levels_ksp_type chebyshev \
  -mg_levels_pc_type sor \
  -ksp_rtol 1e-8 \
  -ksp_view \
  -pc_mg_log \
  -log_view
```

#### Precompile code on compute nodes
On some machines, it can be useful to precompile `PETSc.jl` on a single core rather than having many processors trying to do the same thing simultaneously.

This is an example on how this can be done by getting access on an interactive node using slurm. Note that julia was installed in the home directory using `juliaup`.
```bash
salloc --nodes=1 --ntasks=1 --time=00:30:00 \
    --partition=standard --account=your_project_number

# Once on the compute node:
module --force purge
module load CrayEnv
module load craype
module load gcc-native/13.2
module load cray-mpich/8.1.32

export HOME=/users/kausbori
export JULIA_DEPOT_PATH=/users/kausbori/.julia
export MPITRAMPOLINE_LIB=/users/kausbori/mpiwrapper/lib64/libmpiwrapper.so
export JULIA_CPU_TARGET="generic"

JULIA=/users/kausbori/.julia/juliaup/julia-1.12.6+0.x64.linux.gnu/bin/julia

# Clear stale cache
rm -rf /users/kausbori/.julia/compiled/v1.12/PETSc/
rm -rf /users/kausbori/.julia/compiled/v1.12/PETSc_jll/
rm -rf /users/kausbori/.julia/compiled/v1.12/MPI/

# Precompile
$JULIA --project=/users/kausbori/PETSc_jl_scalability \
    -e 'using PETSc; println("OK")'
```

---

## Performance and Weak Scalability

This section summarises weak scalability results for a 3D Laplacian benchmark ([`ex45.jl`](https://github.com/JuliaParallel/PETSc.jl/blob/main/examples/ex45.jl)) using a CG solver with geometric multigrid preconditioning (`-pc_type mg`). The problem size is scaled proportionally with the number of MPI ranks so that the work per rank stays constant.

We have performed these simulations on LUMI-C (Finnland) and provide job submission scripts (`submit_scaling.sh`, `job.sh`), along with a parsing file that collects and summarizes the results (`parse_scaling.jl`). 
All files are uploaded under [PETSc.jl/examples/scalability_tests](PETSc.jl/examples/scalability_tests), and can be started with
```bash
$ ./submit_scaling.sh 512 1025 1025 1025 6 16
```
which will start a $1025 \times 1025 \times 1015$ simulation on 512 cores, with 6 multigrid levels and the coarse grid solver being solved on 16 cores.

We consider 3 cases:
- Using `PETSc.jl` with MPITrampoline linked to the local MPI ("PETSc.jl"). This is generally the easiest setup
- Using `PETSc.jl` linked to our locally build PETSc version ("local lib")
- Using a C-compiled version of `ex45_julia.c`  ("native")

#### Results
Here the collected results (all simulations, except the largest ones, where done 3 times; I only show the fastest ones here). 

| JobID | Ntasks | MG | NC | Grid | Backend | SolveTime (s) | KSPSolve (s) | TotGFlops | GFlops/s | KSP | L2 error | Max error | Residual | Converged |
|-------|--------|----|----|------|---------|--------------|-------------|-----------|----------|-----|----------|-----------|----------|-----------|
| 17684772 | 64 | 5 | 16 | 513³ | PETSc.jl | 50.800 | 27.108 | 335.60 | 6.00 | 9 | 4.4372e-06 | 1.2557e-05 | 3.5610e-05 | ✅ |
| 17769825 | 64 | 5 | 16 | 513³ | local lib | 51.032 | 27.006 | 335.60 | 6.00 | 9 | 4.4372e-06 | 1.2557e-05 | 3.5610e-05 | ✅ |
| 17767475 | 64 | 5 | 16 | 513³ | native | 38.770 | 26.917 | 337.90 | 8.50 | 9 | 4.4372e-06 | 1.2557e-05 | 3.5610e-05 | ✅ |
| 17724458 | 512 | 6 | 16 | 1025³ | PETSc.jl | 61.715 | 32.482 | 2878.00 | 37.52 | 10 | 1.1093e-06 | 3.1236e-06 | 9.6257e-05 | ✅ |
| 17769827 | 512 | 6 | 16 | 1025³ | local lib | 58.254 | 32.033 | 2878.00 | 40.54 | 10 | 1.1093e-06 | 3.1236e-06 | 9.6257e-05 | ✅ |
| 17767644 | 512 | 6 | 16 | 1025³ | native | 42.700 | 30.122 | 2896.00 | 65.64 | 10 | 1.1093e-06 | 3.1236e-06 | 9.6257e-05 | ✅ |
| 17768041 | 512 | 6 | 16 | 1025³ | native | 42.924 | 30.307 | 2896.00 | 65.34 | 10 | 1.1093e-06 | 3.1236e-06 | 9.6257e-05 | ✅ |
| 17658195 | 4096 | 7 | 16 | 2049³ | PETSc.jl | 72.949 | 34.332 | 21390.00 | 258.00 | 9 | 2.7738e-07 | 7.8741e-07 | 1.9254e-04 | ✅ |
| 17769830 | 4096 | 7 | 16 | 2049³ | local lib | 68.095 | 34.220 | 21390.00 | 270.00 | 9 | 2.7738e-07 | 7.8741e-07 | 1.9254e-04 | ✅ |
| 17767610 | 4096 | 7 | 16 | 2049³ | native | 41.345 | 28.214 | 21540.00 | 506.10 | 9 | 2.7738e-07 | 7.8741e-07 | 1.9254e-04 | ✅ |
| 17726872 | 32768 | 8 | 16 | 4097³ | PETSc.jl | 142.540 | 63.300 | 171000.00 | 746.60 | 9 | 6.9301e-08 | 1.9765e-07 | 6.6391e-04 | ✅ |
| 17767541 | 32768 | 8 | 16 | 4097³ | native | 69.282 | 50.858 | 172200.00 | 2386.00 | 9 | 6.9301e-08 | 1.9765e-07 | 6.6391e-04 | ✅ |
17769918  |  32768  |   8 |   16 |  4097³ | local lib |    135.402  |     73.108 |  171000.00   |   263.20 |    9 |   6.9301e-08  |  1.9765e-07 |   6.6391e-04 | ✅ |
---
In this table `KSPSolve` indicates the time spend in the solver (taken from the log) and `SolveTime` the time for the full solution.

Below, we break this for each of the cases:

#### a) PETSc.jl with precompiled `PETSc_jll` binaries

the results of using PETSc.jl with precompiled `jll` libraries are:

| Ntasks | Grid | DOFs/core | KSPSolve (s) | SolveTime (s) | Efficiency | Converged |
|--------|------|-----------|-------------|--------------|------------|-----------|
| 64 | 513³ | 2,109,464 | 27.916 | 55.843 | 100.0% | ✅ |
| 64 | 513³ | 2,109,464 | 27.108 | 50.800 | 103.0% | ✅ |
| 64 | 513³ | 2,109,464 | 28.121 | 56.352 | 99.3% | ✅ |
| 512 | 1025³ | 2,103,302 | 33.670 | 66.789 | 82.9% | ✅ |
| 512 | 1025³ | 2,103,302 | 32.482 | 61.715 | 85.9% | ✅ |
| 512 | 1025³ | 2,103,302 | 34.236 | 65.473 | 81.5% | ✅ |
| 4096 | 2049³ | 2,100,226 | 34.332 | 72.949 | 81.3% | ✅ |
| 4096 | 2049³ | 2,100,226 | 44.244 | 79.557 | 63.1% | ✅ |
| 32768 | 4097³ | 2,098,688 | 63.300 | 142.540 | 44.1% | ✅ |

Please note that in the way we run this on LUMI-C, the 64 core case is on a single node and does not have inter-node communication. Despite this, weak scalability is pretty good (one could also argue to use the 512 core case as reference as this includes communication, in which case it would even be better).
There is some variability in the timing when repeating the same run (a few %).

#### b) PETSc.jl linked against a local PETSc build (system MPI)

| Ntasks | Grid | DOFs/core | KSPSolve (s) | SolveTime (s) | Efficiency | Converged |
|--------|------|-----------|-------------|--------------|------------|-----------|
| 64 | 513³ | 2,109,464 | 27.065 | 51.123 | 100.0% | ✅ |
| 64 | 513³ | 2,109,464 | 27.006 | 51.032 | 100.2% | ✅ |
| 64 | 513³ | 2,109,464 | 27.551 | 56.452 | 98.2% | ✅ |
| 512 | 1025³ | 2,103,302 | 34.456 | 61.041 | 78.5% | ✅ |
| 512 | 1025³ | 2,103,302 | 32.033 | 58.254 | 84.5% | ✅ |
| 512 | 1025³ | 2,103,302 | 30.708 | 61.971 | 88.1% | ✅ |
| 4096 | 2049³ | 2,100,226 | 38.414 | 69.384 | 70.5% | ✅ |
| 4096 | 2049³ | 2,100,226 | 35.292 | 68.905 | 76.7% | ✅ |
| 4096 | 2049³ | 2,100,226 | 34.220 | 68.095 | 79.1% | ✅ |
| 32768 | 4097³ | 2,098,688 | 73.108  | 135.402  | 37.0% | ✅ |


#### c) Native C build (`ex45_julia.c`)

The equivalent PETSc C example compiled natively, serving as the baseline.


| Ntasks | Grid | DOFs/core | KSPSolve (s) | SolveTime (s) | Efficiency | Converged |
|--------|------|-----------|-------------|--------------|------------|-----------|
| 64 | 513³ | 2,109,464 | 26.917 | 38.770 | 100.0% | ✅ |
| 64 | 513³ | 2,109,464 | 27.155 | 39.011 | 99.1% | ✅ |
| 64 | 513³ | 2,109,464 | 26.854 | 39.065 | 100.2% | ✅ |
| 512 | 1025³ | 2,103,302 | 31.492 | 44.062 | 85.5% | ✅ |
| 512 | 1025³ | 2,103,302 | 30.122 | 42.700 | 89.4% | ✅ |
| 512 | 1025³ | 2,103,302 | 30.607 | 43.331 | 87.9% | ✅ |
| 512 | 1025³ | 2,103,302 | 30.307 | 42.924 | 88.8% | ✅ |
| 4096 | 2049³ | 2,100,226 | 30.694 | 46.659 | 87.7% | ✅ |
| 4096 | 2049³ | 2,100,226 | 28.214 | 41.345 | 95.4% | ✅ |
| 4096 | 2049³ | 2,100,226 | 28.498 | 42.759 | 94.5% | ✅ |
| 32768 | 4097³ | 2,098,688 | 50.858 | 69.282 | 52.9% | ✅ |


#### d) Backend Comparison 

If we compare the case on 4096 cores with a 2049³ grid, for 3D Poisson, 64 nodes × 64 tasks/node, MG levels = 7, coarse ranks = 16 we get:

| Metric | native C | PETSc.jl (`_jll`) | local lib |
|--------|----------|-------------------|-----------|
| **Runs** | 3 | 2 | 3 |
| **SolveTime mean (s)** | 43.6 | 76.3 | 68.8 |
| **SolveTime range (s)** | 41.3–46.7 | 72.9–79.6 | 68.1–69.4 |
| **KSPSolve mean (s)** | 29.1 | 39.3 | 35.9 |
| **KSPSolve range (s)** | 28.2–30.7 | 34.3–44.2 | 34.2–38.4 |
| **KSP iterations** | 9 | 9 | 9 |
| **L2 error** | 2.7738e-07 | 2.7738e-07 | 2.7738e-07 |
| **Converged** | ✅ | ✅ | ✅ |

The relative performance is thus:

| Backend | SolveTime overhead | KSPSolve overhead |
|---------|--------------------|-------------------|
| native C | — (reference) | — (reference) |
| PETSc.jl (best run) | +21.5% | +1.6% |
| PETSc.jl (worst run) | +32.5% | +30.9% |
| local lib | +56.9% | +50.8% |

From this we can conclude:
- **Numerical results are identical** across all backends — same iterations, same L2/Max error.
- **KSPSolve gap vs native** is ~6s for local lib and ~10s for `_jll` — much of this is MG setup overhead inside the solver
- **SolveTime gap vs native** (~25s for local lib, ~32s for `_jll`) includes Julia setup: mesh construction, matrix assembly, MG hierarchy setup
- **local lib** is faster than `_jll` at 4096 tasks (35.9s vs 39.3s mean KSPSolve) — likely because it uses the system Cray libsci BLAS directly without MPItrampoline overhead.
- There is a (significant) drop in performance for high core counts. this is likely related to the multigrid setup and coarse grid solvers and not to PETSc.jl as we see a similar behavior for the native compilation as well.

Overall it shows that there is overhead in using the Julia interface to `PETSc.jl` compared to native C-code. 
On the other hand, this is done in a higher level language and can thus seemlessly integrate with the full Julia ecosystem. It should also be kept in mind that the residual routines etc. are all written in julia. 

As a start one can configure `PETSc.jl` to use MPITrampoline, which doesn't require any PETSc installation and should thus work on any machine. For the last bit of performance one can configure a native PETSc installation and use that instead.

#### Weak scalability plot
Results can be summarized in the plot below:
![weak_scalability_LUMI](../assets/img/weak_scaling_ex45_LUMI.png)

================================================
FILE: docs/src/man/installation.md
================================================
# Installation

## Using pre-built libraries

The easiest way to install the package is:
```julia
julia> ]
(@v1.12) pkg> add PETSc
```
which will install a pre-built PETSc library (`PETSc_jll`) as well as `MPI.jl` on your system. This will work both in serial and in parallel on your machine.

!!! warning "Windows Users"
    The prebuild binaries currently do not work on Windows as we had to build `PETSc_jll` without MPI due to compatibility issues with `MicrosoftMPI_jll`.

    **Windows users are therefore advised to install the [Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux) (WSL) and run PETSc.jl from within WSL.** This will provide full functionality with both serial and parallel (MPI) support.

## Using a custom PETSc build

Sometimes, you may be interested in a PETSc installation that comes with additional external packages, or that you compiled yourself. Ensure the library is compiled as a **dynamic** (not static) library.

Use `set_library!` to configure the path once — it is stored in `LocalPreferences.toml` and no environment variables are needed afterwards:

```julia
using PETSc
PETSc.set_library!(
    "/path/to/custom/libpetsc.so";
    PetscScalar = Float64,
    PetscInt    = Int64,
)
# Restart Julia — PETSc_jll is not loaded and your library is used automatically.
```

To revert to the bundled binaries: `PETSc.unset_library!()`.

To check what is currently configured: `PETSc.library_info()`.

For a one-off session without changing persistent settings, use `set_petsclib` directly:

```julia
petsclib = PETSc.set_petsclib("/path/to/custom/libpetsc.so";
                              PetscScalar=Float64, PetscInt=Int64)
PETSc.initialize(petsclib, log_view=true)
# ... your code ...
PETSc.finalize(petsclib)
```

## HPC systems

On many high-performance clusters, you will need to use the cluster's MPI installation. There are a number of options to do so - see the [Running on HPC Systems](hpc.md) page for details.


================================================
FILE: docs/src/man/is_lowlevel.md
================================================
# IS (Index Sets) - Low-level Interface

The IS (Index Set) component provides data structures and operations for managing sets of indices, which are fundamental for parallel data distribution, matrix/vector assembly, and mapping between different numbering schemes.

## Overview

Index sets are used throughout PETSc for:
- **Parallel data distribution**: Specifying which indices are owned by each processor
- **Submatrix/subvector extraction**: Selecting specific rows/columns
- **Scatter/gather operations**: Defining communication patterns
- **Field splitting**: Identifying DOFs belonging to different fields
- **Reordering**: Specifying permutations for bandwidth reduction

PETSc provides several IS types:
- **General IS**: Arbitrary list of indices
- **Stride IS**: Regularly spaced indices (start, step, length)
- **Block IS**: Block-structured indices
- **Complement IS**: All indices except specified ones

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Get PETSc types
PetscInt = petsclib.PetscInt

# Create an index set from an array of indices (0-based)
# Note: indices should be PetscInt type (typically Int64 or Int32 depending on PETSc configuration)
indices = PetscInt[0, 2, 4, 6, 8]
is = LibPETSc.ISCreateGeneral(petsclib, LibPETSc.PETSC_COMM_SELF, PetscInt(length(indices)), indices, LibPETSc.PETSC_COPY_VALUES)

# Create a stride index set: indices = first:step:(first + step*(n-1))
is_stride = LibPETSc.ISCreateStride(petsclib, LibPETSc.PETSC_COMM_SELF, PetscInt(10), PetscInt(0), PetscInt(2))  # 0, 2, 4, ..., 18

# Get the size of an index set (returns value directly)
n = LibPETSc.ISGetSize(petsclib, is)

# Get local size (returns value directly)
local_n = LibPETSc.ISGetLocalSize(petsclib, is)

# Get indices as an array
indices = LibPETSc.ISGetIndices(petsclib, is)
# ... use indices ...
LibPETSc.ISRestoreIndices(petsclib, is, indices)

# Cleanup
LibPETSc.ISDestroy(petsclib, is)
LibPETSc.ISDestroy(petsclib, is_stride)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Common Operations

### Creating Index Sets

```julia
# General index set from array
LibPETSc.ISCreateGeneral(petsclib, comm, n, indices, copymode, is)

# Stride index set: first, first+step, first+2*step, ...
LibPETSc.ISCreateStride(petsclib, comm, n, first, step, is)

# Block index set: block-structured indices
LibPETSc.ISCreateBlock(petsclib, comm, blocksize, n, indices, copymode, is)
```

### Set Operations

```julia
# Union of two index sets
LibPETSc.ISSum(petsclib, is1, is2, is_union)

# Difference: is1 - is2
LibPETSc.ISDifference(petsclib, is1, is2, is_diff)

# Intersection
LibPETSc.ISIntersect(petsclib, is1, is2, is_intersect)

# Complement: all indices in [0, n) not in is
LibPETSc.ISComplement(petsclib, is, nmin, nmax, is_complement)
```

### Querying Properties

```julia
# Check if index set is sorted
is_sorted = Ref{PetscBool}()
LibPETSc.ISSorted(petsclib, is, is_sorted)

# Check if identity permutation
is_identity = Ref{PetscBool}()
LibPETSc.ISIdentity(petsclib, is, is_identity)

# Check if a permutation
is_perm = Ref{PetscBool}()
LibPETSc.ISPermutation(petsclib, is, is_perm)
```

## Index Set Types

Available through `ISSetType`:
- **ISGENERAL**: General list of indices
- **ISSTRIDE**: Arithmetic sequence
- **ISBLOCK**: Block-structured indices

## Scatter Context

Index sets are used to create scatter contexts for moving data between vectors:

```julia
# Create scatter context
scatter = Ref{LibPETSc.VecScatter}()
LibPETSc.VecScatterCreate(petsclib, vec_from, is_from, vec_to, is_to, scatter)

# Perform scatter operation
LibPETSc.VecScatterBegin(petsclib, scatter[], vec_from, vec_to, INSERT_VALUES, SCATTER_FORWARD)
LibPETSc.VecScatterEnd(petsclib, scatter[], vec_from, vec_to, INSERT_VALUES, SCATTER_FORWARD)
```

## Parallel Considerations

- Index sets use global indexing by default
- Each processor owns a portion of the index set
- Use `ISGetLocalSize` to get the local portion
- Scatter operations handle parallel communication automatically

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/IS_wrappers.jl"]
Order   = [:function]
```

## IS Add-ons

Additional IS utilities and helper functions:

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/ISaddons_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/ksp.md
================================================
# KSP

The KSP (Krylov Subspace Methods) module provides iterative linear solvers for systems of the form `Ax = b`. PETSc offers a wide variety of Krylov methods and preconditioners.

## Overview

KSP provides:
- **Krylov methods**: GMRES, CG, BiCGStab, and many more
- **Preconditioners**: Jacobi, ILU, multigrid, direct solvers, etc.
- **Runtime configuration**: Choose methods via command-line options
- **Convergence monitoring**: Built-in residual tracking

## Creating a KSP Solver

### From a Matrix

```julia
# Basic creation with default options
ksp = KSP(A)

# With preconditioner matrix P (for different preconditioning)
ksp = KSP(A, P)

# With options
ksp = KSP(A; 
    ksp_type = "gmres",
    pc_type = "ilu",
    ksp_rtol = 1e-8
)
```

### From a DM

```julia
# Create KSP associated with a DM (for multigrid, etc.)
ksp = KSP(dm; 
    ksp_type = "cg",
    pc_type = "mg"
)
```

### From a Sparse Matrix

```julia
# Directly from Julia SparseMatrixCSC
using SparseArrays
S = sprand(100, 100, 0.1) + 10I
ksp = KSP(petsclib, MPI.COMM_SELF, S)
```

## Solving

```julia
# Solve Ax = b
solve!(x, ksp, b)

# Or allocate solution vector
x = solve(ksp, b)
```

## Common Solver/Preconditioner Options

### Krylov Methods (`ksp_type`)
- `cg` - Conjugate Gradient (symmetric positive definite)
- `gmres` - Generalized Minimum Residual
- `bicgstab` - BiConjugate Gradient Stabilized
- `richardson` - Richardson iteration
- `preonly` - Apply preconditioner only (for direct solvers)

### Preconditioners (`pc_type`)
- `jacobi` - Diagonal scaling
- `ilu` - Incomplete LU factorization
- `lu` - Direct LU factorization
- `mg` - Geometric multigrid
- `gamg` - Algebraic multigrid
- `none` - No preconditioning

### Convergence Options
- `ksp_rtol` - Relative tolerance (default: 1e-5)
- `ksp_atol` - Absolute tolerance
- `ksp_max_it` - Maximum iterations
- `ksp_monitor` - Print residual each iteration

## Example: Multigrid Solver

```julia
ksp = KSP(dm;
    ksp_type = "cg",
    pc_type = "mg",
    pc_mg_levels = 4,
    pc_mg_galerkin = true,
    mg_levels_ksp_type = "richardson",
    mg_levels_pc_type = "jacobi",
    mg_coarse_pc_type = "lu"
)
```

## Functions

```@autodocs
Modules = [PETSc]
Pages   = ["ksp.jl"]
```


================================================
FILE: docs/src/man/ksp_lowlevel.md
================================================
# KSP - Low-level Interface

This page documents the low-level, automatically wrapped PETSc KSP (Krylov Subspace Methods) functions available through `LibPETSc`. These functions provide direct access to the PETSc C API.

For the high-level Julia interface, see [KSP](@ref).

## Overview

The KSP interface includes:
- **Core KSP operations**: Linear solver creation, configuration, and solution (~244 functions in `KSP_wrappers.jl`)
- **KSPGuess**: Initial guess generation for iterative solvers (~12 functions in `KSPGuess_wrappers.jl`)

## Usage

All low-level KSP functions require a `petsclib` parameter as the first argument:

```julia
using PETSc

# Get the library instance
petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

# Get PETSc types
PetscInt = petsclib.PetscInt

# Assume mat, b, x are already created

# Create KSP solver
ksp = LibPETSc.KSPCreate(petsclib, LibPETSc.PETSC_COMM_SELF)
LibPETSc.KSPSetOperators(petsclib, ksp, mat, mat)
LibPETSc.KSPSetFromOptions(petsclib, ksp)

# Solve Ax = b
LibPETSc.KSPSolve(petsclib, ksp, b, x)

# Get convergence info
reason = LibPETSc.KSPGetConvergedReason(petsclib, ksp)

iterations = LibPETSc.KSPGetIterationNumber(petsclib, ksp)

# Clean up
LibPETSc.KSPDestroy(petsclib, ksp)
PETSc.finalize(petsclib)
```

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/KSP_wrappers.jl", "autowrapped/KSPGuess_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/lowlevel_intro.md
================================================
# Low-Level Interface (LibPETSc)

PETSc.jl provides two ways to interact with the PETSc library:

1. **High-level interface**: Julia-friendly wrappers that handle memory management, use 1-based indexing, and provide convenient syntax (e.g., `A[1,2] = 3.0`)
2. **Low-level interface** (`LibPETSc`): Direct access to nearly the entire PETSc C API (~13,000+ functions)

This guide focuses on the low-level interface.

## When to Use the Low-Level Interface

Use the low-level `LibPETSc` interface when:

- You need PETSc functionality not yet wrapped in the high-level interface
- You're porting existing PETSc C/Fortran code to Julia
- You need fine-grained control over PETSc objects
- You want to access advanced or experimental PETSc features
- Performance-critical code requires eliminating Julia wrapper overhead

For most common tasks (creating vectors/matrices, solving linear systems), the high-level interface is recommended.

## Basic Usage Pattern

The low-level interface follows the PETSc C API closely. All functions require a `petsclib` parameter as the first argument:

```julia
using PETSc

# Get a library instance (usually you'll use the first one)
petsclib = PETSc.petsclibs[1]

# Initialize PETSc
PETSc.initialize(petsclib)

# Create and use PETSc objects
vec = LibPETSc.VecCreate(petsclib, LibPETSc.PETSC_COMM_SELF)
LibPETSc.VecSetSizes(petsclib, vec, 10, 10)
LibPETSc.VecSetType(petsclib, vec, "seq")  # Set vector type
LibPETSc.VecSetFromOptions(petsclib, vec)

# Work with the vector
LibPETSc.VecSet(petsclib, vec, 1.0)
println("Vector size: ", LibPETSc.VecGetSize(petsclib, vec))

# Clean up
LibPETSc.VecDestroy(petsclib, vec)
PETSc.finalize(petsclib)
```

## Key Concepts

### 1. The `petsclib` Parameter

Every low-level function requires a `petsclib` instance. This specifies which PETSc library variant to use (e.g., Float64 vs Float32, Int32 vs Int64):

```julia
# Default libraries available (uses prebuilt PETSc binaries)
petsclib = PETSc.petsclibs[1]  # Usually Float64, Int64

# Access the scalar and integer types
PetscScalar = petsclib.PetscScalar  # The floating-point type (e.g., Float64)
PetscInt = petsclib.PetscInt        # The integer type (e.g., Int64)
PetscReal = petsclib.PetscReal      # The real type (real part of PetscScalar)
```

#### Using a Custom PETSc Build

You can link to your own custom build of PETSc instead of using the prebuilt binaries. This is useful when you need:
- Specific configure options not available in the default build
- Custom optimizations for your hardware
- Debug builds for troubleshooting
- Integration with specific external packages

To persistently configure a custom PETSc installation (recommended):

```julia
using PETSc
PETSc.set_library!("/path/to/your/libpetsc.so"; PetscScalar=Float64, PetscInt=Int64)
# Restart Julia — PETSc_jll is not loaded and your library is used automatically.
```

To revert: `PETSc.unset_library!()`. To inspect the current config: `PETSc.library_info()`.

For a one-off session without changing persistent settings:

```julia
petsclib = PETSc.set_petsclib("/path/to/your/libpetsc.so";
                              PetscScalar=Float64, PetscInt=Int64)
PETSc.initialize(petsclib)
# ... your code using petsclib ...
PETSc.finalize(petsclib)
```

**Important notes for custom builds:**
- The library must be compiled as a dynamic/shared library (`libpetsc.so` / `.dylib`)
- `PetscScalar` and `PetscInt` must match how your PETSc was configured
- Your PETSc must be linked against the same MPI that `MPI.jl` uses
- You can check available precompiled libraries with `[PETSc.petsclibs...]`

### 2. Zero-Based Indexing

**Important**: The low-level interface uses 0-based indexing (C convention), while Julia uses 1-based indexing:

```julia
# Low-level (0-based)
indices = PetscInt[0, 1, 2]  # First three elements
LibPETSc.VecSetValues(petsclib, vec, 3, indices, values, INSERT_VALUES)

# High-level (1-based)
vec[1] = value  # First element
```

### 3. Error Handling

Low-level functions return `PetscErrorCode`. Use the `@chk` macro to check for errors:

```julia
using PETSc.LibPETSc: @chk

err = LibPETSc.VecCreate(petsclib, MPI.COMM_SELF)
@chk err  # Throws an error if PETSc returned non-zero
```

### 4. String Convenience Wrappers

Many PETSc `SetType` functions accept C string pointers. For convenience, PETSc.jl provides Julia `String` overloads:

```julia
# String convenience wrapper (recommended)
LibPETSc.MatSetType(petsclib, mat, "seqaij")
LibPETSc.VecSetType(petsclib, vec, "seq")
LibPETSc.KSPSetType(petsclib, ksp, "gmres")
LibPETSc.SNESSetType(petsclib, snes, "newtonls")
LibPETSc.PCSetType(petsclib, pc[], "ilu")
LibPETSc.TSSetType(petsclib, ts, "bdf")
LibPETSc.TaoSetType(petsclib, tao, "lmvm")
LibPETSc.DMSetType(petsclib, dm, "da")
LibPETSc.PetscViewerSetType(petsclib, viewer, "ascii")

# Equivalent low-level C pointer syntax (not recommended unless necessary)
ptr = Base.unsafe_convert(Ptr{Int8}, pointer(Vector{UInt8}("seqaij\0")))
LibPETSc.MatSetType(petsclib, mat, ptr)
```

The string wrappers handle the C string conversion internally, making the code cleaner and more Julia-friendly.

Most wrapper functions already include error checking, but when calling C functions directly, use `@chk`.

### 4. Memory Management

PETSc objects created with `Create` functions must be destroyed with corresponding `Destroy` functions:

```julia
# Create
mat = LibPETSc.MatCreate(petsclib, MPI.COMM_WORLD)
LibPETSc.MatSetSizes(petsclib, mat, m_local, n_local, m_global, n_global)

# ... use mat ...

# Destroy when done
LibPETSc.MatDestroy(petsclib, mat)
```

For serial (single-process) objects, the high-level interface handles this automatically via finalizers.

### 5. Assembly

After setting values in vectors or matrices, you must call assembly functions:

```julia
# Set values
LibPETSc.VecSetValues(petsclib, vec, n, indices, values, INSERT_VALUES)

# Assemble
LibPETSc.VecAssemblyBegin(petsclib, vec)
LibPETSc.VecAssemblyEnd(petsclib, vec)
```

## Common Patterns

### Creating and Filling a Vector

```julia
petsclib = PETSc.petsclibs[1]
PetscInt = petsclib.PetscInt
PetscScalar = petsclib.PetscScalar

# Create vector
vec = LibPETSc.VecCreate(petsclib, MPI.COMM_SELF)
LibPETSc.VecSetSizes(petsclib, vec, 10, 10)
LibPETSc.VecSetType(petsclib, vec, "seq")  # Set vector type
LibPETSc.VecSetFromOptions(petsclib, vec)

# Set values (0-based indices!)
indices = PetscInt[0, 1, 2, 3, 4]
values = PetscScalar[1.0, 2.0, 3.0, 4.0, 5.0]
LibPETSc.VecSetValues(petsclib, vec, 5, indices, values, INSERT_VALUES)

# Assemble
LibPETSc.VecAssemblyBegin(petsclib, vec)
LibPETSc.VecAssemblyEnd(petsclib, vec)

# View (print to stdout)
LibPETSc.VecView(petsclib, vec, C_NULL)

# Clean up
LibPETSc.VecDestroy(petsclib, vec)
```

### Creating a Sparse Matrix

```julia
# Create matrix
mat = LibPETSc.MatCreate(petsclib, MPI.COMM_SELF)
LibPETSc.MatSetSizes(petsclib, mat, 5, 5, 5, 5)
LibPETSc.MatSetType(petsclib, mat, "seqaij")  # String convenience wrapper
LibPETSc.MatSetUp(petsclib, mat)

# Set values (0-based indexing!)
row = PetscInt[0]
cols = PetscInt[0, 1]
vals = PetscScalar[2.0, -1.0]
LibPETSc.MatSetValues(petsclib, mat, 1, row, 2, cols, vals, INSERT_VALUES)

# Assemble
LibPETSc.MatAssemblyBegin(petsclib, mat, LibPETSc.MAT_FINAL_ASSEMBLY)
LibPETSc.MatAssemblyEnd(petsclib, mat, LibPETSc.MAT_FINAL_ASSEMBLY)

# View
LibPETSc.MatView(petsclib, mat, C_NULL)

# Clean up
LibPETSc.MatDestroy(petsclib, mat)
```

### Solving a Linear System

```julia
# Create KSP solver
ksp = LibPETSc.KSPCreate(petsclib, MPI.COMM_SELF)
LibPETSc.KSPSetOperators(petsclib, ksp, mat, mat)
LibPETSc.KSPSetFromOptions(petsclib, ksp)

# Solve Ax = b
LibPETSc.KSPSolve(petsclib, ksp, b, x)

# Get convergence info
reason = LibPETSc.KSPGetConvergedReason(petsclib, ksp)

iterations = LibPETSc.KSPGetIterationNumber(petsclib, ksp)

println("Converged in $(iterations) iterations, reason: $(reason)")

# Clean up
LibPETSc.KSPDestroy(petsclib, ksp)
```

## Mixing High-Level and Low-Level Interfaces

You can mix both interfaces. High-level objects provide `.ptr` field to access the underlying C pointer:

```julia
# Create with high-level interface
vec_high = VecSeq(petsclib, 10)

# Use with low-level interface
LibPETSc.VecSet(petsclib, vec_high.ptr, 5.0)

# Or use the object directly (if it's a compatible type)
LibPETSc.VecView(petsclib, vec_high, C_NULL)
```

## Finding Functions

The low-level interface provides wrappers for most PETSc functions. To find a function:

1. **Check the PETSc manual**: https://petsc.org/release/docs/
2. **Use Julia's help system**: Type `?LibPETSc.FunctionName`
3. **Browse the documentation**: See the reference pages for each PETSc class (Vec, Mat, KSP, etc.)
4. **Autocomplete**: In the REPL, type `LibPETSc.Vec` and press Tab to see all Vec functions

## Type Conventions

Low-level functions use these type patterns:

```julia
# PETSc objects (opaque pointers)
CVec = Ptr{Cvoid}        # Vector
CMat = Ptr{Cvoid}        # Matrix
CDM = Ptr{Cvoid}         # DM (domain management)
CKSP = Ptr{Cvoid}        # KSP (linear solver)
CSNES = Ptr{Cvoid}       # SNES (nonlinear solver)

# PETSc data types (depend on petsclib)
PetscInt                 # Integer type (Int32 or Int64)
PetscScalar              # Scalar type (Float64, Float32, ComplexF64, etc.)
PetscReal                # Real type (real part of PetscScalar)
```

## Reference Pages

Detailed documentation for low-level functions by category:

- [Vec (Vectors)](@ref vec_lowlevel.md) - ~293 functions for vector operations
- [Mat (Matrices)](@ref mat_lowlevel.md) - ~756 functions for matrix operations
- [KSP (Linear Solvers)](@ref ksp_lowlevel.md) - ~256 functions for iterative linear solvers
- [SNES (Nonlinear Solvers)](@ref snes_lowlevel.md) - ~333 functions for nonlinear solvers

## Getting Help

If you encounter issues:

1. Check the [PETSc documentation](https://petsc.org/release/docs/)
2. Review the [examples](https://github.com/JuliaParallel/PETSc.jl/tree/main/examples)
3. Ask questions on [Julia Discourse](https://discourse.julialang.org/) with the `petsc` tag
4. Open an issue on [GitHub](https://github.com/JuliaParallel/PETSc.jl/issues)


================================================
FILE: docs/src/man/mat.md
================================================
# Mat

PETSc matrices (`Mat`) provide sparse and dense matrix storage with efficient parallel operations. They are essential for discretizing PDEs and setting up linear/nonlinear systems.

## Overview

PETSc matrices support:
- **Sparse formats**: AIJ (CSR), BAIJ (block CSR), and more
- **Dense format**: For small matrices or dense operations
- **Parallel distribution**: Row-based distribution across MPI processes
- **Matrix-free operations**: Via MatShell for custom operators

## Creating Matrices

### Sparse Matrices (AIJ/CSR Format)

```julia
# Create sparse matrix with estimated non-zeros per row
A = MatSeqAIJ(petsclib, num_rows, num_cols, nnz_per_row)

# From Julia SparseMatrixCSC
using SparseArrays
S = sprand(100, 100, 0.1)
A = MatCreateSeqAIJ(petsclib, MPI.COMM_SELF, S)

# With varying non-zeros per row
nnz = PetscInt[5, 3, 4, ...]  # One value per row
A = MatSeqAIJ(petsclib, num_rows, num_cols, nnz)
```

### Dense Matrices

```julia
# Wrap a Julia matrix (no copy)
julia_mat = rand(10, 10)
A = MatSeqDense(petsclib, julia_mat)
```

### From DM Objects

```julia
# Create matrix with sparsity pattern from DM
A = DMCreateMatrix(dm)
```

### Matrix Shell (Matrix-Free)

```julia
# Create a shell matrix with custom mult operation
A = MatShell(petsclib, m, n, mult_function, context)
```

## Setting Values

```julia
# Set individual element (0-based internally, 1-based in Julia)
A[i, j] = value

# Use setvalues! for efficient batch insertion
setvalues!(A, rows, cols, values, INSERT_VALUES)

# For stencil-based assembly
setvalues!(A, stencil_row, stencil_col, value, INSERT_VALUES)
```

## Assembly

Matrices must be assembled after setting values:

```julia
# Set all values first
A[1, 1] = 2.0
A[1, 2] = -1.0
# ...

# Then assemble
assemble!(A)
```

## Common Operations

```julia
size(A)              # Get (rows, cols)
ownershiprange(A)    # Get rows owned by this process
setup!(A)            # Complete matrix setup
```

## Functions

```@autodocs
Modules = [PETSc]
Pages   = ["mat.jl"]
```


================================================
FILE: docs/src/man/mat_lowlevel.md
================================================
# Mat - Low-level Interface

This page documents the low-level, automatically wrapped PETSc Mat (matrix) functions available through `LibPETSc`. These functions provide direct access to the PETSc C API.

For the high-level Julia interface, see [Mat](@ref).

## Overview

The Mat interface includes:
- **Core Mat operations**: Creation, manipulation, and mathematical operations on matrices (~632 functions in `Mat_wrappers.jl`)
- **Mat utilities**: Additional matrix operations and conversions (~124 functions in `Mataddons_wrappers.jl`)

## Usage

All low-level Mat functions require a `petsclib` parameter as the first argument:

```julia
using PETSc

# Get the library instance
petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

# Get PETSc types
PetscInt = petsclib.PetscInt
PetscScalar = petsclib.PetscScalar

# Create a sparse matrix
mat = LibPETSc.MatCreate(petsclib, LibPETSc.PETSC_COMM_SELF)
LibPETSc.MatSetSizes(petsclib, mat, PetscInt(10), PetscInt(10), PetscInt(10), PetscInt(10))
LibPETSc.MatSetType(petsclib, mat, "seqaij")
LibPETSc.MatSetUp(petsclib, mat)

# Set values (0-based indexing!)
row = PetscInt[0]
cols = PetscInt[0, 1]
vals = PetscScalar[2.0, -1.0]
LibPETSc.MatSetValues(petsclib, mat, PetscInt(1), row, PetscInt(2), cols, vals, LibPETSc.INSERT_VALUES)

# Assemble
LibPETSc.MatAssemblyBegin(petsclib, mat, LibPETSc.MAT_FINAL_ASSEMBLY)
LibPETSc.MatAssemblyEnd(petsclib, mat, LibPETSc.MAT_FINAL_ASSEMBLY)

# Clean up
LibPETSc.MatDestroy(petsclib, mat)
PETSc.finalize(petsclib)
```

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/Mat_wrappers.jl", "autowrapped/Mataddons_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/pc_lowlevel.md
================================================
# PC (Preconditioners) - Low-level Interface

The PC (Preconditioner) component provides methods for preconditioning linear systems to accelerate the convergence of iterative solvers. Preconditioners are essential for efficiently solving large sparse linear systems with KSP.

## Overview

Preconditioners transform the linear system $Ax = b$ into an equivalent system that is easier to solve iteratively. PETSc provides a wide variety of preconditioners:

- **Basic methods**: Jacobi, block Jacobi, SOR, ILU, ICC
- **Multigrid**: Algebraic multigrid (GAMG, HYPRE BoomerAMG), geometric multigrid (MG)
- **Domain decomposition**: Additive Schwarz (ASM), block Jacobi with local solves
- **Direct solvers**: LU, Cholesky (using external packages like MUMPS, SuperLU, UMFPACK)
- **Sparse approximations**: ILU(k), ICC(k), approximate inverses
- **Physics-based**: Field-split, composite preconditioners for coupled systems
- **Matrix-free**: Shell preconditioners for matrix-free implementations

The PC object can be used standalone or automatically by KSP during the solution process.

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a PC object
pc = Ref{LibPETSc.PC}()
LibPETSc.PCCreate(petsclib, LibPETSc.PETSC_COMM_SELF, pc)

# Set the preconditioner type
LibPETSc.PCSetType(petsclib, pc[], "ilu")  # String convenience wrapper

# Set the operator matrix
# LibPETSc.PCSetOperators(petsclib, pc[], A, A)

# Configure preconditioner-specific options
# For ILU: set fill level
# LibPETSc.PCFactorSetLevels(petsclib, pc[], 2)

# Set options from command line/options database
LibPETSc.PCSetFromOptions(petsclib, pc[])

# Set up the preconditioner
LibPETSc.PCSetUp(petsclib, pc[])

# Apply the preconditioner: y = P^{-1} x
# LibPETSc.PCApply(petsclib, pc[], x_vec, y_vec)

# Cleanup
LibPETSc.PCDestroy(petsclib, pc)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Integration with KSP

Preconditioners are typically used with KSP solvers:

```julia
# Create KSP and get its PC
ksp = Ref{LibPETSc.KSP}()
LibPETSc.KSPCreate(petsclib, LibPETSc.PETSC_COMM_SELF, ksp)

pc = Ref{LibPETSc.PC}()
LibPETSc.KSPGetPC(petsclib, ksp[], pc)

# Configure the preconditioner
LibPETSc.PCSetType(petsclib, pc[], "gamg")  # String convenience wrapper

# For GAMG, set additional options
LibPETSc.PCGAMGSetType(petsclib, pc[], LibPETSc.PCGAMGAGG)
LibPETSc.PCGAMGSetNlevels(petsclib, pc[], 10)
```

## Common Preconditioner Types

Available through `PCSetType`:

### Direct Solvers
- `PCLU`: LU factorization
- `PCCHOLESKY`: Cholesky factorization (symmetric positive definite)
- `PCQR`: QR factorization

### Incomplete Factorizations
- `PCILU`: Incomplete LU factorization
- `PCICC`: Incomplete Cholesky factorization
- Configure fill with `PCFactorSetLevels`

### Iterative/Relaxation Methods
- `PCJACOBI`: Jacobi (diagonal scaling)
- `PCSOR`: Successive over-relaxation
- `PCPBJACOBI`: Point-block Jacobi
- `PCBJACOBI`: Block Jacobi with local solves

### Multigrid Methods
- `PCMG`: Geometric multigrid
- `PCGAMG`: Algebraic multigrid (PETSc's built-in)
- `PCHYPRE`: HYPRE preconditioners (BoomerAMG, ParaSails, etc.)
- `PCML`: ML algebraic multigrid (Trilinos)

### Domain Decomposition
- `PCASM`: Additive Schwarz method
- `PCGASM`: Generalized additive Schwarz
- `PCBDDC`: Balancing domain decomposition by constraints

### Physics-Based
- `PCFIELDSPLIT`: Field-split for coupled systems
- `PCLSC`: Least-squares commutators (for saddle-point problems)
- `PCCOMPOSITE`: Combine multiple preconditioners

### Special Purpose
- `PCSHELL`: User-defined shell preconditioner
- `PCNONE`: No preconditioning (identity)
- `PCKSP`: Use another KSP as preconditioner
- `PCREDISTRIBUTE`: Redistribute matrix for better load balancing

## Multigrid Configuration

For geometric multigrid (PCMG):
```julia
LibPETSc.PCSetType(petsclib, pc[], LibPETSc.PCMG)
LibPETSc.PCMGSetLevels(petsclib, pc[], nlevels, C_NULL)
# Set up grid hierarchy, smoothers, coarse solver
```

For algebraic multigrid (PCGAMG):
```julia
LibPETSc.PCSetType(petsclib, pc[], LibPETSc.PCGAMG)
LibPETSc.PCGAMGSetType(petsclib, pc[], LibPETSc.PCGAMGAGG)  # Aggregation
LibPETSc.PCGAMGSetNSmooths(petsclib, pc[], 1)
LibPETSc.PCGAMGSetThreshold(petsclib, pc[], [0.0], 1)
```

## Field Split for Coupled Systems

For systems with multiple fields (e.g., velocity-pressure):
```julia
LibPETSc.PCSetType(petsclib, pc[], LibPETSc.PCFIELDSPLIT)
LibPETSc.PCFieldSplitSetType(petsclib, pc[], LibPETSc.PC_COMPOSITE_SCHUR)

# Define fields using index sets
# LibPETSc.PCFieldSplitSetIS(petsclib, pc[], "velocity", velocity_is)
# LibPETSc.PCFieldSplitSetIS(petsclib, pc[], "pressure", pressure_is)
```

## External Solver Packages

PETSc can use external direct solver packages:
- **MUMPS**: Parallel direct solver
- **SuperLU**: Sparse direct solver
- **UMFPACK**: Unsymmetric multifrontal solver
- **Pardiso**: Intel MKL parallel direct solver
- **STRUMPACK**: Structured sparse solver

Set via `PCFactorSetMatSolverType` after choosing `PCLU` or `PCCHOLESKY`.

## Performance Considerations

- **Jacobi/Block Jacobi**: Fast to apply, limited effectiveness
- **ILU/ICC**: Good for moderately difficult problems, configure fill with `PCFactorSetLevels`
- **AMG**: Excellent for elliptic PDEs, automatic coarse grid construction
- **Direct solvers**: Robust but memory-intensive for large 3D problems
- **Field split**: Essential for coupled multi-physics problems

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/PC_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/petscsection_lowlevel.md
================================================
# PetscSection - Low-level Interface

The PetscSection component provides a flexible mechanism for describing the layout of degrees of freedom (DOFs) in a discretization, particularly for finite element methods and other multi-field problems where different points have different numbers of DOFs.

## Overview

PetscSection is used to:
- **Define DOF layouts**: Specify how many DOFs exist at each point (vertex, edge, face, cell)
- **Multi-field problems**: Handle systems with multiple variables per point
- **Local-to-global mapping**: Convert between local and global numbering
- **FEM discretizations**: Manage DOF distributions for finite elements
- **DM integration**: Work with DM objects for mesh-based computations

A section maps from points (mesh entities like vertices, cells) to DOF ranges, answering:
- How many DOFs are at point `p`?
- What is the offset of the DOFs for point `p`?
- Which field do specific DOFs belong to?

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)
PetscInt = petsclib.PetscInt

# Create a section
section = Ref{LibPETSc.PetscSection}()
LibPETSc.PetscSectionCreate(petsclib, LibPETSc.PETSC_COMM_SELF, section)

# Set chart: range of valid point indices [pStart, pEnd)
LibPETSc.PetscSectionSetChart(petsclib, section[], 0, 10)

# Set DOF count for each point
for p in 0:9
    num_dofs = (p < 4) ? 1 : 2  # Different DOFs per point
    LibPETSc.PetscSectionSetDof(petsclib, section[], p, num_dofs)
end

# Setup: compute offsets
LibPETSc.PetscSectionSetUp(petsclib, section[])

# Query the section
dof = LibPETSc.PetscSectionGetDof(petsclib, section[], 5)
println("DOF count for point 5: ", dof)

offset = LibPETSc.PetscSectionGetOffset(petsclib, section[], 5)
println("Offset for point 5: ", offset)

# Get total storage size
storage_size = LibPETSc.PetscSectionGetStorageSize(petsclib, section[])
println("Total storage size: ", storage_size)

# Cleanup
LibPETSc.PetscSectionDestroy(petsclib, section)  # pass the Ref directly

PETSc.finalize(petsclib)
MPI.Finalize()
```

## Multi-Field Sections

For problems with multiple fields (e.g., velocity + pressure):

```julia
# Create section with 2 fields
section = Ref{LibPETSc.PetscSection}()
LibPETSc.PetscSectionCreate(petsclib, LibPETSc.PETSC_COMM_SELF, section)
@assert section[] != C_NULL

LibPETSc.PetscSectionSetNumFields(petsclib, section[], 2)

# Set field names
LibPETSc.PetscSectionSetFieldName(petsclib, section[], 0, "velocity")
LibPETSc.PetscSectionSetFieldName(petsclib, section[], 1, "pressure")

# Set chart
LibPETSc.PetscSectionSetChart(petsclib, section[], 0, 10)

# Set field components: velocity has 3 components (vx, vy, vz), pressure has 1
LibPETSc.PetscSectionSetFieldComponents(petsclib, section[], 0, 3)
LibPETSc.PetscSectionSetFieldComponents(petsclib, section[], 1, 1)

# Set DOFs per field per point
for p in 0:9
    LibPETSc.PetscSectionSetFieldDof(petsclib, section[], p, 0, 3)  # 3 velocity DOFs
    LibPETSc.PetscSectionSetFieldDof(petsclib, section[], p, 1, 1)  # 1 pressure DOF
    LibPETSc.PetscSectionSetDof(petsclib, section[], p, 4)          # Total: 4 DOFs
end

LibPETSc.PetscSectionSetUp(petsclib, section[])
```

## Constrained DOFs

Mark certain DOFs as constrained (e.g., for boundary conditions):

```julia
# Set chart and DOFs...
# Note: Constraint-related functions may have wrapper issues

# Set constraint DOF count
# LibPETSc.PetscSectionSetConstraintDof(petsclib, section[], point, num_constrained)

# Specify which DOFs are constrained
# constrained_indices = PetscInt[0, 2]
# LibPETSc.PetscSectionSetConstraintIndices(petsclib, section[], point, constrained_indices)

# Query constrained storage size
# LibPETSc.PetscSectionGetConstrainedStorageSize(petsclib, section[])
```

## Integration with DM

Sections are commonly used with DM objects:

```julia
# Get section from DM
dm_section = Ref{LibPETSc.PetscSection}()
# LibPETSc.DMGetSection(petsclib, dm, dm_section)

# Set section on DM
# LibPETSc.DMSetSection(petsclib, dm, section[])

# Get local section (ghosted)
# local_section = Ref{LibPETSc.PetscSection}()
# LibPETSc.DMGetLocalSection(petsclib, dm, local_section)
```

## Common Workflows

### 1. FEM Discretization

```julia
# Create section for Q1 finite elements on structured grid
# - Vertices: 1 DOF each
# - Cells: 0 DOFs

section = Ref{LibPETSc.PetscSection}()
LibPETSc.PetscSectionCreate(petsclib, comm, section)
@assert section[] != C_NULL

LibPETSc.PetscSectionSetChart(petsclib, section[], vStart, cEnd)

# Set DOFs (vStart to vEnd are vertices, vEnd to cEnd are cells)
for v in vStart:vEnd-1
    LibPETSc.PetscSectionSetDof(petsclib, section[], v, 1)
end

LibPETSc.PetscSectionSetUp(petsclib, section[])
```

### 2. Point Closure

Get all DOFs in the closure of a point (point + its boundary):

```julia
# Get closure DOFs for a cell
closure_size = Ref{PetscInt}()
closure = Ref{Ptr{PetscInt}}()
# LibPETSc.DMPlexGetTransitiveClosure(petsclib, dm, cell, PETSC_TRUE, closure_size, closure)

# Map closure points to DOF offsets using section
# ... use section to get DOF offsets for each point in closure ...

# LibPETSc.DMPlexRestoreTransitiveClosure(petsclib, dm, cell, PETSC_TRUE, closure_size, closure)
```

## Querying Section Properties

```julia
# Get total number of fields
num_fields = LibPETSc.PetscSectionGetNumFields(petsclib, section[])

# Get field components
components = LibPETSc.PetscSectionGetFieldComponents(petsclib, section[], field)

# Get field name (returns String directly)
name = LibPETSc.PetscSectionGetFieldName(petsclib, section[], field)

# Get maximum DOF count across all points
max_dof = LibPETSc.PetscSectionGetMaxDof(petsclib, section[])
```

## Cloning and Permutation

```julia
# Clone a section (wrapper may have issues, use with caution)
new_section = Ref{LibPETSc.PetscSection}()
# LibPETSc.PetscSectionClone(petsclib, section[], new_section)

# Note: Clone/Permute functions may require direct ccall if wrapper signatures are incorrect
```

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/PetscSection_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/petscsf_lowlevel.md
================================================
# PetscSF (Star Forest) - Low-level Interface

The PetscSF (Star Forest) component provides efficient parallel communication patterns for distributed data structures. A star forest is a specialized graph structure optimized for scatter/gather operations in parallel computing.

## Overview

PetscSF enables:
- **Point-to-point communication**: Efficient MPI communication patterns
- **Scatter/gather operations**: Move data between processors
- **Halo exchange**: Update ghost/boundary values
- **Reduction operations**: Parallel sums, max, min across shared data
- **Irregular communication**: Handle non-uniform data distributions

A star forest consists of:
- **Roots**: Data owned locally
- **Leaves**: Data needed from remote processes (or local)
- **Communication pattern**: Which leaves come from which roots

PetscSF is the underlying communication layer for DM ghost point updates and other parallel operations.

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)
PetscInt = petsclib.PetscInt

# Create a star forest
sf = LibPETSc.PetscSFCreate(petsclib, MPI.COMM_WORLD)

# Define communication pattern
# nleaves: number of leaves (data items we need)
# ilocal: local indices for leaves (can be C_NULL if identity)
# iremote: (rank, index) pairs specifying which process/index to get from

nleaves = 5
# number of roots owned locally (for this simple example set equal to nleaves)
nroots = 5
ilocal = [0, 1, 2, 3, 4]  # Local indices where data will be stored
iremote = [
    LibPETSc.PetscSFNode(0, 0),
    LibPETSc.PetscSFNode(0, 1),
    LibPETSc.PetscSFNode(0, 2),
    LibPETSc.PetscSFNode(0, 3),
    LibPETSc.PetscSFNode(0, 4),
]

LibPETSc.PetscSFSetGraph(petsclib, sf, nroots, nleaves, ilocal, LibPETSc.PETSC_COPY_VALUES,
                         iremote, LibPETSc.PETSC_COPY_VALUES)

# Setup
LibPETSc.PetscSFSetUp(petsclib, sf)

# Cleanup
LibPETSc.PetscSFDestroy(petsclib, sf)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Communication Operations

### Broadcast (Scatter)

Send data from roots to leaves:

```julia
# Root data: data we own
root_data = Float64[1.0, 2.0, 3.0, 4.0, 5.0]

# Leaf data: buffer to receive data
leaf_data = zeros(Float64, nleaves)

# Broadcast: send root data to leaves
LibPETSc.PetscSFBcastBegin(petsclib, sf, LibPETSc.MPI_DOUBLE, root_data, leaf_data,
                           LibPETSc.MPI_REPLACE)
LibPETSc.PetscSFBcastEnd(petsclib, sf, LibPETSc.MPI_DOUBLE, root_data, leaf_data,
                         LibPETSc.MPI_REPLACE)
```

### Reduce

Accumulate data from leaves back to roots:

```julia
# Leaf contributions
leaf_data = Float64[0.1, 0.2, 0.3, 0.4, 0.5]

# Root accumulator
root_data = zeros(Float64, nroots)

# Reduce: accumulate leaf data to roots
LibPETSc.PetscSFReduceBegin(petsclib, sf, LibPETSc.MPI_DOUBLE, leaf_data, root_data,
                            LibPETSc.MPI_SUM)
LibPETSc.PetscSFReduceEnd(petsclib, sf, LibPETSc.MPI_DOUBLE, leaf_data, root_data,
                          LibPETSc.MPI_SUM)
```

### Fetch and Operations

Atomic operations for concurrent updates:

```julia
# Fetch data and apply operation
LibPETSc.PetscSFFetchAndOpBegin(petsclib, sf, LibPETSc.MPI_DOUBLE, root_data,
                                leaf_data, leaf_updates, LibPETSc.MPI_SUM)
LibPETSc.PetscSFFetchAndOpEnd(petsclib, sf, LibPETSc.MPI_DOUBLE, root_data,
                              leaf_data, leaf_updates, LibPETSc.MPI_SUM)
```

## MPI Operations

Supported MPI operations for reduce:
- `MPI_SUM`: Sum values
- `MPI_MAX`: Maximum value
- `MPI_MIN`: Minimum value
- `MPI_REPLACE`: Replace (last write wins)
- `MPI_PROD`: Product

## Star Forest Types

Available through `PetscSFSetType`:
- **PETSCSFBASIC**: Basic implementation
- **PETSCSFNEIGHBOR**: MPI neighborhood collectives (efficient for structured patterns)
- **PETSCSFALLGATHERV**: All-gather based
- **PETSCSFALLGATHER**: All-gather for small data
- **PETSCSFGATHERV**: Gather-based
- **PETSCSFGATHER**: Simple gather
- **PETSCSFALLTOALL**: All-to-all based

## Graph Queries

```julia
# Get number of roots (locally owned data)
nroots = Ref{PetscInt}()
LibPETSc.PetscSFGetGraph(petsclib, sf, nroots, C_NULL, C_NULL, C_NULL)

# Get number of leaves
nleaves = Ref{PetscInt}()
LibPETSc.PetscSFGetGraph(petsclib, sf, C_NULL, nleaves, C_NULL, C_NULL)

# Get full graph
ilocal_ptr = Ref{Ptr{PetscInt}}()
iremote_ptr = Ref{Ptr{LibPETSc.PetscSFNode}}()
LibPETSc.PetscSFGetGraph(petsclib, sf, nroots, nleaves, ilocal_ptr, iremote_ptr)
```

## Multi-Root Support

Handle communication with multiple root data per point:

```julia
# Create multi-SF for multiple DOFs per point
nroots_mult = nroots * num_components
multi_sf = LibPETSc.PetscSFCreateEmbeddedRootSF(petsclib, sf, nroots_mult, iroot_indices)
```

## Common Use Cases

### 1. Ghost Point Updates (Halo Exchange)

```julia
# After modifying owned data, update ghost points
# 1. Pack local data
# 2. Broadcast to leaves (ghost points)
LibPETSc.PetscSFBcastBegin(petsclib, sf, datatype, local_data, ghost_data, op)
LibPETSc.PetscSFBcastEnd(petsclib, sf, datatype, local_data, ghost_data, op)
```

### 2. Parallel Assembly

```julia
# After local assembly, accumulate contributions from other processes
# 1. Each process computes local contributions
# 2. Reduce to accumulate at owners
LibPETSc.PetscSFReduceBegin(petsclib, sf, datatype, local_contrib, global_data, MPI_SUM)
LibPETSc.PetscSFReduceEnd(petsclib, sf, datatype, local_contrib, global_data, MPI_SUM)
```

### 3. DM Point Communication

```julia
# Get natural SF for a DM (describes point distribution)
dm_sf = Ref{LibPETSc.PetscSF}()
# LibPETSc.DMGetPointSF(petsclib, dm, dm_sf)

# Use to communicate point-based data
```

## Performance Considerations

- **Choose appropriate type**: `PETSCSFNEIGHBOR` is often best for structured grids
- **Reuse SF objects**: Creating the communication pattern is expensive
- **Batch communications**: Combine multiple small messages when possible
- **Alignment**: Use properly aligned data types for better performance

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/PetscSF_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/petscviewer_lowlevel.md
================================================
# PetscViewer - Low-level Interface

The PetscViewer component provides flexible I/O capabilities for visualizing and saving PETSc objects, including vectors, matrices, and other data structures. Viewers support multiple output formats for analysis, debugging, and post-processing.

## Overview

PETSc viewers enable:
- **Text output**: ASCII formatted data for debugging
- **Binary I/O**: Efficient storage and checkpointing
- **Visualization**: Integration with visualization tools (VTK, HDF5, MATLAB)
- **Monitoring**: Runtime inspection of solver progress
- **Logging**: Recording solver statistics and performance data

Available viewer types:
- **PETSCVIEWERASCII**: Human-readable text output
- **PETSCVIEWERBINARY**: Platform-independent binary format
- **PETSCVIEWERVTK**: VTK format for ParaView, VisIt
- **PETSCVIEWERHDF5**: HDF5 hierarchical data format
- **PETSCVIEWERDRAW**: X-window graphics (2D plots, contours)
- **PETSCVIEWERSOCKET**: Network streaming to MATLAB, Python
- **PETSCVIEWERMATLAB**: MATLAB-compatible output

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a viewer for ASCII output to stdout
viewer = LibPETSc.PetscViewerCreate(petsclib, LibPETSc.PETSC_COMM_SELF)
LibPETSc.PetscViewerSetType(petsclib, viewer, "ascii")  # String convenience wrapper
LibPETSc.PetscViewerFileSetMode(petsclib, viewer, LibPETSc.FILE_MODE_WRITE)

# View a vector
# LibPETSc.VecView(petsclib, vec, viewer)

# View a matrix  
# LibPETSc.MatView(petsclib, mat, viewer)

# Cleanup - wrap in Ref since PetscViewerDestroy expects Ptr{PetscViewer}
viewer_ref = Ref(viewer)
LibPETSc.PetscViewerDestroy(petsclib, viewer_ref)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Convenience Functions

For commonly used viewers, PETSc.jl provides convenience functions:

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Get stdout viewer (single process)
viewer_stdout_self = LibPETSc.PETSC_VIEWER_STDOUT_SELF(petsclib)

# Get stdout viewer (all processes)
viewer_stdout_world = LibPETSc.PETSC_VIEWER_STDOUT_WORLD(petsclib)

# Get stderr viewer (single process)
viewer_stderr_self = LibPETSc.PETSC_VIEWER_STDERR_SELF(petsclib)

# Get stderr viewer (all processes)
viewer_stderr_world = LibPETSc.PETSC_VIEWER_STDERR_WORLD(petsclib)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()

# Use them to view objects
# LibPETSc.VecView(petsclib, vec, viewer_stdout_self)
# LibPETSc.MatView(petsclib, mat, viewer_stderr_world)
```

## Output to Files

### ASCII File Output

```julia
# Create ASCII file viewer
viewer = Ref{LibPETSc.PetscViewer}()
LibPETSc.PetscViewerASCIIOpen(petsclib, LibPETSc.PETSC_COMM_SELF, "output.txt", viewer)

# Set format (optional)
LibPETSc.PetscViewerPushFormat(petsclib, viewer[], LibPETSc.PETSC_VIEWER_ASCII_MATLAB)

# View object
# LibPETSc.MatView(petsclib, mat, viewer[])

LibPETSc.PetscViewerDestroy(petsclib, viewer)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

### Binary File Output

```julia
# Create binary viewer for checkpointing
viewer = Ref{LibPETSc.PetscViewer}()
LibPETSc.PetscViewerBinaryOpen(petsclib, MPI.COMM_WORLD, "checkpoint.dat", 
                               LibPETSc.FILE_MODE_WRITE, viewer)

# Save vector
# LibPETSc.VecView(petsclib, vec, viewer[])

# Save matrix
# LibPETSc.MatView(petsclib, mat, viewer[])

LibPETSc.PetscViewerDestroy(petsclib, viewer)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

### Loading from Binary Files

```julia
# Open for reading
viewer = Ref{LibPETSc.PetscViewer}()
LibPETSc.PetscViewerBinaryOpen(petsclib, MPI.COMM_WORLD, "checkpoint.dat",
                               LibPETSc.FILE_MODE_READ, viewer)

# Load vector
vec = LibPETSc.VecCreate(petsclib, MPI.COMM_WORLD)
LibPETSc.VecLoad(petsclib, vec, viewer[])

LibPETSc.PetscViewerDestroy(petsclib, viewer)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Visualization Formats

### VTK Output

```julia
# Create VTK viewer for ParaView/VisIt
viewer = Ref{LibPETSc.PetscViewer}()
LibPETSc.PetscViewerVTKOpen(petsclib, MPI.COMM_WORLD, "solution.vtu",
                            LibPETSc.FILE_MODE_WRITE, viewer)

# View DM-based solution
# LibPETSc.DMView(petsclib, dm, viewer[])
# LibPETSc.VecView(petsclib, solution, viewer[])

LibPETSc.PetscViewerDestroy(petsclib, viewer)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

### HDF5 Output

```julia
# Create HDF5 viewer for hierarchical data
viewer = Ref{LibPETSc.PetscViewer}()
LibPETSc.PetscViewerHDF5Open(petsclib, MPI.COMM_WORLD, "data.h5",
                             LibPETSc.FILE_MODE_WRITE, viewer)

# Organize data in groups
LibPETSc.PetscViewerHDF5PushGroup(petsclib, viewer[], "/timestep_001")
# LibPETSc.VecView(petsclib, vec, viewer[])
LibPETSc.PetscViewerHDF5PopGroup(petsclib, viewer[])

LibPETSc.PetscViewerDestroy(petsclib, viewer)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Standard Viewers

PETSc provides predefined viewers:

```julia
# Standard output
LibPETSc.PETSC_VIEWER_STDOUT_SELF(petsclib)
LibPETSc.PETSC_VIEWER_STDOUT_WORLD(petsclib)

# Standard error
LibPETSc.PETSC_VIEWER_STDERR_SELF(petsclib)
LibPETSc.PETSC_VIEWER_STDERR_WORLD(petsclib)

# Example: view to stdout
# LibPETSc.VecView(petsclib, vec, LibPETSc.PETSC_VIEWER_STDOUT_WORLD(petsclib))
```

## Format Options

Control output detail with `PetscViewerPushFormat`:

- **PETSC_VIEWER_DEFAULT**: Standard format
- **PETSC_VIEWER_ASCII_MATLAB**: MATLAB-compatible format
- **PETSC_VIEWER_ASCII_DENSE**: Dense matrix format
- **PETSC_VIEWER_ASCII_INFO**: Summary information only
- **PETSC_VIEWER_ASCII_INFO_DETAIL**: Detailed information

## Draw Viewer (Graphics)

For interactive 2D visualization:

```julia
# Create draw viewer (X-window)
viewer = Ref{LibPETSc.PetscViewer}()
LibPETSc.PetscViewerDrawOpen(petsclib, LibPETSc.PETSC_COMM_SELF, C_NULL, "Plot", 
                              0, 0, 600, 600, viewer)

# View vector as bar chart
# LibPETSc.VecView(petsclib, vec, viewer[])

# View matrix structure
# LibPETSc.MatView(petsclib, mat, viewer[])

LibPETSc.PetscViewerDestroy(petsclib, viewer)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Socket Viewer (MATLAB/Python)

Stream data to external tools:

```julia
# Create socket viewer
viewer = Ref{LibPETSc.PetscViewer}()
LibPETSc.PetscViewerSocketOpen(petsclib, MPI.COMM_WORLD, "localhost", 5000, viewer)

# Send data
# LibPETSc.VecView(petsclib, vec, viewer[])

LibPETSc.PetscViewerDestroy(petsclib, viewer)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Monitoring Convergence

Viewers are used with KSP/SNES monitors:

```julia
# Monitor KSP residuals (automatic viewer to stdout)
# LibPETSc.KSPMonitorSet(petsclib, ksp, LibPETSc.KSPMonitorDefault, 
#                        LibPETSc.PETSC_VIEWER_STDOUT_SELF(petsclib), C_NULL)
```

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/PetscViewer_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/snes.md
================================================
# SNES

The SNES (Scalable Nonlinear Equations Solvers) module provides methods for solving nonlinear systems of the form `F(x) = 0`. It builds on KSP for the linear solves within Newton-like methods.

## Overview

SNES provides:
- **Newton methods**: Newton line search, Newton trust region
- **Quasi-Newton**: L-BFGS, Broyden
- **Nonlinear Richardson**: With various line search strategies
- **FAS multigrid**: Full Approximation Scheme for nonlinear problems
- **Composite solvers**: Combine multiple nonlinear solvers

## Creating a SNES Solver

```julia
# Basic creation
snes = SNES(petsclib, MPI.COMM_WORLD)

# With options
snes = SNES(petsclib, MPI.COMM_WORLD;
    snes_type = "newtonls",
    snes_rtol = 1e-8,
    snes_max_it = 50
)
```

## Setting the Nonlinear Function

Define the residual function `F(x)`:

```julia
function residual!(fx, snes, x)
    # Compute F(x) and store in fx
    fx[1] = x[1]^2 + x[2] - 1
    fx[2] = x[1] + x[2]^2 - 1
    return 0
end

setfunction!(snes, residual!, f_vec)
```

## Setting the Jacobian

Define the Jacobian `J = dF/dx`:

```julia
function jacobian!(J, snes, x)
    # Fill Jacobian matrix
    J[1, 1] = 2*x[1]
    J[1, 2] = 1.0
    J[2, 1] = 1.0
    J[2, 2] = 2*x[2]
    assemble!(J)
    return 0
end

setjacobian!(snes, jacobian!, J, J)  # (J, P) where P is preconditioner matrix
```

## Using a DM

For PDE problems, associate the SNES with a DM:

```julia
setDM!(snes, dm)

# Get the DM from SNES
dm = getDM(snes)
```

## Solving

```julia
# Solve with initial guess x
solve!(x, snes)

# Get solution vector
sol = get_solution(snes)
```

## Common Solver Options

### Nonlinear Solver Types (`snes_type`)
- `newtonls` - Newton with line search (default)
- `newtontr` - Newton with trust region
- `nrichardson` - Nonlinear Richardson
- `qn` - Quasi-Newton (L-BFGS)
- `fas` - Full Approximation Scheme multigrid

### Convergence Options
- `snes_rtol` - Relative tolerance
- `snes_atol` - Absolute tolerance
- `snes_stol` - Step tolerance
- `snes_max_it` - Maximum iterations
- `snes_monitor` - Print residual each iteration

### Line Search Options
- `snes_linesearch_type` - `bt` (backtracking), `basic`, `l2`, `cp`

## Example: Full Setup

```julia
petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

snes = SNES(petsclib, MPI.COMM_WORLD;
    snes_monitor = true,
    ksp_type = "gmres",
    pc_type = "ilu"
)

setfunction!(snes, residual!, f)
setjacobian!(snes, jacobian!, J, J)
setfromoptions!(snes)

solve!(x, snes)
```

## Functions

```@autodocs
Modules = [PETSc]
Pages   = ["snes.jl"]
```


================================================
FILE: docs/src/man/snes_lowlevel.md
================================================
# SNES - Low-level Interface

This page documents the low-level, automatically wrapped PETSc SNES (Scalable Nonlinear Equations Solvers) functions available through `LibPETSc`. These functions provide direct access to the PETSc C API.

For the high-level Julia interface, see [SNES](@ref).

## Overview

The SNES interface includes:
- **Core SNES operations**: Nonlinear solver creation, configuration, and solution (~283 functions in `SNES_wrappers.jl`)
- **SNESLineSearch**: Line search methods for nonlinear solvers (~50 functions in `SNESLineSearch_wrappers.jl`)

## Usage

All low-level SNES functions require a `petsclib` parameter as the first argument:

```julia
using PETSc

# Get the library instance
petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

# Get PETSc types
PetscInt = petsclib.PetscInt

# Create SNES solver
snes = LibPETSc.SNESCreate(petsclib, LibPETSc.PETSC_COMM_SELF)

# Set function and Jacobian (callbacks would need to be defined)
# LibPETSc.SNESSetFunction(petsclib, snes, r, compute_function, ctx)
# LibPETSc.SNESSetJacobian(petsclib, snes, J, J, compute_jacobian, ctx)

# Configure from command line
LibPETSc.SNESSetFromOptions(petsclib, snes)

# Solve
# LibPETSc.SNESSolve(petsclib, snes, C_NULL, x)

# Get convergence info
reason = LibPETSc.SNESGetConvergedReason(petsclib, snes)

iterations = LibPETSc.SNESGetIterationNumber(petsclib, snes)

println("SNES reason: $(reason), iterations: $(iterations)")

# Clean up
LibPETSc.SNESDestroy(petsclib, snes)
PETSc.finalize(petsclib)
```

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/SNES_wrappers.jl", "autowrapped/SNESLineSearch_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/tao_lowlevel.md
================================================
# Tao (Optimization) - Low-level Interface

The Tao (Toolkit for Advanced Optimization) component provides methods for solving optimization problems, including unconstrained minimization, bound-constrained optimization, and constrained optimization.

## Overview

Tao supports various optimization algorithms:
- **Unconstrained**: Conjugate gradient (CG), limited-memory BFGS (BLMVM), Nelder-Mead
- **Bound-constrained**: Bound-constrained BFGS (BNCG, BNLS, BNTL), active-set methods
- **Constrained**: Augmented Lagrangian methods (ALMM), interior point methods
- **Least-squares**: Gauss-Newton methods, Levenberg-Marquardt
- **Complementarity**: Semismooth methods for complementarity problems
- **PDE-constrained**: Methods suitable for PDE-constrained optimization

The Tao object manages the optimization process, line searches, convergence monitoring, and provides a unified interface across different optimization algorithms.

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a Tao object
tao = LibPETSc.TaoCreate(petsclib, LibPETSc.PETSC_COMM_SELF)

# Set the optimization algorithm (e.g., LMVM, BLMVM, NLS)
LibPETSc.TaoSetType(petsclib, tao, "lmvm")  # String convenience wrapper

# Set the objective function and gradient
# LibPETSc.TaoSetObjective(petsclib, tao, objective_function_ptr, C_NULL)
# LibPETSc.TaoSetGradient(petsclib, tao, C_NULL, gradient_function_ptr, C_NULL)

# For bound-constrained problems, set variable bounds
# LibPETSc.TaoSetVariableBounds(petsclib, tao, lower_bound_vec, upper_bound_vec)

# Set convergence tolerances
LibPETSc.TaoSetTolerances(petsclib, tao, 1e-8, 1e-8, 1e-8)

# Set maximum iterations
LibPETSc.TaoSetMaximumIterations(petsclib, tao, 1000)

# Set options from command line/options database
LibPETSc.TaoSetFromOptions(petsclib, tao)

# Set initial guess
# LibPETSc.TaoSetSolution(petsclib, tao, initial_vec)

# Solve the optimization problem
# LibPETSc.TaoSolve(petsclib, tao)

# Get solution information
reason = LibPETSc.TaoGetConvergedReason(petsclib, tao)
iter = LibPETSc.TaoGetIterationNumber(petsclib, tao)

println("Tao reason: $(reason), iterations: $(iter)")

# Cleanup
LibPETSc.TaoDestroy(petsclib, tao)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Common Workflow

1. **Create and configure Tao**: Use `TaoCreate`, `TaoSetType`
2. **Define objective**: `TaoSetObjective`, `TaoSetGradient`, optionally `TaoSetHessian`
3. **Set constraints** (if any): `TaoSetVariableBounds`, `TaoSetConstraints`
4. **Configure solver**: `TaoSetTolerances`, `TaoSetMaximumIterations`
5. **Set initial guess**: `TaoSetSolution`
6. **Solve**: `TaoSolve`
7. **Retrieve solution**: `TaoGetSolution`, `TaoGetConvergedReason`

## Optimization Algorithms

Available through `TaoSetType`:
- **Unconstrained**:
  - `TAOLMVM`: Limited-memory variable metric (quasi-Newton)
  - `TAOCG`: Conjugate gradient methods
  - `TAONM`: Nelder-Mead simplex method
  - `TAONLS`: Newton line search
  - `TAONTL`: Newton trust-region with line search

- **Bound-constrained**:
  - `TAOBLMVM`: Bound-constrained limited-memory variable metric
  - `TAOBNCG`: Bound-constrained conjugate gradient
  - `TAOBQNLS`: Bound-constrained quasi-Newton line search
  - `TAOBNTL`: Bound-constrained Newton trust-region
  - `TAOTRON`: Trust-region Newton method

- **Constrained**:
  - `TAOALMM`: Augmented Lagrangian multiplier method
  - `TAOIPM`: Interior point method
  - `TAOPDIPM`: Primal-dual interior point method

- **Least-squares**:
  - `TAOPOUNDERS`: POUNDERs model-based method
  - `TAOBRGN`: Bounded regularized Gauss-Newton

- **Complementarity**:
  - `TAOSSLS`: Semismooth least squares
  - `TAOASLS`: Active-set least squares

## Convergence Criteria

Tao monitors several convergence criteria:
- **Gradient tolerance**: `||∇f|| < gatol` or `||∇f||/||f|| < grtol`
- **Function tolerance**: `|f - f_prev| < fatol` or `|f - f_prev|/|f| < frtol`
- **Step tolerance**: `||x - x_prev|| < steptol`

Set using `TaoSetTolerances(tao, gatol, grtol, gttol)`.

## Hessian Options

For second-order methods:
- **Exact Hessian**: Provide via `TaoSetHessian`
- **Finite-difference approximation**: Use matrix-free approach
- **Quasi-Newton approximation**: LMVM methods build approximation automatically

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/Tao_wrappers.jl"]
Order   = [:function]
```

## Tao Add-ons

Additional Tao utilities and helper functions:

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/Tao_addons_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/ts_lowlevel.md
================================================
# TS (Time Stepping) - Low-level Interface

<a id="ch_ts"></a>

The TS (Time Stepping) component provides methods for solving time-dependent differential equations, including ordinary differential equations (ODEs), differential-algebraic equations (DAEs), and time-dependent partial differential equations (PDEs).

## Overview

TS supports various time integration methods including:
- **Explicit methods**: Forward Euler, Runge-Kutta methods (RK2, RK3, RK4, etc.)
- **Implicit methods**: Backward Euler, Crank-Nicolson, BDF methods
- **IMEX methods**: Implicit-Explicit combinations for stiff-nonstiff problems
- **Adaptive methods**: Adaptive time stepping with error control
- **Specialized methods**: Theta methods, Rosenbrock methods, SSP methods

The TS object manages the time integration loop, adaptive time stepping, event detection, and provides interfaces to various time stepping schemes.

## Basic Usage

```julia
using PETSc, MPI

# Initialize MPI and PETSc
MPI.Init()
petsclib = PETSc.getlib()
PETSc.initialize(petsclib)

# Create a TS object
ts = LibPETSc.TSCreate(petsclib, LibPETSc.PETSC_COMM_SELF)

# Set the problem type (ODE or DAE)
LibPETSc.TSSetProblemType(petsclib, ts, LibPETSc.TS_NONLINEAR)

# Set the time stepping method (e.g., BDF, RK, Theta)
LibPETSc.TSSetType(petsclib, ts, "bdf")  # String convenience wrapper

# Set time span
LibPETSc.TSSetTime(petsclib, ts, 0.0)  # Initial time
LibPETSc.TSSetMaxTime(petsclib, ts, 1.0)  # Final time
LibPETSc.TSSetExactFinalTime(petsclib, ts, LibPETSc.TS_EXACTFINALTIME_STEPOVER)

# Set initial time step
LibPETSc.TSSetTimeStep(petsclib, ts, 0.01)

# Set the right-hand side function (for ODE: du/dt = f(t,u))
# LibPETSc.TSSetRHSFunction(petsclib, ts, C_NULL, rhs_function_ptr, C_NULL)

# Set options from command line/options database
LibPETSc.TSSetFromOptions(petsclib, ts)

# Set initial condition
# LibPETSc.TSSetSolution(petsclib, ts, initial_vec)

# Solve
# LibPETSc.TSSolve(petsclib, ts, solution_vec)

# Get solution time (returns value directly)
final_time = LibPETSc.TSGetSolveTime(petsclib, ts)

# Cleanup
LibPETSc.TSDestroy(petsclib, ts)

# Finalize PETSc and MPI
PETSc.finalize(petsclib)
MPI.Finalize()
```

## Common Workflow

1. **Create and configure TS**: Use `TSCreate`, `TSSetType`, `TSSetProblemType`
2. **Set time parameters**: `TSSetTime`, `TSSetMaxTime`, `TSSetTimeStep`, `TSSetMaxSteps`
3. **Define equations**: `TSSetRHSFunction`, `TSSetIFunction`, `TSSetIJacobian`
4. **Configure adaptivity**: `TSAdaptSetType`, `TSSetTolerances`
5. **Set initial condition**: `TSSetSolution`
6. **Solve**: `TSSolve`
7. **Retrieve solution**: `TSGetSolution`, `TSGetTime`, `TSGetStepNumber`

## Time Integration Schemes

Available through `TSSetType`:
- `TSEULER`: Forward/Backward Euler
- `TSRK`: Runge-Kutta methods (various orders)
- `TSBDF`: Backward Differentiation Formulas
- `TSTHETA`: Theta method (generalizes Euler, Crank-Nicolson)
- `TSROSW`: Rosenbrock-W methods for stiff problems
- `TSARKIMEX`: Additive Runge-Kutta IMEX methods
- `TSGLEE`: Generalized Linear Evolution Equations
- `TSALPHA`: Alpha methods for second-order systems
- `TSSSP`: Strong Stability Preserving methods

## Event Detection

TS supports event detection (zero-crossing of event functions) during time integration:
- `TSSetEventHandler`: Configure events to detect
- Events can trigger actions like termination, adaptation, or post-event processing

## Adaptive Time Stepping

TS includes sophisticated adaptive time stepping:
- `TSAdaptChoose`: Select time step based on error estimates
- Various adapt types: `TSADAPTBASIC`, `TSADAPTNONE`, `TSADAPTDSP`
- Tolerance control: `TSSetTolerances` for absolute/relative error

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/TS_wrappers.jl"]
Order   = [:function]
```

## TS Add-ons

Additional TS utilities and helper functions:

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/TSaddons_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: docs/src/man/utilities.md
================================================
# Utilities

This page documents utility functions for initialization, options handling, system information, and code auditing.

## Initialization

The initialization functions manage PETSc's lifecycle, including MPI initialization and cleanup.

```@autodocs
Modules = [PETSc]
Pages   = ["init.jl"]
```

## Options

PETSc uses an options database to configure solvers and other objects at runtime. These functions provide access to the options system.

```@autodocs
Modules = [PETSc]
Pages   = ["options.jl"]
```

## System Utilities

General system-level utilities for working with PETSc objects.

```@autodocs
Modules = [PETSc]
Pages   = ["sys.jl"]
```

## Code Auditing

The audit utilities help identify potential memory leaks by tracking PETSc object creation and destruction.

```@autodocs
Modules = [PETSc]
Pages   = ["audit.jl"]
```


================================================
FILE: docs/src/man/vec.md
================================================
# Vec

PETSc vectors (`Vec`) are the fundamental building blocks for storing solution data, right-hand sides, and other distributed arrays. PETSc.jl provides a Julia-friendly interface that makes `Vec` objects behave like native Julia arrays.

## Overview

PETSc vectors support:
- **Distributed parallel storage**: Split across MPI processes
- **Sequential storage**: For serial computations
- **Ghost points**: For communication in stencil operations
- **Julia array interface**: Use familiar indexing and broadcasting syntax

## Creating Vectors

### Sequential Vectors

```julia
# Create a sequential vector of length n
v = VecSeq(petsclib, n)

# Wrap an existing Julia array (no copy)
julia_array = zeros(100)
v = VecSeq(petsclib, julia_array)
```

### From DM Objects

```julia
# Create global and local vectors from a DM
global_vec = DMGlobalVec(dm)
local_vec = DMLocalVec(dm)
```

## Julia Array Interface

PETSc vectors implement the Julia array interface:

```julia
v[1] = 1.0           # Set single element
v[1:10] .= 2.0       # Set range
x = v[5]             # Get element
length(v)            # Get length
size(v)              # Get size tuple
```

## Assembly

After setting values, vectors must be assembled:

```julia
v[1] = 1.0
v[2] = 2.0
assemble!(v)  # Finalize vector assembly
```

## Ghost Point Updates

For vectors with ghost points (from DMDA/DMStag):

```julia
# Update ghost values from neighboring processes
ghostupdate!(vec, INSERT_VALUES, SCATTER_FORWARD)

# Or use begin/end for non-blocking:
ghostupdatebegin!(vec, INSERT_VALUES, SCATTER_FORWARD)
# ... do other work ...
ghostupdateend!(vec, INSERT_VALUES, SCATTER_FORWARD)
```

## Functions

```@autodocs
Modules = [PETSc]
Pages   = ["vec.jl"]
```


================================================
FILE: docs/src/man/vec_lowlevel.md
================================================
# Vec - Low-level Interface

This page documents the low-level, automatically wrapped PETSc Vec functions available through `LibPETSc`. These functions provide direct access to the PETSc C API.

For the high-level Julia interface, see [Vec](@ref).

## Overview

The Vec interface includes:
- **Core Vec operations**: Creation, manipulation, and mathematical operations on vectors (~186 functions in `Vec_wrappers.jl`)
- **Vec utilities**: Scatter operations, FFTW integration, and advanced vector operations (~76 functions in `Vecs_wrappers.jl`)
- **VecTagger**: Tools for tagging/marking vector elements based on criteria (~31 functions in `VecTagger_wrappers.jl`)

## Usage

All low-level Vec functions require a `petsclib` parameter as the first argument:

```julia
using PETSc

# Get the library instance
petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)

# Get PETSc types
PetscInt = petsclib.PetscInt

# Create a vector
vec = LibPETSc.VecCreate(petsclib, LibPETSc.PETSC_COMM_SELF)
LibPETSc.VecSetSizes(petsclib, vec, PetscInt(10), PetscInt(10))
LibPETSc.VecSetType(petsclib, vec, "seq")  # Set vector type
LibPETSc.VecSetFromOptions(petsclib, vec)

# Set values
LibPETSc.VecSet(petsclib, vec, 1.0)

# Query the vector
size = LibPETSc.VecGetSize(petsclib, vec)
println("Vector size: $size")

# Get values (0-based indexing!)
idx = PetscInt(0)
val = LibPETSc.VecGetValues(petsclib, vec, PetscInt(1), [idx])
println("Value at index 0: $(val[1])")

# Clean up
LibPETSc.VecDestroy(petsclib, vec)
PETSc.finalize(petsclib)
```

## Function Reference

```@autodocs
Modules = [PETSc.LibPETSc]
Pages   = ["autowrapped/Vec_wrappers.jl", "autowrapped/Vecs_wrappers.jl", "autowrapped/VecTagger_wrappers.jl"]
Order   = [:function]
```


================================================
FILE: examples/Liouville_Bratu_Gelfand.jl
================================================
# INCLUDE IN MPI TEST
#=
In this example we solve the [Liouville–Bratu–Gelfand
equation](https://en.wikipedia.org/wiki/Liouville%E2%80%93Bratu%E2%80%93Gelfand_equation):
```math
∇² ψ + λ exp(ψ)
```
with zero Dirichlet boundary conditions.

To solve the problem we use the standard central, finite difference
approximation of the Laplacian in `d`-dimensions

This example is motivated by the following PETSc examples:
- [`snes/ex5`](https://petsc.org/release/src/snes/tutorials/ex5.c.html)
- [`snes/ex14`](https://petsc.org/release/src/snes/tutorials/ex14.c.html)
=#

using MPI
using PETSc
using OffsetArrays: OffsetArray
using LinearAlgebra: norm

opts = if !isinteractive()
    PETSc.parse_options(ARGS)
else
    (ksp_monitor = true, ksp_view = true)
end

# Set our MPI communicator
comm = MPI.COMM_WORLD

# Set our PETSc Scalar Type
PetscScalar = Float64

# get the PETSc lib with our chosen `PetscScalar` type
petsclib = PETSc.getlib(; PetscScalar = PetscScalar)

# Initialize PETSc
PETSc.initialize(petsclib)

# dimensionality of the problem
dim = haskey(opts, :dim) ? opts.dim : 3

# Set the total number of grid points in each direction
Nq = ntuple(_ -> 10, dim)

# Set the boundary conditions on each side
bcs = ntuple(_ -> PETSc.DM_BOUNDARY_NONE, dim)

# Set parameter
λ = PetscScalar(6)

# Create the PETSC dmda object
da = PETSc.DMDA(
    petsclib,
    comm,
    bcs,                     # boundary conditions
    Nq,                      # Global grid size
    1,                       # Number of DOF per node
    1,                       # Stencil width
    PETSc.DMDA_STENCIL_STAR; # Stencil type
    opts...,
)

# Create the PETSC snes object
snes = PETSc.SNES(petsclib, comm; opts...)

# add the da to the snes
PETSc.setDM!(snes, da)

# Set up the initial guess
x = PETSc.DMGlobalVec(da)
xl = PETSc.DMLocalVec(da)
PETSc.withlocalarray!(xl; read = false) do l_x
    corners = PETSc.getcorners(da)

    # Get the global grid dimensions
    Nq = PETSc.getinfo(da).global_size

    # Figure out the interior points 
    int_min = min(CartesianIndex(corners.size), CartesianIndex(2, 2, 2))
    int_max = max(CartesianIndex(corners.size .- 1), CartesianIndex(1, 1, 1))
    interior = (int_min):(int_max)

    # Allows us to adress the local array with global indexing
    ox = @view PETSc.reshapelocalarray(l_x, da)[1, :, :, :]

    # Set up the global coordinates in each direction
    # -1 to 1 when Nq > 1 and 0 otherwise
    coords = map(
        Nq ->
            Nq == 1 ? range(PetscScalar(0), stop = 0, length = Nq) :
            range(-PetscScalar(1), stop = 1, length = Nq),
        Nq,
    )

    scaling = λ / (λ + 1)

    # Loop over all the points on the processor and set the initial condition to
    # be a hat function
    for i in ((corners.lower):(corners.upper))
        ox[i] =
            scaling * sqrt(
                min(
                    (1 - abs(coords[1][i[1]])),
                    (1 - abs(coords[2][i[2]])),
                    (1 - abs(coords[3][i[3]])),
                ),
            )
    end
end
PETSc.dm_local_to_global!(xl, x, da, PETSc.INSERT_VALUES)

# Set up the nonlinear function
r = similar(x)
PETSc.setfunction!(snes, r) do g_fx, snes, g_x
    # Get the DMDA associated with the snes
    da = PETSc.getDM(snes)

    # Get a local vector and transfer the data from the global vector into it
    l_x = PETSc.DMLocalVec(da)
    PETSc.dm_global_to_local!(g_x, l_x, da, PETSc.INSERT_VALUES)

    ghostcorners = PETSc.getghostcorners(da)
    corners = PETSc.getcorners(da)

    # Global grid size
    Nq = PETSc.getinfo(da).global_size

    # grid spacing in each dimension
    Δx, Δy, Δz = PetscScalar(1) ./ Nq

    # Get local arrays
    PETSc.withlocalarray!(
        (g_fx, l_x);
        read = (false, true),
        write = (true, false),
    ) do fx, x

        # reshape the array and allow for global indexing
        x = @view PETSc.reshapelocalarray(x, da)[1, :, :, :]
        fx = @view PETSc.reshapelocalarray(fx, da)[1, :, :, :]

        # Store a tuple of stencils in each direction
        stencils = (
            (
                CartesianIndex(-1, 0, 0),
                CartesianIndex(0, 0, 0),
                CartesianIndex(1, 0, 0),
            ),
            (
                CartesianIndex(0, -1, 0),
                CartesianIndex(0, 0, 0),
                CartesianIndex(0, 1, 0),
            ),
            (
                CartesianIndex(0, 0, -1),
                CartesianIndex(0, 0, 0),
                CartesianIndex(0, 0, 1),
            ),
        )
        # Weights for each direction
        weights = (Δy * Δz / Δx, Δx * Δz / Δy, Δx * Δy / Δz)

        # loop over indices and set the function value
        for ind in ((corners.lower):(corners.upper))
            # If on the boundary just set equal to the incoming data
            # otherwise apply the finite difference operator
            if any(ntuple(j -> ind[j] == 1 || ind[j] == Nq[j], dim))
                fx[ind] = x[ind]
            else
                # Apply the source
                u = -Δx * Δy * Δz * λ * exp(x[ind])

                # Apply the finite diffference stencil
                for (s, w) in zip(stencils[1:dim], weights[1:dim])
                    u +=
                        w * (-x[ind + s[1]] + 2 * x[ind + s[2]] - x[ind + s[3]])
                end
                fx[ind] = u
            end
        end
    end

    # Clean up the local vector
    PETSc.destroy(l_x)
    return 0
end

J = LibPETSc.DMCreateMatrix(petsclib, da)
PETSc.setjacobian!(snes, J) do J, snes, g_x
    # Get the DMDA associated with the snes
    da = PETSc.getDM(snes)

    # Get the corners of the points we own
    corners = PETSc.getcorners(da)

    # Global grid size
    Nq = PETSc.getinfo(da).global_size

    # grid spacing in each dimension
    Δx, Δy, Δz = PetscScalar(1) ./ Nq

    # Store a tuple of stencils in each direction
    stencils = (
        (
            CartesianIndex(-1, 0, 0),
            CartesianIndex(0, 0, 0),
            CartesianIndex(1, 0, 0),
        ),
        (
            CartesianIndex(0, -1, 0),
            CartesianIndex(0, 0, 0),
            CartesianIndex(0, 1, 0),
        ),
        (
            CartesianIndex(0, 0, -1),
            CartesianIndex(0, 0, 0),
            CartesianIndex(0, 0, 1),
        ),
    )
    # Weights for each direction
    weights = (Δy * Δz / Δx, Δx * Δz / Δy, Δx * Δy / Δz)

    # Get a local array of the solution vector
    PETSc.withlocalarray!(g_x; write = false) do l_x
        # reshape so we can use multi-D indexing
        x = @view PETSc.reshapelocalarray(l_x, da)[1, :, :, :]

        # loop over indices and set the function value
        for ind in ((corners.lower):(corners.upper))
            # If on the boundary just set equal to the incoming data
            # otherwise apply the finite difference operator
            if any(ntuple(j -> ind[j] == 1 || ind[j] == Nq[j], dim))
                J[ind, ind] = 1
            else
                # We accumulate the diagonal and add it at the end
                Jii = -Δx * Δy * Δz * λ * exp(x[ind]) # Apply the source

                # Apply the finite diffference stencil
                for (s, w) in zip(stencils[1:dim], weights[1:dim])
                    Jii += w * 2
                    J[ind, ind + s[1]] = -w
                    J[ind, ind + s[3]] = -w
                end
                J[ind, ind] = Jii
            end
        end
    end

    # Assemble the Jacobian matrix
    PETSc.assemble!(J)
    return 0
end

if MPI.Comm_rank(comm) == 0
    println(@elapsed(PETSc.solve!(x, snes)))
else
    PETSc.solve!(x, snes)
end
g = similar(x)
snes.f!(g, snes, x)
nm = norm(g)
if MPI.Comm_rank(comm) == 0
    @show nm
end

# Do some clean up
PETSc.destroy(J)
PETSc.destroy(x)
PETSc.destroy(g)
PETSc.destroy(r)
PETSc.destroy(da)
PETSc.destroy(snes)

PETSc.finalize(petsclib)



================================================
FILE: examples/SNES_ex2.jl
================================================
# This implements src/snes/examples/tutorials/ex2.c from PETSc using the PETSc.jl package, using SNES
#
# This solves the equations sequentially
# 
# Newton method to solve u'' + u^{2} = f, sequentially.

using PETSc, MPI, LinearAlgebra, SparseArrays, UnicodePlots

if ~MPI.Initialized()
    MPI.Init()
end

petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)
comm = MPI.COMM_WORLD
"""    
    FormInitialGuess!(x)
Computes initial guess 
"""
function FormInitialGuess!(x)
    for i in eachindex(x)
        x[i] = 0.50;
    end
    return nothing
end

""" 
    F = SetInitialArrays(n)
Computes rhs forcing function 
""" 
function SetInitialArrays(n)
    h =  1.0/(n-1.0)
    F = zeros(n);
    xp = 0.0;
    for i=1:n 
        v    = 6.0*xp + (xp+1.e-12)^6.0; 
        F[i] = v;
        xp   = xp+h;
    end

    return F
end

"""
    FormResidual!(cf,cx, args...)
Computes the residual `f`, given solution vector `x`
"""
function FormResidual!(f,snes, x)
    n       =   length(x);
    xp      =   LinRange(0.0,1.0, n);
    F       =   6.0.*xp .+ (xp .+1.e-12).^6.0;      # define source term function
    
    dx      =   1.0/(n-1.0);
    f[1]    =   x[1] - 0.0;
    for i=2:n-1
        f[i] = (x[i-1] - 2.0*x[i] + x[i+1])/dx^2 + x[i]*x[i] - F[i]
    end
    f[n]    =   x[n] - 1.0;

    return 0
end

"""
    Computes the jacobian, given solution vector x
"""
function FormJacobian!(J, snes, x)
    n = length(x)
    dx  =   1.0/(n-1.0);

    # interior points (hand-coded jacobian)
    for i=2:n-1
        J[i,i-1] = 1.0/dx^2;
        J[i,i  ] = -2.0/dx^2 + 2.0*x[i];
        J[i,i+1] = 1.0/dx^2;
    end

    # boundary points
    J[1,1] = 1.0;
    J[n,n] = 1.0;
    if !isnothing(snes)
        PETSc.assemble!(J)
    end
    return 0
end


# ==========================================
# Main code 


# Compute initial solution
n   =   21;
F   =   SetInitialArrays(n);
x   =   zeros(n);

FormInitialGuess!(x);

# Compute initial jacobian using a julia structure to obtain the nonzero structure
# Note that we can also obtain this structure in a different manner
Jstruct  = zeros(n,n);
FormJacobian!(Jstruct, nothing, x);                                              # jacobian in julia form
Jsp      =   sparse(Float64.(abs.(Jstruct) .> 0))                       # sparse julia, with 1.0 in nonzero spots
PJ       =   PETSc.MatSeqAIJWithArrays(petsclib, comm, Jsp);  # transfer to PETSc format

# Setup snes
x_s = LibPETSc.VecCreateSeqWithArray(petsclib,comm, 1, length(x), x)    # solution vector
res = LibPETSc.VecCreateSeqWithArray(petsclib,comm, 1, length(F), F)    # residual vector
b   = LibPETSc.VecCreateSeqWithArray(petsclib,comm, 1, length(F), F)    # residual vector

S = PETSc.SNES(petsclib,comm; 
        snes_rtol=1e-12, 
        snes_monitor=true,
        snes_converged_reason=false);

# Set functions for residual and jacobian computations
PETSc.setfunction!(S, FormResidual!, res)
PETSc.setjacobian!(S, FormJacobian!, PJ)

# solve
PETSc.solve!(x_s, S);

# Extract & plot solution
x_sol = x_s[:];                  # convert solution to julia format
FormResidual!(res,S, x_s)

@show norm(res[:])

# cleanup
PETSc.destroy(x_s)
PETSc.destroy(res)
PETSc.destroy(b)
PETSc.destroy(PJ)
PETSc.destroy(S)
PETSc.finalize(petsclib)

# plot solution in REPL
lineplot(LinRange(0,1,n),x_sol,xlabel="width",ylabel="solution")



================================================
FILE: examples/SNES_ex2b.jl
================================================
# This implements src/snes/examples/tutorials/ex2.c from PETSc using the PETSc.jl package, using SNES
#
# This is the same as SNES_ex2.jl, except that we show how automatic differentiation can be used to
# compute the jacobian. That implies that the user does not have to provide a hand-coded jacobian.
# Note that the way we compute the jacobian here is not very efficient as we do not use the sparsity structure of the 
# matrix; see the other examples for faster implementions. 
#
# Newton method to solve u'' + u^{2} = f, sequentially.

using PETSc, MPI, LinearAlgebra, SparseArrays, UnicodePlots, ForwardDiff
using Test

if ~MPI.Initialized()
    MPI.Init()
end

petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)
comm = MPI.COMM_WORLD


"""
    Computes initial guess 
"""
function FormInitialGuess!(x)
    for i=1:length(x)
        x[i] = 0.50;
    end
end

""" 
    F = SetInitialArrays(n)
Computes rhs forcing function 
""" 
function SetInitialArrays(n)
    h =  1.0/(n-1.0)
    F = zeros(n);
    xp = 0.0;
    for i=1:n 
        v    = 6.0*xp + (xp+1.e-12)^6.0; 
        F[i] = v;
        xp   = xp+h;
    end

    return F
end

"""
    Computes the residual f, given solution vector x
"""
function FormResidual1!(f,snes, x)
    n       =   length(x);
    xp      =   LinRange(0.0,1.0, n);
    F       =   6.0.*xp .+ (xp .+1.e-12).^6.0;      # define source term function
    
    dx      =   1.0/(n-1.0);
    f[1]    =   x[1] - 0.0;
    for i=2:n-1
        f[i] = (x[i-1] - 2.0*x[i] + x[i+1])/dx^2 + x[i]*x[i] - F[i]
    end
    f[n]    =   x[n] - 1.0;
    
    return 0
end


"""
    Wrapper which makes it easier to compute the jacobian using automatic differntiation
"""
function  ForwardDiff_res(x)

    f   = zero(x)               # vector of zeros, of same type as x
    FormResidual1!(f,nothing, x);

    return f;
end


"""
    Computes the jacobian, given solution vector x
"""
function FormJacobian1!(J, snes, x)
   
    # Use AD to compute jacobian; by transferring x into sparse, the output will be sparse
    J_julia  =  ForwardDiff.jacobian(ForwardDiff_res,x[:]);
    copyto!(J, sparse(J_julia))

    if !isnothing(snes)
        PETSc.assemble!(J)
    end
    return 0
end

# ==========================================
# Main code 


# Compute initial solution
n   =   21;
F   =   SetInitialArrays(n);
x   =   zeros(n);

FormInitialGuess!(x);

# Compute initial jacobian using a julia structure to obtain the nonzero structure
# Note that we can also obtain this structure in a different manner
Jstruct  = zeros(n,n);
FormJacobian1!(Jstruct, nothing, x);                              # jacobian in julia form
Jsp      =   sparse(Float64.(abs.(Jstruct) .> 0))       # sparse julia, with 1.0 in nonzero spots
PJ       =   PETSc.MatSeqAIJWithArrays(petsclib, comm, Jsp);  # transfer to PETSc format

# Setup SNES
#x_s = PETSc.VecSeq(petsclib, comm, x);                  # solution vector
#b   = PETSc.VecSeq(petsclib, comm, x);                  # solution vector
#res = PETSc.VecSeq(petsclib, comm, zeros(size(x)));     # residual vector

# Setup snes
x_s = LibPETSc.VecCreateSeqWithArray(petsclib,comm, 1, length(x), x)    # solution vector
res = LibPETSc.VecCreateSeqWithArray(petsclib,comm, 1, length(F), F)    # residual vector
b   = LibPETSc.VecCreateSeqWithArray(petsclib,comm, 1, length(F), F)    # residual vector

snes = PETSc.SNES(petsclib,comm; 
        snes_rtol=1e-12, 
        snes_monitor=nothing,
        snes_converged_reason=nothing);
PETSc.setfunction!(snes, FormResidual1!, res)
PETSc.setjacobian!(snes, FormJacobian1!, PJ)

# solve
PETSc.solve!(x_s, snes);

# Extract & plot solution
x_sol = x_s[:];                  # convert solution to julia format
FormResidual1!(res,snes, x_s)

@show norm(res[:])

# cleanup
PETSc.destroy(x_s)
PETSc.destroy(res)
PETSc.destroy(b)
PETSc.destroy(PJ)
PETSc.destroy(snes)
PETSc.finalize(petsclib)

# plot solution in REPL
lineplot(LinRange(0,1,n),x_sol,xlabel="width",ylabel="solution")



================================================
FILE: examples/convergence_test.jl
================================================
# INCLUDE IN MPI TEST
#
#!/usr/bin/env julia
using PETSc, MPI, Printf

# Convergence test usage (examples)
#
# Run convergence tests for `ex45` (3D) or `ex50` (2D) from the repository root.
# - Direct (LU) baseline for ex45 (3D):
#     julia --project=. examples/convergence_test.jl -Ns="65,129,257" -levels="1,1,1" -ksp_type preonly -pc_type lu -example ex45
# - Geometric multigrid for ex45:
#     julia --project=. examples/convergence_test.jl -Ns="65,129,257,513" -levels="3,4,5,6" -ksp_type cg -pc_type mg -example ex45
#
# - Direct (LU) baseline for ex50 (2D):
#     julia --project=. examples/convergence_test.jl -Ns="65,129,257" -levels="1,1,1" -ksp_type preonly -pc_type lu -example ex50
# - Geometric multigrid for ex50:
#     julia --project=. examples/convergence_test.jl -Ns="65,129,257,513" -levels="3,4,5,6" -ksp_type cg -pc_type mg -example ex50
#
# Notes:
# - `-levels` maps one MG level per entry in `-Ns` (comma-separated).
# - Use `-ksp_type preonly -pc_type lu` for a direct factor baseline.
# - Use `-ksp_type cg -pc_type mg` (and tune MG-specific flags) for geometric multigrid.
# - PETSc CLI flags (e.g. `-ksp_monitor`, `-pc_mg_type`, `-mg_levels_ksp_type`) are forwarded
#   to the solver; pass them on the command line to tune behavior at runtime.

# This script runs the selected example (`ex50` by default) for a sequence
# of grid resolutions and prints a convergence table similar to
# `ex50_run_convergence.jl`. Resolutions are shown as `NxNxN` (or `NxN` for 2D).

include(joinpath(@__DIR__, "ex45.jl"))
include(joinpath(@__DIR__, "ex50.jl"))

# Parse PETSc/CLI options and build a kwargs dict to forward to `solve_ex45`.
opts = PETSc.parse_options(ARGS)
ns_opt = get(opts, Symbol("Ns"), nothing)
N_start = parse(Int, get(opts, Symbol("N_start"), "9"))
reverse_order = get(opts, Symbol("reverse"), false)
example = get(opts, Symbol("example"), "ex50")
dim = parse(Int, get(opts, Symbol("dim"), example == "ex50" ? "2" : "3"))
levels_opt = get(opts, Symbol("levels"), nothing)

if ns_opt !== nothing
    Ns = [parse(Int, strip(s)) for s in split(string(ns_opt), ",") if strip(s) != ""]
else
    Ns = [N_start * (2^(i-1)) for i in 1:5]
end
if reverse_order
    Ns = reverse(Ns)
end

# Parse explicit per-resolution MG levels if provided (comma-separated)
if levels_opt !== nothing
    Levels = [parse(Int, strip(s)) for s in split(string(levels_opt), ",") if strip(s) != ""]
    if length(Levels) != length(Ns)
        error("Provided -levels must contain the same number of entries as Ns")
    end
else
    Levels = nothing
end

# Forwardable solver options: everything in opts except the control args above
forward_keys = Set([:Ns, :N_start, :reverse, :dim, :example])
solver_kwargs = Dict{Symbol,Any}()
for k in keys(opts)
    if !(k in forward_keys)
        solver_kwargs[k] = opts[k]
    end
end

petsclib = PETSc.getlib(; PetscScalar = Float64)
PETSc.initialize(petsclib)

results = []
for (i, N) in enumerate(Ns)
    if MPI.Comm_rank(MPI.COMM_WORLD) == 0
        if example == "ex50"
            @printf("Running ex50 with N=%d\n", N)
        else
            @printf("Running ex45 with N=%d\n", N)
        end
    end
    # Build per-run options dict (copy solver_kwargs)
    opts_dict = Dict(solver_kwargs)
    # If user provided explicit per-resolution MG levels, use them
    if Levels !== nothing
        opts_dict[:pc_mg_levels] = string(Levels[i])
    else
        # keep any provided pc_mg_levels or leave absent
        if get(opts_dict, :pc_type, "") == "mg" && !haskey(opts_dict, :pc_mg_levels)
            # default geometric mapping: increase levels with resolution
            opts_dict[:pc_mg_levels] = string(1 + (i - 1))
        end
    end

    # For the smallest resolution run twice to exclude compilation time
    if i == 1
        if example == "ex50"
            solve_ex50(N; opts_dict...)  # warm-up / discard
        else
            solve_ex45(N; opts_dict...)  # warm-up / discard
        end
    end
    # call selected solver programmatically, forwarding any solver kwargs
    if example == "ex50"
        res = solve_ex50(N; opts_dict...)
    else
        res = solve_ex45(N; opts_dict...)
    end
    # res is a NamedTuple: (norm, final_grid, niter, solve_time, L2, max)
    final_grid = res.final_grid
    h = 1.0 / N
    push!(results, (N, h, res.L2, res.solve_time, res.niter))
end

# Compute orders and time-scaling (𝒪(Time))
orders = fill(NaN, length(results))
time_orders = fill(NaN, length(results))
for i in 1:length(results)-1
    e1 = results[i][3]
    e2 = results[i+1][3]
    h1 = results[i][2]
    h2 = results[i+1][2]
    if e1 > 0 && e2 > 0
        orders[i] = log(e1 / e2) / log(h1 / h2)
    end
    t1 = results[i][4]
    t2 = results[i+1][4]
    N1 = results[i][1]
    N2 = results[i+1][1]
    if t1 > 0 && t2 > 0 && N1 > 0 && N2 > 0
        time_orders[i] = log(t2 / t1) / (dim * log(N2 / N1))
    end
end

if MPI.Comm_rank(MPI.COMM_WORLD) == 0
    # Header matching ex50_convergence.jl but hide MG Lvl unless PC is multigrid
    pc_type_val = get(solver_kwargs, :pc_type, "gamg")
    show_mg = pc_type_val == "mg"
    @printf("\nConvergence Analysis Results (KSP: %s, PC: %s):\n", get(solver_kwargs, :ksp_type, "cg"), pc_type_val)
    if show_mg
        @printf("%-11s %-8s %-10s %-10s %-12s %-8s %-10s %-8s\n", "N", "MG Lvl", "h", "KSP Iters", "L2 Error", "𝒪(N)", "Time (s)", "𝒪(Time)")
        @printf("%-11s %-8s %-10s %-10s %-12s %-8s %-10s %-8s\n", repeat("-",11), repeat("-",8), repeat("-",10), repeat("-",10), repeat("-",12), repeat("-",8), repeat("-",10), repeat("-",8))
    else
        @printf("%-11s %-10s %-10s %-12s %-8s %-10s %-8s\n", "N", "h", "KSP Iters", "L2 Error", "𝒪(N)", "Time (s)", "𝒪(Time)")
        @printf("%-11s %-10s %-10s %-12s %-8s %-10s %-8s\n", repeat("-",11), repeat("-",10), repeat("-",10), repeat("-",12), repeat("-",8), repeat("-",10), repeat("-",8))
    end

    for i in 1:length(results)
        Nval, h, e, time, iters = results[i]
        ord = orders[i]
        tord = time_orders[i]
        # Format N as NxN or NxNxN depending on spatial dimension
        Nstr = dim == 3 ? string(Nval, "x", Nval, "x", Nval) : string(Nval, "x", Nval)
        if show_mg
            # display MG level for this row
            mglvl = get(solver_kwargs, :pc_mg_levels, "--")
            if Levels !== nothing
                mglvl = string(Levels[i])
            else
                if haskey(solver_kwargs, :pc_mg_levels)
                    mglvl = string(solver_kwargs[:pc_mg_levels])
                else
                    if pc_type_val == "mg"
                        mglvl = string(1 + (i - 1))
                    end
                end
            end

            if i == 1
                @printf("%-11s %-8s %-10.6f %-10d %-12.6e %-8s %-10.4f %-8s\n", Nstr, mglvl, h, iters, e, "-", time, "-")
            else
                s_ord = isnan(ord) ? "N/A" : @sprintf("%8.2f", ord)
                s_tord = isnan(tord) ? "N/A" : @sprintf("%8.2f", tord)
                if s_ord == "N/A" && s_tord == "N/A"
                    @printf("%-11s %-8s %-10.6f %-10d %-12.6e %-8s %-10.4f %-8s\n", Nstr, mglvl, h, iters, e, "N/A", time, "N/A")
                elseif s_ord == "N/A"
                    @printf("%-11s %-8s %-10.6f %-10d %-12.6e %-8s %-10.4f %-8.2f\n", Nstr, mglvl, h, iters, e, "N/A", time, tord)
                elseif s_tord == "N/A"
                    @printf("%-11s %-8s %-10.6f %-10d %-12.6e %-8.2f %-10.4f %-8s\n", Nstr, mglvl, h, iters, e, ord, time, "N/A")
                else
                    @printf("%-11s %-8s %-10.6f %-10d %-12.6e %-8.2f %-10.4f %-8.2f\n", Nstr, mglvl, h, iters, e, ord, time, tord)
                end
            end
        else
            if i == 1
                @printf("%-11s %-10.6f %-10d %-12.6e %-8s %-10.4f %-8s\n", Nstr, h, iters, e, "-", time, "-")
            else
                s_ord = isnan(ord) ? "N/A" : @sprintf("%8.2f", ord)
                s_tord = isnan(tord) ? "N/A" : @sprintf("%8.2f", tord)
                if s_ord == "N/A" && s_tord == "N/A"
                    @printf("%-11s %-10.6f %-10d %-12.6e %-8s %-10.4f %-8s\n", Nstr, h, iters, e, "N/A", time, "N/A")
                elseif s_ord == "N/A"
                    @printf("%-11s %-10.6f %-10d %-12.6e %-8s %-10.4f %-8.2f\n", Nstr, h, iters, e, "N/A", time, tord)
                elseif s_tord == "N/A"
                    @printf("%-11s %-10.6f %-10d %-12.6e %-8.2f %-10.4f %-8s\n", Nstr, h, iters, e, ord, time, "N/A")
                else
                    @printf("%-11s %-10.6f %-10d %-12.6e %-8.2f %-10.4f %-8.2f\n", Nstr, h, iters, e, ord, time, tord)
                end
            end
        end
    end
end

PETSc.finalize(petsclib)


================================================
FILE: examples/dmda_laplacian.jl
================================================

# # Finite Difference Laplacian
#
# This example solves a simple 2-D Laplacian with zero Dirichlet boundary 
# conditions using the PETSc DMDA interface.
#
# The approach taken is that the 5-point Laplacian is applied in the interior
# and boundary points are included in the solve, but the forcing for the
# boundary data is the Dirichlet boundary condition and the matrix entry for
# these points is set to the identity.
#
# The exact solution is u(x,y) = sin(2πx)sin(2πy) on [0,1]².
#
# ## Command Line Options:
#
# Grid configuration:
#   --N <value>          Grid size (default: 65, creates N×N grid)
#
# PETSc options (use single dash):
#   -ksp_type <type>     KSP solver type (preonly, gmres, cg, etc.)
#   -pc_type <type>      Preconditioner type (lu, mg, sor, ilu, etc.)
#   -ksp_rtol <value>    Relative convergence tolerance
#   -ksp_monitor         Monitor convergence
#
# ## Usage Examples:
#
# 1. Run with default settings (65×65 grid, LU solver):
#
#     julia --project=.. dmda_laplacian.jl
#
# 2. Solve on a 129×129 grid with direct LU solver:
#
#     julia --project=.. dmda_laplacian.jl --N 129 -ksp_type preonly -pc_type lu
#
# 3. Large grid with iterative SOR solver:
#
#     julia --project=.. dmda_laplacian.jl --N 257 -pc_type sor -ksp_rtol 1e-10 -ksp_monitor
#
# 4. Small grid with ILU preconditioner:
#
#     julia --project=.. dmda_laplacian.jl --N 33 -pc_type ilu -ksp_rtol 1e-10 -ksp_monitor
#
# 5. Geometric multigrid solver on 513×513 grid:
#
#     julia --project=.. dmda_laplacian.jl --N 513 -pc_type mg -ksp_rtol 1e-12 -mg_coarse_pc_type lu
#
# 6. Run in parallel with MPI (4 processes):
#
#     julia> using MPI
#     julia> mpiexec(cmd -> run(`$cmd -n 4 julia --project dmda_laplacian.jl --N 129 -pc_type mg`))
#
# For convergence analysis across multiple grid resolutions, see dmda_laplacian_convergence.jl

using MPI
using PETSc
using UnicodePlots: heatmap
using ForwardDiff: derivative
using Printf

# boundary data and forcing  
exact(x, y) = sin(2π * x) * sin(2π * y)
# The discrete operator is -∇², so forcing = -∇²(exact)
# For exact(x,y) = sin(2πx)sin(2πy), we have ∇²u = -2(2π)²sin(2πx)sin(2πy)
# So forcing = -∇²u = 8π²sin(2πx)sin(2πy)
forcing(x, y) = 8 * π^2 * sin(2π * x) * sin(2π * y)

# Parse options
opts = if !isinteractive()
    PETSc.parse_options(ARGS)
else
    (ksp_monitor = false, ksp_type = "preonly", pc_type = "lu", ksp_rtol = 1e-12)
end

# Parse grid size from command line (default: 65)
N = parse(Int, get(opts, Symbol("-N"), "65"))

# Set our MPI communicator
comm = MPI.COMM_WORLD

# Set our PETSc Scalar Type
PetscScalar = Float64

# get the PETSc lib with our chosen `PetscScalar` type
petsclib = PETSc.getlib(; PetscScalar = PetscScalar)

# Initialize PETSc
PETSc.initialize(petsclib)

# Grid size (set via --N option, default: 65)
Nq = (N, N)

# Create the DMDA
da = PETSc.DMDA(
    petsclib,
    comm,
    (PETSc.DM_BOUNDARY_NONE, PETSc.DM_BOUNDARY_NONE),
    Nq,
    1,
    1,
    PETSc.DMDA_STENCIL_STAR;
    opts...,
)

# Setup the Krylov solver
ksp = PETSc.KSP(da; opts...)

# Define the operator assembly function
function assemble_operator!(A, _, ksp)
    da = PETSc.getDM(ksp)
    corners = PETSc.getcorners(da)
    Nq = PETSc.getinfo(da).global_size[1:2]
    
    Δx = PetscScalar(1 / (Nq[1] - 1))
    Δy = PetscScalar(1 / (Nq[2] - 1))
    
    interior = (CartesianIndex(2, 2, 1)):(CartesianIndex(Nq[1] - 1, Nq[2] - 1, 1))
    
    sten = (
        CartesianIndex(-1, 0, 0),
        CartesianIndex(1, 0, 0),
        CartesianIndex(0, -1, 0),
        CartesianIndex(0, 1, 0),
        CartesianIndex(0, 0, 0),
    )
    vals = (-1 / Δx^2, -1 / Δx^2, -1 / Δy^2, -1 / Δy^2, 2 / Δx^2 + 2 / Δy^2)
    
    for i in ((corners.lower):(corners.upper))
        if i ∈ interior
            for (s, v) in zip(sten, vals)
                A[i, i + s] = v
            end
        else
            A[i, i] = 1
        end
    end
    
    PETSc.assemble!(A)
    return 0
end

PETSc.setcomputeoperators!(ksp, assemble_operator!)

# Set the right-hand side
PETSc.setcomputerhs!(ksp) do petsc_b, ksp
    da = PETSc.getDM(ksp)
    corners = PETSc.getcorners(da)
    Nq = PETSc.getinfo(da).global_size[1:2]
    
    g_x = range(PetscScalar(0), length = Nq[1], stop = 1)
    g_y = range(PetscScalar(0), length = Nq[2], stop = 1)
    
    l_x = g_x[(corners.lower[1]):(corners.upper[1])]
    l_y = g_y[(corners.lower[2]):(corners.upper[2])]
    
    PETSc.withlocalarray!(petsc_b; read = false) do b
        b = reshape(b, Int64(corners.size[1]), Int64(corners.size[2]))
        b .= forcing.(l_x, l_y')
        
        if corners.lower[1] == 1
            b[1, :] .= exact.(l_x[1], l_y)
        end
        if corners.lower[2] == 1
            b[:, 1] .= exact.(l_x, l_y[1])
        end
        if corners.upper[1] == Nq[1]
            b[end, :] .= exact.(l_x[end], l_y)
        end
        if corners.upper[2] == Nq[2]
            b[:, end] .= exact.(l_x, l_y[end])
        end
    end
    
    return 0
end

# Solve the problem
PETSc.solve!(ksp)

# Check convergence
reason = PETSc.LibPETSc.KSPGetConvergedReason(petsclib, ksp)

# Get the solution and compute error
sol = PETSc.get_solution(ksp)
corners = PETSc.getcorners(da)
Nq = PETSc.getinfo(da).global_size[1:2]

g_x = range(PetscScalar(0), length = Nq[1], stop = 1)
g_y = range(PetscScalar(0), length = Nq[2], stop = 1)

l_x = g_x[(corners.lower[1]):(corners.upper[1])]
l_y = g_y[(corners.lower[2]):(corners.upper[2])]

Δx = PetscScalar(l_x.step)

x = sol[:]
u = reshape(x, corners.size[1], corners.size[2])

# Compute error only in interior (excluding boundaries)
loc_err2_sum = 0.0
loc_max_err = 0.0
loc_npts = 0

for i in 1:length(l_x), j in 1:length(l_y)
    global loc_err2_sum, loc_max_err, loc_npts
    gi = corners.lower[1] + i - 1
    gj = corners.lower[2] + j - 1
    
    if gi > 1 && gi < Nq[1] && gj > 1 && gj < Nq[2]
        err = abs(u[i,j] - exact(l_x[i], l_y[j]))
        loc_err2_sum += err^2
        loc_max_err = max(loc_max_err, err)
        loc_npts += 1
    end
end

root = 0
err2_sum = MPI.Reduce(loc_err2_sum, +, root, comm)
npts_total = MPI.Reduce(loc_npts, +, root, comm)
max_err = MPI.Reduce(loc_max_err, max, root, comm)

# Report results on root processor
if root == MPI.Comm_rank(comm)
    L2_error = sqrt(err2_sum / npts_total)
    
    # Report convergence status
    if Integer(reason) > 0
        println("KSP converged with reason: $reason")
    elseif Integer(reason) < 0
        println("WARNING: KSP diverged with reason: $reason")
    else
        println("KSP still iterating: $reason")
    end
    
    println("L2-error (interior): $(Printf.@sprintf("%.6e", L2_error))")
    println("Max-error (interior): $(Printf.@sprintf("%.6e", max_err))")
    println("Mesh spacing h: $(Printf.@sprintf("%.6e", Δx))")
    
    # Compute error field for plotting
    u_exact = exact.(l_x, l_y')
    error_field = abs.(u .- u_exact)
    
    # Plot solution
    println("\nSolution heatmap:")
    println(heatmap(u'))
    
    # Plot error
    println("\nError heatmap:")
    println(heatmap(error_field'))
end

# Clean up
PETSc.destroy(ksp)
PETSc.destroy(da)

PETSc.finalize(petsclib)



================================================
FILE: examples/dmda_laplacian_convergence.jl
================================================
# EXCLUDE FROM TESTING
# # Finite Difference Laplacian - Convergence Analysis
#
# This example performs convergence analysis for a 2-D Laplacian solver with zero 
# Dirichlet boundary conditions using the PETSc DMDA interface.
#
# The approach taken is that the 5-point Laplacian is applied in the interior
# and boundary points are included in the solve, but the forcing for the
# boundary data is the Dirichlet boundary condition and the matrix entry for
# these points is set to the identity.
#
# By default, runs convergence tests on grids 17×17, 33×33, 65×65, and 129×129.
# Use --single_solve flag for a single solve on a 65×65 grid instead.
#
# ## Command Line Options:
#
# Grid configuration:
#   --grids <values>         Comma-separated list of grid sizes (e.g., 17,65,257,1025)
#   --N_min <value>          Minimum grid size for auto-generation (default: 17)
#   --N_max <value>          Maximum grid size for auto-generation (default: 129)
#                            Auto-generates: 17, 33, 65, 129, 257, 513, 1025, 2049, ...
#
# Multigrid configuration:
#   --mg_levels_list <vals>  Comma-separated MG levels per grid (e.g., 3,5,7,9)
#   --mg_levels <value>      Fixed number of MG levels for all grids
#                            If neither specified: auto-scales with grid (N=17→3, N=33→4, ...)
#
# Other options:
#   -single_solve           Run single solve instead of convergence test
#
# PETSc options (use single dash):
#   -ksp_type <type>         KSP solver type (preonly, gmres, richardson, etc.)
#   -pc_type <type>          Preconditioner type (lu, mg, sor, ilu, etc.)
#   -ksp_rtol <value>        Relative convergence tolerance
#   -mg_coarse_pc_type <type> Coarse grid preconditioner for multigrid
#   -ksp_monitor             Monitor convergence
#
# ## Usage Examples:
#
# 1. Default convergence analysis (N=17,33,65,129 with LU solver):
#
#     julia --project=.. dmda_laplacian_convergence.jl
#
# 2. Custom grid sizes with geometric multigrid:
#
#     julia --project=.. dmda_laplacian_convergence.jl --grids 17,65,257,1025 -pc_type mg -mg_coarse_pc_type lu
#
# 3. Custom grids with specific MG levels for each:
#
#     julia --project=.. dmda_laplacian_convergence.jl --grids 33,129,513 --mg_levels_list 4,6,8 -pc_type mg -mg_coarse_pc_type lu
#
# 4. Auto-generate grids with fixed MG levels:
#
#     julia --project=.. dmda_laplacian_convergence.jl --N_max 513 --mg_levels 7 -pc_type mg -mg_coarse_pc_type lu
#
# 5. Direct solver with preonly KSP:
#
#     julia --project=.. dmda_laplacian_convergence.jl --N_max 257 -ksp_type preonly -pc_type lu
#
# 6. Iterative SOR solver:
#
#     julia --project=.. dmda_laplacian_convergence.jl --grids 17,65,257,1025 -pc_type sor -ksp_rtol 1e-10
#
# 7. Single solve with multigrid:
#
#     julia --project=.. dmda_laplacian_convergence.jl --single_solve -pc_type mg -ksp_rtol 1e-12 -mg_coarse_pc_type lu
#
# 8. Run in parallel with MPI (4 processes):
#
#     julia> using MPI
#     julia> mpiexec(cmd -> run(`$cmd -n 4 julia --project dmda_laplacian_convergence.jl -pc_type mg`))
#
# Notes:
# - Geometric multigrid (MG) uses the DMDA grid hierarchy for automatic coarsening
# - By default, MG levels automatically scale with grid size (N=17→3, N=33→4, N=65→5, N=129→6)
# - Use --mg_levels to fix the number of levels for all grids
# - PETSc options use single dash (-), Julia script options use double dash (--)

using MPI
using PETSc
using UnicodePlots: heatmap
using ForwardDiff: derivative
using Printf
using Statistics: mean

# Set up the problem by using an exact solution and then using this to set the
# boundary data and forcing  
exact(x, y) = sin(2π * x) * sin(2π * y)
# The discrete operator is -∇², so forcing = -∇²(exact)
# For exact(x,y) = sin(2πx)sin(2πy), we have ∇²u = -2(2π)²sin(2πx)sin(2πy)
# So forcing = -∇²u = 8π²sin(2πx)sin(2πy)
forcing(x, y) = 8 * π^2 * sin(2π * x) * sin(2π * y)

"""
    solve_laplacian(petsclib, comm, N::Int, opts)

Solve the 2D Laplacian problem on an N×N grid and return the L2 and max errors
(computed on interior points only).
"""
function solve_laplacian(petsclib, comm, N::Int, opts; mg_levels=nothing)
    PetscScalar = petsclib.PetscScalar
    Nq = (N, N)
    
    # Add mg_levels to opts if using multigrid and mg_levels is specified
    if mg_levels !== nothing && get(opts, :pc_type, nothing) == "mg"
        opts = merge(opts, (var"-pc_mg_levels" = mg_levels,))
    end
    
    # Add direct coarse grid solver for multigrid if not already specified
    if get(opts, :pc_type, nothing) == "mg" && !haskey(opts, :mg_coarse_pc_type)
        opts = merge(opts, (var"-mg_coarse_pc_type" = "lu",))
    end
    
    # Create the DMDA
    da = PETSc.DMDA(
        petsclib,
        comm,
        (PETSc.DM_BOUNDARY_NONE, PETSc.DM_BOUNDARY_NONE),
        Nq,
        1,
        1,
        PETSc.DMDA_STENCIL_STAR;
        opts...,
    )
    
    # Setup the Krylov solver
    ksp = PETSc.KSP(da; opts...)
    
    # Define the operator assembly function
    function assemble_operator!(A, _, ksp)
        da = PETSc.getDM(ksp)
        corners = PETSc.getcorners(da)
        Nq = PETSc.getinfo(da).global_size[1:2]
        
        Δx = PetscScalar(1 / (Nq[1] - 1))
        Δy = PetscScalar(1 / (Nq[2] - 1))
        
        interior = (CartesianIndex(2, 2, 1)):(CartesianIndex(Nq[1] - 1, Nq[2] - 1, 1))
        
        sten = (
            CartesianIndex(-1, 0, 0),
            CartesianIndex(1, 0, 0),
            CartesianIndex(0, -1, 0),
            CartesianIndex(0, 1, 0),
            CartesianIndex(0, 0, 0),
        )
        vals = (-1 / Δx^2, -1 / Δx^2, -1 / Δy^2, -1 / Δy^2, 2 / Δx^2 + 2 / Δy^2)
        
        for i in ((corners.lower):(corners.upper))
            if i ∈ interior
                for (s, v) in zip(sten, vals)
                    A[i, i + s] = v
                end
            else
                A[i, i] = 1
            end
        end
        
        PETSc.assemble!(A)
        return 0
    end
    
    PETSc.setcomputeoperators!(ksp, assemble_operator!)
    
    # Set the right-hand side
    PETSc.setcomputerhs!(ksp) do petsc_b, ksp
        da = PETSc.getDM(ksp)
        corners = PETSc.getcorners(da)
        Nq = PETSc.getinfo(da).global_size[1:2]
        
        g_x = range(PetscScalar(0), length = Nq[1], stop = 1)
        g_y = range(PetscScalar(0), length = Nq[2], stop = 1)
        
        l_x = g_x[(corners.lower[1]):(corners.upper[1])]
        l_y = g_y[(corners.lower[2]):(corners.upper[2])]
        
        PETSc.withlocalarray!(petsc_b; read = false) do b
            b = reshape(b, Int64(corners.size[1]), Int64(corners.size[2]))
            b .= forcing.(l_x, l_y')
            
            if corners.lower[1] == 1
                b[1, :] .= exact.(l_x[1], l_y)
            end
            if corners.lower[2] == 1
                b[:, 1] .= exact.(l_x, l_y[1])
            end
            if corners.upper[1] == Nq[1]
                b[end, :] .= exact.(l_x[end], l_y)
            end
            if corners.upper[2] == Nq[2]
                b[:, end] .= exact.(l_x, l_y[end])
            end
        end
        
        return 0
    end
    
    # Solve the problem and time it
    t_start = time()
    PETSc.solve!(ksp)
    t_solve = time() - t_start
    
    # Check convergence and get iteration count
    reason = PETSc.LibPETSc.KSPGetConvergedReason(petsclib, ksp)
    niter = PETSc.LibPETSc.KSPGetIterationNumber(petsclib, ksp)
    if Integer(reason) < 0 && MPI.Comm_rank(comm) == 0
        @warn "KSP diverged for N=$N with reason: $reason"
    end
    
    # Get the solution and compute error
    sol = PETSc.get_solution(ksp)
    corners = PETSc.getcorners(da)
    Nq = PETSc.getinfo(da).global_size[1:2]
    
    g_x = range(PetscScalar(0), length = Nq[1], stop = 1)
    g_y = range(PetscScalar(0), length = Nq[2], stop = 1)
    
    l_x = g_x[(corners.lower[1]):(corners.upper[1])]
    l_y = g_y[(corners.lower[2]):(corners.upper[2])]
    
    Δx = PetscScalar(l_x.step)
    
    x = sol[:]
    u = reshape(x, corners.size[1], corners.size[2])
    
    # Compute error only in interior (excluding boundaries)
    loc_err2_sum = 0.0
    loc_max_err = 0.0
    loc_npts = 0
    
    for i in 1:length(l_x)
        for j in 1:length(l_y)
            gi = corners.lower[1] + i - 1
            gj = corners.lower[2] + j - 1
            
            if gi > 1 && gi < Nq[1] && gj > 1 && gj < Nq[2]
                err = abs(u[i,j] - exact(l_x[i], l_y[j]))
                loc_err2_sum += err^2
                loc_max_err = max(loc_max_err, err)
                loc_npts += 1
            end
        end
    end
    
    root = 0
    err2_sum = MPI.Reduce(loc_err2_sum, +, root, comm)
    npts_total = MPI.Reduce(loc_npts, +, root, comm)
    max_err = MPI.Reduce(loc_max_err, max, root, comm)
    
    L2_error = nothing
    if root == MPI.Comm_rank(comm)
        L2_error = sqrt(err2_sum / npts_total)
    end
    
    # Return u for visualization (only needed for single solve mode)
    return L2_error, max_err, Δx, reason, niter, t_solve, u, l_x, l_y, ksp, da
end

"""
    run_convergence_analysis(petsclib, comm, grid_sizes, opts; mg_levels_list=nothing, mg_levels_override=nothing)

Run a convergence analysis for the specified grid sizes and return errors and mesh spacings.
For multigrid solvers:
- If mg_levels_list is provided, uses the specified level for each grid
- If mg_levels_override is provided, uses that fixed level for all grids
- Otherwise, automatically scales levels with grid size
"""
function run_convergence_analysis(petsclib, comm, grid_sizes, opts; mg_levels_list=nothing, mg_levels_override=nothing)
    L2_errors = Float64[]
    max_errors = Float64[]
    h_values = Float64[]
    iterations = Int[]
    solve_times = Float64[]
    mg_levels_used = Union{Int,Nothing}[]
    
    # Check if using multigrid
    using_mg = get(opts, :pc_type, nothing) == "mg"
    
    for (i, N) in enumerate(grid_sizes)
        # Determine MG levels for this grid
        mg_levels = if mg_levels_list
Download .txt
gitextract_n_mpc822/

├── .JuliaFormatter.toml
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── CompatHelper.yml
│       ├── TagBot.yml
│       ├── ci.yml
│       └── documentation.yml
├── .gitignore
├── .zenodo.json
├── CITATION.bib
├── LICENSE
├── Project.toml
├── README.md
├── docs/
│   ├── .documenter.enc
│   ├── Project.toml
│   ├── make.jl
│   └── src/
│       ├── index.md
│       └── man/
│           ├── FAQ.md
│           ├── ao_lowlevel.md
│           ├── contributing.md
│           ├── design.md
│           ├── dm.md
│           ├── dm_lowlevel.md
│           ├── dmda.md
│           ├── dmda_lowlevel.md
│           ├── dmforest_lowlevel.md
│           ├── dmnetwork_lowlevel.md
│           ├── dmplex_lowlevel.md
│           ├── dmshell_lowlevel.md
│           ├── dmstag.md
│           ├── dmstag_lowlevel.md
│           ├── dmswarm_lowlevel.md
│           ├── funding.md
│           ├── getting_started.md
│           ├── hpc.md
│           ├── installation.md
│           ├── is_lowlevel.md
│           ├── ksp.md
│           ├── ksp_lowlevel.md
│           ├── lowlevel_intro.md
│           ├── mat.md
│           ├── mat_lowlevel.md
│           ├── pc_lowlevel.md
│           ├── petscsection_lowlevel.md
│           ├── petscsf_lowlevel.md
│           ├── petscviewer_lowlevel.md
│           ├── snes.md
│           ├── snes_lowlevel.md
│           ├── tao_lowlevel.md
│           ├── ts_lowlevel.md
│           ├── utilities.md
│           ├── vec.md
│           └── vec_lowlevel.md
├── examples/
│   ├── Liouville_Bratu_Gelfand.jl
│   ├── SNES_ex2.jl
│   ├── SNES_ex2b.jl
│   ├── convergence_test.jl
│   ├── dmda_laplacian.jl
│   ├── dmda_laplacian_convergence.jl
│   ├── dmstag_ex8.jl
│   ├── ex1.jl
│   ├── ex16.jl
│   ├── ex45.jl
│   ├── ex50.jl
│   ├── ex51.jl
│   ├── ex51_implicit.jl
│   ├── laplacian.jl
│   ├── porosity_waves.jl
│   ├── scalability_tests/
│   │   ├── ex45_julia.c
│   │   ├── job.sh
│   │   ├── makefile
│   │   ├── parse_scaling.jl
│   │   ├── submit_scaling.sh
│   │   └── weak_scaling_plot.jl
│   └── stokes2d_linear_dmstag.jl
├── src/
│   ├── LibPETSc.jl
│   ├── LibPETSc_const.jl
│   ├── LibPETSc_lib.jl
│   ├── LibPETSc_startup.jl
│   ├── PETSc.jl
│   ├── audit.jl
│   ├── autowrapped/
│   │   ├── AO_wrappers.jl
│   │   ├── Characteristic_wrappers.jl
│   │   ├── DM_wrappers.jl
│   │   ├── DMaddons_wrappers.jl
│   │   ├── IS_wrappers.jl
│   │   ├── ISaddons_wrappers.jl
│   │   ├── KSPGuess_wrappers.jl
│   │   ├── KSP_wrappers.jl
│   │   ├── Mat_wrappers.jl
│   │   ├── Mataddons_wrappers.jl
│   │   ├── PC_wrappers.jl
│   │   ├── PF_wrappers.jl
│   │   ├── PetscBag_wrappers.jl
│   │   ├── PetscBench_wrappers.jl
│   │   ├── PetscContainer_wrappers.jl
│   │   ├── PetscConvEst_wrappers.jl
│   │   ├── PetscDLLibrary_wrappers.jl
│   │   ├── PetscDS_wrappers.jl
│   │   ├── PetscDevice_wrappers.jl
│   │   ├── PetscDraw_wrappers.jl
│   │   ├── PetscDualSpace_wrappers.jl
│   │   ├── PetscFE_wrappers.jl
│   │   ├── PetscFV_wrappers.jl
│   │   ├── PetscFunctionList_wrappers.jl
│   │   ├── PetscGridHash_wrappers.jl
│   │   ├── PetscHeap_wrappers.jl
│   │   ├── PetscIntStack_wrappers.jl
│   │   ├── PetscKDTree_wrappers.jl
│   │   ├── PetscLayout_wrappers.jl
│   │   ├── PetscLimiter_wrappers.jl
│   │   ├── PetscLog_wrappers.jl
│   │   ├── PetscMatlabEngine_wrappers.jl
│   │   ├── PetscObject_wrappers.jl
│   │   ├── PetscOmpCtrl_wrappers.jl
│   │   ├── PetscOptions_addons_wrappers.jl
│   │   ├── PetscOptions_wrappers.jl
│   │   ├── PetscPartitioner_wrappers.jl
│   │   ├── PetscRandom_wrappers.jl
│   │   ├── PetscRegressor_wrappers.jl
│   │   ├── PetscSF_wrappers.jl
│   │   ├── PetscSection_wrappers.jl
│   │   ├── PetscSegBuffer_wrappers.jl
│   │   ├── PetscSpace_wrappers.jl
│   │   ├── PetscToken_wrappers.jl
│   │   ├── PetscViennaCLIndices_wrappers.jl
│   │   ├── PetscViewer_wrappers.jl
│   │   ├── Petsccomm_wrappers.jl
│   │   ├── SNESLineSearch_wrappers.jl
│   │   ├── SNES_wrappers.jl
│   │   ├── Sys_wrappers.jl
│   │   ├── TS_wrappers.jl
│   │   ├── TSaddons_wrappers.jl
│   │   ├── Tao_addons_wrappers.jl
│   │   ├── Tao_wrappers.jl
│   │   ├── VecTagger_wrappers.jl
│   │   ├── Vec_wrappers.jl
│   │   ├── Vecs_wrappers.jl
│   │   ├── enums_wrappers.jl
│   │   ├── petsc_library.jl
│   │   ├── petsc_wrappers_version.jl
│   │   ├── petscarray.jl
│   │   ├── senums_wrappers.jl
│   │   ├── struct_wrappers.jl
│   │   └── typedefs_wrappers.jl
│   ├── deprecated/
│   │   ├── const.jl
│   │   ├── dm.jl
│   │   ├── dm_wrapped.jl
│   │   ├── dmda.jl
│   │   ├── dmstag.jl
│   │   ├── dmstag_wrapped.jl
│   │   ├── ksp.jl
│   │   ├── ksp_wrapped.jl
│   │   ├── lib.jl
│   │   ├── mat.jl
│   │   ├── matshell.jl
│   │   ├── options.jl
│   │   ├── pc.jl
│   │   ├── ref.jl
│   │   ├── snes.jl
│   │   ├── sys.jl
│   │   ├── utils.jl
│   │   ├── vec.jl
│   │   └── viewer.jl
│   ├── dm.jl
│   ├── dmda.jl
│   ├── dmstag.jl
│   ├── init.jl
│   ├── ksp.jl
│   ├── mat.jl
│   ├── options.jl
│   ├── snes.jl
│   ├── startup.jl
│   ├── string_wrappers.jl
│   ├── string_wrappers_extra.jl
│   ├── sys.jl
│   ├── ts.jl
│   └── vec.jl
├── test/
│   ├── 01-hello.jl
│   ├── dmda.jl
│   ├── dmnetwork.jl
│   ├── dmproduct.jl
│   ├── dmshell.jl
│   ├── dmstag.jl
│   ├── doc_examples_is.jl
│   ├── doc_examples_tao.jl
│   ├── doc_examples_ts.jl
│   ├── doc_examples_viewer.jl
│   ├── examples.jl
│   ├── init.jl
│   ├── ksp.jl
│   ├── lib.jl
│   ├── low_level_is.jl
│   ├── low_level_petscsection.jl
│   ├── low_level_tao.jl
│   ├── low_level_ts.jl
│   ├── low_level_viewer.jl
│   ├── mat.jl
│   ├── matshell.jl
│   ├── mpi_examples.jl
│   ├── mpimat.jl
│   ├── mpivec.jl
│   ├── old_test.jl
│   ├── options.jl
│   ├── runtests.jl
│   ├── snes.jl
│   ├── snes_helpers.jl
│   ├── tao_helpers.jl
│   ├── test_dmstag.jl
│   ├── test_snes.jl
│   ├── testutils.jl
│   ├── ts_ex16.jl
│   ├── ts_ex51.jl
│   ├── ts_ex51_implicit.jl
│   └── vec.jl
└── wrapping/
    ├── Project.toml
    ├── currently_wrapped_classes.md
    ├── find_doc_strings.jl
    ├── generatejuliabindings.jl
    ├── local_types.jl
    ├── prologue.jl
    └── test.jl
Download .txt
SYMBOL INDEX (7 symbols across 1 files)

FILE: examples/scalability_tests/ex45_julia.c
  type AppCtx (line 26) | typedef struct {
  function PetscReal (line 34) | static inline PetscReal exact_sol(PetscReal x, PetscReal y, PetscReal z)
  function PetscReal (line 39) | static inline PetscReal forcing(PetscReal x, PetscReal y, PetscReal z)
  function main (line 45) | int main(int argc, char **argv)
  function PetscErrorCode (line 167) | PetscErrorCode ComputeInitialGuess(KSP ksp, Vec b, void *ctx)
  function PetscErrorCode (line 174) | PetscErrorCode ComputeRHS(KSP ksp, Vec b, void *ctx)
  function PetscErrorCode (line 214) | PetscErrorCode ComputeMatrix(KSP ksp, Mat jac, Mat B, void *ctx)
Condensed preview — 221 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,352K chars).
[
  {
    "path": ".JuliaFormatter.toml",
    "chars": 133,
    "preview": "indent = 4\nmargin = 80\nalways_for_in = true\nwhitespace_typedefs = true\nwhitespace_ops_in_indices = true\nremove_extra_new"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 256,
    "preview": "# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\nversion: 2\nupda"
  },
  {
    "path": ".github/workflows/CompatHelper.yml",
    "chars": 933,
    "preview": "name: CompatHelper\non:\n  schedule:\n    - cron: 0 0 * * *\n  workflow_dispatch:\njobs:\n  CompatHelper:\n    runs-on: ubuntu-"
  },
  {
    "path": ".github/workflows/TagBot.yml",
    "chars": 828,
    "preview": "name: TagBot\non:\n  issue_comment:\n    types:\n      - created\n  workflow_dispatch:\n    inputs:\n      lookback:\n        de"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1333,
    "preview": "name: CI\non:\n  push:\n    branches:\n      - main\n      - staging\n      - trying\n    tags: '*'\n  pull_request:\n\nconcurrenc"
  },
  {
    "path": ".github/workflows/documentation.yml",
    "chars": 737,
    "preview": "name: Documentation\n\non:\n  push:\n    branches:\n      - main\n      - staging\n      - trying\n    tags: '*'\n  pull_request:"
  },
  {
    "path": ".gitignore",
    "chars": 181,
    "preview": "deps/ComplexDouble/\ndeps/RealDouble/\ndeps/RealSingle/\ndeps/deps.jl\ndeps/petsc-*.tar.gz\n*.jl.cov\n*.jl.mem\ndocs/build/\ndoc"
  },
  {
    "path": ".zenodo.json",
    "chars": 1780,
    "preview": "{\n  \"title\": \"PETSc.jl: Julia bindings for PETSc\",\n  \"description\": \"PETSc.jl provides an interface to the Portable, Ext"
  },
  {
    "path": "CITATION.bib",
    "chars": 453,
    "preview": "@software{petsc_jl,\n  author       = {Kaus, Boris and\n                  Shah, Viral B. and\n                  Kozdon, Jer"
  },
  {
    "path": "LICENSE",
    "chars": 1148,
    "preview": "MIT License\n\nCopyright (c) 2026 Boris Kaus, Viral B. Shah, Valentin Churavy, Erik Schnetter, Jeremy E. Kozdon, Simon Byr"
  },
  {
    "path": "Project.toml",
    "chars": 1757,
    "preview": "name = \"PETSc\"\nuuid = \"ace2c81b-2b5f-4b1e-a30d-d662738edfe0\"\nversion = \"0.4.9\"\nauthors = [\"Boris Kaus <kaus@uni-mainz.de"
  },
  {
    "path": "README.md",
    "chars": 3534,
    "preview": "# PETSc.jl\n\n[![Build Status](https://github.com/JuliaParallel/PETSc.jl/workflows/CI/badge.svg)](https://github.com/Julia"
  },
  {
    "path": "docs/Project.toml",
    "chars": 151,
    "preview": "[deps]\nDocumenter = \"e30172f5-a6a5-5a46-863b-614d45cd2de4\"\nMPI = \"da04e1cc-30fd-572f-bb4f-1f8673147195\"\nPETSc = \"ace2c81"
  },
  {
    "path": "docs/make.jl",
    "chars": 2631,
    "preview": "using Documenter, PETSc\n\nmakedocs(;\n    modules=[PETSc],\n    sitename=\"PETSc.jl\",\n    checkdocs=:exports,  # Only check "
  },
  {
    "path": "docs/src/index.md",
    "chars": 3632,
    "preview": "# PETSc.jl\n\n[PETSc.jl](https://github.com/JuliaParallel/PETSc.jl) is a Julia wrapper for the Portable, Extensible Toolki"
  },
  {
    "path": "docs/src/man/FAQ.md",
    "chars": 2309,
    "preview": "# Frequently Asked Questions\n\n\n## 1. Can I use my own PETSc library?\nYes. You do need to compile PETSc as a dynamic (sha"
  },
  {
    "path": "docs/src/man/ao_lowlevel.md",
    "chars": 5645,
    "preview": "# AO (Application Ordering) - Low-level Interface\n\nThe AO (Application Ordering) component provides mappings between the"
  },
  {
    "path": "docs/src/man/contributing.md",
    "chars": 1291,
    "preview": "# Contributing\n\nContributions are highly welcome, in particular since only part of the PETSc functionality is currently "
  },
  {
    "path": "docs/src/man/design.md",
    "chars": 4104,
    "preview": "# Design notes\n\n\n\n- PETSc can only be built for a single `PetscScalar` type. A workaround is to build multiple PETSc lib"
  },
  {
    "path": "docs/src/man/dm.md",
    "chars": 2389,
    "preview": "# DM\n\nThe `DM` module provides the base functionality for managing distributed data structures in PETSc. It serves as a "
  },
  {
    "path": "docs/src/man/dm_lowlevel.md",
    "chars": 2594,
    "preview": "# DM (Domain Management)\n\nThe DM (Domain Management) object encapsulates the relationship between a mesh data structure "
  },
  {
    "path": "docs/src/man/dmda.md",
    "chars": 860,
    "preview": "# DMDA\n\nThe `DMDA` (Distributed Array) module provides functionality for creating and managing structured grids in 1D, 2"
  },
  {
    "path": "docs/src/man/dmda_lowlevel.md",
    "chars": 1743,
    "preview": "# DMDA (Structured Grids)\n\nDMDA manages structured grids with regular topology in 1D, 2D, and 3D. It's designed for fini"
  },
  {
    "path": "docs/src/man/dmforest_lowlevel.md",
    "chars": 2469,
    "preview": "# DMForest (Adaptive Mesh Refinement)\n\nDMForest manages adaptive mesh refinement (AMR) using forest-of-octrees (2D: quad"
  },
  {
    "path": "docs/src/man/dmnetwork_lowlevel.md",
    "chars": 1849,
    "preview": "# DMNetwork (Network/Graph Problems)\n\nDMNetwork manages network and graph-based problems, such as power grids, transport"
  },
  {
    "path": "docs/src/man/dmplex_lowlevel.md",
    "chars": 2138,
    "preview": "# DMPlex (Unstructured Meshes)\n\nDMPlex manages unstructured meshes using a flexible topology representation. It supports"
  },
  {
    "path": "docs/src/man/dmshell_lowlevel.md",
    "chars": 3673,
    "preview": "# DMShell and Other DM Types\n\nThis page documents DMShell (user-defined DM), DMProduct (Cartesian product of DMs), DMRed"
  },
  {
    "path": "docs/src/man/dmstag.md",
    "chars": 3422,
    "preview": "# DMStag\n\nThe DMStag (Staggered Grid DM) module provides data management for staggered grids, commonly used in finite di"
  },
  {
    "path": "docs/src/man/dmstag_lowlevel.md",
    "chars": 1790,
    "preview": "# DMStag (Staggered Grids)\n\nDMStag manages staggered grid discretizations, commonly used in computational fluid dynamics"
  },
  {
    "path": "docs/src/man/dmswarm_lowlevel.md",
    "chars": 2844,
    "preview": "# DMSwarm (Particle Methods)\n\nDMSwarm manages particle-based methods including particle-in-cell (PIC), smoothed particle"
  },
  {
    "path": "docs/src/man/funding.md",
    "chars": 835,
    "preview": "# Funding\n\nThe most recent release of PETSc.jl was developed by Boris Kaus and Jeremy Kozdon, and Boris was funded by:\n\n"
  },
  {
    "path": "docs/src/man/getting_started.md",
    "chars": 10221,
    "preview": "# Getting started\n\n- [1. Solving a linear system of equations](#1-solving-a-linear-system-of-equations)\n- [2. Nonlinear "
  },
  {
    "path": "docs/src/man/hpc.md",
    "chars": 15431,
    "preview": "# Running on HPC Systems\n\nPETSc.jl can be used on HPC clusters in two main configurations: using precompiled binaries vi"
  },
  {
    "path": "docs/src/man/installation.md",
    "chars": 1992,
    "preview": "# Installation\n\n## Using pre-built libraries\n\nThe easiest way to install the package is:\n```julia\njulia> ]\n(@v1.12) pkg>"
  },
  {
    "path": "docs/src/man/is_lowlevel.md",
    "chars": 4409,
    "preview": "# IS (Index Sets) - Low-level Interface\n\nThe IS (Index Set) component provides data structures and operations for managi"
  },
  {
    "path": "docs/src/man/ksp.md",
    "chars": 2225,
    "preview": "# KSP\n\nThe KSP (Krylov Subspace Methods) module provides iterative linear solvers for systems of the form `Ax = b`. PETS"
  },
  {
    "path": "docs/src/man/ksp_lowlevel.md",
    "chars": 1438,
    "preview": "# KSP - Low-level Interface\n\nThis page documents the low-level, automatically wrapped PETSc KSP (Krylov Subspace Methods"
  },
  {
    "path": "docs/src/man/lowlevel_intro.md",
    "chars": 10165,
    "preview": "# Low-Level Interface (LibPETSc)\n\nPETSc.jl provides two ways to interact with the PETSc library:\n\n1. **High-level interf"
  },
  {
    "path": "docs/src/man/mat.md",
    "chars": 2020,
    "preview": "# Mat\n\nPETSc matrices (`Mat`) provide sparse and dense matrix storage with efficient parallel operations. They are essen"
  },
  {
    "path": "docs/src/man/mat_lowlevel.md",
    "chars": 1681,
    "preview": "# Mat - Low-level Interface\n\nThis page documents the low-level, automatically wrapped PETSc Mat (matrix) functions avail"
  },
  {
    "path": "docs/src/man/pc_lowlevel.md",
    "chars": 5644,
    "preview": "# PC (Preconditioners) - Low-level Interface\n\nThe PC (Preconditioner) component provides methods for preconditioning lin"
  },
  {
    "path": "docs/src/man/petscsection_lowlevel.md",
    "chars": 6168,
    "preview": "# PetscSection - Low-level Interface\n\nThe PetscSection component provides a flexible mechanism for describing the layout"
  },
  {
    "path": "docs/src/man/petscsf_lowlevel.md",
    "chars": 6232,
    "preview": "# PetscSF (Star Forest) - Low-level Interface\n\nThe PetscSF (Star Forest) component provides efficient parallel communica"
  },
  {
    "path": "docs/src/man/petscviewer_lowlevel.md",
    "chars": 7198,
    "preview": "# PetscViewer - Low-level Interface\n\nThe PetscViewer component provides flexible I/O capabilities for visualizing and sa"
  },
  {
    "path": "docs/src/man/snes.md",
    "chars": 2556,
    "preview": "# SNES\n\nThe SNES (Scalable Nonlinear Equations Solvers) module provides methods for solving nonlinear systems of the for"
  },
  {
    "path": "docs/src/man/snes_lowlevel.md",
    "chars": 1698,
    "preview": "# SNES - Low-level Interface\n\nThis page documents the low-level, automatically wrapped PETSc SNES (Scalable Nonlinear Eq"
  },
  {
    "path": "docs/src/man/tao_lowlevel.md",
    "chars": 4648,
    "preview": "# Tao (Optimization) - Low-level Interface\n\nThe Tao (Toolkit for Advanced Optimization) component provides methods for s"
  },
  {
    "path": "docs/src/man/ts_lowlevel.md",
    "chars": 3993,
    "preview": "# TS (Time Stepping) - Low-level Interface\n\n<a id=\"ch_ts\"></a>\n\nThe TS (Time Stepping) component provides methods for so"
  },
  {
    "path": "docs/src/man/utilities.md",
    "chars": 841,
    "preview": "# Utilities\n\nThis page documents utility functions for initialization, options handling, system information, and code au"
  },
  {
    "path": "docs/src/man/vec.md",
    "chars": 1731,
    "preview": "# Vec\n\nPETSc vectors (`Vec`) are the fundamental building blocks for storing solution data, right-hand sides, and other "
  },
  {
    "path": "docs/src/man/vec_lowlevel.md",
    "chars": 1734,
    "preview": "# Vec - Low-level Interface\n\nThis page documents the low-level, automatically wrapped PETSc Vec functions available thro"
  },
  {
    "path": "examples/Liouville_Bratu_Gelfand.jl",
    "chars": 7885,
    "preview": "# INCLUDE IN MPI TEST\n#=\nIn this example we solve the [Liouville–Bratu–Gelfand\nequation](https://en.wikipedia.org/wiki/L"
  },
  {
    "path": "examples/SNES_ex2.jl",
    "chars": 3338,
    "preview": "# This implements src/snes/examples/tutorials/ex2.c from PETSc using the PETSc.jl package, using SNES\n#\n# This solves th"
  },
  {
    "path": "examples/SNES_ex2b.jl",
    "chars": 3971,
    "preview": "# This implements src/snes/examples/tutorials/ex2.c from PETSc using the PETSc.jl package, using SNES\n#\n# This is the sa"
  },
  {
    "path": "examples/convergence_test.jl",
    "chars": 8701,
    "preview": "# INCLUDE IN MPI TEST\n#\n#!/usr/bin/env julia\nusing PETSc, MPI, Printf\n\n# Convergence test usage (examples)\n#\n# Run conve"
  },
  {
    "path": "examples/dmda_laplacian.jl",
    "chars": 7134,
    "preview": "\n# # Finite Difference Laplacian\n#\n# This example solves a simple 2-D Laplacian with zero Dirichlet boundary \n# conditio"
  },
  {
    "path": "examples/dmda_laplacian_convergence.jl",
    "chars": 19219,
    "preview": "# EXCLUDE FROM TESTING\n# # Finite Difference Laplacian - Convergence Analysis\n#\n# This example performs convergence anal"
  },
  {
    "path": "examples/dmstag_ex8.jl",
    "chars": 5565,
    "preview": "# INCLUDE IN MPI TEST\n# PETSc DMStag tutorial ex8.c\n# Solves the 1D Poisson equation on a vertex-centered DMStag grid,\n#"
  },
  {
    "path": "examples/ex1.jl",
    "chars": 8169,
    "preview": "# 1D staggered FD example\nusing PETSc, MPI, LinearAlgebra, SparseArrays# ForwardDiff\n\nif ~MPI.Initialized()\n    MPI.Init"
  },
  {
    "path": "examples/ex16.jl",
    "chars": 17211,
    "preview": "using PETSc, MPI, Printf\n\n# Van der Pol example adapted from PETSc TS tutorial `ex16.c`, see\n# https://petsc.org/main/sr"
  },
  {
    "path": "examples/ex45.jl",
    "chars": 9328,
    "preview": "# INCLUDE IN MPI TEST\n# Translation of PETSc's ex45.c to PETSc.jl\nusing PETSc, MPI, Printf\n\n# boundary data and forcing "
  },
  {
    "path": "examples/ex50.jl",
    "chars": 14746,
    "preview": "# INCLUDE IN MPI TEST\n#\n# This example demonstrates solving a 2D Poisson equation with Neumann boundary\n# conditions usi"
  },
  {
    "path": "examples/ex51.jl",
    "chars": 8357,
    "preview": "using PETSc, MPI, Printf\n\n# Small ODE to test TS accuracy, see\n# https://petsc.org/main/src/ts/tutorials/ex51.c.html.\n#\n"
  },
  {
    "path": "examples/ex51_implicit.jl",
    "chars": 15322,
    "preview": "using PETSc, MPI, Printf\n\n# Small ODE to test implicit TS accuracy with Gauss/IRK schemes; adapted from\n# https://petsc."
  },
  {
    "path": "examples/laplacian.jl",
    "chars": 1818,
    "preview": "# Finite Difference Laplacian\n#\n# In this example a simple finite difference, 1-D laplacian with Dirichlet\n# boundary co"
  },
  {
    "path": "examples/porosity_waves.jl",
    "chars": 14213,
    "preview": "# INCLUDE IN MPI TEST\n#=\nIn this example we solve the 1D viscoelastic porosity wave equations which\ndescribe how magma a"
  },
  {
    "path": "examples/scalability_tests/ex45_julia.c",
    "chars": 9584,
    "preview": "/*\n  Laplacian in 3D with manufactured solution.\n\n  PDE:  -Laplacian u = 12*pi^2 * sin(2*pi*x) * sin(2*pi*y) * sin(2*pi*"
  },
  {
    "path": "examples/scalability_tests/job.sh",
    "chars": 2464,
    "preview": "#!/bin/bash -l\n#SBATCH --job-name=scaling\n#SBATCH --partition=standard\n#SBATCH --cpus-per-task=1\n#SBATCH --exclusive\n#SB"
  },
  {
    "path": "examples/scalability_tests/makefile",
    "chars": 912,
    "preview": "-include ../../../../petscdir.mk\n\nLOCDIR           = src/ksp/ksp/tutorials/\nMANSEC           = KSP\nCLEANFILES       = rh"
  },
  {
    "path": "examples/scalability_tests/parse_scaling.jl",
    "chars": 9278,
    "preview": "# EXCLUDE FROM TESTING\n#!/usr/bin/env julia\n# parse_scaling.jl\n# Usage: julia parse_scaling.jl scaling_*.out\n\nusing Prin"
  },
  {
    "path": "examples/scalability_tests/submit_scaling.sh",
    "chars": 1649,
    "preview": "#!/bin/bash\n# submit_scaling.sh\n# Usage: ./submit_scaling.sh <ntasks> <Nx> <Ny> <Nz> <mg_levels> [ncoarse]\n#\n# Arguments"
  },
  {
    "path": "examples/scalability_tests/weak_scaling_plot.jl",
    "chars": 4040,
    "preview": "# EXCLUDE FROM TESTING\nusing GLMakie\nusing Statistics\n\n# All KSPSolve times per (ntasks, backend)\n# Multiple runs where "
  },
  {
    "path": "examples/stokes2d_linear_dmstag.jl",
    "chars": 46389,
    "preview": "# INCLUDE IN MPI TEST\n# This shows how to solve the 2D incompressible Stokes equations using SNES solvers,\n# using a sta"
  },
  {
    "path": "src/LibPETSc.jl",
    "chars": 1140,
    "preview": "module LibPETSc\n\nimport PETSc: _doc_external\n\nusing Libdl\nusing MPI\n\nexport PetscLibType,\n    petsclibs,\n    #PetscBool,"
  },
  {
    "path": "src/LibPETSc_const.jl",
    "chars": 291,
    "preview": "# define common PETSc constants\n# this excludes configurable constants (e.g. PetscScalar) which are set in lib.jl\n\nconst"
  },
  {
    "path": "src/LibPETSc_lib.jl",
    "chars": 2752,
    "preview": "\"\"\"\n    PetscLibType{PetscScalar, PetscInt}(petsc_library)\n\nA container for specific PETSc libraries.\n\nAll other contain"
  },
  {
    "path": "src/LibPETSc_startup.jl",
    "chars": 2729,
    "preview": "# Functions needed to find libraries\n#=\nfunction getlibs()\n    libs = ()\n    petsc_libs = ENV[\"JULIA_PETSC_LIBRARY\"]\n\n  "
  },
  {
    "path": "src/PETSc.jl",
    "chars": 1367,
    "preview": "# __precompile__(false)\n\nmodule PETSc\n\nusing MPI, LinearAlgebra, SparseArrays, OffsetArrays, Preferences\n\nMPI.Initialize"
  },
  {
    "path": "src/audit.jl",
    "chars": 6857,
    "preview": "\"\"\"\n    audit_petsc_file(path::AbstractString)\n\nScan a PETSc.jl source file and report PETSc object creations and destro"
  },
  {
    "path": "src/autowrapped/AO_wrappers.jl",
    "chars": 26833,
    "preview": "# autodefined type arguments for class ------\n# -------------------------------------------------------\n\"\"\"\n\tAOFinalizeP"
  },
  {
    "path": "src/autowrapped/Characteristic_wrappers.jl",
    "chars": 10944,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_Characteristic end\nconst Characteristic = Ptr{_n_Charact"
  },
  {
    "path": "src/autowrapped/DM_wrappers.jl",
    "chars": 1448501,
    "preview": "using OffsetArrays\n# autodefined type arguments for class ------\nmutable struct PetscPoCintFn end\n\nmutable struct _n_DMI"
  },
  {
    "path": "src/autowrapped/DMaddons_wrappers.jl",
    "chars": 200386,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_DMAdaptor end\nconst DMAdaptor = Ptr{_n_DMAdaptor}\n\n# ---"
  },
  {
    "path": "src/autowrapped/IS_wrappers.jl",
    "chars": 86131,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_ISColoringValue end\nconst ISColoringValue = Ptr{_n_ISCol"
  },
  {
    "path": "src/autowrapped/ISaddons_wrappers.jl",
    "chars": 52982,
    "preview": "\"\"\"\n\tnltog::ISLocalToGlobalMapping = ISLocalToGlobalMappingDuplicate(petsclib::PetscLibType,ltog::ISLocalToGlobalMapping"
  },
  {
    "path": "src/autowrapped/KSPGuess_wrappers.jl",
    "chars": 11837,
    "preview": "\"\"\"\n\tKSPGuessRegister(petsclib::PetscLibType,sname::String, fnc::external) \nRegisters a method for initial guess computa"
  },
  {
    "path": "src/autowrapped/KSP_wrappers.jl",
    "chars": 262186,
    "preview": "# autodefined type arguments for class ------\nmutable struct KSPConvergedReasonViewFn end\n\nmutable struct KSPMonitorFn e"
  },
  {
    "path": "src/autowrapped/Mat_wrappers.jl",
    "chars": 743837,
    "preview": "# autodefined type arguments for class Mat ------\nmutable struct _n_MatNullSpace end\nconst MatNullSpace = Ptr{_n_MatNull"
  },
  {
    "path": "src/autowrapped/Mataddons_wrappers.jl",
    "chars": 123740,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_MatColoring end\nconst MatColoring = Ptr{_n_MatColoring}\n"
  },
  {
    "path": "src/autowrapped/PC_wrappers.jl",
    "chars": 393624,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PCRiCchardsonConvergedReason end\nconst PCRiCchardsonConv"
  },
  {
    "path": "src/autowrapped/PF_wrappers.jl",
    "chars": 11268,
    "preview": "# autodefined type arguments for class ------\n# -------------------------------------------------------\n\"\"\"\n\tPFSet(petsc"
  },
  {
    "path": "src/autowrapped/PetscBag_wrappers.jl",
    "chars": 25353,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscBag end\nconst PetscBag = Ptr{_n_PetscBag}\n\n# ------"
  },
  {
    "path": "src/autowrapped/PetscBench_wrappers.jl",
    "chars": 13283,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscBench end\nconst PetscBench = Ptr{_n_PetscBench}\n\n# "
  },
  {
    "path": "src/autowrapped/PetscContainer_wrappers.jl",
    "chars": 5364,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscContainer end\nconst PetscContainer = Ptr{_n_PetscCo"
  },
  {
    "path": "src/autowrapped/PetscConvEst_wrappers.jl",
    "chars": 11226,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscConvEst end\nconst PetscConvEst = Ptr{_n_PetscConvEs"
  },
  {
    "path": "src/autowrapped/PetscDLLibrary_wrappers.jl",
    "chars": 7460,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscDLLibrary end\nconst PetscDLLibrary = Ptr{_n_PetscDL"
  },
  {
    "path": "src/autowrapped/PetscDS_wrappers.jl",
    "chars": 119420,
    "preview": "# autodefined type arguments for class ------\nmutable struct PetscPoCintJacFn end\n\nmutable struct PetscRiemannFn end\n\nmu"
  },
  {
    "path": "src/autowrapped/PetscDevice_wrappers.jl",
    "chars": 21339,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscDevice end\nconst PetscDevice = Ptr{_n_PetscDevice}\n"
  },
  {
    "path": "src/autowrapped/PetscDraw_wrappers.jl",
    "chars": 151701,
    "preview": "# -------------------------------------------------------\n# autodefined type arguments for class ------\n\nmutable struct "
  },
  {
    "path": "src/autowrapped/PetscDualSpace_wrappers.jl",
    "chars": 82695,
    "preview": "\"\"\"\n\tPetscDualSpaceRegister(petsclib::PetscLibType,sname::String, fnc::external) \nAdds a new `PetscDualSpaceType`\n\nNot C"
  },
  {
    "path": "src/autowrapped/PetscFE_wrappers.jl",
    "chars": 146382,
    "preview": "\"\"\"\n\tPetscFEFinalizePackage(petsclib::PetscLibType) \nThis function finalizes everything in the `PetscFE` package. It is "
  },
  {
    "path": "src/autowrapped/PetscFV_wrappers.jl",
    "chars": 29124,
    "preview": "# -------------------------------------------------------\n\"\"\"\n\tPetscFVFinalizePackage(petsclib::PetscLibType) \nThis func"
  },
  {
    "path": "src/autowrapped/PetscFunctionList_wrappers.jl",
    "chars": 7147,
    "preview": "\"\"\"\n\tPetscFunctionListDestroy(petsclib::PetscLibType,fl::PetscFunctionList) \nDestroys a list of registered routines.\n\nIn"
  },
  {
    "path": "src/autowrapped/PetscGridHash_wrappers.jl",
    "chars": 4528,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscGridHash end\nconst PetscGridHash = Ptr{_n_PetscGrid"
  },
  {
    "path": "src/autowrapped/PetscHeap_wrappers.jl",
    "chars": 4473,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscHeap end\nconst PetscHeap = Ptr{_n_PetscHeap}\n\n# ---"
  },
  {
    "path": "src/autowrapped/PetscIntStack_wrappers.jl",
    "chars": 5043,
    "preview": "\n\"\"\"\n\tPetscIntStackDestroy(petsclib::PetscLibType,stack::PetscIntStack) \nThis function destroys a stack.\n\nNot Collective"
  },
  {
    "path": "src/autowrapped/PetscKDTree_wrappers.jl",
    "chars": 4843,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscKDTree end\nconst PetscKDTree = Ptr{_n_PetscKDTree}\n"
  },
  {
    "path": "src/autowrapped/PetscLayout_wrappers.jl",
    "chars": 21993,
    "preview": "# autodefined type arguments for class ------\n# -------------------------------------------------------\n\"\"\"\n\tmap::PetscL"
  },
  {
    "path": "src/autowrapped/PetscLimiter_wrappers.jl",
    "chars": 8600,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscLimiter end\nconst PetscLimiter = Ptr{_n_PetscLimite"
  },
  {
    "path": "src/autowrapped/PetscLog_wrappers.jl",
    "chars": 60709,
    "preview": "\n\"\"\"\n\tPetscLogHandlerStart(petsclib::PetscLibType,h::PetscLogHandler) \nConnect a log handler to PETSc's global logging s"
  },
  {
    "path": "src/autowrapped/PetscMatlabEngine_wrappers.jl",
    "chars": 10172,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscMatlabEngine end\nconst PetscMatlabEngine = Ptr{_n_P"
  },
  {
    "path": "src/autowrapped/PetscObject_wrappers.jl",
    "chars": 54286,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscOptionItems end\nconst PetscOptionItems = Ptr{_n_Pet"
  },
  {
    "path": "src/autowrapped/PetscOmpCtrl_wrappers.jl",
    "chars": 6102,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscOmpCtrl end\nconst PetscOmpCtrl = Ptr{_n_PetscOmpCtr"
  },
  {
    "path": "src/autowrapped/PetscOptions_addons_wrappers.jl",
    "chars": 3128,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscOptionsHelpPrCinted end\nconst PetscOptionsHelpPrCin"
  },
  {
    "path": "src/autowrapped/PetscOptions_wrappers.jl",
    "chars": 74698,
    "preview": "# autodefined type arguments for class ------\n# -------------------------------------------------------\n\n\"\"\"\n\tPetscOptio"
  },
  {
    "path": "src/autowrapped/PetscPartitioner_wrappers.jl",
    "chars": 18430,
    "preview": "\"\"\"\n\tPetscPartitionerRegister(petsclib::PetscLibType,sname::String, fnc::external) \nAdds a new PetscPartitioner implemen"
  },
  {
    "path": "src/autowrapped/PetscRandom_wrappers.jl",
    "chars": 17809,
    "preview": "\"\"\"\n\tPetscRandomDestroy(petsclib::PetscLibType,r::PetscRandom) \nDestroys a `PetscRandom` object that was created by `Pet"
  },
  {
    "path": "src/autowrapped/PetscRegressor_wrappers.jl",
    "chars": 23251,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscRegressor end\nconst PetscRegressor = Ptr{_n_PetscRe"
  },
  {
    "path": "src/autowrapped/PetscSF_wrappers.jl",
    "chars": 59529,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_MPI_Group end\nconst MPI_Group = Ptr{_n_MPI_Group}\n\n\n\n\"\"\""
  },
  {
    "path": "src/autowrapped/PetscSection_wrappers.jl",
    "chars": 113259,
    "preview": "mutable struct _p_PetscSectionSym end\nconst PetscSectionSym = Ptr{_p_PetscSectionSym}\n\n\n\"\"\"\n\tPetscSectionCreate(petsclib"
  },
  {
    "path": "src/autowrapped/PetscSegBuffer_wrappers.jl",
    "chars": 8553,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscSegBuffer end\nconst PetscSegBuffer = Ptr{_n_PetscSe"
  },
  {
    "path": "src/autowrapped/PetscSpace_wrappers.jl",
    "chars": 36322,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscSpace end\nconst PetscSpace = Ptr{_n_PetscSpace}\n\nmu"
  },
  {
    "path": "src/autowrapped/PetscToken_wrappers.jl",
    "chars": 2534,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscToken end\nconst PetscToken = Ptr{_n_PetscToken}\n\n# "
  },
  {
    "path": "src/autowrapped/PetscViennaCLIndices_wrappers.jl",
    "chars": 104,
    "preview": "# autodefined type arguments for class ------\n# -------------------------------------------------------\n"
  },
  {
    "path": "src/autowrapped/PetscViewer_wrappers.jl",
    "chars": 220241,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_DMPlexStorageVersion end\nconst DMPlexStorageVersion = Pt"
  },
  {
    "path": "src/autowrapped/Petsccomm_wrappers.jl",
    "chars": 15100,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_PetscSubcomm end\nconst PetscSubcomm = Ptr{_n_PetscSubcom"
  },
  {
    "path": "src/autowrapped/SNESLineSearch_wrappers.jl",
    "chars": 63899,
    "preview": "# autodefined type arguments for class ------\nmutable struct SNESLineSearchVIProjectFn end\n\nmutable struct SNESLineSearc"
  },
  {
    "path": "src/autowrapped/SNES_wrappers.jl",
    "chars": 303565,
    "preview": "# autodefined type arguments for class ------\nmutable struct SNESFunctionFn end\n\nmutable struct SNESNGSFn end\n\nmutable s"
  },
  {
    "path": "src/autowrapped/Sys_wrappers.jl",
    "chars": 421976,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_SSL_CTX end\nconst SSL_CTX = Ptr{_n_SSL_CTX}\n\nmutable str"
  },
  {
    "path": "src/autowrapped/TS_wrappers.jl",
    "chars": 367812,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_TSTrajectory end\nconst TSTrajectory = Ptr{_n_TSTrajector"
  },
  {
    "path": "src/autowrapped/TSaddons_wrappers.jl",
    "chars": 95926,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_TSMonitorDrawCtx end\nconst TSMonitorDrawCtx = Ptr{_n_TSM"
  },
  {
    "path": "src/autowrapped/Tao_addons_wrappers.jl",
    "chars": 39527,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_TaoMonitorDrawCtx end\nconst TaoMonitorDrawCtx = Ptr{_n_T"
  },
  {
    "path": "src/autowrapped/Tao_wrappers.jl",
    "chars": 178009,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_TaoLineSearch end\nconst TaoLineSearch = Ptr{_n_TaoLineSe"
  },
  {
    "path": "src/autowrapped/VecTagger_wrappers.jl",
    "chars": 29819,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_VecTagger end\nconst VecTagger = Ptr{_n_VecTagger}\n\n# ---"
  },
  {
    "path": "src/autowrapped/Vec_wrappers.jl",
    "chars": 202721,
    "preview": "# autodefined type arguments for class Vec ------\nmutable struct _n_ViennaCLVector end\nconst ViennaCLVector = Ptr{_n_Vie"
  },
  {
    "path": "src/autowrapped/Vecs_wrappers.jl",
    "chars": 76348,
    "preview": "# autodefined type arguments for class ------\nmutable struct _n_Vecs end\nconst Vecs = Ptr{_n_Vecs}\n\nmutable struct n_Pet"
  },
  {
    "path": "src/autowrapped/enums_wrappers.jl",
    "chars": 38258,
    "preview": "@enum PetscRegressorLinearType begin\n    REGRESSOR_LINEAR_OLS = 0\n    REGRESSOR_LINEAR_LASSO = 1\n    REGRESSOR_LINEAR_RI"
  },
  {
    "path": "src/autowrapped/petsc_library.jl",
    "chars": 14756,
    "preview": "#\n# START OF PROLOGUE\n#\n\nusing MPI\nconst MPI_Comm = MPI.Comm\nconst MPI_Datatype = MPI.MPI_Datatype\nconst MPI_File = MPI."
  },
  {
    "path": "src/autowrapped/petsc_wrappers_version.jl",
    "chars": 160,
    "preview": "# Auto-generated by wrapping/generatejuliabindings.jl — do not edit\n# PETSc installation: /Users/kausb/Downloads/petsc\nc"
  },
  {
    "path": "src/autowrapped/petscarray.jl",
    "chars": 5392,
    "preview": "# this creates a Julia wrapper around a PETSc array\n# that holds a pointer to the underlying PETSc data\n# and a Julia ar"
  },
  {
    "path": "src/autowrapped/senums_wrappers.jl",
    "chars": 1597,
    "preview": "# not quite sure yet how to deal with this\nPetscRegressorType=Ptr{Cchar}\nDMFieldType=Ptr{Cchar}\nDMPlexTransformType=Ptr{"
  },
  {
    "path": "src/autowrapped/struct_wrappers.jl",
    "chars": 11580,
    "preview": "struct PetscFEGeom\n    mode::PetscFEGeomMode\n    isAffine::PetscBool\n    dim::PetscInt\n    dimEmbed::PetscInt\n    numCel"
  },
  {
    "path": "src/autowrapped/typedefs_wrappers.jl",
    "chars": 2140,
    "preview": "const LandauIdx = PetscInt\nconst MatScalar = PetscScalar\nconst MatReal = PetscReal\nconst PetscElemScalar = PetscScalar\nc"
  },
  {
    "path": "src/deprecated/const.jl",
    "chars": 5254,
    "preview": "# define common PETSc constants\n# this excludes configurable constants (e.g. PetscScalar) which are set in lib.jl\n\nconst"
  },
  {
    "path": "src/deprecated/dm.jl",
    "chars": 9181,
    "preview": "const CDM = Ptr{Cvoid}\nabstract type AbstractDM{PetscLib} end\n\nimport PETSc.LibPETSc: DMType, PetscViewer, ISColoringTyp"
  },
  {
    "path": "src/deprecated/dm_wrapped.jl",
    "chars": 218174,
    "preview": "\"\"\"\n\t UNTESTED !!!\n\tnewdm = DMClone(dm::AbstractDM{PetscLib})\n\nCreates a `DM` object with the same topology as the origi"
  },
  {
    "path": "src/deprecated/dmda.jl",
    "chars": 13454,
    "preview": "abstract type AbstractDMDA{PetscLib} <: AbstractDM{PetscLib} end\n\nmutable struct DMDA{PetscLib} <: AbstractDMDA{PetscLib"
  },
  {
    "path": "src/deprecated/dmstag.jl",
    "chars": 15106,
    "preview": "abstract type AbstractDMStag{PetscLib} <: AbstractDM{PetscLib} end\n\nmutable struct DMStag{PetscLib} <: AbstractDMStag{Pe"
  },
  {
    "path": "src/deprecated/dmstag_wrapped.jl",
    "chars": 71345,
    "preview": "\"\"\"\n\tdm = DMStagCreate1d(petsclib::PetscLib,comm::MPI_Comm,bndx::DMBoundaryType,M::Int,dof0::Int,dof1::Int,stencilType::"
  },
  {
    "path": "src/deprecated/ksp.jl",
    "chars": 15918,
    "preview": "const CKSP = Ptr{Cvoid}\nconst CKSPType = Cstring\nconst KSPType = LibPETSc.KSPType\n\nPetscReal = LibPETSc.PETSC_REAL\nimpor"
  },
  {
    "path": "src/deprecated/ksp_wrapped.jl",
    "chars": 153904,
    "preview": "\"\"\"\n\t UNTESTED !!!\n\tinksp = KSPCreate(comm::MPI_Comm)\n\nCreates the `KSP` context.\n\nCollective\n\nInput Parameter:\n===\n- `c"
  },
  {
    "path": "src/deprecated/lib.jl",
    "chars": 3762,
    "preview": "using Libdl\n\"\"\"\n    PetscLibType{PetscScalar, PetscInt}(petsc_library)\n\nA container for specific PETSc libraries.\n\nAll o"
  },
  {
    "path": "src/deprecated/mat.jl",
    "chars": 18442,
    "preview": "const CMat = Ptr{Cvoid}\n\nabstract type AbstractMat{PetscLib, PetscScalar} <: AbstractMatrix{PetscScalar} end\n\nBase.eltyp"
  },
  {
    "path": "src/deprecated/matshell.jl",
    "chars": 2612,
    "preview": "\"\"\"\n    MatShell(\n        petsclib::PetscLib,\n        obj::OType,\n        comm::MPI.Comm,\n        local_rows,\n        lo"
  },
  {
    "path": "src/deprecated/options.jl",
    "chars": 7126,
    "preview": "const CPetscOptions = Ptr{Cvoid}\n\n\"\"\"\n    AbstractOptions{PetscLib <: PetscLibType}\n\nAbstract type of PETSc solver optio"
  },
  {
    "path": "src/deprecated/pc.jl",
    "chars": 2865,
    "preview": "\nconst CPC = Ptr{Cvoid}\nconst CPCType = Cstring\nconst PCType = LibPETSc.PCType\n\nabstract type AbstractPC{PetscLib, Petsc"
  },
  {
    "path": "src/deprecated/ref.jl",
    "chars": 1173,
    "preview": "\n@for_libpetsc begin\n\n    function incref(::Type{$PetscScalar}, obj)\n        @chk ccall((:PetscObjectReference, $libpets"
  },
  {
    "path": "src/deprecated/snes.jl",
    "chars": 8520,
    "preview": "const CSNES = Ptr{Cvoid}\nconst CSNESType = Cstring\n\nabstract type AbstractSNES{PetscLib} end\n\n\"\"\"\n    SNESPtr{PetscLib}("
  },
  {
    "path": "src/deprecated/sys.jl",
    "chars": 1818,
    "preview": "const CPetscObject = Ptr{Cvoid}\n\nconst UnionPetscTypes = Union{\n    AbstractVec,\n    AbstractMat,\n    AbstractKSP,\n    #"
  },
  {
    "path": "src/deprecated/utils.jl",
    "chars": 1600,
    "preview": "#\n# A few utility functions to help wrapping PETSc\n\n\nexport PETSc_unsafe_wrap, PETSc_RefPtr\n\nMPI_Comm = MPI.Comm\n\n\n\"\"\"\n "
  },
  {
    "path": "src/deprecated/vec.jl",
    "chars": 17171,
    "preview": "const CVec = Ptr{Cvoid}\n\nabstract type AbstractVec{PetscLib, PetscScalar} <: AbstractVector{PetscScalar} end\n\nBase.eltyp"
  },
  {
    "path": "src/deprecated/viewer.jl",
    "chars": 3092,
    "preview": "# ideally we would capture the output directly, but this looks difficult\n# easiest option is to redirect stdout\n# based "
  },
  {
    "path": "src/dm.jl",
    "chars": 13113,
    "preview": "import .LibPETSc: AbstractPetscDM, PetscDM, CDM\n\n# Custom display for REPL\nfunction Base.show(io::IO, v::AbstractPetscDM"
  },
  {
    "path": "src/dmda.jl",
    "chars": 6940,
    "preview": "import .LibPETSc: AbstractPetscDM, PetscDM, CDM\n\n\n\"\"\"\n    DMDA(\n        petsclib::PetscLib\n        comm::MPI.Comm,\n     "
  },
  {
    "path": "src/dmstag.jl",
    "chars": 12386,
    "preview": "\n\n# Helper to convert points_per_proc tuples to PetscInt\nto_petscint_tuple(t::Tuple, PetscInt) = map(arr -> PetscInt.(ar"
  },
  {
    "path": "src/init.jl",
    "chars": 13178,
    "preview": "\"\"\"\n   initialized(petsclib)\n\nCheck if `petsclib` is initialized\n\n# External Links\n$(_doc_external(\"Sys/PetscInitialized"
  },
  {
    "path": "src/ksp.jl",
    "chars": 8880,
    "preview": "import .LibPETSc: AbstractPetscKSP, CKSP, PetscKSP, AbstractPetscDM\n\n# Custom display for REPL\nfunction Base.show(io::IO"
  },
  {
    "path": "src/mat.jl",
    "chars": 25070,
    "preview": "\nimport .LibPETSc: AbstractPetscMat, PetscMat, CMat, MatStencil, InsertMode\n\n# Custom display for REPL\nfunction Base.sho"
  },
  {
    "path": "src/options.jl",
    "chars": 5798,
    "preview": "\n\nimport .LibPETSc: AbstractPetscOptions, PetscOptions, COptions\n\n# Custom display for REPL\nfunction Base.show(io::IO, v"
  },
  {
    "path": "src/snes.jl",
    "chars": 7721,
    "preview": "import .LibPETSc: AbstractPetscSNES, CSNES, PetscSNES\n\n# Custom display for REPL\nfunction Base.show(io::IO, v::AbstractP"
  },
  {
    "path": "src/startup.jl",
    "chars": 1797,
    "preview": "# Functions needed to find libraries\nfunction getlibs()\n    libs = ()\n    petsc_libs = ENV[\"JULIA_PETSC_LIBRARY\"]\n\n    f"
  },
  {
    "path": "src/string_wrappers.jl",
    "chars": 2717,
    "preview": "# Convenience wrappers for PETSc SetType functions that accept Julia strings\n# instead of C string pointers\n#\n# These wr"
  },
  {
    "path": "src/string_wrappers_extra.jl",
    "chars": 962,
    "preview": "\"\"\"\n    TSSetType(petsclib, ts, type::String)\n\nConvenience wrapper for setting TS (time-stepping) type using a Julia str"
  },
  {
    "path": "src/sys.jl",
    "chars": 811,
    "preview": "\n\n\"\"\"\n    comm = function getcomm(\n                            obj::Union{\n                                PetscVec{Pets"
  },
  {
    "path": "src/ts.jl",
    "chars": 10188,
    "preview": "\"\"\"\n    TSSetRHSFunction(petsclib, ts, r, fptr::Ptr{Cvoid}, ctx = C_NULL)\n\nConvenience overload for low-level TS RHS cal"
  },
  {
    "path": "src/vec.jl",
    "chars": 12594,
    "preview": "# these are julia-specific PETSc functions, which makes PETSc a bit easier to use\n# a (nearly) full wrapper is in wrappi"
  },
  {
    "path": "test/01-hello.jl",
    "chars": 308,
    "preview": "using MPI\nusing PETSc\nif !Sys.iswindows()\n    MPI.Init()\nend\n\n# Windows PETSc binaries are built without MPI support, us"
  },
  {
    "path": "test/dmda.jl",
    "chars": 21360,
    "preview": "using Test\nusing PETSc, MPI\nif !Sys.iswindows()\n    MPI.Initialized() || MPI.Init()\nend\n\n\n@testset \"DMDACreate1D\" begin\n"
  },
  {
    "path": "test/dmnetwork.jl",
    "chars": 1395,
    "preview": "using Test\nusing PETSc, MPI\n\n# Initialize MPI & PETSc only if needed\nmpi_started = false\nif !MPI.Initialized()\n    MPI.I"
  },
  {
    "path": "test/dmproduct.jl",
    "chars": 1867,
    "preview": "using Test\nusing PETSc, MPI\n\n# Initialize MPI & PETSc only if needed\nmpi_started = false\nif !MPI.Initialized()\n    MPI.I"
  },
  {
    "path": "test/dmshell.jl",
    "chars": 825,
    "preview": "using Test\nusing PETSc, MPI\n\n# Initialize MPI & PETSc only if needed\n mpi_started = false\n if !MPI.Initialized()\n     MP"
  },
  {
    "path": "test/dmstag.jl",
    "chars": 11992,
    "preview": "using Test\nusing PETSc, MPI\nif !Sys.iswindows()\n    MPI.Initialized() || MPI.Init()\nend\n\n\n@testset \"DMStagCreate1d\" begi"
  },
  {
    "path": "test/doc_examples_is.jl",
    "chars": 4728,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Documentation examples for IS\" begin\n    petsclib = PETSc.getlib(PetscScalar"
  },
  {
    "path": "test/doc_examples_tao.jl",
    "chars": 4730,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Documentation examples for Tao\" begin\n    petsclib = PETSc.getlib(PetscScala"
  },
  {
    "path": "test/doc_examples_ts.jl",
    "chars": 3096,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Documentation examples for TS\" begin\n    petsclib = PETSc.getlib(PetscScalar"
  },
  {
    "path": "test/doc_examples_viewer.jl",
    "chars": 2440,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Documentation examples for PetscViewer\" begin\n    petsclib = PETSc.getlib(Pe"
  },
  {
    "path": "test/examples.jl",
    "chars": 594,
    "preview": "using PETSc\nusing Test\nusing MPI\n\nusing .PETScTestUtils: find_sources\n\n@testset \"examples\" begin\n  examples_dir = joinpa"
  },
  {
    "path": "test/init.jl",
    "chars": 7697,
    "preview": "using Test\nusing PETSc\n\n@testset \"init\" begin\n    for petsclib in PETSc.petsclibs\n        # The first time through final"
  },
  {
    "path": "test/ksp.jl",
    "chars": 6423,
    "preview": "using Test\nusing MPI\nif !Sys.iswindows()\n    MPI.Initialized() || MPI.Init()\nend\nusing PETSc\nusing LinearAlgebra: mul!\nu"
  },
  {
    "path": "test/lib.jl",
    "chars": 613,
    "preview": "using Test\nusing PETSc\n\n@testset \"lib\" begin\n    for petsclib in PETSc.petsclibs\n        PetscScalar = petsclib.PetscSca"
  },
  {
    "path": "test/low_level_is.jl",
    "chars": 5228,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Low-level IS (Index Set) functions\" begin\n    petsclib = PETSc.getlib(PetscS"
  },
  {
    "path": "test/low_level_petscsection.jl",
    "chars": 8659,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n# Initialize PETSc\npetsclib = PETSc.getlib()\nPETSc.initialize(petsclib)\n        test_c"
  },
  {
    "path": "test/low_level_tao.jl",
    "chars": 3386,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Low-level Tao (optimization) functions\" begin\n    petsclib = PETSc.getlib(Pe"
  },
  {
    "path": "test/low_level_ts.jl",
    "chars": 2372,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Low-level TS (Time Stepping) functions\" begin\n    petsclib = PETSc.getlib(Pe"
  },
  {
    "path": "test/low_level_viewer.jl",
    "chars": 2062,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"Low-level PetscViewer convenience functions\" begin\n    petsclib = PETSc.getl"
  },
  {
    "path": "test/mat.jl",
    "chars": 17381,
    "preview": "using Test\nusing PETSc, MPI\nusing LinearAlgebra: norm, mul!, Adjoint, Transpose, issymmetric, ishermitian\nusing SparseAr"
  },
  {
    "path": "test/matshell.jl",
    "chars": 1258,
    "preview": "using Test\nusing PETSc\nusing MPI\n\n@testset \"MatShell\" begin\n    for petsclib in PETSc.petsclibs\n        petsclib = PETSc"
  },
  {
    "path": "test/mpi_examples.jl",
    "chars": 632,
    "preview": "using Test\nusing MPI\n\nusing .PETScTestUtils: find_sources\n\n@testset \"mpi examples\" begin\n  examples_dir = joinpath(dirna"
  },
  {
    "path": "test/mpimat.jl",
    "chars": 2429,
    "preview": "using Test\nusing MPI\nif !Sys.iswindows()\n    MPI.Initialized() || MPI.Init()\nend\nusing PETSc\nusing LinearAlgebra: mul!, "
  },
  {
    "path": "test/mpivec.jl",
    "chars": 5303,
    "preview": "using Test\nusing MPI\nif !Sys.iswindows()\n    MPI.Initialized() || MPI.Init()\nend\nusing PETSc\nusing LinearAlgebra: norm\n\n"
  }
]

// ... and 21 more files (download for full content)

About this extraction

This page contains the full source code of the JuliaParallel/PETSc.jl GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 221 files (7.6 MB), approximately 2.0M tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!