From eb2100288446130219debce3a8946fbb2a8132f2 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Mon, 24 Jul 2023 15:38:41 +0100 Subject: [PATCH 01/14] added numerical integrator and libray call to __innit --- gusto/__init__.py | 1 + gusto/numerical_integrator.py | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 gusto/numerical_integrator.py diff --git a/gusto/__init__.py b/gusto/__init__.py index c932d6811..c007a3310 100644 --- a/gusto/__init__.py +++ b/gusto/__init__.py @@ -21,3 +21,4 @@ from gusto.timeloop import * # noqa from gusto.transport_methods import * # noqa from gusto.wrappers import * # noqa +from gusto.numerical_integrator import * # noqa diff --git a/gusto/numerical_integrator.py b/gusto/numerical_integrator.py new file mode 100644 index 000000000..4705eef8c --- /dev/null +++ b/gusto/numerical_integrator.py @@ -0,0 +1,56 @@ +import numpy as np + + +class NumericalIntegral(object): + """ + A class for numerically evaluating and tabulating some 1D integral. + :arg lower_bound: lower bound of integral + :arg upper_bound: upper_bound of integral + :arg num_points: number of points to tabulate integral at + """ + def __init__(self, lower_bound, upper_bound, num_points=500): + + # if upper_bound <= lower_bound: + # raise ValueError('lower_bound must be lower than upper_bound') + self.x = np.linspace(lower_bound, upper_bound, num_points) + self.x_double = np.linspace(lower_bound, upper_bound, 2*num_points-1) + self.lower_bound = lower_bound + self.upper_bound = upper_bound + self.num_points = num_points + self.tabulated = False + + def tabulate(self, expression): + """ + Tabulate some integral expression using Simpson's rule. + :arg expression: a function representing the integrand to be evaluated. + Should take a numpy array as an argument. + """ + + self.cumulative = np.zeros_like(self.x) + self.interval_areas = np.zeros(len(self.x)-1) + # Evaluate expression in advance to make use of numpy optimisation + # We evaluate at the tabulation points and the midpoints of the intervals + f = expression(self.x_double) + + # Just do Simpson's rule for evaluating area of each interval + self.interval_areas = ((self.x[1:] - self.x[:-1]) / 6.0 + * (f[2::2] + 4.0 * f[1::2] + f[:-1:2])) + + # Add the interval areas together to create cumulative integral + for i in range(self.num_points - 1): + self.cumulative[i+1] = self.cumulative[i] + self.interval_areas[i] + + self.tabulated = True + + def evaluate_at(self, points): + """ + Evaluates the integral at some point using linear interpolation. + :arg points: the point value, or array of point values to evaluate + the integral at. + """ + # Do linear interpolation from tabulated values + if not self.tabulated: + raise RuntimeError( + 'Integral must be tabulated before we can evaluate it at a point') + + return np.interp(points, self.x, self.cumulative) From bbe94ce61678ba60cf5f68b0abe57838b1010e42 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Mon, 24 Jul 2023 16:22:56 +0100 Subject: [PATCH 02/14] tidied up linting --- gusto/numerical_integrator.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/gusto/numerical_integrator.py b/gusto/numerical_integrator.py index 4705eef8c..d19847a33 100644 --- a/gusto/numerical_integrator.py +++ b/gusto/numerical_integrator.py @@ -4,9 +4,11 @@ class NumericalIntegral(object): """ A class for numerically evaluating and tabulating some 1D integral. - :arg lower_bound: lower bound of integral - :arg upper_bound: upper_bound of integral - :arg num_points: number of points to tabulate integral at + Args: + lower_bound(float): lower bound of integral + upper_bound(float): upper bound of integral + num_points(float): number of points to tabulate integral at + """ def __init__(self, lower_bound, upper_bound, num_points=500): @@ -22,8 +24,9 @@ def __init__(self, lower_bound, upper_bound, num_points=500): def tabulate(self, expression): """ Tabulate some integral expression using Simpson's rule. - :arg expression: a function representing the integrand to be evaluated. - Should take a numpy array as an argument. + Args: + expression (func): a function representing the integrand to be + evaluated. should take a numpy array as an argument. """ self.cumulative = np.zeros_like(self.x) @@ -45,8 +48,9 @@ def tabulate(self, expression): def evaluate_at(self, points): """ Evaluates the integral at some point using linear interpolation. - :arg points: the point value, or array of point values to evaluate - the integral at. + Args: + points (float or iter) the point value, or array of point values to + evaluate the integral at. """ # Do linear interpolation from tabulated values if not self.tabulated: From e8e5ae2f22b453f66fe8344736f17f3b61b919ed Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Tue, 25 Jul 2023 16:18:05 +0100 Subject: [PATCH 03/14] fixed lint --- gusto/numerical_integrator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gusto/numerical_integrator.py b/gusto/numerical_integrator.py index d19847a33..2e71bc612 100644 --- a/gusto/numerical_integrator.py +++ b/gusto/numerical_integrator.py @@ -25,7 +25,7 @@ def tabulate(self, expression): """ Tabulate some integral expression using Simpson's rule. Args: - expression (func): a function representing the integrand to be + expression (func): a function representing the integrand to be evaluated. should take a numpy array as an argument. """ @@ -49,7 +49,7 @@ def evaluate_at(self, points): """ Evaluates the integral at some point using linear interpolation. Args: - points (float or iter) the point value, or array of point values to + points (float or iter) the point value, or array of point values to evaluate the integral at. """ # Do linear interpolation from tabulated values From 855c10e256d4ee95be3a391f9dd1571514c62de2 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Tue, 25 Jul 2023 16:21:48 +0100 Subject: [PATCH 04/14] added numerical integrator test --- unit-tests/test_numerical_integrator.py | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 unit-tests/test_numerical_integrator.py diff --git a/unit-tests/test_numerical_integrator.py b/unit-tests/test_numerical_integrator.py new file mode 100644 index 000000000..13b39fe00 --- /dev/null +++ b/unit-tests/test_numerical_integrator.py @@ -0,0 +1,34 @@ +""" +Tests various Gusto ActiveTracer objects. +""" +from gusto import NumericalIntegral +from numpy import sin, pi +import pytest + + +def quadratic(x): + return x**2 + + +def sine(x): + return sin(x) + + +@pytest.mark.parametrize("integrand_name", ["quadratic", "sine"]) +def test_numerical_integrator(integrand_name): + if integrand_name == "quadratic": + integrand = quadratic + upperbound = 3 + answer = 9 + elif integrand_name == "sine": + integrand = sine + upperbound = 2*pi + answer = 0 + else: + raise ValueError(f'{integrand_name} integrand not recognised') + numerical_integral = NumericalIntegral(0, upperbound) + numerical_integral.tabulate(integrand) + area = numerical_integral.evaluate_at(upperbound) + err_tol = 1e-14 + assert abs(area-answer) < err_tol, \ + f'numerical integrator is incorrect for {integrand_name} function' From 1284f36026c06c8ccc68785a825015d8f5420b01 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Tue, 25 Jul 2023 16:28:28 +0100 Subject: [PATCH 05/14] typo --- unit-tests/test_numerical_integrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit-tests/test_numerical_integrator.py b/unit-tests/test_numerical_integrator.py index 13b39fe00..a2e3b3599 100644 --- a/unit-tests/test_numerical_integrator.py +++ b/unit-tests/test_numerical_integrator.py @@ -1,5 +1,5 @@ """ -Tests various Gusto ActiveTracer objects. +Tests the numerical integrator. """ from gusto import NumericalIntegral from numpy import sin, pi From 7c05364d0c27c6bc38f7e32e656680b1c522a08b Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Wed, 26 Jul 2023 11:40:18 +0100 Subject: [PATCH 06/14] adjusted docstring --- gusto/numerical_integrator.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gusto/numerical_integrator.py b/gusto/numerical_integrator.py index 2e71bc612..085dfae9d 100644 --- a/gusto/numerical_integrator.py +++ b/gusto/numerical_integrator.py @@ -51,6 +51,9 @@ def evaluate_at(self, points): Args: points (float or iter) the point value, or array of point values to evaluate the integral at. + Return: + returns the numerical approximation of the integral from lower + bound to point(s) """ # Do linear interpolation from tabulated values if not self.tabulated: From 4c873b23471c5aa7287e93b1ea5aa03766e9c001 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Wed, 26 Jul 2023 11:40:52 +0100 Subject: [PATCH 07/14] adjusted sine test --- unit-tests/test_numerical_integrator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit-tests/test_numerical_integrator.py b/unit-tests/test_numerical_integrator.py index a2e3b3599..da8fa06c4 100644 --- a/unit-tests/test_numerical_integrator.py +++ b/unit-tests/test_numerical_integrator.py @@ -22,8 +22,8 @@ def test_numerical_integrator(integrand_name): answer = 9 elif integrand_name == "sine": integrand = sine - upperbound = 2*pi - answer = 0 + upperbound = pi + answer = 2 else: raise ValueError(f'{integrand_name} integrand not recognised') numerical_integral = NumericalIntegral(0, upperbound) From 6af56ff6f772e3aab84d9322f414588712fb8d27 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Wed, 26 Jul 2023 11:43:19 +0100 Subject: [PATCH 08/14] put in alphabetical order --- gusto/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gusto/__init__.py b/gusto/__init__.py index c007a3310..d1a210f01 100644 --- a/gusto/__init__.py +++ b/gusto/__init__.py @@ -13,6 +13,7 @@ from gusto.limiters import * # noqa from gusto.linear_solvers import * # noqa from gusto.meshes import * # noqa +from gusto.numerical_integrator import * # noqa from gusto.physics import * # noqa from gusto.preconditioners import * # noqa from gusto.recovery import * # noqa @@ -21,4 +22,3 @@ from gusto.timeloop import * # noqa from gusto.transport_methods import * # noqa from gusto.wrappers import * # noqa -from gusto.numerical_integrator import * # noqa From ec18dbe50b49fd2c6f5d30946f5d289cf9fe1bd4 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Wed, 26 Jul 2023 15:19:26 +0100 Subject: [PATCH 09/14] fixed lint --- gusto/numerical_integrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gusto/numerical_integrator.py b/gusto/numerical_integrator.py index 085dfae9d..d04ab2f2d 100644 --- a/gusto/numerical_integrator.py +++ b/gusto/numerical_integrator.py @@ -52,7 +52,7 @@ def evaluate_at(self, points): points (float or iter) the point value, or array of point values to evaluate the integral at. Return: - returns the numerical approximation of the integral from lower + returns the numerical approximation of the integral from lower bound to point(s) """ # Do linear interpolation from tabulated values From e8ec956040aec4bc270f3a0950aca8b8b2599497 Mon Sep 17 00:00:00 2001 From: "dw603@exeter.ac.uk" Date: Wed, 26 Jul 2023 15:21:10 +0100 Subject: [PATCH 10/14] err tol too low --- unit-tests/test_numerical_integrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit-tests/test_numerical_integrator.py b/unit-tests/test_numerical_integrator.py index da8fa06c4..53d0b1131 100644 --- a/unit-tests/test_numerical_integrator.py +++ b/unit-tests/test_numerical_integrator.py @@ -29,6 +29,6 @@ def test_numerical_integrator(integrand_name): numerical_integral = NumericalIntegral(0, upperbound) numerical_integral.tabulate(integrand) area = numerical_integral.evaluate_at(upperbound) - err_tol = 1e-14 + err_tol = 1e-10 assert abs(area-answer) < err_tol, \ f'numerical integrator is incorrect for {integrand_name} function' From 3ba3ab2648571e19d37b64c33f389e9030346202 Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Wed, 26 Jul 2023 15:08:47 +0100 Subject: [PATCH 11/14] Link to Python 3 docs not 2.7 --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 642702049..28a7e16a4 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -254,5 +254,5 @@ 'pyop2': ('https://op2.github.io/PyOP2', None), 'ufl': ('https://fenics.readthedocs.io/projects/ufl/en/latest/', None), 'h5py': ('http://docs.h5py.org/en/latest/', None), - 'python':('https://docs.python.org/2.7/', None), + 'python':('https://docs.python.org/', None), } From bca44c1c08d22817837df5aad9bb8bbbd2fee932 Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Wed, 26 Jul 2023 15:36:12 +0100 Subject: [PATCH 12/14] Split out linting on CI --- .github/workflows/build.yml | 5 ----- .github/workflows/lint.yml | 40 +++++++++++++++++++++++++++++++++++++ Makefile | 20 +++++++++++++------ 3 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0e499306..756567ae7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,8 +50,3 @@ jobs: . /home/firedrake/firedrake/bin/activate python $(which firedrake-clean) python -m pytest -n 12 -v examples - - name: Lint - if: ${{ always() }} - run: | - . /home/firedrake/firedrake/bin/activate - make lint diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..8c29106e4 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,40 @@ +name: Run lint + +on: + # Push to master or PR + push: + branches: + - master + pull_request: + +jobs: + linter: + name: "Run linter" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + - name: Setup flake8 annotations + uses: rbialon/flake8-annotations@v1 + - name: Install linter + run: | + pip install flake8 pylint + - name: Lint codebase + run: | + make lint GITHUB_ACTIONS_FORMATTING=1 + actionlint: + name: "Lint Github actions YAML files" + # There's a way to add error formatting so GH actions adds messages to code, + # but I can't work out the right number of quotes to get it to work + # https://github.com/rhysd/actionlint/blob/main/docs/usage.md + # #example-error-annotation-on-github-actions + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check workflow files + uses: docker://rhysd/actionlint:latest + with: + args: -color diff --git a/Makefile b/Makefile index f077972cb..4ffd2902f 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,22 @@ +# Adds file annotations to Github Actions (only useful on CI) +GITHUB_ACTIONS_FORMATTING=0 +ifeq ($(GITHUB_ACTIONS_FORMATTING), 1) + FLAKE8_FORMAT=--format='::error file=%(path)s,line=%(row)d,col=%(col)d,title=%(code)s::%(path)s:%(row)d:%(col)d: %(code)s %(text)s' +else + FLAKE8_FORMAT= +endif + lint: @echo " Linting gusto codebase" - @python3 -m flake8 gusto + @python3 -m flake8 $(FLAKE8_FORMAT) gusto @echo " Linting gusto examples" - @python3 -m flake8 examples + @python3 -m flake8 $(FLAKE8_FORMAT) examples @echo " Linting gusto unit-tests" - @python3 -m flake8 unit-tests + @python3 -m flake8 $(FLAKE8_FORMAT) unit-tests @echo " Linting gusto integration-tests" - @python3 -m flake8 integration-tests + @python3 -m flake8 $(FLAKE8_FORMAT) integration-tests @echo " Linting gusto plotting scripts" - @python3 -m flake8 plotting + @python3 -m flake8 $(FLAKE8_FORMAT) plotting test: @echo " Running all tests" @@ -24,4 +32,4 @@ integration_test: example: @echo " Running all examples" - @python3 -m pytest examples $(PYTEST_ARGS) \ No newline at end of file + @python3 -m pytest examples $(PYTEST_ARGS) From 73b8bb5ba968eafc13c9b65251d3e74d7b7e22d8 Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Wed, 26 Jul 2023 15:36:55 +0100 Subject: [PATCH 13/14] Check docs build as part of CI --- .github/workflows/docs.yml | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..e6bec0821 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,57 @@ +name: Check docs build cleanly + +on: + # Run on pushes to master + push: + branches: + - master + # And all pull requests + pull_request: + +concurrency: + # Cancels jobs running if new commits are pushed + group: > + ${{ github.workflow }}- + ${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build_docs: + name: Run doc build + # The type of runner that the job will run on + runs-on: ubuntu-latest + # The docker container to use. + container: + image: firedrakeproject/firedrake-docdeps:latest + options: --user root + volumes: + - ${{ github.workspace }}:/home/firedrake/output + # Steps represent a sequence of tasks that will be executed as + # part of the jobs + steps: + - uses: actions/checkout@v3 + - name: Install checkedout Gusto + run: | + . /home/firedrake/firedrake/bin/activate + python -m pip install -e . + - name: Install Read the Docs theme + run: | + . /home/firedrake/firedrake/bin/activate + python -m pip install sphinx_rtd_theme + - name: Check documentation links + if: ${{ github.ref == 'refs/heads/master' }} + run: | + . /home/firedrake/firedrake/bin/activate + cd docs + make linkcheck + - name: Build docs + run: | + . /home/firedrake/firedrake/bin/activate + cd docs + make html + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + name: github-pages + path: /__w/gusto/gusto/docs/build/html + retention-days: 1 From 0b70ad467d051cffcfa42083754cadee14d3f132 Mon Sep 17 00:00:00 2001 From: Jack Betteridge Date: Wed, 26 Jul 2023 17:17:25 +0100 Subject: [PATCH 14/14] Lint actions --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 756567ae7..fc81554d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,15 +38,15 @@ jobs: - name: Gusto unit-tests run: | . /home/firedrake/firedrake/bin/activate - python $(which firedrake-clean) + which firedrake-clean python -m pytest -n 12 -v unit-tests - name: Gusto integration-tests run: | . /home/firedrake/firedrake/bin/activate - python $(which firedrake-clean) + firedrake-clean python -m pytest -n 12 -v integration-tests - name: Gusto examples run: | . /home/firedrake/firedrake/bin/activate - python $(which firedrake-clean) + firedrake-clean python -m pytest -n 12 -v examples