Release notes
v0.5.2
0.5.2 is what a staggered-grid solver needs from PETSc.jl: DMStag gets locations keyed by axis, stencils built from 1-based indices, assembly and halo exchange, and field views that hand a callback concretely typed arrays. TS gets what a long run needs, and Vec and Mat get the Base verbs that were missing. The rest is leaks closed in the generated layer and two performance traps, one of which made a 4-rank DMStag solve 25 times slower. Working code is most likely to notice the three behaviour changes below; the rest is additive.
Behaviour changes
- When several MPI ranks share a node,
initializesets Julia's BLAS pool to one thread, unless-blas_num_threads,OPENBLAS_NUM_THREADSorOMP_NUM_THREADSsays otherwise. Each rank otherwise ran a pool of busy-waiting threads, which made a 4-rank DMStag Stokes solve 25× slower. Serial runs and one rank per node are unaffected. solution(ksp),solution(ts),local_coordinates(dm)and thevatol/vrtoloftolerances(ts)return a borrowedPetscVec, assolution(snes)already did, instead of aVecPtr. Both areAbstractPetscVecs with the same ownership, so only code that checks forVecPtrby type notices.LibPETScrelease functions that free a C array of handles (VecDestroyVecs,MatDestroyMatrices,DMPlexRestoreConeRecursive, …) no longer accept a Julia array of raw handles, which PETSc would have freed as its own memory. They take the pointer PETSc returned;MatDestroySubMatrices,MatDestroyMatrices,VecNestRestoreSubVecsReadand the two subdomain destroyers also take the vector the matching wrapper returned.
Fixed
with_local_array!spent 23 allocations per call and did not infer its return type: it queried every vector'sPetscMemTypewith a checkout of its own before checking the vectors out for real, andmemtype_backenddispatched on aValbuilt from that run-time value. Host memory, the common case, now answers before the dispatch and the extra query is gone, so a one-vector checkout costs 2 allocations and infers.with_field_views!had the same fault plus aread/writevariable that changed type, and goes from 24 allocations to 8. A SNES solve whose callbacks check arrays out drops from 210 allocations to 47. Device memory dispatches as before, so the CUDA extension is unaffected.v[i] = aon aPetscVecandv[i]built their one-element index vector twice over.v[i] = anow allocates nothing andv[i]two.- Out-of-place broadcasting over a
PetscVec,w = 2 .* x, read one entry at a time throughVecGetValues, which cost 4003 allocations at 10 000 entries and failed on a distributed vector with a raw PETSc error. It runs on the local arrays now, at about 5 allocations whatever the length, and still gives aVector. A distributed vector throws anArgumentError, since aVectorof the whole vector only means something where one rank holds all of it. - Broadcasting into a
PetscVec(y .= 2 .* x, a typicalMatShellbody) read and wrote the vectors one entry at a time, throughVecGetValuesandVecSetValues: at 10 000 entries aMatShellproduct took 139 000 allocations and was 500× slower than the same loop on the local arrays. The vectors now take part as their local arrays, at a fixed cost of about 10 allocations. On several ranks the broadcast runs over the entries each rank owns. LibPETSc.ISColoringGetISwithPETSC_OWN_POINTERreturned the index sets as borrowed, so they and the C array holding them leaked. The index sets are now owned by the caller and the array is freed.ISColoringRestoreISaccepts the vectorISColoringGetISreturns.LibPETSc.DMCreateFieldISleaked the field names and both C arrays.- The TS and SNES manual pages still described the 0.5.0 callback rules (return an error code, exceptions become a
PetscError). - 14
LibPETScfunctions that return a vector of new handles leaked the C array PETSc allocated for them:DMCreateDomainDecomposition,DMCreateFieldDecomposition,DMCreateSuperDM,DMCreateSectionSuperDM,MatSubdomainsCreateCoalesce,PCASMCreateSubdomains,PCASMCreateSubdomains2D,PCGASMCreateSubdomains,PCGASMCreateSubdomains2D,PCGASMGetSubdomains,PCGetCoarseOperators,PCGetInterpolations,PetscQuadratureComputePermutationsandVecConcatenate. The array is now freed once the handles are copied. LibPETSc.MatCreateSubMatricesandMatCreateSubMatricesMPIdid not work withMAT_REUSE_MATRIX, andMatDestroySubMatricescould not take their result. Pass the returned vector back to either.LibPETSc.VecNestRestoreSubVecsReadaccepts the vectorVecNestGetSubVecsReadreturns; before, it only took a raw pointer that no wrapper handed out, so the read lock could not be released.LibPETSc.PCASMDestroySubdomainsandPCGASMDestroySubdomainsaccept the vectors the subdomain creators return.- On an Int32 PETSc build (the
PetscInt = "Int32"preference),LibPETSc.DMStagStencil,MatStenciland the other C structs hadInt64fields, so PETSc read them wrong. They now take the integer width of the loaded library, and the two stencil constructors convert their indices to it. LibPETSc.DMStagRestoreProductCoordinateArraysReadrestored the arrays through the writable restore, which does not match the read-only get.- The
KSPandSNESdocstrings did not say when the options given to the constructor are applied: once at construction forKSP, at everysolve!forSNES(andTS). -blas_num_threadshad no effect. Every PETScjll build calls BLAS through libblastrampoline, so PETSc's BLAS runs in Julia's OpenBLAS pool, which PETSc cannot size.initializenow forwards the option to `LinearAlgebra.BLAS.setnum_threads`.initialize(petsclib; options)appended-no_signal_handlerto the caller'soptionsvector.- On Windows, the
optionsandlog_viewgiven toinitializenever reached PETSc: they went throughENV["PETSC_OPTIONS"], which PETSc there reads from its own copy of the environment. They are now PETSc's command line (LibPETSc.PetscInitializetakes aVector{String}), and still override aPETSC_OPTIONSset before Julia starts.
Added
- A performance contract:
test/core/performance.jlchecks that 29 calls meant for a loop infer a concrete return type and allocate the same amount at two problem sizes, anddocs/src/man/performance.mdsays how to keep that cost (array blocks, BLAS threads per rank, concretely typed objects, precompiling beforempiexec,-log_view). copyto!(dst, src)between twoPetscVecs.- Matrix methods:
fill!(A, 0),zero_rows!andzero_rows_local!for Dirichlet rows,set_option!,diagonal!(d, A)andisassembled. destroy!onIS,AO,PFandTaohandles.LibPETSc.PetscFree, for memory PETSc hands to the caller.- Long
TSruns:set_pre_step!(f!, ts)andset_post_step!(f!, ts)hooks that may change the run,set_max_snes_failures!,set_max_step_rejections!,set_error_if_step_fails!,set_step_number!for restarts, andequation_type/set_equation_type!. set_solution!(snes, x), as onTS.LibPETSc.PETSC_UNLIMITEDandLibPETSc.PETSC_CURRENT, which PETSc's manual pages ask for.- DMStag locations by axis:
vertex_location(dm),edge_location(dm, a, b),face_location(dm, axis)andelement_location(dm), which mean the same in 2D and 3D, unlikeDMSTAG_DOWN. stencil(dm, loc, I; dof = 0)builds aDMStagStencilfrom a 1-based element index, allocation free.- Assembly with DMStag stencils:
set_values!(J, dm, rows, cols, vals, mode),set_values!(v, dm, positions, vals, mode)andzero_rows_local!(J, dm, rows, diag). They take anyAbstractVector, and pass aVectoror a prefix viewview(buf, 1:n)of one without a copy. LibPETSc.IS(dm, loc => dof, ...), the index set of whole DMStag fields, forset_fieldsplit_is!.length(is)on anIS: its global size, aslengthof aPetscVec.on_lower_side(loc, axis): whether a DMStag location sits on the lower side of its element alongaxis, so on the axes where it has one more index than there are elements.local_to_local!(dst, dm, src)and the in-placelocal_to_local!(v, dm), to refresh ghost points.with_product_coordinates(f, dm): read-only access to a DMStag's per-axis coordinates, handed back whenfreturns.set_matrix_preallocate_only!(dm, flag), so a matrix fromdmgets its nonzero pattern from the first assembly.with_field_views!(f, dm, vecs...; fields, read, write): concretely typed views of DMStag local vectors bylocation => dof, indexed likestencil, handed back whenfreturns. About 9 allocations per vector, whatever the grid size. The docstring shows two DMs checked out by nesting, with one vector handed back mid-scope for a halo exchange.
v0.5.1
0.5.1 settles how a wrapper relates to its PETSc object and what happens when PETSc calls back into Julia (docs/src/man/naming.md §18), fixes a use-after-free, and adds what a nested or logged solve needs. Working code is most likely to notice the four behaviour changes below; each replaces behaviour that was unsafe or contradicted the docs.
Behaviour changes
- Handles returned by a
LibPETScGetfunction are borrowed, as in PETSc:destroy!on them does nothing. This coverssnes(ts),ksp(ts),pc(ksp)and callback arguments, which could be destroyed from under a running solver. The 36Getfunctions that return a new reference (MatGetFactor,DMLabelGetStratumIS, …; seewrapping/generator/rules/ownership.toml) still return an owner. - An exception in a
TScallback comes out ofsolve!orstep!as itself, not as aPetscError. LibPETSc.PCis a handle type,PC{PetscLib}, instead of a raw pointer. Code that passes a PC between calls is unaffected; code that expects aPtrneedspc.ptr.add_boundary!andadd_natural_boundary!returndminstead of the boundary number, whichLibPETSc.DMAddBoundarystill returns.
Fixed
- Use-after-free:
PetscVec(petsclib, array)andPetscVec(petsclib, comm, array)did not keeparrayalive, so after a garbage collection the vector could use reclaimed memory.ksp \ bwith a Julia vector andM * xwith aMatShellhad the same bug. The same applied to matrices built on CSR arrays once a solver outlived the handle. - An exception in a SNES, KSP or
MatShellcallback unwound through PETSc's C code, which is undefined behaviour.solve!,step!andsetup!now rethrow the original exception; a solve started throughLibPETSclogs it and throwsPetscError. - A callback set through a borrowed handle, such as
set_function!(f!, snes(ts), r), could be garbage-collected while the solver still used it. Callbacks, the user context and constructor options now live with the PETSc object (naming.md §18.3); the old fields such assnes.user_ctxstill work. set_convergence_test!overwrotesnes.user_ctx.KSP(petsclib, comm, S::SparseMatrixCSC)leaked its matrix.TSIRKtypes (-ts_irk_type gauss, …) were lost afterfinalizeandinitialize.PetscOptions(petsclib)succeeded on an uninitialized library; it now throwsPetscNotInitializedlike every other constructor.set_from_options!(ts)ignored the options given to theTSconstructor.converged_reason(ksp),solution(snes)andset_from_options!(snes), shown in the manual, did not exist.
Added
pc(ksp), withset_type!,type_nameandset_fieldsplit_is!, and a Julia:shellpreconditioner throughset_shell_apply!(apply!, p)andset_shell_setup!(setup!, p).- Solver readers:
ksp(snes),solution(snes),iteration_number,converged_reason,ksp_iterations(snes),function_norm(snes)andprev_time(ts). - Nested solvers without
LibPETSc:set_operators!(ksp, A, P = A),set_dm_active!,set_options_prefix!andoptions_prefix, andset_from_options!onKSPandSNES. set_function_domain_error!(snes), anduser_ctx/set_user_ctx!onSNESandTS.- Every handle carries an
ownfield, read byowns(obj); constructors takeownas a keyword.destroy!(pc)destroys aPCcreated withLibPETSc.PCCreate.
Changed
- Every
!function returns the object it mutates (naming.md §7), for exampleset_type!(ksp, :cg)returnskspandsolve!(x, ksp, b)returnsx; about 60 returnednothing. The exceptions aredestroy!and other releases, which returnnothing, andwith_local_array!, which returns its block's result. - A callback's return value is ignored: it fails by throwing. Until v0.6, a nonzero
Integerstill fails the call and warns. KSP,MatShelland the CSR andSparseMatrixCSCPetscMatconstructors attach a finalizer on one process, likeSNESandTS.save_vtk!andvtk_merge_tensor!are nowsave_vtkandvtk_merge_tensor, since they mutate no argument; the old names warn until v0.6.MPIPreferencesis a test-only dependency.
v0.5.0
PETSc.jl 0.5 wraps PETSc 3.25.4 (PETSc_jll 3.25) and requires Julia 1.12. The low-level LibPETSc bindings are regenerated by a new rules-based generator (wrapping/, see wrapping/WRAPPING.md) that can be rerun for every PETSc release; the hand edits that used to live in src/autowrapped/ are expressed as rules and overrides. This changes the calling convention of many LibPETSc functions.
Breaking changes (low-level LibPETSc)
- Outputs are returned, not written into caller-supplied handles or
Refs.PCCreate(petsclib, comm)returns thePC;KSPGetPC(petsclib, ksp)returns thePC;DMPlexDistribute(petsclib, dm, overlap)returns(sf, dmParallel);ISSorted(petsclib, is)returns aPetscBool;PetscSFGetGraph(petsclib, sf)returns(nroots, nleaves, ilocal, iremote). About 480 functions changed this way (wrapping/DEVIATIONS.mdlists every category). XDestroytakes the handle or aRefto it:PetscViewerDestroy(petsclib, viewer).- String enums are Julia
Strings.PCSetType(petsclib, pc, "ilu")works directly;Base.unsafe_convert(Ptr{Int8}, "ilu")is no longer accepted, and the hand-writtenStringoverloads (src/string_wrappers*.jl) are gone. The registered names are constants:LibPETSc.PCMG == "mg",LibPETSc.KSPGMRES,LibPETSc.MATSEQAIJ, ...XGetTypereturns aString(""when unset). PetscKSP->KSP,PetscSNES->SNES,AbstractPetscKSP->AbstractKSP,AbstractPetscSNES->AbstractSNES;AbstractPETScMemBackend->AbstractPetscMemBackend(PR #260 naming conventions). The old names remain as deprecated aliases until v0.6.- Input arguments accept abstract types (
AbstractPetscVec,AbstractVector{<:Number},Integer), returned handles are concrete (PR #263). A call with unsupported argument types throws instead of silently returningnothing. - Callbacks and contexts are
Ptr{Cvoid};const T *xinputs areVector{T};char[]inputs areString;const char *x[]outputs return aString. - PETSc-owned output arrays with a documented length come back as
Vectors (arrays of handles asVector{IS}etc.); those without one return the raw pointer. PetscSplitOwnership*,PetscSortRemoveDups*and thenmaxofPetscOptionsGet*Arraytake and return their in/out scalar.- 95 header-inline functions and macros without a symbol in
libpetsc(PetscStrcmp,PetscTime,VecSetValue,MatSetValue,PetscOptionsBegin, ...) are no longer wrapped. - Deprecated enum values are not emitted.
KSPSetDMActivetakes theKSPDMActiveflag (PETSc 3.25).
High-level API renamed (naming conventions)
The high-level interface follows docs/src/man/naming.md from v0.5 on. Roughly a hundred names change, and the full rename table is at the end of that page — what follows is what the changes are and how to move.
The register and the shims. scripts/renames.jl is the register: a plain list of old => new pairs plus the internal and unchanged-public sets. src/deprecations.jl, src/public_names.jl, src/audit_names.jl and test/test_deprecations.jl are generated from it by scripts/generate_renames.jl, so they cannot drift apart, and scripts/api_surface.jl --check (run by the test suite) fails if a binding is in none of its sets. Every renamed name keeps a forwarding shim that warns once per call site — through @warn, not Base.depwarn, so it prints whatever --depwarn is set to — and the shims are removed in v0.6.
Exports. PETSc now exports only types and construction entry points:
export LibPETSc
export DMDA, DMStag, DMPlex
export PetscVec, PetscMat, PetscOptions
export KSP, SNES, TS
export petsclibsv0.4 exported twelve functions and no types at all. audit_petsc_file, set_petsclib, set_library!, unset_library!, library_info, AbstractPetscMemBackend, AbstractPETScMemBackend, determine_memtype, get_petsc_arrays, restore_petsc_arrays and dmda_star_fd_coloring lose their export (HostBackend was exported but never defined). No shim can help here: the replacements are not exported either, so using PETSc code qualifies the call (PETSc.set_library!) or imports the name. The rest of the API is marked public, so names(PETSc) reports it without exporting it.
Construction goes through the type. PetscVec(petsclib, …) replaces VecSeq and as_petsc_vec; PetscMat(petsclib, …) replaces MatSeqAIJ, MatSeqDense, MatCreateSeqAIJ, MatSeqAIJWithArrays and MatAIJ; PetscOptions replaces Options; PetscLibType(path; …) replaces set_petsclib. KSP, SNES and TS are types rather than factory functions, so ksp isa PETSc.KSP holds.
A typed DM hierarchy. DMDA{L,N}, DMStag{L,N} and DMPlex{L} are concrete types under LibPETSc.AbstractPetscDM, not one type with a runtime string flavour. Code annotated ::PetscDM no longer matches — use AbstractPetscDM. PETSc.narrow(dm) turns a low-level handle into the typed one; it queries PETSc, so its return type is a wide Union and hot code should narrow once behind a function barrier. The nine DM-flavour @asserts are gone: dispatch enforces what they checked.
Borrowed handles. A reader that hands back a PETSc object owned by another object — dm(ksp), solution(snes), local_coordinates(dm), tolerances(ts)'s vectors — returns a borrowed handle: no finalizer, and destroy! on it is a no-op (PETSc.owns tells the two apart). Nothing changed at runtime; destroying such a handle was corrupting the owner's already. destroy is now destroy!, per the mutation convention.
Type names are Symbol. type_name on a Vec, Mat, KSP, SNES, TS or DM returns a Symbol, or nothing when PETSc has no type for the object yet, so type_name(ksp) == "gmres" is now false; compare against :gmres. The new set_type!(obj, :gmres) covers Vec, Mat, KSP, SNES and DM as well as TS, and set_type!(obj, "gmres") warns until v0.6. This break has no shim on the reader side.
Argument order, and petsclib dropped. The written vector leads, the DM follows it, and the library is recovered from the object: project_function!(X, dm, time, funcs, ctxs, mode), project_field!(X, dm, time, U, funcs, mode), global_to_local!(lvec, dm, gvec, mode), local_to_global!(gvec, dm, lvec, mode), l2diff(dm, time, funcs, ctxs, X), add_boundary!(dm, …), add_natural_boundary!(dm, …), set_snes_local_fem!(dm), save_vtk!(vec, filename), star_fd_coloring(da). The v0.4 spellings forward and warn, but a call passed through invoke or a function reference is not caught.
Callback setters take the callback first, only. set_function!, set_snes_jacobian!, set_convergence_test!, set_compute_rhs!, set_compute_operators!, set_rhs_function!, set_rhs_jacobian!, set_ifunction!, set_ijacobian!, set_monitor! and add_coarsen_hook! no longer accept the subject-first order v0.4 also offered, so do syntax is always available. No shim: dispatch cannot tell the two orders apart.
Dimension-correct returns. corners, ghost_corners, local_indices, global_indices, info and size answer with the DM's own dimension: lower/upper are CartesianIndex{N}, size/nextra are NTuple{N,Int}, and center/vertex are keyed x, y (, z) by dimension. v0.4 padded everything to three, so corners(dm2d).size[3] returned 1 and now throws a BoundsError. This is also a performance fix: the tuples are built with ntuple(…, Val(N)) and infer concretely instead of allocating. info drops the duplicate s field, renames dof to ndofs and mpi_proc_size to procs, and ghost_corners(::DMStag) has no nextra field: DMStagGetGhostCorners never reported one, though the v0.4 docstring promised it.
ownership_range(A) is 1-based. That was already the default; the positional ownership_range(A, false) still returns PETSc's numbering, warns, and is a MethodError in v0.6. set_values! spells its index parameters rows_0b/cols_0b and star_fd_coloring returns row_coo_local_0b, col_coo_local_0b, perturb_cols_1b, coo_idxs_1b and local_rows_1b, so every bulk index vector says which base it uses.
Smaller changes. library_info() returns (; source, path, scalar, int, real) and prints its old report from a show method. Argument problems raise ArgumentError, DimensionMismatch or PetscNotInitialized rather than AssertionError or a bare error. PETSc.KSP(…) / PETSc.SNES(…) construct the LibPETSc.KSP / LibPETSc.SNES types.
The rename table, and the reasoning behind each rule, are in docs/src/man/naming.md; the C-function-to-Julia-name lookup is the generated "C to Julia name index" page in the manual.
Changed
ForwardDiff,UnicodePlots,StatisticsandPkgare no longer dependencies of the package: nothing insrc/used them, and the tests and examples that do now pull them through[extras]andexamples/Project.toml. Installing PETSc.jl resolves 86 packages instead of 121, which cuts cold precompilation by roughly a factor of five.Only one
PetscIntwidth ofPETSc_jllis loaded per process (#241):PETSc.petsclibsholds the four scalar variants ofInt64by default,PETSc.set_petscint!(Int32)switches to theInt32libraries on the next session. Loading both widths cross-binds the identically named HYPRE/SuperLU_DIST symbols of the two integer ABIs. Code indexingpetsclibs[5:8]breaks.
Added
- PetscSF communication:
PetscSFBcastBegin/End,PetscSFReduceBegin/End,PetscSFFetchAndOpBegin/End(hand-written;getAPI.pyrejectsMPI_Datatypefunctions). PetscDrawandTSMonitorLGCtxhandles work (they were empty placeholder structs).- Windows CI with MPI (PETSc_jll 3.25.4 ships MPI-enabled Windows binaries).
- Documentation pages for every wrapper file, and the maintainer guide "Regenerating the wrappers".
- Test coverage:
test/wrapper_quality.jl(every generated method infers a concrete return type; representatives are allocation free),test/wrapper_leaks.jl, an ambiguity budget,test/low_level_petscsf.jl.
Fixed
PetscBoolis one byte, matching PETSc'stypedef bool PetscBool(since 3.24). The previous 32-bit type read three bytes PETSc never wrote, so aPETSC_FALSEoutput could come back as true (#268),Vector{PetscBool}arguments had the wrong stride and theMatFactorInfo,PetscFEGeomandPetscEventPerfInfostruct layouts were off.Re-initialising PETSc after
finalizeworks with Tao, TaoTerm and TSTrajectory (PETSc 3.25.x does not reset theirRegisterAllCalledflags; on Windows the internal symbols are not exported, seePETSc.tao_usable_after_reinitialize()).Re-initialising PETSc no longer overwrites three bytes of PETSc's memory: the
TaoRegisterAllCalled,TaoTermRegisterAllCalledandTSTrajectoryRegisterAllCalledflags were reset with a 4-byte store, whilePetscBoolis 1 byte.unsafe_local_arrayno longer touches a destroyed vector or a finalized library.Double free of DMDA coordinate vectors;
PCMGSetLevelsreading uninitialised communicators.Block assignment
A[rows, cols] = blockon aPetscMatworks (#248, #271). It threw aMethodError, and the values were laid out column by column whereMatSetValuesreads them row by row. A block whose size does not match the index ranges raises aDimensionMismatch.PetscOptionsrecords the initialize/finalize cycle it was created in, likePetscVec,PetscMat,KSP,SNES,TSand the DMs, sodestroy!and its finalizer no longer callPetscOptionsDestroyon an object from an earlier cycle (#270).
Removed
- The old PythonCall-based generator (
wrapping/generatejuliabindings.jl),src/deprecated/,src/startup.jl,src/string_wrappers*.jl. LibPETSc.petsc_versionandPETSc.SUPPORTED_PETSC_VERSIONS, added in 0.4.14 for running on PETSc 3.22 and 3.25. 0.5 binds PETSc 3.25 only;PETSc.check_wrappers_version(petsclib).installed_versionreturns the library version.