Skip to content

Commit

Permalink
add Rust code snippets to the tutorial (incl. CI execution) (#450)
Browse files Browse the repository at this point in the history
Co-authored-by: Sylwester Arabas <[email protected]>
  • Loading branch information
Sfonxu and slayoo authored Oct 24, 2024
1 parent dc969ff commit 8fff7ce
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 1 deletion.
43 changes: 43 additions & 0 deletions .github/workflows/readme_rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: readme_rust

defaults:
run:
shell: bash

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 13 * * 4'

jobs:
rust:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dtolnay/rust-toolchain@stable
with:
components: rust-src
- uses: actions/setup-python@v5
with:
python-version: 3.9
- run: pip install -e .
- run: pip install pytest-codeblocks pytest

- run: |
cat >Cargo.toml <<EOF
[package]
name = "PyMPDATA"
version = "0.1.0"
edition = "2021"
[dependencies]
pyo3 = { version = "0.22.2", features=["auto-initialize"]}
EOF
- run: mkdir src
- run: python -c "import pytest_codeblocks; code=pytest_codeblocks.extract_from_file('docs/markdown/pympdata_landing.md'); f=open('src/main.rs', 'w'); f.writelines(block.code for block in code if block.syntax=='Rust'); f.close()"
- run: cat -n src/main.rs
- run: cargo run

93 changes: 92 additions & 1 deletion docs/markdown/pympdata_landing.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The Numba's deviation from Python semantics rendering [closure variables
information on domain extents, algorithm variant used and problem
characteristics (e.g., coordinate transformation used, or lack thereof).

# Tutorial (in Python, Julia and Matlab)
# Tutorial (in Python, Julia, Rust and Matlab)
## Options class

The [``Options``](https://open-atmos.github.io/PyMPDATA/PyMPDATA/options.html) class
Expand Down Expand Up @@ -93,6 +93,19 @@ options = Options(pyargs('n_iters', 2));
<details open>
<summary>Python code (click to expand)</summary>

<details>
<summary>Rust code (click to expand)</summary>
```Rust
use pyo3::prelude::*;
use pyo3::types::{IntoPyDict, PyDict, PyTuple};

fn main() -> PyResult<()> {
Python::with_gil(|py| {
let options_args = [("n_iters", 2)].into_py_dict_bound(py);
let options = py.import_bound("PyMPDATA")?.getattr("Options")?.call((), Some(&options_args))?;
```
</details>

```Python
from PyMPDATA import Options
options = Options(n_iters=2)
Expand Down Expand Up @@ -236,6 +249,45 @@ advector = VectorField(pyargs(...
));
```
</details>

<details>
<summary>Rust code (click to expand)</summary>
```Rust
let vector_field = py.import_bound("PyMPDATA")?.getattr("VectorField")?;
let scalar_field = py.import_bound("PyMPDATA")?.getattr("ScalarField")?;
let periodic = py.import_bound("PyMPDATA.boundary_conditions")?.getattr("Periodic")?;

let nx_ny = [24, 24];
let cx_cy = [-0.5, -0.25];
let boundary_con = PyTuple::new_bound(py, [periodic.call0()?, periodic.call0()?]).into_any();
let halo = options.getattr("n_halo")?;

let indices = PyDict::new_bound(py);
Python::run_bound(py, &format!(r#"
import numpy as np
nx, ny = {}, {}
xi, yi = np.indices((nx, ny), dtype=float)
data=np.exp(
-(xi+.5-nx/2)**2 / (2*(ny/10)**2)
-(yi+.5-nx/2)**2 / (2*(ny/10)**2)
)
"#, nx_ny[0], nx_ny[1]), None, Some(&indices)).unwrap();

let advectee_arg = vec![("data", indices.get_item("data")?), ("halo", Some(halo.clone())), ("boundary_conditions", Some(boundary_con))].into_py_dict_bound(py);
let advectee = scalar_field.call((), Some(&advectee_arg))?;
let full = PyDict::new_bound(py);
Python::run_bound(py, &format!(r#"
import numpy as np
nx, ny = {}, {}
Cx, Cy = {}, {}
data = (np.full((nx + 1, ny), Cx), np.full((nx, ny + 1), Cy))
"#, nx_ny[0], nx_ny[1], cx_cy[0], cx_cy[1]), None, Some(&full)).unwrap();
let boundary_con = PyTuple::new_bound(py, [periodic.call0()?, periodic.call0()?]).into_any();
let advector_arg = vec![("data", full.get_item("data")?), ("halo", Some(halo.clone())), ("boundary_conditions", Some(boundary_con))].into_py_dict_bound(py);
let advector = vector_field.call((), Some(&advector_arg))?;
```
</details>

<details open>
<summary>Python code (click to expand)</summary>

Expand Down Expand Up @@ -306,6 +358,18 @@ stepper = Stepper(pyargs(...
```
</details>
<details open>

<details>
<summary>Rust code (click to expand)</summary>

```Rust
let n_dims: i32 = 2;
let stepper_arg = PyDict::new_bound(py);
let _ = PyDictMethods::set_item(&stepper_arg, "options", &options);
let _ = PyDictMethods::set_item(&stepper_arg, "n_dims", &n_dims);
```
</details>

<summary>Python code (click to expand)</summary>

```Python
Expand All @@ -332,6 +396,17 @@ stepper = Stepper(pyargs(...
));
```
</details>

<details>
<summary>Rust code (click to expand)</summary>

```Rust
let _stepper_arg_alternative = vec![("options", &options), ("grid", &PyTuple::new_bound(py, nx_ny).into_any())].into_py_dict_bound(py);
let stepper_ = py.import_bound("PyMPDATA")?.getattr("Stepper")?;
let stepper = stepper_.call((), Some(&stepper_arg))?; //or use stepper args alternative
```
</details>

<details open>
<summary>Python code (click to expand)</summary>

Expand Down Expand Up @@ -406,6 +481,22 @@ solver.advance(pyargs('n_steps', 75));
state = solver.advectee.get();
```
</details>

<details>
<summary>Rust code (click to expand)</summary>

```Rust
let solver_ = py.import_bound("PyMPDATA")?.getattr("Solver")?;
let solver = solver_.call((), Some(&vec![("stepper", stepper), ("advectee", advectee), ("advector", advector)].into_py_dict_bound(py)))?;
let _state_0 = solver.getattr("advectee")?.getattr("get")?.call0()?.getattr("copy")?.call0()?;
solver.getattr("advance")?.call((), Some(&vec![("n_steps", 75)].into_py_dict_bound(py)))?;
let _state = solver.getattr("advectee")?.getattr("get")?.call0()?;
Ok(())
})
}
```
</details>

<details open>
<summary>Python code (click to expand)</summary>

Expand Down

0 comments on commit 8fff7ce

Please sign in to comment.