Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • generic-software-skills/lecture-materials
  • m301114/lecture-materials
2 results
Show changes
Commits on Source (412)
Showing
with 1007 additions and 66 deletions
variables:
GIT_DEPTH: 50
QUARTO_VERSION: 1.4.553
PIXI_HOME: .pixi
workflow:
# these rules ensure that whenever there's a merge request, a "merge request pipeline" is run,
......@@ -23,13 +23,20 @@ run_pre_commit_hooks:
variables:
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
cache:
paths:
- paths:
- ${PRE_COMMIT_HOME}
- key:
files:
- pixi.lock
paths:
- .pixi
script:
- python3 -V # Print out python version for debugging
- python3 -m pip install pre-commit
- curl -fsSL https://pixi.sh/install.sh | sh
- export PATH="${PATH}:${PIXI_HOME}/bin"
- pixi info
- pixi install
- echo running pre-commit for revision range $COMPARE_SOURCE ... HEAD
- pre-commit run --from-ref $COMPARE_SOURCE --to-ref HEAD
- pixi run pre-commit run --from-ref $COMPARE_SOURCE --to-ref HEAD
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
variables:
......@@ -42,22 +49,21 @@ run_pre_commit_hooks:
stage: build
tags:
- conda
variables:
QUARTODEB: quarto_cache/quarto-${QUARTO_VERSION}.deb
cache:
- key: cache-default
paths:
- .jupyter_cache
- quarto_cache
- key:
files:
- pixi.lock
paths:
- .pixi
before_script:
- mkdir -p quarto_cache
- if [ ! -f ${QUARTODEB} ] ; then wget "https://github.com/quarto-dev/quarto-cli/releases/download/v${QUARTO_VERSION}/quarto-${QUARTO_VERSION}-linux-amd64.deb" -O ${QUARTODEB} ; fi
- dpkg -i ${QUARTODEB}
- python3 -m pip install -r requirements.txt
- quarto check install
- quarto check jupyter
- export PATH="${PATH}:${PIXI_HOME}/bin"
- pixi run quarto check install
- pixi run quarto check jupyter
script:
- ./make_presentations
- pixi run render
artifacts:
paths:
- public
......
......@@ -3,9 +3,11 @@ project:
pre-render: scripts/prepare.py
post-render: scripts/md2ical.py
output-dir: public
resources:
- lectures/git2/code/*
website:
title: "Generic Software Skills"
title: Generic Software Skills
navbar:
left:
- href: index.qmd
......@@ -21,46 +23,56 @@ website:
href: https://gitlab.dkrz.de/generic-software-skills/lecture-materials/-/issues/new
sidebar:
search: true
collapse-level: 1
contents:
- section: "Lectures"
- section: Lectures
contents:
# - "lectures/example-lecture/slides.qmd"
- "lectures/intro/slides.qmd"
- "lectures/command-line/slides.qmd"
- "lectures/git/slides.qmd"
# - "lectures/programming-paradigms/slides.qmd"
# - "lectures/data-structures/slides.qmd"
# - "lectures/complexity/slides.qmd"
# - "lectures/debugging/slides.qmd"
# - "lectures/good-scientific-practice/slides.qmd"
# - "lectures/user-experience/slides.qmd"
# - "lectures/testing/slides.qmd"
# - "lectures/git2/slides.qmd"
# - "lectures/parallelism/slides.qmd"
# - "lectures/hardware/slides.qmd"
# - "lectures/file-and-data-systems/slides.qmd"
# - "lectures/memory-hierarchies/slides.qmd"
# - "lectures/student-talks/slides.qmd"
- section: "Exercises"
# - lectures/example-lecture/slides.qmd
- lectures/intro/slides.qmd
- lectures/command-line/slides.qmd
- lectures/git/slides.qmd
- lectures/coding-environment-reproducibility/slides.qmd
- lectures/tooling-ci/slides.qmd
- lectures/testing/slides.qmd
- lectures/refactoring/slides.qmd
- lectures/debugging-strategies/slides.qmd
- lectures/user-experience/slides.qmd
- lectures/error-handling-observability/slides.qmd
- lectures/data-structures/slides.qmd
- lectures/complexity/slides.qmd
- lectures/programming-paradigms/slides.qmd
- lectures/git2/slides.qmd
- lectures/good-practice/slides.qmd
# - lectures/student-talks/slides.qmd
- section: Exercises
contents:
- "exercises/git.qmd"
# - "exercises/programming_paradigms.qmd"
# - "exercises/data_structures.qmd"
# - "exercises/complexity.qmd"
# - "exercises/debugging.qmd"
# - "exercises/good_scientific_practice.qmd"
# - "exercises/user_experience.qmd"
# - "exercises/testing.qmd"
# - "exercises/git2.qmd"
# - "exercises/parallelism.qmd"
# - "exercises/hardware.qmd"
# - "exercises/file_and_data_systems.qmd"
# - "exercises/memory_hierarchies.qmd"
# - "exercises/student_talks.qmd"
- exercises/git.qmd
- exercises/coding-environment-reproducibility.qmd
- exercises/tooling-ci.qmd
- exercises/programming_paradigms.qmd
- exercises/data_structures.qmd
- exercises/complexity.qmd
- exercises/debugging-strategies.qmd
- exercises/user-experience.qmd
- exercises/testing.qmd
- exercises/git2/exercise.qmd
- exercises/parallelism/parallelism.qmd
- exercises/hardware/hardware.qmd
- exercises/file-and-data-systems.qmd
- exercises/memory-hierarchies.qmd
# - exercises/student_talks.qmd
- section: Archive
contents:
- lectures/parallelism/slides.qmd
- lectures/hardware/slides.qmd
- lectures/file-and-data-systems/slides.qmd
- lectures/memory-hierarchies/slides.qmd
format:
html:
theme: cosmo
css: styles.css
toc: true
license: "CC BY"
license: CC BY
name: software-skills
channels:
- conda-forge
dependencies:
- python=3.12
- quarto
- pip
- pip:
- -r requirements.txt
---
title: "Coding environment & reproducibility"
---
1. Create a branch called `reproducibility`
1. Create a file `reproducibility.md` and answer the following questions:
* **List reasons why an isolated coding environment is useful.**
* **What are the pros and cons of requiring specific version numbers?**
1. Add and commit your changes
1. Push your branch to your Git repo and create a merge request
1. Request a review from one of the lecturers
---
title: "Complexity"
---
### Using `git bisect` to find committed bugs
_The tasks should be done using the test repository <https://gitlab.dkrz.de/generic-software-skills/bisect-example>._
**General information**
`git bisect` allows to specify a good and a bad commit and applies a binary search to locate
the commit which introduced a bug our flawed behaviour.
In general, you start the procedure by typing
```bash
git bisect start
```
Then a good and a bad commit have to be defined. In our case the bad commit is the most recent one,
while the good commit is the fourth commit of the repository.
Find out the hash of the respective commits and replace them in the commands below
```bash
git bisect good <commit>
git bisect bad <commit>
```
Now git will start bisecting and needs input whether the selected commit is good or bad
(by testing and issueing `git bisect good` or `git bisect bad`).
If you're done, enter the following to clean up and return to the original HEAD
```bash
git bisect reset
```
_For more information about `git bisect`, refer to the documentation at <https://git-scm.com/docs/git-bisect>._
**Tasks**
1. Which commit does `git bisect` return, if you use simply run `test_myint.py` from the selected commit every time?
2. How can you make testing more consistent for more or even all of the commits? Can you spot additional issues with this method?
3. Try to give a best practices about git commits and the checked in code based on the experience from the previous two tasks (3-5 recommendations)
### Function interface matters
**General information**
The function [`numpy.histogram`](https://numpy.org/doc/stable/reference/generated/numpy.histogram.html) computes a histogram from the values of an array.
Although it would be easy to specify the histogram bins e.g. using
```python
np.histogram(..., bins=np.linspace(a, b, n))
```
it's also possible to pass the range and number of bins explicitly, e.g.: using
```python
np.histogram(..., bins=n, range=(a, b))
```
**Tasks**
Why is this a good interface design?
---
title: "Data structures"
---
### 1. Investigate performance of different data structures
Finish task 5. from the _Data Layout_ hands-on session and come up with two other solutions to the data table class and their performance numbers
### 2. Arbitrary-precision integers
In Python, integers do not have a fixed byte size but extend to arbitrary precision (within the memory limits of your computer system).
To understand the implications of this, try to implement your own idea of an arbitary-precision integer class using an appropriate Container Data Type. The class should at least allow conversion from and to string, addition, and subtraction, as in
```python
n = MyInt('271828182845904523536028747135266249')
m = MyInt('314159265358979323846264338327950288')
s = n + m
d = m - n
print(s, d)
```
If that was too easy, you are welcome to do _multiplication_ as well!
> Solution of at least one assignment to be uploaded, named and documented adequately in your Gitlab course repository until Monday, April 29th 2024, 23:59:59.999 CEST
> Next week we will randomly select some participants to present and discuss their solutions
---
title: "Debugging strategies"
---
### 1. Approach on locating Python bugs
_The tasks should be done using the test repository <https://github.com/gweis/isodate>,
specifically version `v0.6.1` (commit `4f36d7e6f`)._
_It is a Python library to parse date and times according to the ISO 8601 standard.
Although it works great in many use cases, there are some documented bugs in the code for former versions.
But the linked repository was not chosen to cast a bad light on it in any way.
In fact, it does a good job providing tests and stating which problems occured in the code._
**Tasks**
1. Check out the code and run the test suite. This can typically be done by issueing
`python -m unittest` in the `src/isodate` directory (not within the `tests` directory).
You should find that it reports two failures and one error.
Document the actual output stating that.
2. Pick one of the three issues and investigate it. Narrow down the code area, where the issue occurs.
Document your strategy of narrowing it down step by step.
**This task is NOT about fixing any of the issues^[You can try, if you want. But this is NOT part of the homework]!**
### 2. Approach on locating Fortran bugs
For this exercise, check out the Fortran code [schnecke_flt.f90](../lectures/debugging-strategies/static/schnecke_flt.f90).
You can compile it on Levante e.g. by loading GCC 11.2.0 compilers with
```bash
module load gcc/11.2.0-gcc-11.2.0
```
and then run
```bash
gfortran schnecke_flt.f90
```
**Task**
- Use additional compiler flags and `gdb` to locate the error. Document your approach.
---
title: "File and Data Systems"
---
Create
* 10000 files of 1 MB each
* 100 files of 100 MB each
* 1 file of 10 000 MB
in your `/scratch/` directory on levante.
* Read them once and measure the durations for the read operations
* Read every file 10x and measure the durations for the read operations
* Repeat the read part of the exercise after a few hours.
Discuss your results.
Create a directory `/dev/shm/$USER` (replace `$USER` with your user ID) and repeat the exercise in there - you will probably need about 100 GB RAM to do this (or simply compute node) - no delayed repeat needed here (why?).
......@@ -2,11 +2,12 @@
title: "Git"
---
Think about different occasions in your studies or work in which Git may help
you. Are there any limitations? Summarise your thoughts in a text file and add
it to your personal course repository.
1. Create a branch called `git`
1. Create a file called `git.md` answer the following questions:
1. Think about different occasions in your studies or work in which Git may help you.
1. What are possible limitations for version control?
1. Add the file `git.md` and commit your changes
1. Push your branch to your Git repo[^1] and create a merge request
1. Request a review from one of the lecturers
If you already have some existing scripts/code from you day-to-day work, you
may want to add those to a git repository and refer to it.
Next week we will randomly select some participants to discuss their thoughts.
[^1]: https://gitlab.dkrz.de/generic-software-skills/students2025/\<YOUR_USERNAME\>
/appendix.py
/exercise.py
/testrepo
__pycache__
*.pyc
---
title: "Git 2 exercise - Appendix"
---
```{python}
#| echo: false
import datetime
```
Git objects have to be formatted in a special way such that they are considered valid objects by `git`.
We've talked about the individual objects
This section briefly describes how they are formatted and provides example code to generate them using python.
#### Object header
Every git object is prefixed with a particular header, before being stored in the git object store.
The header consists of *object type*, *object size* (in bytes) and a `0`-byte as delimiter to the actual contents:
```
<object type> <object size>\0
```
The header can be generated using this function:
```{python}
def make_object_header(object_type: str, object_contents: bytes) -> bytes:
return f"{object_type} {len(object_contents)}".encode("ascii") + b"\0"
```
#### `blob` object
A "blob" object contains of any sequence of bytes and is used to represent file contents in git.
```
<header><contents>
```
```{python}
def make_blob_object(contents: bytes) -> bytes:
return make_object_header("blob", contents) + contents
```
#### `tree` object
A tree object contains a sequence of tree entries (either *blob*s or other *tree*s).
Each tree entry consists of a *mode*, a *name* and a *hash*.
* The *mode* is
* "100644" for a normal file
* "100755" for an executable file
* "40000" for a directory
* there exist other modes, but not every combination of numbers is valid
* The *name* represents the file or directory name
* The *hash* is the hash of the referenced item (*blob* or *tree*). The *hash* is stored in binary representation.
```
<header><mode1> <name1>\0<hash1><mode2> <name2>\0<hash2>...
```
```{python}
def make_tree_object(items) -> bytes:
"""
items: list of (mode, name, hash) tuples
hash: a hashlib.sha1 object
"""
tree_object = b""
for mode, name, hash in sorted(items, key=lambda item: item[1]):
tree_object += (
f"{mode} {name}".encode("utf-8") + b"\0" + hash.digest()
)
return make_object_header("tree", tree_object) + tree_object
```
#### `commit` object
The commit object contains references to a *tree* and zero or more references to *parent* commits.
Hashes are stored in hexadecimal form. The format is as follows:
```
<header>tree <tree_hash>
parent <parent_hash>
...
author Author Name <author.name@email.example> <timestamp> <timezone>
committer Committer Name <committer.name@email.example> <timestamp> <timezone>
<commit message>
```
```{python}
def make_commit_object(
tree,
author,
message,
parents=None,
committer=None,
author_time=None,
committer_time=None,
) -> bytes:
"""
all hashes must be hashlib.sha1 objects
"""
parents = parents or []
committer = committer or author
author_time = author_time or datetime.datetime.now().astimezone()
committer_time = committer_time or author_time
commit_object = f"tree {tree.hexdigest()}\n"
for parent in parents:
commit_object += f"parent {parent.hexdigest()}\n"
commit_object += f"author {author} {author_time.timestamp():.0f} {author_time:%z}\n"
commit_object += (
f"committer {committer} {committer_time.timestamp():.0f} {committer_time:%z}\n"
)
commit_object += "\n" + message
commit_object = commit_object.encode("utf-8")
return make_object_header("commit", commit_object) + commit_object
```
---
title: "Git 2"
---
# Build a git repository
## 1 using "normal" git commands
Please have a look at the following git repository structure:
![git objects (from the [git book](https://git-scm.com/book/id/v2/Git-Internals-Git-Objects))](git_data_model.png)
* Write a script which creates a git repository matching the depicted structure.
You may use the usual git frontend commands, e.g. starting with `git init`, `git add`, `git commit` etc...
* Have a look at the resulting git object hashes and compare them with the hashes shown in the figure.
Likely not all hashes in your repository will be equal to the ones in the picture. Explain which hashes are or should be equal and which should not and why.
## 2 manually
* Write another script, which creates a similar git repository, but do so **without** using the `git` command at all.
You can choose any programming language of your choice and of course may reuse or extend code snippets from the lecture.
You can find further code snippets for details of the git objects in the [appendix](appendix.qmd) of this exercise.
* Check if the repository is accessible using `git log`.
::: {.callout-tip}
You may find that `git status` shows that your repository is not in a clean state.
That's ok and likely because we didn't talk in detail about the "index".
You can fix the index of your test (!) repository using `git restore --staged .` and afterwards the working direcory using `git restore .`.
:::
import zlib
from pathlib import Path
from hashlib import sha1
from appendix import make_blob_object, make_tree_object, make_commit_object
class SimpleGit:
def __init__(self, root):
self.root = Path(root)
def init(self) -> None:
(self.root / "refs" / "heads").mkdir(parents=True)
(self.root / "HEAD").write_text("ref: refs/heads/main")
def head_ref(self):
head = (self.root / "HEAD").read_text()
assert head.startswith("ref: ")
return head.split(" ", 1)[-1]
def update_current_ref(self, new_hash) -> None:
(self.root / self.head_ref()).write_text(new_hash.hexdigest())
def object_path(self, h) -> Path:
h = h.hexdigest()
return self.root / "objects" / h[:2] / h[2:]
def put_object(self, git_object: bytes) -> str:
h = sha1(git_object)
p = self.object_path(h)
p.parent.mkdir(parents=True, exist_ok=True)
p.write_bytes(zlib.compress(git_object))
return h
def get_object(self, h: str) -> bytes:
return zlib.decompress(self.object_path(h).read_bytes())
def put_blob(self, contents: bytes) -> str:
return self.put_object(make_blob_object(contents))
def put_tree(self, items):
return self.put_object(make_tree_object(items))
def commit(
self,
tree,
author,
message,
parents=None,
committer=None,
author_time=None,
committer_time=None,
):
h = self.put_object(
make_commit_object(
tree, author, message, parents, committer, author_time, committer_time
)
)
self.update_current_ref(h)
return h
exercises/git2/git_data_model.png

21 KiB

#!/bin/bash
set -e
set -x
rm -rf testrepo
jupytext --to py appendix.qmd
python3 test_git2.py
cd testrepo
git status
git log
from git2 import SimpleGit
sg = SimpleGit("testrepo/.git")
FILE = "100644"
FOLDER = "40000"
sg.init()
bv1 = sg.put_blob(b"version 1\n")
t1 = sg.put_tree(
[
(FILE, "test.txt", bv1),
]
)
c1 = sg.commit(t1, "Tobias Kölling <tobias.koelling@mpimet.mpg.de>", "first commit")
bv2 = sg.put_blob(b"version 2\n")
bnew = sg.put_blob(b"new file\n")
t2 = sg.put_tree(
[
(FILE, "test.txt", bv2),
(FILE, "new.txt", bnew),
]
)
c2 = sg.commit(
t2, "Tobias Kölling <tobias.koelling@mpimet.mpg.de>", "second commit", parents=[c1]
)
t3 = sg.put_tree(
[
(FILE, "test.txt", bv2),
(FILE, "new.txt", bnew),
(FOLDER, "bak", t1),
]
)
c3 = sg.commit(
t3, "Tobias Kölling <tobias.koelling@mpimet.mpg.de>", "third commit", parents=[c2]
)
---
title: "Computing devices"
---
### Tasks:
Get the example code from [here](https://gitlab.dkrz.de/generic-software-skills/lecture-materials/-/blob/main/exercises/hardware/main-mpi.c?ref_type=heads).
1. Do a strong-scaling experiment starting with 2 MPI processes (ranks) and up to 32
ranks and plot the result. Use the following commands for compilation and for running with `<rank-number>` ranks:
```sh
mpicc main-mpi.c -o mpi.x -lm
mpirun -n <rank-number> mpi.x
```
2. Replace the call to `maxval` to `minval` and compute the minimum height of a wave. Search for the comment `// HOMEWORK:`.
3. Compare the complexity of the MPI implementation with the OpenMP one from last week. Give your feedback on which is easier to use/learn and a few reasons why.
\ No newline at end of file
// --------------------------------//
// author: Jan Frederik Engels
// contributors: Julius Plehn
// date: June 2024
// --------------------------------//
#include <math.h>
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
// compile using (for example):
// mpicc main-mpi.c -o mpi.x -lm
// definition of constants
// some values are given in meter
// some in seconds
//
#define x_0 10000
#define delta_x 1
#define coeff_sech 0.01
// depth of sea at deepest point
#define b_0 1000
// gravitational acceleration
#define g 10
#define delta_t 0.001
double *h;
double *h1;
double *h2;
double *u;
double *u1;
double *u2;
long local_npoints;
long local_start, local_end;
long start_index, end_index;
int mpi_size, mpi_rank;
enum halo { HALO_NONE = 0, HALO_LEFT = 1, HALO_RIGHT = 2 };
enum halo halo_property;
long count_points() { return (int)(2 * x_0 / delta_x); }
double get_x(long i) {
if (halo_property & HALO_LEFT) {
i = local_start + i - 1;
} else {
i = local_start + i;
}
return i * delta_x - x_0;
}
double waterdepth(long i) {
double x = get_x(i);
return b_0 * (x * x / (x_0 * x_0) - 1);
}
void initialise_h(double *h) {
double x, tmp;
long start_index_init = 0;
long end_index_init = end_index;
if (halo_property & HALO_LEFT)
start_index_init = 1;
if (mpi_rank == mpi_size - 1)
end_index_init += 1;
for (long i = start_index_init; i <= end_index_init; i++) {
x = get_x(i);
tmp = 2 / (exp(coeff_sech * x) + exp(-coeff_sech * x));
if (tmp > 1e-10)
h[i] = tmp * tmp;
else
h[i] = 0;
}
}
void initialise_u(double *u) {
long start_index_init = 0;
if (halo_property & HALO_LEFT)
start_index_init = 1;
for (long i = start_index_init; i <= end_index; i++) {
u[i] = 0;
}
}
void print_values(FILE *data, double *h, double *u) {
long i;
fprintf(data, "#x\th\tu\tb\n");
for (i = start_index; i < end_index; i++) {
fprintf(data, "%g\t%g\t%g\t%g\n", get_x(i), h[i], u[i],
waterdepth(local_start + i));
}
}
double lu(double *h, double *u, long i) {
return u[i] * (u[i + 1] - u[i - 1]) + g * (h[i + 1] - h[i - 1]);
}
double lh(double *h, double *u, long i) {
return u[i] * (h[i + 1] - h[i - 1]) -
u[i] * (waterdepth(i + 1) - waterdepth(i - 1)) +
(h[i] - waterdepth(i)) * (u[i + 1] - u[i - 1]);
}
// sets h1,u1 using values (startconditions) from u2/h2
void euler(double *h1, double *h2, double *u1, double *u2) {
long i;
for (i = start_index; i <= end_index; i++) {
u1[i] = u2[i] - (delta_t / (2. * delta_x)) * lu(h2, u2, i);
h1[i] = h2[i] - (delta_t / (2. * delta_x)) * lh(h2, u2, i);
}
}
long findmax(double *h, long oldmaxi, long outputmth) {
long i, maxi = oldmaxi - 10;
for (i = oldmaxi - 10;
(i < oldmaxi + (int)(outputmth / delta_x) + 30) && (i < end_index);
i++) {
if (h[i] > h[maxi]) {
maxi = i;
}
}
return maxi;
}
double minval(double *h) {
double hmin = 1000000;
for (long i = start_index; i <= end_index; i++) {
if (h[i] < hmin)
hmin = h[i];
}
return hmin;
}
double maxval(double *h) {
double hmax = -10;
for (long i = start_index; i <= end_index; i++) {
if (h[i] > hmax)
hmax = h[i];
}
return hmax;
}
double ng_derivative(double xi, double xi_1, double deltax) {
return (xi - xi_1) / deltax;
}
void swap(double **var, double **var1, double **var2) {
double *temp;
temp = *var2;
*var2 = *var1;
*var1 = *var;
*var = temp;
}
void set_boundaries(double *var) {
long npoints = local_npoints;
if (mpi_rank == 0) {
var[0] = 0;
}
if (mpi_rank == mpi_size - 1) {
var[npoints] = 0;
}
}
// Exchange h2 halos
void exchange_halos(double *var) {
if (mpi_size > 1) {
if (mpi_rank % 2 == 0) {
if (mpi_rank != mpi_size - 1) {
// Send to the right neighbor
int index = local_npoints - 1;
if (mpi_rank > 0)
index += 1;
MPI_Send(&var[index], 1, MPI_DOUBLE, mpi_rank + 1, 1,
MPI_COMM_WORLD);
// Receive from the right
index = local_npoints;
if (mpi_rank > 0)
index += 1;
MPI_Recv(&var[index], 1, MPI_DOUBLE, mpi_rank + 1, 1,
MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
if (mpi_rank != 0) {
// Send to the left neighbor
MPI_Send(&var[1], 1, MPI_DOUBLE, mpi_rank - 1, 1,
MPI_COMM_WORLD);
// Receive from the left
MPI_Recv(&var[0], 1, MPI_DOUBLE, mpi_rank - 1, 1,
MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
} else {
if (mpi_rank != mpi_size - 1) {
// Receive from the right
int index = local_npoints;
if (mpi_rank > 0)
index += 1;
MPI_Recv(&var[index], 1, MPI_DOUBLE, mpi_rank + 1, 1,
MPI_COMM_WORLD, MPI_STATUS_IGNORE);
// Send to the right neighbor
index = local_npoints - 1;
if (mpi_rank > 0)
index += 1;
MPI_Send(&var[index], 1, MPI_DOUBLE, mpi_rank + 1, 1,
MPI_COMM_WORLD);
}
// Receive from the left
MPI_Recv(&var[0], 1, MPI_DOUBLE, mpi_rank - 1, 1, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
// Send to the left neighbor
MPI_Send(&var[1], 1, MPI_DOUBLE, mpi_rank - 1, 1, MPI_COMM_WORLD);
}
}
}
void print_var(double *var, int count, int rank) {
if (mpi_rank == rank) {
printf("Rank: %d | ", rank);
for (int i = 0; i < count; ++i) {
printf("%g ", var[i]);
}
printf("\n");
}
}
int main(int argc, char *argv) {
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
if (mpi_rank == 0) {
printf("Running with %d MPI processes\n", mpi_size);
}
long i;
long npoints = count_points();
if (mpi_rank == 0) {
printf("npoints: %d\n", npoints);
}
// Figure out halos
int added_halos;
if (mpi_size > 1) {
if (mpi_rank == 0) {
halo_property = HALO_RIGHT;
added_halos = 1;
} else if (mpi_rank == mpi_size - 1) {
halo_property = HALO_LEFT;
added_halos = 1;
} else {
halo_property = HALO_LEFT | HALO_RIGHT;
added_halos = 2;
}
} else {
halo_property = HALO_NONE;
added_halos = 0;
}
// Decomposition
local_npoints = npoints / mpi_size;
int remainder = npoints % mpi_size;
start_index = 1;
if (mpi_rank < remainder) {
local_start = mpi_rank * (local_npoints + 1);
local_end = local_start + local_npoints;
local_npoints += 1;
} else {
local_start = mpi_rank * local_npoints + remainder;
local_end = local_start + (local_npoints - 1);
}
// Specify ranges once and take halos into consideration
end_index = local_npoints - 1;
if (halo_property & HALO_LEFT && halo_property & HALO_RIGHT) {
end_index = local_npoints;
}
h = malloc(sizeof(double) * (local_npoints + added_halos));
h1 = malloc(sizeof(double) * (local_npoints + added_halos));
h2 = malloc(sizeof(double) * (local_npoints + added_halos));
u = malloc(sizeof(double) * (local_npoints + added_halos));
u1 = malloc(sizeof(double) * (local_npoints + added_halos));
u2 = malloc(sizeof(double) * (local_npoints + added_halos));
if (h == NULL || h1 == NULL || h2 == NULL || u == NULL || u1 == NULL ||
u2 == NULL) {
fprintf(stderr, "Insufficient memory");
exit(23);
}
long maxi = ((npoints) / 2) + 5;
long oldmaxi;
long oldmaxi_output = (npoints) / 2;
int maximth = 20;
double t = 0.0;
long n = 0;
int outputmth = 1000; // write every mth set of values to stderr
int dataqth = 200; // write every qth set of values to output#
char file[60]; // char pointer for filename
FILE *out;
double *temp;
int first = 1;
double before, after, duration;
initialise_u(u2);
initialise_h(h2);
exchange_halos(h2);
exchange_halos(u2);
euler(h1, h2, u1, u2);
if (mpi_rank == 0)
fprintf(stderr, "#time h_max\n");
before = MPI_Wtime();
while (t < 50) {
if (!first) {
swap(&h, &h1, &h2);
swap(&u, &u1, &u2);
} else {
first = 0;
}
exchange_halos(h1);
exchange_halos(u1);
set_boundaries(u);
set_boundaries(h);
for (long i = 1; i <= end_index; i++) {
u[i] = u2[i] - (delta_t / delta_x) * lu(h1, u1, i);
h[i] = h2[i] - (delta_t / delta_x) * lh(h1, u1, i);
}
t += delta_t;
if ((n % maximth) == 0) {
if ((n % outputmth) == 0) {
// HOMEWORK: replace maxval with minval
double maxh = maxval(h);
double global_max;
// HOMEWORK: adapt MPI_Reduce to compute the MIN
MPI_Reduce(&maxh, &global_max, 1, MPI_DOUBLE, MPI_MAX, 0,
MPI_COMM_WORLD);
if (mpi_rank == 0)
fprintf(stderr, "%g\t%g\n", t, global_max);
}
}
n++;
}
MPI_Barrier(MPI_COMM_WORLD);
after = MPI_Wtime();
duration = after - before;
if (mpi_rank == 0)
printf("Run took %g seconds.\n", duration);
// print_values(stdout, h, u);
MPI_Finalize();
// deallocate memory
free(h);
free(h1);
free(h2);
free(u);
free(u1);
free(u2);
return 0;
}
#include <stdio.h>
#include <math.h>
#include <mpi.h> // include the header for the MPI library
int main(int argc, char** argv){
int no_of_ranks, my_rank, n_per_rank;
int n = 10;
int my_first, my_last;
MPI_Init(&argc, &argv); // initiate MPI computation
MPI_Comm_size(MPI_COMM_WORLD, &no_of_ranks); // get number of MPI ranks
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); // get rank of this process
// Calculate the number of iterations per rank
n_per_rank = floor(n/no_of_ranks);
if( n%no_of_ranks > 0 ){
// Add 1 in case the number of ranks doesn't divide n
n_per_rank += 1;
}
// Figure out the first and the last iteration for this rank
my_first = my_rank * n_per_rank;
my_last = my_first + n_per_rank;
// Run only the part of the loop this rank needs to run
// The if statement makes sure we don't go over
for( int i=my_first; i<my_last; i++ ) {
if( i < n ) {
printf("I am process %d and I am printing the number %d.\n", my_rank, i);
}
}
MPI_Finalize(); // terminate MPI computation
}
#include <stdio.h>
#include <mpi.h> // include the header for the MPI library
// Find the maximum of numbers in a vector
double find_maximum( double * vector, int N ){
double max = 0;
// TODO: Create a new variable for the global maximum, look below for the name.
for( int i=0; i<N; i++){
if( vector[i] > max ){
max = vector[i];
}
}
// TODO: Use MPI_Allreduce to find the maximum over all the ranks
// MPI_Allreduce(const void *sendbuf, void *recvbuf, int count,
// MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
// sendbuf: of what do we want to compute the maximum?
// recvbuf: Where should the global maximum be written? Do we need a new
// variable for this?
// count: How many variables do we want to exchange?
// datatype: MPI_DOUBLE
// op: MPI_MAX
// comm: Which communicator contains all ranks? The right one is in use here.
// Summing up:
// MPI_Allreduce(&max, &globalmax, ..... );
// TODO: return the global maximum
return max;
}
int main(int argc, char** argv){
int no_of_ranks, my_rank;
int n = 1024;
double vector[n];
double max;
double my_first;
MPI_Init(&argc, &argv); // initiate MPI computation
MPI_Comm_size(MPI_COMM_WORLD, &no_of_ranks); // get number of MPI ranks
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); // get rank of this process
// Each rank will have n numbers,
// starting from where the previous left off
my_first = n * my_rank;
// Generate a vector
for( int i=0; i<n; i++){
double tmp = my_first + i - n*no_of_ranks;
vector[i] = tmp*tmp;
}
//Find the maximum and print
max = find_maximum( vector, n );
printf("The largest number is %f\n", max);
MPI_Finalize(); // terminate MPI computation
}