From 30f7c783e17ff10241cfb42b424b8374e196319c Mon Sep 17 00:00:00 2001 From: lmoresi Date: Fri, 3 Jul 2026 20:47:29 +1000 Subject: [PATCH 1/2] fix(rotated-bc): restore Schur preconditioning parity + 3D rotation nullspace (#248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-contained rotated free-slip fieldsplit silently downgraded the native Stokes preconditioning, blowing out the outer Schur iterations on curved/deformed boundaries (Zhong #248 spherical benchmark: 44 outer its vs Nitsche 2 / block-constrained 5; deformed free-surface models pay this per Newton iteration): * The 1/mu pressure-mass Schur preconditioner was dropped: only snes.getJacobian()[0] was read, so the Pmat carrying the DS JacobianPreconditioner _pp_G0 block was never assembled, and the solve fell back to selfp + Jacobi. Now the Pmat is assembled alongside J and its p-p block is installed as a USER Schur preconditioner (exactly the native path's schur_precondition=a11; Q is identity on pressure). * 3D rigid rotations were never treated: _rotated_nullspace was 2D-only, so a spherical shell with free-slip on both boundaries left THREE rotation modes on the operator (near-singular Krylov grind + rotation pollution in the answer). All e_k x r modes are now built, constraint- checked per mode, attached rotated, and projected out of the solution. * The hand-built IS fieldsplit never propagated the constant-pressure nullspace to the inner Schur solve (singular on enclosed domains) — now attached to the Schur complement KSP. * The GAMG fallback velocity block was bare preonly; it now mirrors the native tuning (fgmres 0.1*tol, agg nsmooths 2, additive, repartition). The custom-FMG route stays the preferred velocity block, unchanged in design, but its Galerkin coarse operator inherits the rotation nullspace on closed shells — the redundant/LU coarse solve hit a zero pivot (outer reason -11); the coarse solve is now SVD. * Constraint rows are scaled by mean |diag(A_vv)| instead of 1.0 (unit diagonals amid eta/h^2 entries poisoned diagonal-based approximations along the entire rotated surface). * The nonlinear driver now reuses the KSP/PC context, the PtAP product and the Schur pmat across Newton iterations (values refreshed in place) instead of a full rebuild per iteration; per-iteration linear its are reported in the info dict (ksp_its). * Analytic normals are unwrapped before lambdify, so curvilinear forms (e.g. mesh.CoordinateSystem.unit_e_0) reduce to Cartesian instead of dying with a NameError; stray symbols get a clear error. Zhong benchmark (serial 1/8, tol 1e-7): rotated GAMG 44 -> 1 outer it, rotated FMG diverged -> converged, angular-momentum content back to Nitsche levels. New 3D spherical-shell tests in test_1018 (serial) and test_1064 (parallel golden, np2/np4); all existing goldens unchanged. Underworld development team with AI support from Claude Code --- src/underworld3/utilities/rotated_bc.py | 442 +++++++++++++----- .../test_1064_rotated_freeslip_parallel.py | 50 ++ tests/test_1018_rotated_freeslip.py | 53 +++ 3 files changed, 431 insertions(+), 114 deletions(-) diff --git a/src/underworld3/utilities/rotated_bc.py b/src/underworld3/utilities/rotated_bc.py index 4285b056..e6afd60f 100644 --- a/src/underworld3/utilities/rotated_bc.py +++ b/src/underworld3/utilities/rotated_bc.py @@ -6,7 +6,11 @@ The rotated saddle is solved by a self-contained fieldsplit-Schur KSP by default: the velocity block is geometric FMG on the custom prolongation (``set_custom_fmg``) when a -hierarchy is registered, else GAMG; direct MUMPS LU is opt-in via ``solver._rotated_use_lu``. +hierarchy is registered (the PREFERRED route), else GAMG tuned to the native path's +settings; the Schur complement is preconditioned by the native 1/mu pressure mass +(the Pmat p-p block, exactly the standard path's ``schur_precondition=a11``), with a +constant-pressure nullspace on the inner Schur solve for enclosed domains; direct +MUMPS LU is opt-in via ``solver._rotated_use_lu``. σ_nn / dynamic topography reuse the shared Consistent-Boundary-Flux de-smear in ``underworld3.utilities.boundary_flux``. """ @@ -82,15 +86,31 @@ def coord(q): # analytic / constant normal specs. An analytic (sympy) normal is LAMBDIFIED # once into a fast numpy callable — per-node sympy .subs() is orders of magnitude # slower and, in parallel, serialises on the rank that owns the boundary (the - # others idle), which looked like a hang. + # others idle), which looked like a hang. Components are UNWRAPPED first so a + # normal written in curvilinear terms (e.g. ``mesh.CoordinateSystem.unit_e_0``, + # whose UWexpressions hide the Cartesian coordinates behind ``r`` etc.) reduces + # to pure mesh.X before lambdify — otherwise the generated function references + # the curvilinear symbol by bare name and dies with a NameError at call time. sym_fn = None const_normal = None if normal is not None: try: import sympy if isinstance(normal, sympy.Matrix): - sym_fn = sympy.lambdify(list(solver.mesh.X), - [normal[0, k] for k in range(dim)], "numpy") + from underworld3.function.expressions import unwrap + comps = [sympy.sympify(unwrap(normal[0, k], keep_constants=False, + return_self=False)) + for k in range(dim)] + stray = set().union(*[c.free_symbols for c in comps]) \ + - set(solver.mesh.X) + if stray: + raise ValueError( + f"analytic normal for boundary '{boundary}' contains " + f"symbols {sorted(map(str, stray))} that are not mesh " + "coordinates — express it in mesh.X (e.g. radial X/|X|).") + sym_fn = sympy.lambdify(list(solver.mesh.X), comps, "numpy") + except ValueError: + raise except Exception: sym_fn = None if sym_fn is None: @@ -210,10 +230,15 @@ def solve_rotated_freeslip(solver, boundaries, remove_rotation_gauge=True, verbo snes = solver.snes # Assemble the operator FIRST so its parallel row layout is final before we build - # Q against it (A = exact Jacobian at 0 — linear; b = -F(0)). + # Q against it (A = exact Jacobian at 0 — linear; b = -F(0)). The Pmat is + # assembled alongside: its p-p block is the native 1/mu pressure mass (the + # DS JacobianPreconditioner term) that preconditions the Schur complement — + # petsc4py's computeJacobian(x, J) would silently pass J as its own Pmat and + # the mass block would never be assembled. snes.setUp() U0 = dm.getGlobalVec(); U0.set(0.0) - J = snes.getJacobian()[0]; snes.computeJacobian(U0, J) + J, Jp = snes.getJacobian()[:2] + snes.computeJacobian(U0, J, Jp) Aorig = J.copy() F0 = dm.getGlobalVec(); snes.computeFunction(U0, F0) b = F0.copy(); b.scale(-1.0) @@ -232,15 +257,21 @@ def solve_rotated_freeslip(solver, boundaries, remove_rotation_gauge=True, verbo if gsec.getFieldDof(q, PRE) > 0 and gsec.getFieldOffset(q, PRE) >= 0: pin = gsec.getFieldOffset(q, PRE); break - # constrain rotated normal rows (v_n=0): zero the matrix rows/cols (identity - # diagonal) AND the RHS at those rows — zeroRowsColumns does NOT touch the RHS, - # so a nonzero b there would leak straight into the solution (û_i = b_i / 1), - # independent of the solver/tolerance. + # constrain rotated normal rows (v_n=0): zero the matrix rows/cols AND the RHS + # at those rows — zeroRowsColumns does NOT touch the RHS, so a nonzero b there + # would leak straight into the solution (û_i = b_i / diag), independent of the + # solver/tolerance. + # The constraint diagonal is set to the mean |diag(A_vv)| rather than 1.0: unit + # diagonals amid O(eta/h^2) viscous entries put a spectrum outlier on EVERY + # rotated boundary node (the whole surface on a sphere), which poisons + # diagonal-based Schur approximations and MG smoothing exactly in the boundary + # strip. Any positive diagonal is exact — the solution rows are explicitly + # zeroed after the solve. # zeroRowsColumns takes GLOBAL row indices (correct); the RHS write must use # OWNERSHIP-RELATIVE local indices (bhat.getArray() is this rank's local slice, # so indexing it with global rows overflows on any rank whose ownership does not # start at 0 — the np>1 crash that masqueraded as a hang). - Ahat.zeroRowsColumns(normal_rows, diag=1.0) + Ahat.zeroRowsColumns(normal_rows, diag=_velocity_diag_scale(Ahat, solver)) brs, bre = bhat.getOwnershipRange() bloc = np.asarray([g - brs for g in normal_rows if brs <= g < bre], dtype=np.int64) ba = bhat.getArray(); ba[bloc] = 0.0; bhat.setArray(ba) @@ -259,10 +290,13 @@ def solve_rotated_freeslip(solver, boundaries, remove_rotation_gauge=True, verbo ksp = PETSc.KSP().create(); ksp.setOperators(Ahat); ksp.setType("preonly") pc = ksp.getPC(); pc.setType("lu"); pc.setFactorSolverType("mumps") Uhat = dm.createGlobalVec(); ksp.solve(bhat, Uhat) # returned in info → own it - ksp_reason = ksp.getConvergedReason() + ksp_reason = ksp.getConvergedReason(); ksp_its = ksp.getIterationNumber() else: - Uhat, ksp_reason = _solve_rotated_iterative( - solver, Ahat, bhat, Q, Qt, normal_rows, verbose=verbose) + Mp = _pressure_mass_schur_pmat(solver) + Uhat, ksp_reason, ctx = _solve_rotated_iterative( + solver, Ahat, bhat, Q, Qt, normal_rows, verbose=verbose, Mp=Mp) + ksp_its = ctx["ksp"].getIterationNumber() + _destroy_rotated_ksp_ctx(ctx) # rotate back u = Qᵀ û (U is returned in info → create, don't borrow from the pool) U = dm.createGlobalVec(); Qt.mult(Uhat, U) @@ -271,7 +305,8 @@ def solve_rotated_freeslip(solver, boundaries, remove_rotation_gauge=True, verbo return {"Q": Q, "Qt": Qt, "A": Aorig, "b": b, "U": U, "Uhat": Uhat, "normal_rows": normal_rows, "boundaries": list(boundaries), - "rotation_gauge_removed": removed, "ksp_reason": ksp_reason} + "rotation_gauge_removed": removed, "ksp_reason": ksp_reason, + "ksp_its": ksp_its} def _finalize_rotated_solution(solver, U, Q, normal_rows, remove_rotation_gauge): @@ -281,18 +316,35 @@ def _finalize_rotated_solution(solver, U, Q, normal_rows, remove_rotation_gauge) the linear one-shot and the nonlinear driver. Returns whether the gauge was removed.""" dm = solver.dm - # Remove the rigid-rotation gauge ONLY when it is a genuine null space of the - # constrained problem (closed circular/spherical free-slip); on straight walls - # the constraint pins the rotation, and projecting would corrupt the solution. + # Remove the rigid-rotation gauge — every mode that is a genuine null space of + # the constrained problem (closed circular free-slip: one; full spherical + # shell: all three); on straight walls the constraint pins the rotations, and + # projecting would corrupt the solution. # Done on the GLOBAL vector U with PETSc dots (parallel-correct ownership — a - # local nodal sum would double-count shared nodes at rank boundaries). + # local nodal sum would double-count shared nodes at rank boundaries). The + # surviving modes are orthonormalised (Gram-Schmidt) before projection — the + # three 3D modes are not mutually orthogonal on a general mesh. + # COLLECTIVE: all ranks walk the same mode list, same order. removed = False - if remove_rotation_gauge and _rotation_is_nullspace(solver, Q, normal_rows): - tg = _rigid_rotation_global(solver) - coef = U.dot(tg) / (tg.dot(tg) + 1e-30) - U.axpy(-coef, tg) - dm.restoreGlobalVec(tg) # transient → return to pool - removed = True + if remove_rotation_gauge: + live = [] + for tg in _rigid_rotation_modes(solver): + if _mode_satisfies_constraints(solver, Q, normal_rows, tg): + live.append(tg.copy()) # owned copy — pool vec goes back below + dm.restoreGlobalVec(tg) + ortho = [] + for w in live: + for q in ortho: + w.axpy(-w.dot(q), q) + nrm = w.norm() + if nrm > 1e-14: + w.scale(1.0 / nrm); ortho.append(w) + else: + w.destroy() + for q in ortho: + U.axpy(-U.dot(q), q) + q.destroy() + removed = True # scatter U → velocity/pressure fields for name, var in solver.fields.items(): @@ -422,10 +474,17 @@ def solve_rotated_freeslip_nonlinear(solver, boundaries, remove_rotation_gauge=T uh = u.duplicate(); Q.mult(u, uh); _zero_rows_local(uh, normal_rows); Qt.mult(uh, u) uh.destroy() # transient projection buffer - J = snes.getJacobian()[0] + J, Jp = snes.getJacobian()[:2] + pres_is = solver._subdict["pressure"][0] Fc = dm.createGlobalVec() reaction = dm.createGlobalVec() # the final (un-zeroed) Cartesian residual + # Reused across Newton iterations: the rotated operator (ptap-with-result), the + # 1/mu pressure-mass Schur pmat (values refreshed in place), the constraint + # diagonal scale (frozen at the first tangent — only the magnitude matters), + # and the KSP/PC context (fieldsplit ISs, FMG hierarchy, GAMG setup survive). + Ahat = None; Mp = None; ctx = None; diag_scale = None; lin_its = [] + def rotated_residual(uvec, keep_cartesian=False): snes.computeFunction(uvec, Fc) if keep_cartesian: @@ -457,12 +516,23 @@ def rotated_residual(uvec, keep_cartesian=False): from underworld3 import mpi mpi.pprint(f"[rotated_bc] continuation: Picard→Newton at iter {iters} " f"(rel |F̂| {rnorm/(r0+1e-300):.2e})") - snes.computeJacobian(u, J) - Ahat = J.ptap(Qt); Ahat.zeroRowsColumns(normal_rows, diag=1.0) + snes.computeJacobian(u, J, Jp) # Jp carries the 1/mu mass (Schur pmat) + if Ahat is None: + Ahat = J.ptap(Qt) + else: + J.ptap(Qt, result=Ahat) # same nonzero pattern → in-place refresh + if ctx is None: + Mp = _pressure_mass_schur_pmat(solver) + elif Mp is not None: + Jp.createSubMatrix(pres_is, pres_is, submat=Mp) # viscosity may be u-dependent + if diag_scale is None: + diag_scale = _velocity_diag_scale(Ahat, solver) + Ahat.zeroRowsColumns(normal_rows, diag=diag_scale) bhat = Fhat.copy(); bhat.scale(-1.0) - dhat, last_reason = _solve_rotated_iterative( + dhat, last_reason, ctx = _solve_rotated_iterative( solver, Ahat, bhat, Q, Qt, normal_rows, - custom_Pl=custom_Pl, nsp=nsp, verbose=False) + custom_Pl=custom_Pl, nsp=nsp, Mp=Mp, verbose=False, ctx=ctx) + lin_its.append(ctx["ksp"].getIterationNumber()) d = dm.createGlobalVec(); Qt.mult(dhat, d) # step-norm convergence (SNES_CONVERGED_SNORM): a tiny Newton step means we # are at the solution — the exit for a warm start that is already converged @@ -470,7 +540,7 @@ def rotated_residual(uvec, keep_cartesian=False): # level). ‖u‖=0 on a cold start ⇒ this never fires prematurely (d is large). if d.norm() <= stol * (u.norm() + 1e-30): converged = True - Ahat.destroy(); dhat.destroy(); d.destroy(); bhat.destroy(); Fhat.destroy() + dhat.destroy(); d.destroy(); bhat.destroy(); Fhat.destroy() break # backtracking line search on ‖F̂‖ (full Newton/Picard step first). Cheap # insurance far from the solution; α=1 is accepted immediately near it. If no @@ -485,7 +555,7 @@ def rotated_residual(uvec, keep_cartesian=False): if Ftry.norm() < rnorm: u.destroy(); u = utry; improved = True; Ftry.destroy(); break utry.destroy(); Ftry.destroy(); alpha *= 0.5 - Ahat.destroy(); dhat.destroy(); d.destroy(); bhat.destroy(); Fhat.destroy() + dhat.destroy(); d.destroy(); bhat.destroy(); Fhat.destroy() if not improved: break @@ -505,12 +575,16 @@ def rotated_residual(uvec, keep_cartesian=False): f"the last (unconverged) iterate.") Fc.destroy() # residual output buffer (reaction is kept for info) + _destroy_rotated_ksp_ctx(ctx) # KSP/PC + the owned Schur pmat + if Ahat is not None: + Ahat.destroy() # the reused rotated operator removed = _finalize_rotated_solution(solver, u, Q, normal_rows, remove_rotation_gauge) return {"Q": Q, "Qt": Qt, "reaction": reaction, "U": u, "normal_rows": normal_rows, "boundaries": list(boundaries), "rotation_gauge_removed": removed, "ksp_reason": last_reason, "nonlinear_iterations": iters, "converged": converged, + "ksp_its": lin_its, "continuation_switched": continuation and phase == "newton"} @@ -531,11 +605,54 @@ def _build_rotated_custom_Pl(solver, Q, normal_rows): return list(Ps[:-1]) + [Pfine] +def _pressure_mass_schur_pmat(solver): + """The native 1/mu pressure-mass Schur preconditioner block, extracted from the + solver's assembled Pmat — the SAME p-p block the standard path uses via + ``pc_fieldsplit_schur_precondition=a11`` (the DS JacobianPreconditioner term + ``_pp_G0``). Q is identity on pressure, so the block needs no rotation. + Returns None (→ selfp fallback) when the Pmat is not distinct from the operator + or the block was not assembled. The CALLER must have assembled the Pmat + (``snes.computeJacobian(x, J, Jp)``) and owns the returned Mat.""" + A, P = solver.snes.getJacobian()[:2] + if P is None or P.handle == A.handle: + return None + pres_is = solver._subdict["pressure"][0] + Mp = P.createSubMatrix(pres_is, pres_is) + if Mp.norm() == 0.0: # not assembled → useless + Mp.destroy() + return None + return Mp + + +def _velocity_diag_scale(Ahat, solver): + """Mean |diag| of the (rotated) velocity block — the representative diagonal + for constraint rows (see the zeroRowsColumns call sites). Collective.""" + d = Ahat.getDiagonal() + vis = solver._subdict["velocity"][0] + sub = d.getSubVector(vis) + n = sub.getSize() + s = sub.norm(PETSc.NormType.NORM_1) / max(n, 1) + d.restoreSubVector(vis, sub) + d.destroy() + return float(s) if s > 0.0 else 1.0 + + +def _destroy_rotated_ksp_ctx(ctx): + """Release the reusable rotated-KSP context (KSP and the owned Schur pmat).""" + if ctx is None: + return + if ctx.get("ksp") is not None: + ctx["ksp"].destroy() + if ctx.get("Mp") is not None: + ctx["Mp"].destroy() + + def _solve_rotated_iterative(solver, Ahat, bhat, Q, Qt, normal_rows, verbose=False, - custom_Pl=None, nsp=None): + custom_Pl=None, nsp=None, Mp=None, ctx=None): """Solve the rotated saddle with a SELF-CONTAINED fieldsplit-Schur KSP on the rotated operator. The velocity block is geometric FMG on the CUSTOM prolongation - (PR#290, rotated) when a hierarchy is registered (``set_custom_fmg``), else GAMG. + (PR#290, rotated) when a hierarchy is registered (``set_custom_fmg``), else GAMG + (tuned to the native path's settings). A plain rotated Mat has no DM field info, so UW3's DM-coupled fieldsplit cannot split it — we build the split from EXPLICIT velocity/pressure index sets. For the @@ -544,64 +661,142 @@ def _solve_rotated_iterative(solver, Ahat, bhat, Q, Qt, normal_rows, verbose=Fal automatically and only the FINE prolongation is rotated (Galerkin coarse ops auto-correct). NO direct solve of the fine system. + Preconditioning parity with the native solve (the Schur-iteration fix): + * ``Mp`` — the 1/mu pressure-mass block from the native Pmat + (``_pressure_mass_schur_pmat``) is installed as a USER Schur preconditioner, + exactly what the standard path uses via ``schur_precondition=a11``. Without + it (Mp=None) the Schur pre falls back to selfp, which degrades badly on + curved/deformed boundaries and variable viscosity. + * the pressure sub-solve mirrors the native FGMRES+GASM at the solver + tolerance; the constant-pressure nullspace is attached to the Schur + complement for enclosed domains (the hand-built IS fieldsplit does not + inherit it from the operator). + ``custom_Pl`` / ``nsp`` may be PREBUILT (nonlinear driver: build once, reuse each - Newton step); when None they are built here (linear one-shot).""" + Newton step); when None they are built here (linear one-shot). + + Returns ``(Uhat, reason, ctx)``. Passing ``ctx`` back in reuses the KSP/PC + across Newton iterations — the fieldsplit ISs, Schur USER pmat and FMG + prolongations survive; only the operator-values refresh is paid. The caller + must keep ``Ahat``/``Mp`` the SAME Mat objects (values updated in place) and + release the context with ``_destroy_rotated_ksp_ctx`` when done.""" from underworld3.utilities import custom_mg dm = solver.dm vel_is = solver._subdict["velocity"][0] pres_is = solver._subdict["pressure"][0] - if custom_Pl is None: - custom_Pl = _build_rotated_custom_Pl(solver, Q, normal_rows) + if ctx is None: + if custom_Pl is None: + custom_Pl = _build_rotated_custom_Pl(solver, Q, normal_rows) + + # rotated coupled null space (pressure-const ⊕ Q·rotation) on the operator + if nsp is None: + nsp = _rotated_nullspace(solver, Q, normal_rows) + if nsp is not None: + Ahat.setNullSpace(nsp); Ahat.setTransposeNullSpace(nsp) + + # UNIQUE prefix per KSP (see _ROT_SOLVE_COUNT) so concurrent rotated solves + # do not share global-options state; the keys are removed after setup. + global _ROT_SOLVE_COUNT + _ROT_SOLVE_COUNT += 1 + pfx = f"rotfs{_ROT_SOLVE_COUNT}_" + opts = PETSc.Options() + tol = float(solver.tolerance) + cfg = { + "ksp_type": "fgmres", "ksp_rtol": str(tol), "ksp_max_it": "300", + "pc_type": "fieldsplit", "pc_fieldsplit_type": "schur", + "pc_fieldsplit_schur_fact_type": "full", + # native-parity pressure sub-solve (pyx Stokes defaults): FGMRES at the + # solver tolerance; GASM on the 1/mu mass, jacobi if only selfp exists. + "fieldsplit_pres_ksp_type": "fgmres", + "fieldsplit_pres_ksp_rtol": str(tol), + "fieldsplit_pres_ksp_max_it": "200", + } + if Mp is not None: + cfg["fieldsplit_pres_pc_type"] = "gasm" + else: + cfg["pc_fieldsplit_schur_precondition"] = "selfp" + cfg["fieldsplit_pres_pc_type"] = "jacobi" + if custom_Pl is None: + # GAMG fallback velocity block, tuned to native parity (pyx Stokes + # defaults). NOTE: the custom-FMG route is the preferred velocity + # block — this applies only when no hierarchy is registered. + cfg.update({ + "fieldsplit_vel_ksp_type": "fgmres", + "fieldsplit_vel_ksp_rtol": str(tol * 0.1), + "fieldsplit_vel_ksp_max_it": "200", + "fieldsplit_vel_pc_type": "gamg", + "fieldsplit_vel_pc_gamg_type": "agg", + "fieldsplit_vel_pc_gamg_repartition": "true", + "fieldsplit_vel_pc_mg_type": "additive", + "fieldsplit_vel_pc_gamg_agg_nsmooths": "2", + "fieldsplit_vel_mg_levels_ksp_max_it": "3", + "fieldsplit_vel_mg_levels_ksp_converged_maxits": "true", + }) + else: + # full-MG cycle per Schur application, by design + cfg["fieldsplit_vel_ksp_type"] = "preonly" + for k, v in cfg.items(): + opts[pfx + k] = v + try: + ksp = PETSc.KSP().create(comm=dm.comm); ksp.setOptionsPrefix(pfx) + ksp.setOperators(Ahat) + pc = ksp.getPC(); pc.setType("fieldsplit") + pc.setFieldSplitIS(("vel", vel_is), ("pres", pres_is)) + ksp.setFromOptions() + if Mp is not None: + pc.setFieldSplitSchurPreType( + PETSc.PC.FieldSplitSchurPreType.USER, Mp) + pc.setUp() + if custom_Pl is not None: # geometric FMG via custom P + vel_pc = pc.getFieldSplitSubKSP()[0].getPC() + A_vv, P_vv = vel_pc.getOperators() + vel_pc.reset(); vel_pc.setOperators(A_vv, P_vv) + custom_mg._configure_pcmg(vel_pc, custom_Pl) + # The Galerkin-coarsened ROTATED velocity block inherits every + # rigid-rotation nullspace mode of the constrained problem (a + # closed circle: one; a spherical shell: three) — the default + # redundant/LU coarse solve hits a zero pivot (SUBPC_ERROR, + # outer reason -11). SVD is nullspace-robust and the coarse + # level is small; same choice as the native spherical FMG setups. + vopts = PETSc.Options() + vpfx = vel_pc.getOptionsPrefix() or "" + vopts.setValue(vpfx + "mg_coarse_pc_type", "svd") + vopts.delValue(vpfx + "mg_coarse_redundant_pc_type") + vel_pc.setFromOptions() + vel_pc.setUp() + vopts.delValue(vpfx + "mg_coarse_pc_type") + # Constant-pressure nullspace on the Schur COMPLEMENT (enclosed + # domains): the IS-built fieldsplit does not propagate the coupled + # nullspace to the inner Schur solve, which is otherwise singular + # and grinds against its iteration cap every outer iteration. + cns = None + if getattr(solver, "_petsc_use_pressure_nullspace", False): + S = pc.getFieldSplitSubKSP()[1].getOperators()[0] + cns = PETSc.NullSpace().create(constant=True, comm=dm.comm) + S.setNullSpace(cns) + finally: + # all options consumed by setFromOptions/setUp — drop them so the + # global database stays clean (and bounded under time-stepping). + for k in cfg: + try: + opts.delValue(pfx + k) + except Exception: + pass + ctx = {"ksp": ksp, "pc": pc, "Mp": Mp, "nsp": nsp, "cns": cns, + "custom_Pl": custom_Pl, "pfx": pfx} + else: + ksp = ctx["ksp"] + nsp = ctx["nsp"] + # Same Mat objects, new values (ptap-with-result / createSubMatrix-with- + # submat) — poke the KSP so PCSetUp refreshes on the changed operator. + ksp.setOperators(Ahat) - # rotated coupled null space (pressure-const ⊕ Q·rotation) on the operator - if nsp is None: - nsp = _rotated_nullspace(solver, Q, normal_rows) if nsp is not None: - Ahat.setNullSpace(nsp); Ahat.setTransposeNullSpace(nsp); nsp.remove(bhat) - - # UNIQUE prefix per solve (see _ROT_SOLVE_COUNT) so sequential rotated solves - # do not share global-options state; the keys are removed after the solve. - global _ROT_SOLVE_COUNT - _ROT_SOLVE_COUNT += 1 - pfx = f"rotfs{_ROT_SOLVE_COUNT}_" - opts = PETSc.Options() - cfg = { - "ksp_type": "fgmres", "ksp_rtol": str(float(solver.tolerance)), "ksp_max_it": "300", - "pc_type": "fieldsplit", "pc_fieldsplit_type": "schur", - "pc_fieldsplit_schur_fact_type": "full", "pc_fieldsplit_schur_precondition": "selfp", - "fieldsplit_vel_ksp_type": "preonly", - "fieldsplit_pres_ksp_type": "fgmres", "fieldsplit_pres_ksp_rtol": "1e-6", - "fieldsplit_pres_ksp_max_it": "200", "fieldsplit_pres_pc_type": "jacobi", - } - if custom_Pl is None: # GAMG velocity block - cfg["fieldsplit_vel_pc_type"] = "gamg" - for k, v in cfg.items(): - opts[pfx + k] = v - try: - ksp = PETSc.KSP().create(comm=dm.comm); ksp.setOptionsPrefix(pfx) - ksp.setOperators(Ahat) - pc = ksp.getPC(); pc.setType("fieldsplit") - pc.setFieldSplitIS(("vel", vel_is), ("pres", pres_is)) - ksp.setFromOptions() - pc.setUp() - if custom_Pl is not None: # geometric FMG via custom P - vel_pc = pc.getFieldSplitSubKSP()[0].getPC() - A_vv, P_vv = vel_pc.getOperators() - vel_pc.reset(); vel_pc.setOperators(A_vv, P_vv) - custom_mg._configure_pcmg(vel_pc, custom_Pl) - vel_pc.setUp() - - Uhat = Ahat.createVecRight(); Uhat.set(0.0) - ksp.solve(bhat, Uhat) - finally: - # all options consumed by setFromOptions/setUp/solve — drop them so the - # global database stays clean (and bounded under time-stepping). - for k in cfg: - try: - opts.delValue(pfx + k) - except Exception: - pass + nsp.remove(bhat) # project EVERY rhs + + Uhat = Ahat.createVecRight(); Uhat.set(0.0) + ksp.solve(bhat, Uhat) # An identity constraint row in an ITERATIVE solve only drives its residual # (= û_i) below tolerance, so û_i ~ tol, not exactly 0. Because zeroRowsColumns # made these DOFs fully decoupled (row AND column zeroed), û_i affects no other @@ -612,16 +807,22 @@ def _solve_rotated_iterative(solver, Ahat, bhat, Q, Qt, normal_rows, verbose=Fal ua = Uhat.getArray(); ua[loc] = 0.0; Uhat.setArray(ua) if verbose: from underworld3 import mpi - kind = "custom-FMG" if custom_Pl is not None else "GAMG" - mpi.pprint(f"[rotated_bc] velocity block = {kind}; outer KSP " - f"{ksp.getConvergedReason()} in {ksp.getIterationNumber()} its") - return Uhat, ksp.getConvergedReason() + kind = "custom-FMG" if ctx["custom_Pl"] is not None else "GAMG" + schur = "1/mu-mass" if ctx["Mp"] is not None else "selfp" + mpi.pprint(f"[rotated_bc] velocity block = {kind}; Schur pre = {schur}; " + f"outer KSP {ksp.getConvergedReason()} in " + f"{ksp.getIterationNumber()} its") + return Uhat, ksp.getConvergedReason(), ctx def _rotated_nullspace(solver, Q, normal_rows): - """Coupled Stokes null space in the rotated frame: constant pressure, plus the - rigid rotation Q·(-y,x) when it is a genuine null space of the constraints. - Returns a PETSc.NullSpace on the composite vector, or None. + """Coupled Stokes null space in the rotated frame: constant pressure, plus every + rigid rotation Q·mode that is a genuine null space of the constraints — one + mode (-y,x) in 2D, up to THREE (e_k×r) in 3D (all three on a spherical shell + with free-slip inner and outer boundaries; leaving them off makes the outer + Krylov grind against a near-singular operator and pollutes the solution with + arbitrary rotation content). Returns a PETSc.NullSpace on the composite + vector, or None. Each vector is ZEROED at the constrained normal rows so it is exactly compatible with the strong v_n=0 constraint. Without this, a rotation-mode vector that is @@ -638,12 +839,13 @@ def _rotated_nullspace(solver, Q, normal_rows): pis = solver._subdict["pressure"][0] sp = pv.getSubVector(pis); sp.set(1.0); pv.restoreSubVector(pis, sp) pv.normalize(); vecs.append(pv) - # rigid rotation (rotated), only if it satisfies the constraints - if solver.mesh.dim == 2 and _rotation_is_nullspace(solver, Q, normal_rows): - tg = _rigid_rotation_global(solver) - tr = tg.duplicate(); Q.mult(tg, tr) # tr persists in the NullSpace + # rigid rotations (rotated), each only if it satisfies the constraints. + # COLLECTIVE: all ranks walk the same mode list, same order. + for tg in _rigid_rotation_modes(solver): + if _mode_satisfies_constraints(solver, Q, normal_rows, tg): + tr = tg.duplicate(); Q.mult(tg, tr) # tr persists in the NullSpace + vecs.append(tr) dm.restoreGlobalVec(tg) # tg transient → return to pool - vecs.append(tr) if not vecs: return None # Make every null-space vector EXACTLY compatible with the strong v_n=0 @@ -663,20 +865,17 @@ def _rotated_nullspace(solver, Q, normal_rows): return PETSc.NullSpace().create(constant=False, vectors=ortho, comm=dm.comm) -def _rotation_is_nullspace(solver, Q, normal_rows, tol=1e-8): - """True iff the rigid-body rotation t=(-y,x) satisfies all rotated v_n=0 - constraints — i.e. Q·t is ~0 on every constrained normal row. +def _mode_satisfies_constraints(solver, Q, normal_rows, tg, tol=1e-8): + """True iff the rigid-body mode ``tg`` satisfies all rotated v_n=0 + constraints — i.e. Q·tg is ~0 on every constrained normal row. (A closed + circular boundary admits its one rotation; a full spherical shell admits all + three; straight/partial walls pin them.) COLLECTIVE: every rank runs the same global-vector ops. Do NOT early-return on a per-rank ``not normal_rows`` — in parallel a rank may own no boundary node (empty normal_rows) while others do, and an early return there would desync the collective norms below and deadlock.""" - if solver.mesh.dim != 2: - return False - dm = solver.dm - tg = _rigid_rotation_global(solver) tr = tg.duplicate(); Q.mult(tg, tr) - dm.restoreGlobalVec(tg) # tg transient → return to pool full = tr.norm() # parallel norm # norm of tr restricted to the constrained rows: zero everything else, then .norm() rs, re = tr.getOwnershipRange() @@ -777,19 +976,34 @@ def key(c): return write_boundary_scalar_field(solver, field, hmap, dim) -def _rigid_rotation_global(solver): - """The Cartesian rigid-body rotation t=(-y,x) as a composite GLOBAL vector - (velocity DOFs only, zero pressure). Parallel-safe: built via localToGlobal on - the velocity sub-DM, so shared nodes are handled by PETSc, not double-counted.""" +def _rigid_rotation_modes(solver): + """The Cartesian rigid-body rotation mode(s) as composite GLOBAL vectors + (velocity DOFs only, zero pressure): ``[(-y,x)]`` in 2D, ``[e_k×r]`` for + k=x,y,z in 3D — a spherical shell with free-slip on both boundaries has all + THREE. Parallel-safe: built via localToGlobal on the velocity sub-DM, so + shared nodes are handled by PETSc, not double-counted. The vectors are + borrowed from the DM pool — every one must go back via + ``dm.restoreGlobalVec``. COLLECTIVE: all ranks build the same mode list.""" dm = solver.dm v = solver.Unknowns.u c = v.coords saved = v.data.copy() - v.data[...] = np.column_stack([-c[:, 1], c[:, 0]]) - tg = dm.getGlobalVec(); tg.set(0.0) + if solver.mesh.dim == 2: + fields = [np.column_stack([-c[:, 1], c[:, 0]])] + else: + x, y, z = c[:, 0], c[:, 1], c[:, 2] + zero = np.zeros_like(x) + fields = [np.column_stack([zero, -z, y]), # e_x × r + np.column_stack([z, zero, -x]), # e_y × r + np.column_stack([-y, x, zero])] # e_z × r vis = solver._subdict["velocity"][0] - sg = tg.getSubVector(vis) - solver._subdict["velocity"][1].localToGlobal(v.vec, sg) - tg.restoreSubVector(vis, sg) + modes = [] + for f in fields: + v.data[...] = f + tg = dm.getGlobalVec(); tg.set(0.0) + sg = tg.getSubVector(vis) + solver._subdict["velocity"][1].localToGlobal(v.vec, sg) + tg.restoreSubVector(vis, sg) + modes.append(tg) v.data[...] = saved - return tg + return modes diff --git a/tests/parallel/test_1064_rotated_freeslip_parallel.py b/tests/parallel/test_1064_rotated_freeslip_parallel.py index d63bfdc3..0300dd01 100644 --- a/tests/parallel/test_1064_rotated_freeslip_parallel.py +++ b/tests/parallel/test_1064_rotated_freeslip_parallel.py @@ -48,6 +48,9 @@ # annulus driven by CUSTOM GEOMETRIC FMG on the velocity block (nested hierarchy): # (velocity L2, radial-leakage L2 on Lower arc, radial-leakage L2 on Upper arc) GOLDEN_ANNULUS_FMG = (1.906961759626e-02, 5.428193e-06, 1.177002e-06) +# 3D spherical shell (free-slip both boundaries, all 3 rotation nullspace modes): +# velocity L2. Recompute with `python spherical3d`. +GOLDEN_SPHERICAL3D = 4.069689334228e-03 # NONLINEAR (power-law) box with rotated free-slip through the manual Newton loop # (consistent tangent): (velocity L2, nonlinear iteration count). Recompute # `python nonlinear`. @@ -125,6 +128,35 @@ def _annulus_diagnostics(): return L2, leak_lo, leak_up +def _spherical3d_diagnostics(): + """3D spherical shell with per-node radial free-slip on BOTH boundaries (the + Zhong #248 configuration): all three rigid rotations are nullspace modes. + Returns (velocity L2, outer KSP its, converged reason).""" + RI, RO = 0.55, 1.0 + mesh = uw.meshing.SphericalShell(radiusInner=RI, radiusOuter=RO, + cellSize=0.25, qdegree=2) + x, y, z = mesh.X + r = sympy.sqrt(x**2 + y**2 + z**2) + v = uw.discretisation.MeshVariable("Vs", mesh, mesh.dim, degree=2, continuous=True) + p = uw.discretisation.MeshVariable("Ps", mesh, 1, degree=1, continuous=True) + s = uw.systems.Stokes(mesh, velocityField=v, pressureField=p) + s.constitutive_model = uw.constitutive_models.ViscousFlowModel + s.constitutive_model.Parameters.shear_viscosity_0 = 1.0 + ylm = (3 * (z / r) ** 2 - 1) / 2 + s.bodyforce = ylm * (r - RI) * (RO - r) * 20.0 / r * sympy.Matrix([[x, y, z]]) + nhat = sympy.Matrix([[x / r, y / r, z / r]]) + s.add_rotated_freeslip_bc("Lower", normal=nhat) + s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.tolerance = 1e-7 + s.petsc_use_pressure_nullspace = True + s.petsc_options["snes_type"] = "ksponly" + s.solve() + + L2 = float(np.sqrt(uw.maths.Integral(mesh, v.sym.dot(v.sym)).evaluate())) + info = s._rotated_freeslip_info + return L2, int(info["ksp_its"]), int(info["ksp_reason"]) + + def _annulus_fmg_diagnostics(): """Annulus radial free-slip whose velocity block is CUSTOM GEOMETRIC FMG on a nested hierarchy (coarse annulus -> refine -> refine), via set_custom_fmg — no @@ -313,6 +345,20 @@ def test_rotated_freeslip_annulus_fmg_partition_independent(): f"{leak_up_ref} vs {leak_up}") +def test_rotated_freeslip_spherical3d_partition_independent(): + """3D spherical shell (free-slip inner+outer, all three rotation nullspace + modes): the parallel solve reproduces the serial velocity L2, converges, and + stays within the bounded outer iteration count (the 1/mu-mass Schur + preconditioner — issue #248's rotated blow-out was ~44 its).""" + L2, its, reason = _spherical3d_diagnostics() + L2_ref = GOLDEN_SPHERICAL3D + assert reason > 0, f"3D spherical rotated solve diverged: reason {reason}" + assert its <= 25, f"3D spherical Schur iteration blow-out: {its} outer its" + assert np.isclose(L2, L2_ref, rtol=1e-5, atol=0), ( + f"3D spherical velocity L2 differs serial vs np={uw.mpi.size}: " + f"{L2_ref} vs {L2}") + + def test_rotated_freeslip_box_nonlinear_partition_independent(): """NONLINEAR rotated free-slip is partition-independent: a power-law box solved by the manual Newton/Picard loop reproduces the serial velocity L2 and iteration count @@ -377,6 +423,10 @@ def test_rotated_freeslip_dynamic_topography_partition_independent(): _L2, _lo, _up = _annulus_fmg_diagnostics() if uw.mpi.rank == 0: print(f"DIAG_ANNULUS_FMG {_L2:.12e} {_lo:.6e} {_up:.6e}") + elif _kind == "spherical3d": + _L2, _its, _reason = _spherical3d_diagnostics() + if uw.mpi.rank == 0: + print(f"DIAG_SPHERICAL3D {_L2:.12e} its={_its} reason={_reason}") else: _L2, _verr = _box_diagnostics() if uw.mpi.rank == 0: diff --git a/tests/test_1018_rotated_freeslip.py b/tests/test_1018_rotated_freeslip.py index 79e247d1..0ad51fc8 100644 --- a/tests/test_1018_rotated_freeslip.py +++ b/tests/test_1018_rotated_freeslip.py @@ -67,6 +67,59 @@ def test_rotated_freeslip_box_reproduces_essential(): assert sol.velocity_error(v) < 1e-3 +@pytest.mark.level_2 +def test_rotated_freeslip_spherical_shell_3d(): + """3D spherical shell, free-slip inner+outer (the Zhong #248 configuration): + all THREE rigid rotations must be recognised as nullspace modes and projected + out, the self-contained Schur solve must converge in a bounded iteration + count (the 1/mu-mass Schur preconditioner — ~30 its with selfp), and the + radial leak must be machine-zero on both boundaries.""" + RI, RO = 0.55, 1.0 + mesh = uw.meshing.SphericalShell(radiusInner=RI, radiusOuter=RO, + cellSize=0.25, qdegree=2) + x, y, z = mesh.X + r = sympy.sqrt(x**2 + y**2 + z**2) + v = uw.discretisation.MeshVariable("Vs", mesh, mesh.dim, degree=2, continuous=True) + p = uw.discretisation.MeshVariable("Ps", mesh, 1, degree=1, continuous=True) + s = uw.systems.Stokes(mesh, velocityField=v, pressureField=p) + s.constitutive_model = uw.constitutive_models.ViscousFlowModel + s.constitutive_model.Parameters.shear_viscosity_0 = 1.0 + # Y_20-like radial internal load, zero on the boundaries + ylm = (3 * (z / r) ** 2 - 1) / 2 + g = (r - RI) * (RO - r) * 20.0 + s.bodyforce = ylm * g / r * sympy.Matrix([[x, y, z]]) + nhat = sympy.Matrix([[x / r, y / r, z / r]]) + s.add_rotated_freeslip_bc("Lower", normal=nhat) + s.add_rotated_freeslip_bc("Upper", normal=nhat) + s.petsc_use_pressure_nullspace = True + s.petsc_options["snes_type"] = "ksponly" + s.tolerance = 1e-7 + s.solve() + + info = s._rotated_freeslip_info + assert info["ksp_reason"] > 0, f"rotated KSP diverged: {info['ksp_reason']}" + assert info["ksp_its"] <= 25, ( + f"Schur iteration blow-out: {info['ksp_its']} outer its " + "(1/mu-mass Schur preconditioning regressed?)") + assert info["rotation_gauge_removed"], "3D rotation gauge not detected/removed" + + vc = v.coords + rr = np.linalg.norm(vc, axis=1) + rhat = vc / rr[:, None] + vr = np.einsum("ij,ij->i", v.data, rhat) + vmax = np.linalg.norm(v.data, axis=1).max() + 1e-30 + for lab, mask in [("inner", np.abs(rr - RI) < 1e-3), ("outer", np.abs(rr - RO) < 1e-3)]: + leak = np.abs(vr[mask]).max() / vmax + assert leak < 1e-10, f"{lab} radial leakage {leak:.2e} not machine-zero" + # all three rigid-rotation gauges removed (nodal check, serial test) + for k, t in enumerate([ + np.column_stack([np.zeros(len(vc)), -vc[:, 2], vc[:, 1]]), + np.column_stack([vc[:, 2], np.zeros(len(vc)), -vc[:, 0]]), + np.column_stack([-vc[:, 1], vc[:, 0], np.zeros(len(vc))])]): + rotfrac = abs(np.sum(v.data * t)) / (np.linalg.norm(t) * np.linalg.norm(v.data) + 1e-30) + assert rotfrac < 1e-8, f"rotation mode {k} gauge {rotfrac:.2e} not removed" + + def test_rotated_freeslip_annulus_zero_leakage(): """Annulus: per-node radial free-slip on both arcs → machine-zero v_r leakage, with the analytic radial normal.""" From 0f26b3f85d89139e23390da13296508b8fc76271 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Fri, 3 Jul 2026 20:47:47 +1000 Subject: [PATCH 2/2] chore(env): cap numpy <2.5 for local petsc4py; purge stale petsc4py build cache in uw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two independent environment traps that both present as "numpy.dtype size changed ... Expected 96 from C header, got 88": * numpy 2.5.0 (pulled in by the >=2 range after #301) breaks LOCALLY BUILT petsc4py at import — verified with a clean rebuild against 2.5.0 (fails) vs 2.4.6 (works). conda-forge petsc4py binaries are fine, so CI stays green while every AMR env breaks. Cap at <2.5 until petsc4py/Cython support numpy 2.5. * The in-tree petsc4py build/ cache (shared PETSc source tree) is reused by pip across envs and worktrees even when numpy or the PETSc arch changed, silently installing stale-ABI objects. ./uw now removes it before every petsc4py install. Also of note (not in this commit): pypi gmsh 4.15.1 fails to mesh SphericalShellInternalBoundary at cellSize <= 0.25 ("Identical points in triangulation"); gmsh 4.13.1 works. Pin locally if you hit it. Underworld development team with AI support from Claude Code --- pixi.lock | 234 ++++++++++++++++++++++++++++++++---------------------- pixi.toml | 7 +- uw | 6 ++ 3 files changed, 153 insertions(+), 94 deletions(-) diff --git a/pixi.lock b/pixi.lock index 02207d9a..471215ba 100644 --- a/pixi.lock +++ b/pixi.lock @@ -219,7 +219,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.3-nompi_py312hf6400b3_100.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda @@ -533,7 +533,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.3-nompi_py312h947358d_100.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openmpi-5.0.10-h899237b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda @@ -978,7 +978,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.2.1-he2c55a7_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-pthreads_h6ec200e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda @@ -1477,7 +1477,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.2.1-h5230ea7_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.30-openmp_hea878ba_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda @@ -1992,7 +1992,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.2.1-he2c55a7_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-pthreads_h6ec200e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda @@ -2491,7 +2491,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.2.1-h5230ea7_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.30-openmp_hea878ba_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda @@ -2892,7 +2892,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.4-nompi_py312h25f8dc5_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda @@ -3204,7 +3204,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.4-nompi_py312h5d59a02_102.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda @@ -3650,7 +3650,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-pthreads_h6ec200e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda @@ -4150,7 +4150,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.30-openmp_hea878ba_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda @@ -4550,7 +4550,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.4-nompi_py312h25f8dc5_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openmpi-5.0.10-h611b0e2_0.conda @@ -4865,7 +4865,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.4-nompi_py312h5d59a02_102.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openmpi-5.0.10-h899237b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda @@ -5313,7 +5313,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-pthreads_h6ec200e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda @@ -5816,7 +5816,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.30-openmp_hea878ba_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda @@ -6294,7 +6294,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.3-nompi_py312hf6400b3_100.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda @@ -6730,7 +6730,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.3-nompi_py312h947358d_100.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openldap-2.6.10-hbe55e7a_0.conda @@ -7028,7 +7028,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hae6b9f4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda @@ -7064,7 +7064,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-openmp_hd680484_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.22.0-hd9031aa_0.conda @@ -7121,7 +7121,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.3-nompi_py312hf6400b3_100.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda @@ -7224,7 +7224,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py312h4c3975b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-25.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda @@ -7454,7 +7454,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.3-nompi_py312h947358d_100.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openmpi-5.0.10-h07cac57_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda @@ -7652,7 +7652,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/faiss-1.10.0-cpu_mkl_py312_hd7bdcb3_100.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/faiss-cpu-1.10.0-h718b53a_100.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.1.2-gpl_h1bf8424_901.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.1.2-gpl_h6237aff_902.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-mpi_mpich_h084ba78_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/flexcache-0.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/flexparser-0.4-pyhd8ed1ab_1.conda @@ -7757,7 +7757,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h7b12aa8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libamd-3.3.3-h456b2da_7100101.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.5-h434b012_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hd24cca6_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-devel-1.88.0-hfcd1e18_7.conda @@ -7778,7 +7778,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hae6b9f4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdovi-3.3.2-ha23c83e_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda @@ -7838,7 +7838,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2026.2.1-h21c0c73_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2026.2.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.6.1-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libplacebo-7.360.1-h9eeb4b2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_1.conda @@ -7867,7 +7867,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.52.1-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.23.0-he1eb515_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.24.0-he1eb515_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.16.0-h54a6638_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.15.2-hecca717_0.conda @@ -7923,7 +7923,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-26.4.0-hc039f44_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.4-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-openmp_hd77311e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda @@ -8076,7 +8076,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-9.5.2-py312h244374b_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.2-py312hd2ae51d_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-io-ffmpeg-9.5.2-py312hfb62880_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.47-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-25.10.0-pyhd8ed1ab_0.conda @@ -8440,7 +8440,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.2.1-h5230ea7_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.30-openmp_hea878ba_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda @@ -8831,7 +8831,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.4-nompi_py311ha0596eb_105.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda @@ -9057,7 +9057,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hypre-3.1.0-hfcc2723_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda @@ -9103,7 +9103,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.0-default_h746c552_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcolamd-3.3.4-hf02c80a_7100101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hcf29cc6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hae6b9f4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda @@ -9142,6 +9142,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.3-h9abb657_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.22.0-hd9031aa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libptscotch-7.0.11-int32_hf9c0034_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.3.0-h8f1669f_18.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libscotch-7.0.11-int32_hbb129e3_3.conda @@ -9195,7 +9196,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.4-nompi_py312h25f8dc5_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda @@ -9527,7 +9528,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.4-nompi_py312h5d59a02_102.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda @@ -9773,7 +9774,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-sse-0.4.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hypre-3.1.0-hfcc2723_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda @@ -9848,7 +9849,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.0-default_h746c552_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcolamd-3.3.4-hf02c80a_7100101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hcf29cc6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hae6b9f4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda @@ -9913,6 +9914,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.3-h9abb657_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h2b00c02_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpsl-0.22.0-hd9031aa_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libptscotch-7.0.11-int32_hf9c0034_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.60.2-h61e6d4b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-14.3.0-h8f1669f_18.conda @@ -9993,7 +9995,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.33-openmp_hd77311e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda @@ -10514,7 +10516,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.30-openmp_hea878ba_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda @@ -10933,7 +10935,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.4-nompi_py312h25f8dc5_102.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openmpi-5.0.10-h611b0e2_0.conda @@ -11267,7 +11269,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.4-nompi_py312h5d59a02_102.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openmpi-5.0.10-h899237b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.1-hd24854e_1.conda @@ -11735,7 +11737,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nodejs-25.7.0-he4ff34a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.33-openmp_hd77311e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda @@ -12258,7 +12260,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h784d473_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nodejs-25.7.0-hbfc8e16_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.30-openmp_hea878ba_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda @@ -12514,7 +12516,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/expat-2.8.1-hecca717_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.1.2-gpl_h1bf8424_901.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.1.2-gpl_h6237aff_902.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.11-mpi_mpich_h084ba78_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/flexcache-0.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/flexparser-0.4-pyhd8ed1ab_1.conda @@ -12603,7 +12605,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260526.0-cxx17_h7b12aa8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libamd-3.3.3-h456b2da_7100101.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.4-h96ad9f0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.5-h434b012_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-1.88.0-hd24cca6_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libboost-devel-1.88.0-hfcd1e18_7.conda @@ -12624,7 +12626,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hae6b9f4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdovi-3.3.2-ha23c83e_4.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda @@ -12683,7 +12685,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-frontend-2026.2.1-h21c0c73_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenvino-tensorflow-lite-frontend-2026.2.1-hecca717_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopus-1.6.1-h280c20c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libplacebo-7.360.1-h9eeb4b2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_1.conda @@ -12711,7 +12713,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/liburing-2.12-hb700be7_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libusb-1.0.29-h73b1eb8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.2-h5347b49_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.23.0-he1eb515_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.24.0-he1eb515_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpl-2.16.0-h54a6638_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvpx-1.15.2-hecca717_0.conda @@ -12759,7 +12761,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.7.3-nompi_py312hf6400b3_100.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.4-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/opencl-headers-2025.06.13-h5888daf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openh264-2.6.0-hc22cd8d_0.conda @@ -12893,7 +12895,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-9.5.2-py312h244374b_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-base-9.5.2-py312hd2ae51d_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/vtk-io-ffmpeg-9.5.2-py312hfb62880_5.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wayland-protocols-1.47-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-25.10.0-pyhd8ed1ab_0.conda @@ -13212,7 +13214,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/netcdf4-1.7.3-nompi_py312h947358d_100.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openh264-2.6.0-hb5b2745_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openldap-2.6.10-hbe55e7a_0.conda @@ -16023,9 +16025,9 @@ packages: purls: [] size: 12485347 timestamp: 1773008832077 -- conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.1.2-gpl_h1bf8424_901.conda - sha256: 2d56b0d90a1e581e296a41aa0a0443c85f918a94780e779a23005be9128627be - md5: 0c457f1b2384bb0aa984831a79021a66 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ffmpeg-8.1.2-gpl_h6237aff_902.conda + sha256: be08b871acad2035f02eff5561d2f0c0bbfc4e12da44ed9074699a8e81e82281 + md5: ff2c7afbb2ea85fde7a662d2124baa44 depends: - __glibc >=2.17,<3.0.a0 - alsa-lib >=1.2.16.1,<1.3.0a0 @@ -16035,15 +16037,15 @@ packages: - fontconfig >=2.18.1,<3.0a0 - fonts-conda-ecosystem - gmp >=6.3.0,<7.0a0 - - harfbuzz >=14.2.1 - lame >=3.100,<3.101.0a0 - - libass >=0.17.4,<0.17.5.0a0 + - libass >=0.17.5,<0.17.6.0a0 - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libgcc >=14 + - libharfbuzz >=14.2.1 - libiconv >=1.18,<2.0a0 - - libjxl >=0.11,<1.0a0 + - libjxl >=0.12,<1.0a0 - liblzma >=5.8.3,<6.0a0 - libopenvino >=2026.2.1,<2026.2.2.0a0 - libopenvino-auto-batch-plugin >=2026.2.1,<2026.2.2.0a0 @@ -16062,7 +16064,7 @@ packages: - libplacebo >=7.360.1,<7.361.0a0 - librsvg >=2.62.3,<3.0a0 - libstdcxx >=14 - - libva >=2.23.0,<3.0a0 + - libva >=2.24.0,<3.0a0 - libvorbis >=1.3.7,<1.4.0a0 - libvpl >=2.16.0,<2.17.0a0 - libvpx >=1.15.2,<1.16.0a0 @@ -16086,8 +16088,8 @@ packages: license: GPL-2.0-or-later license_family: GPL purls: [] - size: 13078770 - timestamp: 1782260267617 + size: 13043132 + timestamp: 1783096951485 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ffmpeg-8.0.1-gpl_ha5d8480_114.conda sha256: 5ad931d8ae03ad6a76104dc79189ba0838eed15b814cb12ae94aed0f139e7e66 md5: c61afe756ee72cdb873b4a500a674975 @@ -19644,6 +19646,24 @@ packages: purls: [] size: 152179 timestamp: 1749328931930 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libass-0.17.5-h434b012_0.conda + sha256: 24d4b59a0267e1c159c3af82df106b42faeefccceba3c489044c93abf113c503 + md5: c1cb4d6e8a6e3f724740dee5346fc8b4 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libzlib >=1.3.2,<2.0a0 + - fribidi >=1.0.16,<2.0a0 + - fontconfig >=2.18.1,<3.0a0 + - fonts-conda-ecosystem + - libiconv >=1.18,<2.0a0 + - harfbuzz >=14.2.1 + license: ISC + purls: [] + size: 154964 + timestamp: 1782298715788 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libass-0.17.4-hcbd7ca7_0.conda sha256: 079f5fdf7aace970a0db91cd2cc493c754dfdc4520d422ecec43d2561021167a md5: 0977f4a79496437ff3a2c97d13c4c223 @@ -20356,23 +20376,6 @@ packages: purls: [] size: 480327 timestamp: 1782911787190 -- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.21.0-hcf29cc6_1.conda - sha256: 7d243f45a48f62cb8e3b871dd336137fe0fb90094a191756fb553508837f6338 - md5: 2c1e55d695b11525c760b486ac0be517 - depends: - - __glibc >=2.17,<3.0.a0 - - krb5 >=1.22.2,<1.23.0a0 - - libgcc >=14 - - libnghttp2 >=1.68.1,<2.0a0 - - libssh2 >=1.11.1,<2.0a0 - - libzlib >=1.3.2,<2.0a0 - - openssl >=3.5.7,<4.0a0 - - zstd >=1.5.7,<1.6.0a0 - license: curl - license_family: MIT - purls: [] - size: 478914 - timestamp: 1782802520033 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.18.0-hd5a2499_1.conda sha256: dbc34552fc6f040bbcd52b4246ec068ce8d82be0e76bfe45c6984097758d37c2 md5: 2742a933ef07e91f38e3d33ad6fe937c @@ -20511,6 +20514,18 @@ packages: purls: [] size: 310785 timestamp: 1757212153962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda + sha256: 7d3187c11b7ae66c5595a8afd5a7ce352a490527fdf6614cab129bc7f2c16ba3 + md5: d8d16b9b32a3c5df7e5b3350e2cbe058 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpciaccess >=0.19,<0.20.0a0 + license: MIT + license_family: MIT + purls: [] + size: 311505 + timestamp: 1778975798004 - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 md5: c277e0a4d549b03ac1e9d6cbbe3d017b @@ -21538,6 +21553,7 @@ packages: - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 license: BSD-3-Clause + license_family: BSD purls: [] size: 1849871 timestamp: 1783056664598 @@ -23148,6 +23164,17 @@ packages: purls: [] size: 28424 timestamp: 1749901812541 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda + sha256: f41721636a7c2e51bc2c642e1127955ab9c81145470714fdaac44d4d09e4af41 + md5: 33082e13b4769b48cfeb648e15bfe3fc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 29147 + timestamp: 1773533027610 - conda: https://conda.anaconda.org/conda-forge/linux-64/libplacebo-7.360.1-h9eeb4b2_0.conda sha256: 26cbbd3d7b91801826c779c3f7e87d071856d5cbe3d55b22777ca0d984fb02ed md5: e6324dfe6c02e0736bb9235f8ef3c8a6 @@ -24207,6 +24234,27 @@ packages: purls: [] size: 221308 timestamp: 1765652453244 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libva-2.24.0-he1eb515_0.conda + sha256: 96b938e39493331d796e0b0b805038b9ee11b0c192b571bbc6c7adc3701724ef + md5: 14f10db75eec75c02e56c858016f787e + depends: + - __glibc >=2.17,<3.0.a0 + - libdrm >=2.4.127,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libglx >=1.7.0,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - wayland >=1.25.0,<2.0a0 + - wayland-protocols + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 223532 + timestamp: 1782992630083 - conda: https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h54a6638_2.conda sha256: ca494c99c7e5ecc1b4cd2f72b5584cef3d4ce631d23511184411abcbb90a21a5 md5: b4ecbefe517ed0157c37f8182768271c @@ -26267,45 +26315,45 @@ packages: - pkg:pypi/notebook-shim?source=hash-mapping size: 16817 timestamp: 1733408419340 -- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.5.0-py312h33ff503_0.conda - sha256: c8d5f70715fc6cd3dcd16fdd11b51879ed4484963f066b33fbaf20c4ffb153af - md5: 24f70d3db040fc69ee72cc38e55bc8e3 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda + sha256: dfcbeadb3e7ad0da7a55a0525884ca34c19584154e13cc4159396b305d1bd445 + md5: 6e31d55ee1110fda83b4f4045f4d73ff depends: - python - - libgcc >=14 - libstdcxx >=14 + - libgcc >=14 - __glibc >=2.17,<3.0.a0 - - python_abi 3.12.* *_cp312 - - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 + - python_abi 3.12.* *_cp312 + - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/numpy?source=compressed-mapping - size: 8911732 - timestamp: 1782112536981 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.5.0-py312ha003a3f_0.conda - sha256: 36f7062ce484ce45b8fdb2ee48cbbc7c0023004ba01e692efd5cd70034d93609 - md5: f223ea9a1ca019ddbcfe28ce02ec366c + - pkg:pypi/numpy?source=hash-mapping + size: 8759520 + timestamp: 1779169200325 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda + sha256: b09e03dace335a6f303352fc4e167243e04d7026d55008546fa643d224fb0bad + md5: 9f554fdfa902971390975b489e678c03 depends: - python - - libcxx >=19 - __osx >=11.0 - - libcblas >=3.9.0,<4.0a0 - - liblapack >=3.9.0,<4.0a0 + - libcxx >=19 - python_abi 3.12.* *_cp312 + - liblapack >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping - size: 6964084 - timestamp: 1782112554625 + size: 6843172 + timestamp: 1779169213435 - conda: https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.3-hb9d3cd8_0.conda sha256: 2254dae821b286fb57c61895f2b40e3571a070910fdab79a948ff703e1ea807b md5: 56f8947aa9d5cf37b0b3d43b83f34192 diff --git a/pixi.toml b/pixi.toml index 44a0d558..8b98e421 100644 --- a/pixi.toml +++ b/pixi.toml @@ -68,7 +68,12 @@ python = "3.12.*" # Build requirements cython = ">=3.1,<4" -numpy = ">=2" +# numpy 2.5.0 breaks locally-built petsc4py at import ("numpy.dtype size +# changed ... Expected 96 from C header, got 88 from PyObject") — verified +# with a CLEAN petsc4py rebuild against 2.5.0 (fails) vs 2.4.6 (works). +# conda-forge petsc4py binaries are unaffected; only the AMR envs' local +# builds break. Lift the cap when petsc4py/Cython support numpy 2.5. +numpy = ">=2,<2.5" pip = ">=25,<26" setuptools = ">=75,<76" compilers = "*" diff --git a/uw b/uw index cf61d764..c9bc828e 100755 --- a/uw +++ b/uw @@ -159,6 +159,10 @@ run_build() { # Step 2: Check/build petsc4py if ! petsc4py_installed "$env"; then echo " Installing petsc4py for $env..." + # The in-tree build/ cache is SHARED across envs/worktrees and pip + # silently reuses its objects even when numpy/PETSc changed — + # stale-ABI imports ("numpy.dtype size changed"). Always start clean. + rm -rf "$PETSC_CUSTOM/src/binding/petsc4py/build" $PIXI run -e "$env" pip install "$PETSC_CUSTOM/src/binding/petsc4py" --no-build-isolation --no-cache-dir || { echo -e "${YELLOW}petsc4py build failed${NC}" exit 1 @@ -779,6 +783,7 @@ run_setup() { if petsc_built; then echo -e "${GREEN}✓ Custom PETSc already built${NC}" echo "Installing petsc4py..." + rm -rf "$PETSC_CUSTOM/src/binding/petsc4py/build" # shared stale-ABI cache $PIXI run -e "$new_env" pip install "$PETSC_CUSTOM/src/binding/petsc4py" --no-build-isolation 2>/dev/null else echo -e "${YELLOW}Custom PETSc needs to be built (~1 hour)${NC}" @@ -1340,6 +1345,7 @@ worktree_create() { # into the main repo makes pixi install into the wrong environment. if is_amr_env "$env" && [ -d "$wt_path/petsc-custom/petsc/src/binding/petsc4py" ]; then echo " Installing petsc4py for $env..." + rm -rf "$wt_path/petsc-custom/petsc/src/binding/petsc4py/build" # shared stale-ABI cache (cd "$wt_path" && $PIXI run -e "$env" pip install \ "$wt_path/petsc-custom/petsc/src/binding/petsc4py" --no-build-isolation 2>/dev/null) && \ echo -e " ${GREEN}✓${NC} petsc4py installed" || \