diff --git a/.binder/apt.txt b/.binder/apt.txt index 75a62cd3e..ba2938060 100644 --- a/.binder/apt.txt +++ b/.binder/apt.txt @@ -6,4 +6,3 @@ libxinerama1 libfltk1.3-dev libfreetype6-dev libgl1-mesa-dev -python3-gmsh diff --git a/.binder/environment.yml b/.binder/environment.yml index 6d5aa95d4..ca03de1c0 100644 --- a/.binder/environment.yml +++ b/.binder/environment.yml @@ -2,16 +2,18 @@ name: uw3 channels: - conda-forge dependencies: - - python=3.10 - - petsc=3.21.5 + - python=3.11 + - compilers + - openmpi + - openmpi-mpicc + - openmpi-mpicxx + - petsc=3.21.5 # Fix for now (errors with hdf5 / uw3) - petsc4py=3.21.5 - cython=3.* - mpmath<=1.3 - mesalib - numpy - scipy - - mpich-mpicc - - mpich-mpicxx - mpi4py - h5py - sympy diff --git a/.github/.devcontainer/uw_environment.yml b/.github/.devcontainer/uw_environment.yml index 8d8fe6528..ca03de1c0 100644 --- a/.github/.devcontainer/uw_environment.yml +++ b/.github/.devcontainer/uw_environment.yml @@ -2,28 +2,30 @@ name: uw3 channels: - conda-forge dependencies: - - petsc=3.21.0 - - petsc4py=3.21.0 + - python=3.11 + - compilers + - openmpi + - openmpi-mpicc + - openmpi-mpicxx + - petsc=3.21.5 # Fix for now (errors with hdf5 / uw3) + - petsc4py=3.21.5 - cython=3.* - mpmath<=1.3 - mesalib - numpy - scipy - - mpich-mpicc - - mpich-mpicxx - mpi4py - h5py - sympy - pytest - - ipython + - ipython - pyvista - - quarto - pip - - pip: - - gmsh - - jupyterlab>=4.1 - - jupytext>=1.16.1 - - pygments>=2.17.0 - - trame-vtk - - trame-vuetify - + - pip: + - gmsh + - gmsh-api + - jupyterlab + - jupytext + - pygments + - trame-vtk + - trame-vuetify diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4b1331594..d49979b0a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,8 +1,7 @@ This page provides information about contributing to Underworld’s codebase. +For contributions of Underworld models please go https://github.com/underworld-community -For contributions to Underworld models please go https://github.com/underworld-community - ----- +---- We welcome contributions to Underworld’s codebase in the form of: @@ -11,8 +10,8 @@ We welcome contributions to Underworld’s codebase in the form of: * Suggestions / Requests * Documentation modifications (including docstrings) -For Bug reports and Suggestions / Requests please submit an Issue on the Underworld GitHub Issue Tracker. -Please tag the Issue with a given Label to help us assess the issue and provide simple scripts that explain how to +For Bug reports and Suggestions / Requests please submit an Issue on the Underworld GitHub Issue Tracker. +Please tag the Issue with a given Label to help us assess the issue and provide simple scripts that explain how to reproduce the problem. Click here to submit an Issue https://github.com/underworldcode/underworld3/issues @@ -30,14 +29,14 @@ More specifically: 2. Add the master Underworld repository as an additional remote source (named `uwmaster`) for your local repo and pull down its latest changesets. Checkout to the master/development repo state, and then create a new local branch which will contain your forthcoming changes. ``` bash - + git remote add uw3 https://github.com/underworldcode/underworld3 git pull uw3 git checkout uw3/development git checkout -b newFeature ``` - + 3. Make your changes! Remember to write comments, a test if applicable and follow the code style of the project -4. Push your changes to your GitHub fork and then submit a PR to the `development` branch of Underworld via Github. \ No newline at end of file +4. Push your changes to your GitHub fork and then submit a PR to the `development` branch of Underworld via Github. diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml index f72a436d1..227b863bb 100644 --- a/.github/workflows/draft-pdf.yml +++ b/.github/workflows/draft-pdf.yml @@ -1,12 +1,12 @@ -name: Draft PDF -on: - push: - paths: - - joss-paper/paper.md - - joss-paper/paper.bib - # - assets/img1.png - # - assets/img2.png - - .github/workflows/draft-pdf.yml +name: Draft PDF (JOSS) +on: + push: + paths: + - joss-paper/paper.md + - joss-paper/paper.bib + # - assets/img1.png + # - assets/img2.png + - .github/workflows/draft-pdf.yml jobs: paper: @@ -28,4 +28,4 @@ jobs: # This is the output path where Pandoc will write the compiled # PDF. Note, this should be the same directory as the input # paper.md. In the case we have (symlink), the paper is in the subdir - path: joss-paper/paper.pdf \ No newline at end of file + path: joss-paper/paper.pdf diff --git a/.zenodo.json b/.zenodo.json index 7fcefc0dd..520a81b4b 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -20,9 +20,14 @@ "orcid": "0000-0003-0817-354X", "affiliation": "Research School of Earth Sciences, The Australian National University", }, + { + "name": "Matt Knepley", + "orcid": "0000-0002-2292-0735" + "affiliation": "Computer Science and Engineering, University at Buffalo", + }, { "name": "Ben Knight", - "affiliation": "Monash University", + "affiliation": "School of Earth, Atmospheric & Environmental Science, Monash University", "orcid": "0000-0001-7919-2575" }, { @@ -32,7 +37,7 @@ }, { "name": "John Mansour", - "affiliation": "TBD", + "affiliation": "School of Earth, Atmospheric & Environmental Science, Monash University", "orcid": "0000-0001-5865-1664" }, { @@ -45,4 +50,4 @@ "title": "Underworld3: Mathematically Self-Describing Modelling in Python for Desktop, HPC and Cloud", "upload_type": "software", "access_right": "open" -} \ No newline at end of file +} diff --git a/LICENCE.md b/LICENCE.md index dfe4bee7d..7a83ba969 100644 --- a/LICENCE.md +++ b/LICENCE.md @@ -1,12 +1,11 @@ ### Summary Underworld is an open-source, parallel, particle-in-cell, finite element geodynamics code. Please refer to repository -top level `README.md` for further information. +top level `README.md` for further information. ### Licensing -1) All Underworld source code is released under the LGPL-3 (See LGPLv3.txt in this repository). This covers all -files in `underworld` constituting the Underworld3 Python module (library), and any other material not explicitly identified under (2) below. +1) All Underworld source code is released under the LGPL-3 (See the file LICENCE in his repository). This covers all files in `underworld` constituting the Underworld3 Python module (library), and any other material not explicitly identified under (2) below. 2) Notebooks, stand-alone documentation and Python scripts which show how the code is used and run are licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. We offer this licence to encourage you to modify and share the examples and use them to help you in your research. Where no individual creator is identified in these files, the appropriate attribution is "The Underworld Team". All the files covered by this license are found in the `UserGuide` directory. @@ -32,7 +31,3 @@ Copyright VPAC, 2003-2009 1. Beucher, R., Moresi, L., Giordani, J., Mansour, J., Sandiford, D., Farrington, R., et al. (2019). UWGeodynamics: A teaching and research tool for numerical geodynamic modelling. Journal of Open Source Software. https://doi.org/10.21105/joss.01136 1. Mansour, J., Giordani, J., Moresi, L., Beucher, R., Kaluza, O., Velic, M., et al. (2020). Underworld2: Python Geodynamics Modelling for Desktop, HPC and Cloud. Journal of Open Source Software, 5(47), 1797. https://doi.org/10.21105/joss.01797 - - - - diff --git a/docs/user/Installation.qmd b/docs/user/Installation.qmd index f0d520e3b..26b01435a 100644 --- a/docs/user/Installation.qmd +++ b/docs/user/Installation.qmd @@ -8,9 +8,84 @@ exports: - format: html --- +## Source code build using mamba +We recommend that you use a `conda` / `mamba` virtual environment to build `underworld3` whenever you want to install on a personal workstation or laptop (linux or macOS).The [`mamba` documentation](https://mamba.readthedocs.io/en/latest/index.html) will help you get started if you are not familiar with the philososphy and practice of this package management system. +The `underworld3` build / run-time dependencies can be installed using +```bash + (base) % git clone -b development --single-branch https://github.com/underworldcode/underworld3 /path/to/underworld3 + (base) % cd /path/to/underworld3 + (base) % mamba env create -n uw3 -f environment.yml + (base) % mamba activate uw3 + (uw3) % pip install . +``` -Windows users, if you don't want to create a linux partition, you can use our containers. +## Source code build using mamba and existing PETSc + +If you have an exisiting build of `PETSc` that you need to use, the following +instructions work well. We create the environment as before but remove the pre- +installed `PETSc` and `petsc4py`. + +```bash + (base) % git clone -b development --single-branch https://github.com/underworldcode/underworld3 /path/to/underworld3 + (base) % cd /path/to/underworld3 + (base) % mamba env create -n uw3p -f environment.yml + (base) % mamba activate uw3p + + (uw3p) % mamba remove petsc4py + (uw3p) % mamba remove petsc +``` + +Next set up PETSc as you like it and build it using the tools +within the current mamba virtual environment. Set the `PETSC_ARCH` +environment variable to the name of this virtual environment to keep things +from becoming muddled. See [PETSc Installation]() for details on +how to configure and build what you need. + +```bash + + (uw3p) % export PETSC_DIR=/path/to/petsc + (uw3p) % export PETSC_ARCH="uw3p" + (uw3p) % git clone -b release https://gitlab.com/petsc/petsc.git $PETSC_DIR + (uw3p) % cd $PETSC_DIR + (uw3p) % # Configure & Build step +``` +Next we `pip install` the petsc4py that we just built into the mamba virtual environment. This +ensures that `petsc4py` is available and also that its internal configuration points to the `PETSc` installation we just created. This way we don't need to manage environment variables to point to the build that corresponds to a given virtual environment. + +```bash + (uw3p) % mamba install cmake + (uw3p) % cd $PETSC_DIR/src/binding/petsc4py + (uw3p) % pip install . +``` + +Building `underworld3` against this version of `PETSc` should be identical to the mamba version. + +```bash + (uw3p) % cd /path/to/underworld3 + (uw3p) % pip install . +``` + +#### Did it work ? + +First run the tests: + +```bash + (uw3p) % cd /path/to/underworld3 + (uw3p) % source test.sh +``` + +If you have problems, contact us via the `underworl3` [GitHub issue tracker](https://github.com/underworldcode/underworld3/issues) + +## Docker container + +Windows users, if you don't want to create a linux partition, you can use our containers with `docker` or `podman`. As the code is still in active development, we are not always able to provide containers for each change to the development branch. We ask that you reach out to us on our [GitHub issue tracker](https://github.com/underworldcode/underworld3/issues) to ask for information (use the "question" label). + +## HPC builds + +`Underworld3` is inherently parallel and designed for use in high performance computing environments. The symbolic layer is relatively lightweight and should not adversely affect launch time (drawing down libraries from disk) or execution time (very few calculations are done in python itself). + +In the HPC environment you may find it difficult to control the software stack. You will need to ensure that you can build against PETSc 3.21 or higher, since important functionality that we need is only available from that release onwards. We are happy to provide assistance with builds on specific machines and ask that you contact us through the [GitHub issue tracker](https://github.com/underworldcode/underworld3/issues) so that other people are able to browse the issues and their fixes. diff --git a/docs/user/NextSteps.qmd b/docs/user/NextSteps.qmd index 33c842b42..a90d79a55 100644 --- a/docs/user/NextSteps.qmd +++ b/docs/user/NextSteps.qmd @@ -12,31 +12,86 @@ exports: ### Underworld Documentation and Examples +In addition to the notebooks in this brief set of examples, there are a number of sources of information on using `Underworld3` that you can access: + + - [The Underworld Website / Blog](https://www.underworldcode.org) + + - [The API documentation](https://underworldcode.github.io/underworld3/development_api/index.html) + (all the modules and functions and their full sets of arguments) is automatically generated from the source code and uses the same rich markdown content as the notebook help text. + + - The [`underworld3` GitHub repository](https://github.com/underworldcode/underworld3) is the most active development community for the code. + ### Benchmarks +The [Underworld3 Benchmarks Repository](https://github.com/underworld-community/UW3-benchmarks) is a useful place to find community benchmarks coded in `underworld3` along with accuracy and convergence analysis. This is an open repository where you can make a pull request with new benchmark submissions once you get the hang of things. ### The Underworld Community -Sharing codes / fixes - -Sharing models +The [Underworld Community](https://github.com/underworld-community) organisation on Github is a collection of contributed repositories from the underworld user community for all versions of the code and now includes scripts for underworld3. ### Parallel Execution -Advise using jupytext to render notebooks at .py files. This is helpful -in shared environments because the files contain no output and play -better with version control. +`Underworld3` is inherently parallel and designed for high performance computing. The symbolic layer is a relatively thin veneer that sits on top of the `PETSc` machinery. A script that has been developed in `jupyter` should be transferrable to an HPC environment with no changes (except that inherently serial operations such as visualisation and model reduction are best left to postprocessing / co-processing). + +We recommend installing `jupytext` which allows either a seamless two-way conversion (or pairing) between the `ipynb` format and an annotated python script, or the ability to work entirely with annoted python scripts and not use `ipynb` at all. The python form does not store the output of notebook cells but there are advantages to this when scripts are under version control. + +Almost all of our notebook examples are annotated python for this reason.An exception is the collection of notebooks in this quick-start guide because we want to show you the rendered output in the static web pages. + +```bash + mpirun -np 1024 python3 Modelling_Script.py -uw_resolution 96 +``` + +The main difference between the notebook development environment and HPC is the lack of interactivity, particularly in sending parameters to the script at launch time. Typically, we expect the HPC version to be running at much higher resolution, or for many more timesteps than the development notebook. We use the `PETSc` command line parsing machinery to generate notebooks that also can ingest run-time parameters from a script (as above). + + +### Advanced capabilities + +Digging a bit deeper into `underworld3`, there are many capabilities that require a clear understanding of the concepts that underlie the implementation. The following examples are not *plug-and-play*, but they do only require python coding using the `underworld3` API and no detailed knowledge of `petsc4py` or `PETSc`. [Get in touch with us](https://github.com/underworldcode/underworld3/issues) if you want to try this sort of thing but can't figure it out for yourself. -Not such a good idea if you want to have a tutorial with outputs (like this one) +#### Deforming meshes + +In [Example 8](Notebooks/8-Particle_Swarms.ipynb), we made small variations to the mesh to conform to basal topography. We did not remesh, so we had to be careful to apply a smooth, analytic displacement to every node. For more general free-surface models, we need to calculate a smooth function using the computed boundary motions (e.g, solving a poisson equation with known boundary displacements as boundary conditions). We need to step through time and it is common to stabilize the surface motions through a surface integral term that accounts for the interface displacement during the timestep. The example below shows an `underworld3` forward model with internal loads timestepped close to isostatic equilibrium. + +![*Stokes flow driven by buoyancy in an annulus defined by two embedded surfaces within an enveloping disk mesh. The surfaces deform in response to the flow. The embedding medium has a very low viscosity but still acts to damp rotational modes. The outer boundary of the disk can be set to a far-field gravitational potential for whole-Earth relaxation models*](media/RelaxingMesh.png){width=50%} + +In a more general case, we need to account to horizontal motions. This is more complicated because the horizontal velocities can be large even when vertical equilibrium is achieved. So we need to solve for the advected component of vertical motion in addition to the local component. Hooray for symbolic algebra ! + +#### Weak / penalty boundary conditions + +[Example 8](Notebooks/8-Particle_Swarms.ipynb) introduced the idea of penalty-based boundary conditions where the constraint is weakly enforced by providing a large penalty for violation of the condition. This is very flexible as the penalizing conditions can be adjusted during the run, including changing which part of the boundary is subject to constraints based on the solution or a coupled problem. The channel flow model shown below has a boundary condition that depends on a externally sourced model for ponded material at the base that is derived from a simple topography filling algorithm. + +```{=html} +
+ +
+``` +*Live Image: Stokes flow in a channel with multiple obstructions. Flow is driven from the inlet (a velocity boundary condition). The geometry was contructed with `gmsh`. This is an example for education which demonstrates the emergence of an large-scale pressure gradient as a result of the presence of the obstructions, and also the dispersion of tracers through the complicated flow geometry* + +The penalty approach does allow the solution to deviate from the exact value of the boundary condition, in a similar way to the iterative solvers delivering a solution to within a specified tolerance. There are some cases, for example, enforcing plate motions at the surface, where there are uncertainties in the applied boundary conditions and that these uncertainties may vary in space and time. + +#### Mesh Adaptation + +It is also possible to use the PETSc mesh adaption capabilities, to refine the resolution of a model where it is most needed and coarsen elsewhere. Dynamically-adapting meshes are possible but the interface is very low level at present, particularly in parallel. + +```{=html} +
+ +
+``` +*Live Image: Static mesh adaptation to the slope of a field. The driving buoyancy term is three plume-like upwellings and the slope of this field is shown in colour (red high, blue low). The adapted mesh is shown in green.* +```python + # t is the driving "temperature". We form an isotropic refinement metric from its slope - diff --git a/docs/user/Notebooks/1-Meshes.ipynb b/docs/user/Notebooks/1-Meshes.ipynb index 067505f4b..3a8328be0 100644 --- a/docs/user/Notebooks/1-Meshes.ipynb +++ b/docs/user/Notebooks/1-Meshes.ipynb @@ -64,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 9, "id": "5a4437ff-63ae-4ffa-9f2b-8ec1b8b4baeb", "metadata": { "editable": true, @@ -74,15 +74,7 @@ }, "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[Lyrebird.local:73169] shmem: mmap: an error occurred while determining whether or not /var/folders/tx/95gr762j29z4tt5d1dnqlgth0000gn/T//ompi.Lyrebird.501/jf.0/1742340096/sm_segment.Lyrebird.501.67da0000.0 could be created.\n" - ] - } - ], + "outputs": [], "source": [ "#| output: false # Suppress warnings in html version\n", "\n", diff --git a/docs/user/Notebooks/3-Symbolic_Forms.ipynb b/docs/user/Notebooks/3-Symbolic_Forms.ipynb index cc97b16db..90c71611b 100644 --- a/docs/user/Notebooks/3-Symbolic_Forms.ipynb +++ b/docs/user/Notebooks/3-Symbolic_Forms.ipynb @@ -37,7 +37,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 23, "id": "5a4437ff-63ae-4ffa-9f2b-8ec1b8b4baeb", "metadata": { "editable": true, @@ -46,15 +46,7 @@ }, "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[esdhcp-137.anu.edu.au:13108] shmem: mmap: an error occurred while determining whether or not /var/folders/tx/95gr762j29z4tt5d1dnqlgth0000gn/T//ompi.esdhcp-137.501/jf.0/1966931968/sm_segment.esdhcp-137.501.753d0000.0 could be created.\n" - ] - } - ], + "outputs": [], "source": [ "import underworld3 as uw\n", "import numpy as np\n", diff --git a/docs/user/Notebooks/4-Solvers.ipynb b/docs/user/Notebooks/4-Solvers.ipynb index a5979abec..76963c310 100644 --- a/docs/user/Notebooks/4-Solvers.ipynb +++ b/docs/user/Notebooks/4-Solvers.ipynb @@ -48,7 +48,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 12, "id": "042970c2-ab87-4483-b68c-3fe6b8e633a2", "metadata": { "editable": true, @@ -57,15 +57,7 @@ }, "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[Lyrebird.local:73187] shmem: mmap: an error occurred while determining whether or not /var/folders/tx/95gr762j29z4tt5d1dnqlgth0000gn/T//ompi.Lyrebird.501/jf.0/2790850560/sm_segment.Lyrebird.501.a6590000.0 could be created.\n" - ] - } - ], + "outputs": [], "source": [ "#| output: false # Suppress warnings in html version\n", "\n", diff --git a/docs/user/Notebooks/5-Solvers.ipynb b/docs/user/Notebooks/5-Solvers.ipynb index a19358210..6dbe677ee 100644 --- a/docs/user/Notebooks/5-Solvers.ipynb +++ b/docs/user/Notebooks/5-Solvers.ipynb @@ -443,7 +443,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.11.10" } }, "nbformat": 4, diff --git a/docs/user/Notebooks/6-Timestepping.ipynb b/docs/user/Notebooks/6-Timestepping.ipynb index 01df3b7fc..5bcf94dce 100644 --- a/docs/user/Notebooks/6-Timestepping.ipynb +++ b/docs/user/Notebooks/6-Timestepping.ipynb @@ -48,23 +48,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 15, "id": "dddf1a25-1d89-4c39-90dc-9ca5a45db5dd", - "metadata": { - "collapsed": true, - "jupyter": { - "outputs_hidden": true - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[Lyrebird.local:03200] shmem: mmap: an error occurred while determining whether or not /var/folders/tx/95gr762j29z4tt5d1dnqlgth0000gn/T//ompi.Lyrebird.501/jf.0/1915617280/sm_segment.Lyrebird.501.722e0000.0 could be created.\n" - ] - } - ], + "metadata": {}, + "outputs": [], "source": [ "#| output: false # Suppress warnings in html version\n", "\n", @@ -178,7 +165,7 @@ " u_Field=t_soln,\n", " V_fn=v_soln,\n", " solver_name=\"adv_diff\",\n", - " order=1,\n", + " order=2,\n", " verbose=False,\n", ")\n", "\n", @@ -339,15 +326,15 @@ "output_type": "stream", "text": [ "Timestep: 5, time 0.007655378472205266\n", - "Timestep: 10, time 0.014726579977176624\n", - "Timestep: 15, time 0.018483060525797824\n", - "Timestep: 20, time 0.0212112311837343\n", - "Timestep: 25, time 0.023815491249944375\n", - "Timestep: 30, time 0.026692184130742397\n", - "Timestep: 35, time 0.02990403867073574\n", - "Timestep: 40, time 0.033304683894118385\n", - "Timestep: 45, time 0.03672433425335901\n", - "Timestep: 50, time 0.040108712649056966\n" + "Timestep: 10, time 0.01387213547299994\n", + "Timestep: 15, time 0.017197945909787137\n", + "Timestep: 20, time 0.019855789890665786\n", + "Timestep: 25, time 0.02250450167934171\n", + "Timestep: 30, time 0.02546333347384266\n", + "Timestep: 35, time 0.028720014807997008\n", + "Timestep: 40, time 0.032113891988035236\n", + "Timestep: 45, time 0.035501720967277196\n", + "Timestep: 50, time 0.038852418750145654\n" ] } ], @@ -457,7 +444,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 14, diff --git a/docs/user/Notebooks/8-Particle_Swarms.ipynb b/docs/user/Notebooks/8-Particle_Swarms.ipynb index 6e566ca72..ed4243cb7 100644 --- a/docs/user/Notebooks/8-Particle_Swarms.ipynb +++ b/docs/user/Notebooks/8-Particle_Swarms.ipynb @@ -30,7 +30,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "0974f1a1-60dd-4d52-9855-79e8030450e9", "metadata": {}, "outputs": [], @@ -46,7 +46,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 3, "id": "dddf1a25-1d89-4c39-90dc-9ca5a45db5dd", "metadata": {}, "outputs": [], @@ -195,7 +195,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b567c06831304b4885384029857f9bf3", + "model_id": "16110a6f07c942d28b40970a014d0ba4", "version_major": 2, "version_minor": 0 }, @@ -323,7 +323,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1b5528ee08b945f295f147b2d5f2df21", + "model_id": "ddb5d9712b3c4e82b6e2cd5af8d2aaea", "version_major": 2, "version_minor": 0 }, @@ -520,7 +520,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 1, "id": "4948cff8-89da-4822-aee7-f2207aa513b0", "metadata": {}, "outputs": [ @@ -529,8 +529,8 @@ "text/html": [ "\n", " " + "" ] }, - "execution_count": 17, + "execution_count": 1, "metadata": {}, "output_type": "execute_result" } @@ -550,17 +550,9 @@ "source": [ "#| fig-cap: \"Interactive Image: Convection model output\"\n", "from IPython.display import IFrame\n", - "IFrame(src=f\"html5/terrain_flow_plot.html\", width=1000, height=400)" + "IFrame(src=f\"html5/terrain_flow_plot.html\", width=750, height=300)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "811239df-5cb6-4e2e-a7af-7004fa9d3306", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "c3d1d6ff-68ac-46c0-8a47-e29e95fed462", @@ -615,7 +607,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.11.10" } }, "nbformat": 4, diff --git a/docs/user/Notebooks/html5/annulus_convection_plot.html b/docs/user/Notebooks/html5/annulus_convection_plot.html index 88be31562..af9d28606 100644 --- a/docs/user/Notebooks/html5/annulus_convection_plot.html +++ b/docs/user/Notebooks/html5/annulus_convection_plot.html @@ -11,7 +11,7 @@ diff --git a/docs/user/Notebooks/html5/spherical_mesh_plot.html b/docs/user/Notebooks/html5/spherical_mesh_plot.html index 8a6bce70b..163d89e2a 100644 --- a/docs/user/Notebooks/html5/spherical_mesh_plot.html +++ b/docs/user/Notebooks/html5/spherical_mesh_plot.html @@ -7,11 +7,11 @@
diff --git a/docs/user/Notebooks/html5/temperature_plot.html b/docs/user/Notebooks/html5/temperature_plot.html index 6888e8a4a..b7f4bdc9b 100644 --- a/docs/user/Notebooks/html5/temperature_plot.html +++ b/docs/user/Notebooks/html5/temperature_plot.html @@ -7,11 +7,11 @@
diff --git a/docs/user/Notebooks/html5/terrain_flow_plot.html b/docs/user/Notebooks/html5/terrain_flow_plot.html index ef922d478..354de9d59 100644 --- a/docs/user/Notebooks/html5/terrain_flow_plot.html +++ b/docs/user/Notebooks/html5/terrain_flow_plot.html @@ -7,11 +7,11 @@
diff --git a/docs/user/Notebooks/ns_flow_at_100.png b/docs/user/Notebooks/ns_flow_at_100.png deleted file mode 100644 index 247186808..000000000 Binary files a/docs/user/Notebooks/ns_flow_at_100.png and /dev/null differ diff --git a/docs/user/Notebooks/ns_flow_at_50.png b/docs/user/Notebooks/ns_flow_at_50.png deleted file mode 100644 index fb80cad05..000000000 Binary files a/docs/user/Notebooks/ns_flow_at_50.png and /dev/null differ diff --git a/docs/user/index.qmd b/docs/user/index.qmd index b1e5e8541..a1a0b993c 100644 --- a/docs/user/index.qmd +++ b/docs/user/index.qmd @@ -11,7 +11,7 @@ exports: ```{=html}
-
@@ -25,36 +25,48 @@ exports: Welcome to `Underworld3`, a mathematically self-describing, finite-element code for geodynamic modelling. This quick-start guide has basic installation instructions and a brief introduction to some of the concepts in the `Underworld3` code. +All `Underworld3` source code is released under the LGPL-3 open source licence. This covers all files in `underworld3` constituting the Underworld3 Python module. Notebooks, stand-alone documentation and Python scripts which show how the code is used and run are licensed under the Creative Commons Attribution 4.0 International License. + ## Installation Guide -The quickest option is **not to install** anything but click on the rocket icon (top right) to launch our notebook examples on mybinder.org. If do you want to install the code on your own machine, we recommend using `miniconda` and `mamba` and to create a separate virtual environment and to install the code from source. +The quickest option is **not to install** anything but click on the rocket icon (top right) to launch our notebook examples on mybinder.org. -More details in the [Installation Instructions](Installation.qmd) +If do you want to install the code on your own machine, we recommend using `miniconda` and `mamba`. Create a separate virtual environment and install the code from source. More details on how to do that are in the [Installation Instructions](Installation.qmd) -## Notebooks +## Sample Code Underworld is designed to be run in the jupyter notebook environment where you can take advantage of jupyter's rich display capabilities to explore the mathematics of your problem, visualise results and query classes or live objects. It's a good first step after you install the code to look at the [examples notebooks](Notebooks/Notebook_Index.ipynb). When you are happy that things are working OK, you can move along to look at the full documentation or the benchmarking repository. -**Summary of skills that the notebooks cover** +The notebooks introduce the concept of meshing to describe the domain and boundary conditions for a model and progress through Eulerian data containers, symbolic operators (functions, compositions, derivatives, evaluations). +The examples move on to the template PDE solvers for scalar and vector problems (in this case the Poisson equation and the Stokes equation) and time dependent, coupled problems (a simple convection example in an annulus geometry). They cover constitutive models, boundary conditions and visualisation. +The final examples deal with Lagrangian data containers (particle swarms), first transporting them in a velocity field, and second, accessing the symbolic forms of their data for use in underworld expressions. + +## Learn More + +`underworld3` is a research code under continual development and the examples in the notebooks provided with the code only touch upon the extent of its capabilities. Read the [Next Steps](NextSteps.qmd) page to find out how to learn more about `underworld3`, how to get in touch with the development team, and how to become part of the **underworld community**. +## Acknowledgements -## Next Steps +The Underworld codes represent more than two decades of programming effort by many developers including a wide community of contributors from the open-source community. +As a community-driven development team, we first acknowledge many contributions made through pull-requests to our repositories and direct submission of codes and examples. The underworldcode organisation on GitHub records all such contributions to the software and the underworld community on GitHub tracks the exchange of information and models. +Direct funding support has been provided by: -Some of the cool things about UW3 that you can do with a bit of effort (at the moment) + - AuScope provides direct support for the core development team behind the underworld codes and the underworld cloud suite of tools. AuScope is funded by the Australian Government through the National Collaborative Research Infrastructure Strategy, NCRIS. - - gmsh (anything - Pramoda's work) - - deformed meshes, interesting boundary conditions - - free surfaces, relaxation etc - - + - The development and testing of our codes is also supported by computational resources provided by the Australian Government through the National Computing Infrastructure (NCI) under the National Computational Merit Allocation Scheme. -## Documentation & Cookbooks + - The Australian Research Council (ARC) supported the development of novel algorithms, computational methods and applications under the Discovery Project and Linkage Project programs. AuScope funding was used to make these methods widely and freely available in the underworld codes. Direct support for Underworld was provided by ARC Industrial Transformation Research Hub Program (The Basin Genesis Hub) +In-kind support has also come from the institution that host Underworld developers including: the Australian National University, Monash University, The University of Melbourne, The University of Sydney and the CSIRO. +## Licensing and re-use +All of our software is released under open source licence and all documentation and worked examples are released with a creative commons licence. We are passionate about having our software used and code reuse is strongly encouraged in the interest of scientific reproducibility and replicability. +The developers are active researchers and scientists whose livelihood depends on their creative talents and the acknowledgement of their work. Please respect the effort that goes into developing software and tutorials by citing our work and collaborating on publication with new applications of our software when you can. ## References diff --git a/docs/user/media/ExplicitGrains_Example.png b/docs/user/media/ExplicitGrains_Example.png new file mode 100644 index 000000000..bcb487acd Binary files /dev/null and b/docs/user/media/ExplicitGrains_Example.png differ diff --git a/docs/user/media/RelaxingMesh.png b/docs/user/media/RelaxingMesh.png new file mode 100644 index 000000000..0af8f4fdf Binary files /dev/null and b/docs/user/media/RelaxingMesh.png differ diff --git a/docs/user/media/pyvista/AdaptedSphere.html b/docs/user/media/pyvista/AdaptedSphere.html new file mode 100644 index 000000000..db031a60f --- /dev/null +++ b/docs/user/media/pyvista/AdaptedSphere.html @@ -0,0 +1,19 @@ + + + + + + + +
+ + + + diff --git a/docs/user/media/pyvista/ChannelFlow.html b/docs/user/media/pyvista/ChannelFlow.html new file mode 100644 index 000000000..1d45e3c77 --- /dev/null +++ b/docs/user/media/pyvista/ChannelFlow.html @@ -0,0 +1,19 @@ + + + + + + + +
+ + + + diff --git a/docs/user/media/pyvista/SphereStreamLinesS.html b/docs/user/media/pyvista/SphereStreamLinesS.html new file mode 100644 index 000000000..1be666b87 --- /dev/null +++ b/docs/user/media/pyvista/SphereStreamLinesS.html @@ -0,0 +1,19 @@ + + + + + + + +
+ + + + diff --git a/environment.yml b/environment.yml new file mode 120000 index 000000000..01cf7601a --- /dev/null +++ b/environment.yml @@ -0,0 +1 @@ +.binder/environment.yml \ No newline at end of file diff --git a/joss-paper/JOSS_Checklist.md b/joss-paper/JOSS_Checklist.md index e89164896..c3a1d950e 100644 --- a/joss-paper/JOSS_Checklist.md +++ b/joss-paper/JOSS_Checklist.md @@ -4,6 +4,11 @@ Following the [Submission requirements](https://joss.readthedocs.io/en/latest/su ## Repository format / content + - [ ] README.md + - [ ] Links to uw web page + - [ ] Binder launch link for Quick-start + - [ ] Acknowledgements + - [ ] paper.md - [ ] paper.bib - [x] Install GitHub workflow @@ -11,6 +16,14 @@ Following the [Submission requirements](https://joss.readthedocs.io/en/latest/su - [x] Open source licence file - [?] Publication branch (this is acceptable, must be kept up to date on submission). - [ ] Check Insights / Community Standards for GitHub repo / put into line with UW2 + - [x] Check authors and .zenodo creators align + + - [ ] Quickstart Guide for users (deployable on binder) + - [x] Installation details + - [ ] Notebook examples (ipynb) + - [ ] Links to API docs + - [ ] Links to Github + - [ ] Links to Underworld Community ## Clean up checklist @@ -23,13 +36,11 @@ Following the [Submission requirements](https://joss.readthedocs.io/en/latest/su ## Software checklist Can I submit ? - - [x] The software must have an obvious research application. - [x] You must be a major contributor to the software you are submitting, and have a GitHub account to participate in the review process. - [x] Your paper must not focus on new research results accomplished with the software. The software associated with your submission must: - - [x] Be stored in a repository that can be cloned without registration. - [x] Be stored in a repository that is browsable online without registration. - [x] Have an issue tracker that is readable without registration. diff --git a/setup.py b/setup.py index 36c3c4634..48ff9a994 100644 --- a/setup.py +++ b/setup.py @@ -53,14 +53,20 @@ def configure(): print(f"PETSC_ARCH: {PETSC_ARCH}") # It is preferable to use the petsc4py paths to the - # petsc libraries for consistency - - # if os.environ.get("CONDA_PREFIX") and not os.environ.get("PETSC_DIR"): - # PETSC_DIR = os.environ["CONDA_PREFIX"] - # PETSC_ARCH = os.environ.get("PETSC_ARCH", "") - # else: - # PETSC_DIR = os.environ["PETSC_DIR"] - # PETSC_ARCH = os.environ.get("PETSC_ARCH", "") + # petsc libraries for consistency but the pip installation + # of PETSc sometimes points to the temporary setup up path + + if not os.path.exists(PETSC_DIR): + if os.environ.get("CONDA_PREFIX") and not os.environ.get("PETSC_DIR"): + PETSC_DIR = os.path.join(os.environ["CONDA_PREFIX"],"lib","python3.1", "site-packages", "petsc") # symlink to latest python + PETSC_ARCH = os.environ.get("PETSC_ARCH", "") + else: + PETSC_DIR = os.environ["PETSC_DIR"] + PETSC_ARCH = os.environ.get("PETSC_ARCH", "") + + print(f"PETSC_DIR: {PETSC_DIR}") + print(f"PETSC_ARCH: {PETSC_ARCH}") + from os.path import join, isdir diff --git a/src/underworld3/adaptivity.py b/src/underworld3/adaptivity.py index d1592a555..74cee61cc 100644 --- a/src/underworld3/adaptivity.py +++ b/src/underworld3/adaptivity.py @@ -82,7 +82,6 @@ def _dm_unstack_bcs(dm, boundaries, stacked_bc_label_name): # dmAdaptLabel - but first you need to call dmplex set default distribute False - def mesh_adapt_meshVar(mesh, meshVarH, metricVar, verbose=False, redistribute=False): # Create / use a field on the old mesh to hold the metric # Perhaps that should be a user-definition @@ -207,13 +206,14 @@ def mesh2mesh_swarm(mesh0, mesh1, swarm0, swarmVarList, proxy=True, verbose=Fals # if swarm0.vars["DMSwarm_Xorig"] not in swarmVarList: # swarmVarList.append(swarm0.vars["DMSwarm_Xorig"]) - swarm1 = uw.swarm.Swarm(mesh=mesh1, cycle_rate=swarm0.recycle_rate) + swarm1 = uw.swarm.Swarm(mesh=mesh1, recycle_rate=swarm0.recycle_rate) for swarmVar in swarmVarList: uw.swarm.SwarmVariable( swarmVar.name, swarm1, - num_components=swarmVar.num_components, + size=swarmVar.shape, + vtype=swarmVar.vtype, dtype=swarmVar.dtype, proxy_degree=swarmVar._proxy_degree, proxy_continuous=swarmVar._proxy_continuous, @@ -362,6 +362,10 @@ def mesh2mesh_swarm(mesh0, mesh1, swarm0, swarmVarList, proxy=True, verbose=Fals ## ToDo: this should take a list of vars so that the swarm migration is only done once +## ToDo: this could also be more like the SL advection: launch points from the +## new mesh, use a swarm to find values, snap back when done. +## Could also navigate there by letting the particles walk through the mesh towards +## their goal (if particle-restore works with funny mesh-holes) def mesh2mesh_meshVariable(meshVar0, meshVar1, verbose=False): """Map a meshVar on mesh0 to a meshVar on mesh1 using @@ -377,12 +381,13 @@ def mesh2mesh_meshVariable(meshVar0, meshVar1, verbose=False): var_cpts = meshVar0.num_components tmp_varS = uw.swarm.SwarmVariable( - var_name, tmp_swarm, num_components=var_cpts, _proxy=False + var_name, tmp_swarm, size = (1,var_cpts), vtype=uw.VarType.MATRIX, _proxy=False ) # Maybe 3+ if var is higher order ?? tmp_swarm.populate(fill_param=3) + # Set data on the swarmVar # print(f"Map data to swarm (rbf) - points = {tmp_swarm.dm.getSize()}", flush=True) diff --git a/src/underworld3/discretisation.py b/src/underworld3/discretisation.py index f2e916fb6..1a3d5886b 100644 --- a/src/underworld3/discretisation.py +++ b/src/underworld3/discretisation.py @@ -640,7 +640,7 @@ def nuke_coords_and_rebuild(self): options = PETSc.Options() options.setValue( - "meshproj_{}_petscspace_degree".format(self.mesh_instances), self.degree + f"meshproj_{self.mesh_instances}_petscspace_degree", self.degree ) self.petsc_fe = PETSc.FE().createDefault( @@ -648,7 +648,7 @@ def nuke_coords_and_rebuild(self): self.cdim, self.isSimplex, self.qdegree, - "meshproj_{}_".format(self.mesh_instances), + f"meshproj_{self.mesh_instances}_", ) if ( @@ -715,6 +715,12 @@ def update_lvec(self): subdm.localToGlobal(lvec, subvec, addv=False) a_global.restoreSubVector(subiset, subvec) + + for iset in isets: + iset.destroy() + for dm in dms: + dm.destroy() + self.dm.globalToLocal(a_global, self._lvec) self.dm.restoreGlobalVec(a_global) self._stale_lvec = False @@ -849,7 +855,8 @@ def __exit__(self, *args): subdm.localToGlobal(var.vec, var._gvec, addv=False) subdm.globalToLocal(var._gvec, var.vec, addv=False) - # subdm.destroy() + indexset.destroy() + subdm.destroy() self.mesh._stale_lvec = True var._data = None diff --git a/src/underworld3/function/_function.pyx b/src/underworld3/function/_function.pyx index c91ce23f9..e05bdd321 100644 --- a/src/underworld3/function/_function.pyx +++ b/src/underworld3/function/_function.pyx @@ -213,7 +213,6 @@ def evaluate( expr, if not (isinstance( expr, sympy.Basic ) or isinstance( expr, sympy.Matrix ) ): raise RuntimeError("`evaluate()` function parameter `expr` does not appear to be a sympy expression.") - sympy.core.cache.clear_cache() ## special case @@ -596,9 +595,8 @@ def evalf( expr, sympy.core.cache.clear_cache() - if uw.function.fn_is_constant_expr(expr): - return expr.sub_all(keep_constants=False) + return uw.function.expressions.unwrap(expr, keep_constants=False) if (not coords is None) and not isinstance( coords, np.ndarray ): raise RuntimeError("`evaluate()` function parameter `input` does not appear to be a numpy array.") diff --git a/test.sh b/test.sh index 32872fafa..06f75c499 100755 --- a/test.sh +++ b/test.sh @@ -2,11 +2,11 @@ ## Testing script (runs pytest in batches) # -# We do not run one monolithic pytest because tests produce a large number of +# We do not run one monolithic pytest because tests produce a large number of # PETSc objects which we cannot always guarantee that PETSc / petsc4py will free # This makes it possible for individual tests to interact with each other. -PYTEST="pytest -c tests/pytest.ini" +PYTEST="pytest --config-file='tests/pytest.ini' " # Run simple tests $PYTEST tests/test_00[0-4]*py @@ -16,11 +16,11 @@ $PYTEST tests/test_0050*py $PYTEST tests/test_01*py tests/test_05*py tests/test_06*py # Poisson solvers (including Darcy flow) -$PYTEST tests/test_100[0-9]*py +$PYTEST tests/test_100[0-9]*py # Solver / system tests (advanced solver problems) $PYTEST tests/test_1010*py tests/test_1011*py tests/test_1050*py # Diffusion / Advection tests # $PYTEST tests/test_1100*py -# $PYTEST tests/test_1110*py # Annulus version +# $PYTEST tests/test_1110*py # Annulus version diff --git a/tests/parallel/mpi_runner.sh b/tests/parallel/mpi_runner.sh index 2bad7a866..eed4a0c9b 100755 --- a/tests/parallel/mpi_runner.sh +++ b/tests/parallel/mpi_runner.sh @@ -1,27 +1,26 @@ -export PYTHON=python3.11 +PYTHON=python echo "ptest 001 -np 1" mpirun -np 1 $PYTHON ./ptest_001_start_stop.py echo "ptest 001 -np 1 --discontinuous" -mpirun -np 1 $PYTHON ./ptest_001_start_stop.py --discontinuous +mpirun -np 1 $PYTHON ./ptest_001_start_stop.py --discontinuous -echo "ptest 001 -np 2" -mpirun -np 2 $PYTHON ./ptest_001_start_stop.py +echo "ptest 001 -np 4" +mpirun -np 4 $PYTHON ./ptest_001_start_stop.py echo "ptest 001 -np 2 --discontinuous" -mpirun -np 2 $PYTHON ./ptest_001_start_stop.py --discontinuous +mpirun -np 4 $PYTHON ./ptest_001_start_stop.py --discontinuous echo "ptest 001a -np 1" mpirun -np 1 $PYTHON ./ptest_001a_start_stop_petsc4py.py -echo "ptest 001a -np 2" -mpirun -np 2 $PYTHON ./ptest_001a_start_stop_petsc4py.py +echo "ptest 001a -np 4" +mpirun -np 4 $PYTHON ./ptest_001a_start_stop_petsc4py.py echo "ptest 002 -np 1" mpirun -np 1 $PYTHON ./ptest_002_projection.py -echo "ptest 002 -np 2" -mpirun -np 2 $PYTHON ./ptest_002_projection.py - -echo "ptest 003 -np 1" -mpirun -np 1 $PYTHON ./ptest_003_swarm_projection.py -echo "ptest 003 -np 2" -mpirun -np 2 $PYTHON ./ptest_003_swarm_projection.py +echo "ptest 002 -np 4" +mpirun -np 4 $PYTHON ./ptest_002_projection.py +#echo "ptest 003 -np 1" +#mpirun -np 1 $PYTHON ./ptest_003_swarm_projection.py +#echo "ptest 003 -np 4" +#mpirun -np 4 $PYTHON ./ptest_003_swarm_projection.py diff --git a/tests/parallel/mpi_runner.sh~ b/tests/parallel/mpi_runner.sh~ new file mode 100644 index 000000000..f4ba5fd0c --- /dev/null +++ b/tests/parallel/mpi_runner.sh~ @@ -0,0 +1,26 @@ +PYTHON=python + +echo "ptest 001 -np 1" +mpirun -np 1 $PYTHON ./ptest_001_start_stop.py +echo "ptest 001 -np 1 --discontinuous" +mpirun -np 1 $PYTHON ./ptest_001_start_stop.py --discontinuous + +echo "ptest 001 -np 4" +mpirun -np 4 $PYTHON ./ptest_001_start_stop.py +echo "ptest 001 -np 2 --discontinuous" +mpirun -np 4 $PYTHON ./ptest_001_start_stop.py --discontinuous + +echo "ptest 001a -np 1" +mpirun -np 1 $PYTHON ./ptest_001a_start_stop_petsc4py.py +echo "ptest 001a -np 4" +mpirun -np 4 $PYTHON ./ptest_001a_start_stop_petsc4py.py + +echo "ptest 002 -np 1" +mpirun -np 1 $PYTHON ./ptest_002_projection.py +echo "ptest 002 -np 4" +mpirun -np 4 $PYTHON ./ptest_002_projection.py + +echo "ptest 003 -np 1" +mpirun -np 1 $PYTHON ./ptest_003_swarm_projection.py +echo "ptest 003 -np 4" +mpirun -np 4 $PYTHON ./ptest_003_swarm_projection.py diff --git a/tests/parallel/ptest_002_projection.py b/tests/parallel/ptest_002_projection.py index d697dc383..3e2f3f400 100644 --- a/tests/parallel/ptest_002_projection.py +++ b/tests/parallel/ptest_002_projection.py @@ -1,17 +1,15 @@ -import petsc4py -from petsc4py import PETSc import underworld3 as uw import numpy as np import sympy import argparse +from underworld3.systems.solvers import SNES_Darcy + parser = argparse.ArgumentParser() parser.add_argument("--discontinuous", action="store_true") args = parser.parse_args() -# from underworld3.cython import petsc_discretisation - mesh1 = uw.meshing.Annulus(radiusInner=0.5, radiusOuter=1.0, cellSize=0.1) x, y = mesh1.X @@ -30,23 +28,28 @@ scalar_projection = uw.systems.Projection(mesh1, s_soln, verbose=True) print(f"{uw.mpi.rank} - build projections ... done", flush=True) + + scalar_projection.uw_function = s_values.sym[0] scalar_projection.smoothing = 1.0e-6 print(f"{uw.mpi.rank} - check values ...", flush=True) # S2 coordinates -with mesh1.access(s_values): +with mesh1.access(): print(f"{uw.mpi.rank} ", s_values.coords[0:10], flush=True) # Values on S2 -print(f"{uw.mpi.rank} - set values", flush=True) +# print(f"{uw.mpi.rank} - set values", flush=True) with mesh1.access(s_values): - s_values.data[:, 0] = uw.function.evaluate(s_fn, s_values.coords, coord_sys=mesh1.N) + print(s_values.data[0:10,0]) + s_values.data[:, 0] = 1.0 # uw.function.evalf(sympy.sympify(1), s_values.coords) + + +scalar_projection.solve() print(f"{uw.mpi.rank} - solve projection", flush=True) mesh1.dm.view() -scalar_projection.solve() print(f"Finalised")