diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d8af114e8..6a75dbeec 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -76,8 +76,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Move nutils directory - run: mv nutils _nutils - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: @@ -153,25 +151,37 @@ jobs: run: python -um devtools.gha.delete_coverage_artifacts test-examples: needs: build-python-package - name: 'Test examples ${{ matrix.os }}' + name: 'Test ${{ matrix.example }} on ${{ matrix.os }}' runs-on: ${{ matrix.os }}-latest strategy: matrix: + example: + - adaptivity + - burgers + - cahnhilliard + - coil + - cylinderflow + - drivencavity + - elasticity + - finitestrain + - laplace + - platewithhole + - poisson + - torsion + - turek os: [ubuntu, macos, windows] fail-fast: false env: _wheel: ${{ needs.build-python-package.outputs.wheel }} - NUTILS_MATRIX: scipy NUTILS_NPROCS: 1 NUTILS_DEBUG: all OMP_NUM_THREADS: 1 VECLIB_MAXIMUM_THREADS: 1 PYTHONHASHSEED: 0 + PYTHONUTF8: 1 # pending https://github.com/pypa/pip/pull/13862 steps: - name: Checkout uses: actions/checkout@v4 - - name: Move nutils directory - run: mv nutils _nutils - name: Set up Python uses: actions/setup-python@v5 with: @@ -182,19 +192,17 @@ jobs: name: python-package path: dist/ - name: Install Gmsh - # install gmsh via pip on windows and macos and via apt on linux, as - # the latter version is dynamically linked and requires libgl etc. - run: | - ${{ matrix.os == 'ubuntu' && 'sudo apt install gmsh' || 'python -um pip install gmsh' }} - echo "NUTILS_TESTING_REQUIRES=$NUTILS_TESTING_REQUIRES app:gmsh" >> $GITHUB_ENV + if: ${{ matrix.os == 'ubuntu' }} + # install gmsh dependencies on ubuntu as the pip package is dynamically linked + run: sudo apt install libglu1-mesa - name: Install Nutils and dependencies id: install run: | - python -um pip install --upgrade --upgrade-strategy eager wheel + python -um pip install --upgrade --upgrade-strategy eager pip # Install Nutils from `dist` dir created in job `build-python-package`. - python -um pip install "$_wheel[import-gmsh,matrix-scipy,export-mpl]" + python -um pip install "$_wheel" --requirements-from-script examples/${{ matrix.example }}.py - name: Test - run: python -um unittest discover -b -q -t . -s examples + run: python -um unittest -bv examples.${{ matrix.example }} test-sphinx: name: Test building docs runs-on: ubuntu-latest diff --git a/devtools/container/build.py b/devtools/container/build.py index 50493b366..d73ef2a2d 100644 --- a/devtools/container/build.py +++ b/devtools/container/build.py @@ -68,7 +68,7 @@ container = stack.enter_context(Container.new_from(base, mounts=[Mount(src=wheel, dst=f'/{wheel.name}')])) - container.run('pip', 'install', '--break-system-packages', '--no-cache-dir', f'/{wheel.name}[export-mpl,import-gmsh,matrix-scipy]', env=dict(PYTHONHASHSEED='0')) + container.run('pip', 'install', '--break-system-packages', '--no-cache-dir', f'/{wheel.name}[export-mpl,import-gmsh,matrix-scipy]', 'nutils-units', env=dict(PYTHONHASHSEED='0')) container.add_label('org.opencontainers.image.url', 'https://github.com/evalf/nutils') container.add_label('org.opencontainers.image.source', 'https://github.com/evalf/nutils') container.add_label('org.opencontainers.image.authors', 'Evalf') diff --git a/examples/adaptivity.py b/examples/adaptivity.py index 42d9a6465..f658e8916 100644 --- a/examples/adaptivity.py +++ b/examples/adaptivity.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System from nutils.expression_v2 import Namespace diff --git a/examples/burgers.py b/examples/burgers.py index f9067f1c0..a3c90e26a 100644 --- a/examples/burgers.py +++ b/examples/burgers.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System from nutils.expression_v2 import Namespace diff --git a/examples/cahnhilliard.py b/examples/cahnhilliard.py index 5b3a263b9..369e9209b 100644 --- a/examples/cahnhilliard.py +++ b/examples/cahnhilliard.py @@ -1,23 +1,37 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=10a6", "nutils-units>=0.2", "matplotlib>=3"] +# /// + from nutils import mesh, function, numeric, export, testing from nutils.solver import System from nutils.expression_v2 import Namespace -from nutils.SI import Length, Time, Density, Tension, Energy, Pressure, Velocity, parse +try: + from nutils.units.typing import Length, Time, Density, Tension, Energy, Pressure, Velocity +except ModuleNotFoundError as e: + if hasattr(e, 'add_note'): + e.add_note("Consider installing the units package via: pip install nutils-units") + raise import numpy import treelog as log -def main(size: Length = parse('10cm'), - epsilon: Length = parse('1mm'), - mobility: Time/Density = parse('1mL*s/kg'), - stens: Tension = parse('50mN/m'), - wtensn: Tension = parse('30mN/m'), - wtensp: Tension = parse('20mN/m'), +Mobility = Time / Density +LED = Energy / Length # linear energy density + + +def main(size: Length = Length('10cm'), + epsilon: Length = Length('1mm'), + mobility: Mobility = Mobility('1mL*s/kg'), + stens: Tension = Tension('50mN/m'), + wtensn: Tension = Tension('30mN/m'), + wtensp: Tension = Tension('20mN/m'), nelems: int = 0, etype: str = 'rectilinear', degree: int = 1, - timestep: Time = parse('.1s'), - tol: Energy/Length = parse('1nJ/m'), - endtime: Time = parse('1min'), + timestep: Time = Time('.1s'), + tol: LED = LED('1nJ/m'), + endtime: Time = Time('1min'), seed: int = 0, circle: bool = True, stable: bool = False, @@ -207,14 +221,14 @@ def main(size: Length = parse('10cm'), class test(testing.TestCase): def test_initial(self): - args = main(epsilon=parse('5cm'), mobility=parse('1μL*s/kg'), nelems=3, degree=2, timestep=parse('1h'), endtime=parse('1h'), circle=False) + args = main(epsilon=Length('5cm'), mobility=Mobility('1μL*s/kg'), nelems=3, degree=2, timestep=Time('1h'), endtime=Time('1h'), circle=False) with self.subTest('concentration'): self.assertAlmostEqual64(args['φ0'], ''' eNoBYgCd/xM3LjTtNYs3MDcUyt41uc14zjo0LzKzNm812jFhNNMzwDYgzbMzV8o0yCM1rzWeypE3Tcnx L07NzTa4NlMyETREyrPIGMxYMl82VDbjy1/M8clZyf3IRjday6XLmMl6NRnJMF4tqQ==''') def test_square(self): - args = main(epsilon=parse('5cm'), mobility=parse('1μL*s/kg'), nelems=3, degree=2, timestep=parse('1h'), endtime=parse('2h'), circle=False) + args = main(epsilon=Length('5cm'), mobility=Mobility('1μL*s/kg'), nelems=3, degree=2, timestep=Time('1h'), endtime=Time('2h'), circle=False) with self.subTest('concentration'): self.assertAlmostEqual64(args['φ'], ''' eNoBYgCd/y41EjX2NZ829DXcMxUz0jTANL41ajaNNZox/9EoNRY1LDUkNZAw1cqnysI1njWdNNkxMMuk @@ -225,7 +239,7 @@ def test_square(self): xlrGoziaOEA3os8VyJLHk8hlyTw2sDZXydPISMoPy5zGe8i7yzfIncgAzGLKwgYwXw==''') def test_multipatchcircle(self): - args = main(epsilon=parse('5cm'), mobility=parse('1μL*s/kg'), nelems=3, etype='multipatch', degree=2, timestep=parse('1h'), endtime=parse('2h')) + args = main(epsilon=Length('5cm'), mobility=Mobility('1μL*s/kg'), nelems=3, etype='multipatch', degree=2, timestep=Time('1h'), endtime=Time('2h')) with self.subTest('concentration'): self.assertAlmostEqual64(args['φ'], ''' eNoNz01IlFEUBmByEcVsWkiBoKHYoh9nvnvPOa5GcCE1gqNjDZOBBUM1iSYYEf2JEGZE0SoIokWMCCYk diff --git a/examples/coil.py b/examples/coil.py index aa4b4dd97..2289f8adc 100644 --- a/examples/coil.py +++ b/examples/coil.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import export, function, mesh, testing from nutils.solver import System from nutils.expression_v2 import Namespace diff --git a/examples/cylinderflow.py b/examples/cylinderflow.py index dcf1bf93a..216c17798 100644 --- a/examples/cylinderflow.py +++ b/examples/cylinderflow.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing, numeric from nutils.solver import System from nutils.expression_v2 import Namespace diff --git a/examples/drivencavity.py b/examples/drivencavity.py index 7edc95de5..af57e8d1f 100644 --- a/examples/drivencavity.py +++ b/examples/drivencavity.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System, LinesearchNewton from nutils.expression_v2 import Namespace diff --git a/examples/elasticity.py b/examples/elasticity.py index e27eab59c..0e6107354 100644 --- a/examples/elasticity.py +++ b/examples/elasticity.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System from nutils.expression_v2 import Namespace diff --git a/examples/finitestrain.py b/examples/finitestrain.py index cdf2f6081..e0e8dc600 100644 --- a/examples/finitestrain.py +++ b/examples/finitestrain.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System, Minimize from nutils.expression_v2 import Namespace diff --git a/examples/laplace.py b/examples/laplace.py index f66a45af3..375aaaa68 100644 --- a/examples/laplace.py +++ b/examples/laplace.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System from nutils.expression_v2 import Namespace diff --git a/examples/platewithhole.py b/examples/platewithhole.py index 3be8ba20a..3997c8776 100644 --- a/examples/platewithhole.py +++ b/examples/platewithhole.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System from nutils.expression_v2 import Namespace diff --git a/examples/poisson.py b/examples/poisson.py index 2a83f9172..c5b311d07 100644 --- a/examples/poisson.py +++ b/examples/poisson.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System diff --git a/examples/torsion.py b/examples/torsion.py index bd9a6e169..7f141db80 100644 --- a/examples/torsion.py +++ b/examples/torsion.py @@ -1,3 +1,8 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=9", "matplotlib>=3"] +# /// + from nutils import mesh, function, export, testing from nutils.solver import System, Minimize from nutils.expression_v2 import Namespace diff --git a/examples/turek.py b/examples/turek.py index 35cd10e67..a4324353e 100644 --- a/examples/turek.py +++ b/examples/turek.py @@ -1,7 +1,17 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = ["nutils>=10a6", "nutils-units>=0.2", "gmsh>=4", "meshio>=5", "matplotlib>=3"] +# /// + from nutils import cli, export, function, testing from nutils.mesh import gmsh from nutils.solver import System -from nutils.SI import Length, Density, Viscosity, Velocity, Time, Pressure, Acceleration +try: + from nutils.units.typing import Length, Density, Viscosity, Velocity, Time, Pressure, Acceleration +except ModuleNotFoundError as e: + if hasattr(e, 'add_note'): + e.add_note("Consider installing the units package via: pip install nutils-units") + raise from nutils.expression_v2 import Namespace from collections import defaultdict, deque from dataclasses import dataclass @@ -221,7 +231,7 @@ def main(domain: Domain = Domain(), solid: Optional[Solid] = Solid(), fluid: Opt if dynamic: ns.v, ns.a = dynamic.newmark_defo(ns.d) else: - ns.a = Acceleration.wrap(function.zeros((2,))) + ns.a = numpy.repeat(Acceleration.zero, 2) # Deformed geometry ns.x_i = 'xref_i + d_i' @@ -260,8 +270,8 @@ def main(domain: Domain = Domain(), solid: Optional[Solid] = Solid(), fluid: Opt else: # fully rigid solid ns.x = ns.xref - ns.v = Velocity.wrap(function.zeros((2,))) - ns.a = Acceleration.wrap(function.zeros((2,))) + ns.v = numpy.repeat(Velocity.zero, 2) + ns.a = numpy.repeat(Acceleration.zero, 2) if fluid: @@ -339,7 +349,7 @@ def main(domain: Domain = Domain(), solid: Optional[Solid] = Solid(), fluid: Opt DL = uxy = None # for unit tests only - for t in log.iter.fraction('timestep', dynamic.times) if dynamic else [Time.wrap(float('inf'))]: + for t in log.iter.fraction('timestep', dynamic.times) if dynamic else [Time.zero * float('inf')]: if dynamic: if solid: diff --git a/nutils/SI.py b/src/nutils/SI.py similarity index 99% rename from nutils/SI.py rename to src/nutils/SI.py index e0d250606..9b6a62c2a 100644 --- a/nutils/SI.py +++ b/src/nutils/SI.py @@ -125,7 +125,9 @@ import typing import numpy from functools import partial, partialmethod, reduce -from . import function, topology, sample +from . import function, topology, sample, warnings + +warnings.deprecation("nutils.SI is deprecated and will be removed in Nutils 11; please use the externally installable nutils.units instead (pip install nutils-units)") class DimensionError(TypeError): diff --git a/nutils/__init__.py b/src/nutils/__init__.py similarity index 100% rename from nutils/__init__.py rename to src/nutils/__init__.py diff --git a/nutils/_backports.py b/src/nutils/_backports.py similarity index 100% rename from nutils/_backports.py rename to src/nutils/_backports.py diff --git a/nutils/_graph.py b/src/nutils/_graph.py similarity index 100% rename from nutils/_graph.py rename to src/nutils/_graph.py diff --git a/nutils/_pyast.py b/src/nutils/_pyast.py similarity index 100% rename from nutils/_pyast.py rename to src/nutils/_pyast.py diff --git a/nutils/_util.py b/src/nutils/_util.py similarity index 100% rename from nutils/_util.py rename to src/nutils/_util.py diff --git a/nutils/cache.py b/src/nutils/cache.py similarity index 100% rename from nutils/cache.py rename to src/nutils/cache.py diff --git a/nutils/cli.py b/src/nutils/cli.py similarity index 100% rename from nutils/cli.py rename to src/nutils/cli.py diff --git a/nutils/debug_flags.py b/src/nutils/debug_flags.py similarity index 100% rename from nutils/debug_flags.py rename to src/nutils/debug_flags.py diff --git a/nutils/element.py b/src/nutils/element.py similarity index 100% rename from nutils/element.py rename to src/nutils/element.py diff --git a/nutils/elementseq.py b/src/nutils/elementseq.py similarity index 100% rename from nutils/elementseq.py rename to src/nutils/elementseq.py diff --git a/nutils/evaluable.py b/src/nutils/evaluable.py similarity index 100% rename from nutils/evaluable.py rename to src/nutils/evaluable.py diff --git a/nutils/export.py b/src/nutils/export.py similarity index 100% rename from nutils/export.py rename to src/nutils/export.py diff --git a/nutils/expression_v1.py b/src/nutils/expression_v1.py similarity index 100% rename from nutils/expression_v1.py rename to src/nutils/expression_v1.py diff --git a/nutils/expression_v2.py b/src/nutils/expression_v2.py similarity index 100% rename from nutils/expression_v2.py rename to src/nutils/expression_v2.py diff --git a/nutils/function.py b/src/nutils/function.py similarity index 100% rename from nutils/function.py rename to src/nutils/function.py diff --git a/nutils/matrix/__init__.py b/src/nutils/matrix/__init__.py similarity index 100% rename from nutils/matrix/__init__.py rename to src/nutils/matrix/__init__.py diff --git a/nutils/matrix/_auto.py b/src/nutils/matrix/_auto.py similarity index 100% rename from nutils/matrix/_auto.py rename to src/nutils/matrix/_auto.py diff --git a/nutils/matrix/_base.py b/src/nutils/matrix/_base.py similarity index 100% rename from nutils/matrix/_base.py rename to src/nutils/matrix/_base.py diff --git a/nutils/matrix/_mkl.py b/src/nutils/matrix/_mkl.py similarity index 100% rename from nutils/matrix/_mkl.py rename to src/nutils/matrix/_mkl.py diff --git a/nutils/matrix/_numpy.py b/src/nutils/matrix/_numpy.py similarity index 100% rename from nutils/matrix/_numpy.py rename to src/nutils/matrix/_numpy.py diff --git a/nutils/matrix/_scipy.py b/src/nutils/matrix/_scipy.py similarity index 100% rename from nutils/matrix/_scipy.py rename to src/nutils/matrix/_scipy.py diff --git a/nutils/mesh.py b/src/nutils/mesh.py similarity index 100% rename from nutils/mesh.py rename to src/nutils/mesh.py diff --git a/nutils/numeric.py b/src/nutils/numeric.py similarity index 100% rename from nutils/numeric.py rename to src/nutils/numeric.py diff --git a/nutils/parallel.py b/src/nutils/parallel.py similarity index 100% rename from nutils/parallel.py rename to src/nutils/parallel.py diff --git a/nutils/points.py b/src/nutils/points.py similarity index 100% rename from nutils/points.py rename to src/nutils/points.py diff --git a/nutils/pointsseq.py b/src/nutils/pointsseq.py similarity index 100% rename from nutils/pointsseq.py rename to src/nutils/pointsseq.py diff --git a/nutils/sample.py b/src/nutils/sample.py similarity index 100% rename from nutils/sample.py rename to src/nutils/sample.py diff --git a/nutils/solver.py b/src/nutils/solver.py similarity index 100% rename from nutils/solver.py rename to src/nutils/solver.py diff --git a/nutils/testing.py b/src/nutils/testing.py similarity index 100% rename from nutils/testing.py rename to src/nutils/testing.py diff --git a/nutils/topology.py b/src/nutils/topology.py similarity index 100% rename from nutils/topology.py rename to src/nutils/topology.py diff --git a/nutils/transform.py b/src/nutils/transform.py similarity index 100% rename from nutils/transform.py rename to src/nutils/transform.py diff --git a/nutils/transformseq.py b/src/nutils/transformseq.py similarity index 100% rename from nutils/transformseq.py rename to src/nutils/transformseq.py diff --git a/nutils/types.py b/src/nutils/types.py similarity index 100% rename from nutils/types.py rename to src/nutils/types.py diff --git a/nutils/unit.py b/src/nutils/unit.py similarity index 97% rename from nutils/unit.py rename to src/nutils/unit.py index b22d680bb..8f512b285 100644 --- a/nutils/unit.py +++ b/src/nutils/unit.py @@ -39,6 +39,9 @@ ''' import re +from . import warnings + +warnings.deprecation("nutils.unit is deprecated and will be removed in Nutils 11; please use the externally installable nutils.units instead (pip install nutils-units)") def create(_typename='unit', **units): diff --git a/nutils/warnings.py b/src/nutils/warnings.py similarity index 100% rename from nutils/warnings.py rename to src/nutils/warnings.py