From 5fb1106c0ead23f6356fed5516aedb35e7985f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Oct 2024 18:27:29 +0200 Subject: [PATCH 1/3] DevOps: Fix tutorial scripts and CI/CD Make the exercise/solution conversion script reversible. Remove empty tutorial code cells. --- doc/tutorials/Readme.md | 2 +- doc/tutorials/convert.py | 18 ++++-- .../lennard_jones/lennard_jones.ipynb | 64 ------------------- testsuite/scripts/tutorials/test_convert.py | 22 ++++++- 4 files changed, 33 insertions(+), 73 deletions(-) diff --git a/doc/tutorials/Readme.md b/doc/tutorials/Readme.md index 435919a9c1..d3f8d7bb73 100644 --- a/doc/tutorials/Readme.md +++ b/doc/tutorials/Readme.md @@ -70,7 +70,7 @@ physical systems. Measuring the excess chemical potential of a salt solution using the Widom particle insertion method. [Guide](widom_insertion/widom_insertion.ipynb) * **Grand-Canonical Monte Carlo** - Simulating a polyelectrolyte solution coupled to a reservoir of salt. + Simulating a polyelectrolyte solution coupled to a reservoir of salt. [Guide](grand_canonical_monte_carlo/grand_canonical_monte_carlo.ipynb) [comment]: # (End of tutorials landing page) diff --git a/doc/tutorials/convert.py b/doc/tutorials/convert.py index a54505de5c..7625ee0144 100644 --- a/doc/tutorials/convert.py +++ b/doc/tutorials/convert.py @@ -75,7 +75,7 @@ def add_cell_from_script(nb, filepath): def remove_empty_cells(nb): - for i in range(len(nb['cells']) - 1, 0, -1): + for i in range(len(nb["cells"]))[::-1]: cell = nb['cells'][i] if cell['source'].strip() == '': nb['cells'].pop(i) @@ -90,11 +90,19 @@ def parse_solution_cell(cell): def convert_exercise2_to_code(nb): - for i in range(len(nb["cells"]) - 1, 0, -1): + for i in range(len(nb["cells"]))[::-1]: cell = nb["cells"][i] - solution = parse_solution_cell(cell) - if solution is not None: - cell["source"] = solution + if cell["cell_type"] != "markdown": + continue + source = cell["source"] + if source.startswith(""): + m = re.search("```python\n(.+)\n```", source, flags=re.DOTALL) + solution = "# SOLUTION CELL\n" + m.group(1) + nb["cells"][i] = nbformat.v4.new_code_cell(source=solution) + if (i + 1) != len(nb["cells"]) and \ + nb["cells"][i + 1]["cell_type"] == "code" and \ + not nb["cells"][i + 1]["source"]: + del nb["cells"][i + 1] def disable_plot_interactivity(nb): diff --git a/doc/tutorials/lennard_jones/lennard_jones.ipynb b/doc/tutorials/lennard_jones/lennard_jones.ipynb index 3913acfea3..93ac166699 100644 --- a/doc/tutorials/lennard_jones/lennard_jones.ipynb +++ b/doc/tutorials/lennard_jones/lennard_jones.ipynb @@ -273,14 +273,6 @@ "system = espressomd.System(box_l=BOX_L)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "13275327", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -356,14 +348,6 @@ "particles = system.part.add(type=[0] * N_PART, pos=np.random.random((N_PART, 3)) * system.box_l)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "655d49e6", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -498,14 +482,6 @@ " epsilon=LJ_EPS, sigma=LJ_SIG, cutoff=LJ_CUT, shift=0)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "91ac5e57", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "5e6332fb", @@ -717,14 +693,6 @@ "T_inst = 2. / 3. * e_kin / N_PART" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "c53290b6", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -843,14 +811,6 @@ "steps_per_subsample = int(np.ceil(3 * corr_time / system.time_step))" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "94fc76b6", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -923,14 +883,6 @@ "SEM_pot_energy = np.std(pot_energies) / np.sqrt(len(pot_energies))" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e4a203c", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -1034,14 +986,6 @@ "system.auto_update_accumulators.add(rdf_acc)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "8de0795a", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "51edfcf1", @@ -1082,14 +1026,6 @@ "rs = rdf_obs.bin_centers()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "18a10a12", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, diff --git a/testsuite/scripts/tutorials/test_convert.py b/testsuite/scripts/tutorials/test_convert.py index a2dd24a842..025307d4be 100644 --- a/testsuite/scripts/tutorials/test_convert.py +++ b/testsuite/scripts/tutorials/test_convert.py @@ -188,7 +188,7 @@ def test_cells_prepare_for_html(self): self.assertEqual(cell['source'], 'Question 1') cell = next(cells) self.assertEqual(cell['cell_type'], 'code') - self.assertEqual(cell['source'], '1') + self.assertEqual(cell['source'], '# SOLUTION CELL\n1') cell = next(cells) self.assertEqual(cell['cell_type'], 'markdown') self.assertEqual(cell['source'], '1b') @@ -199,7 +199,7 @@ def test_cells_prepare_for_html(self): self.assertEqual(cell['cell_type'], 'code') self.assertEqual( cell['source'], - '2\nimport matplotlib.pyplot\nglobal_var = 20') + '# SOLUTION CELL\n2\nimport matplotlib.pyplot\nglobal_var = 20') cell = next(cells) self.assertEqual(cell['cell_type'], 'code') self.assertEqual(cell['source'], '3') @@ -231,7 +231,7 @@ def test_cells_conversion(self): self.assertEqual(cell['source'], 'Question 1') cell = next(cells) self.assertEqual(cell['cell_type'], 'code') - self.assertEqual(cell['source'], '1') + self.assertEqual(cell['source'], '# SOLUTION CELL\n1') self.assertEqual(next(cells, 'EOF'), 'EOF') with open(f_input, 'w', encoding='utf-8') as f: @@ -255,6 +255,22 @@ def test_cells_conversion(self): self.assertEqual(cell['source'], '') self.assertEqual(next(cells, 'EOF'), 'EOF') + # check operation is reversible + cmd = ['cells', '--to-py', str(f_input)] + self.run_command(cmd, f_input) + # read processed notebook + with open(f_input, encoding='utf-8') as f: + nb_output = nbformat.read(f, as_version=4) + # check cells + cells = iter(nb_output['cells']) + cell = next(cells) + self.assertEqual(cell['cell_type'], 'markdown') + self.assertEqual(cell['source'], 'Question 1') + cell = next(cells) + self.assertEqual(cell['cell_type'], 'code') + self.assertEqual(cell['source'], '# SOLUTION CELL\n1') + self.assertEqual(next(cells, 'EOF'), 'EOF') + @skipIfMissingModules def test_cells_autopep8(self): root = pathlib.Path("@CMAKE_CURRENT_BINARY_DIR@") From 6ba871882c110824ae3dd862841c60a2ac428c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 11 Oct 2024 16:53:48 +0200 Subject: [PATCH 2/3] docs: Improve tutorials Apply feedback from summer school participants and tutors: LB units conversion, LB Mach number, non-standard LB symbols, broken links to the user guide, better plot axis labels, automatically update ZnDraw visualizer. Also remove the summer school banner. --- Readme.md | 6 -- doc/bibliography.bib | 6 +- .../electrokinetics/electrokinetics.ipynb | 4 +- .../lattice_boltzmann_theory.ipynb | 55 +++++++++++-------- .../lennard_jones/lennard_jones.ipynb | 25 ++++++--- src/python/espressomd/lb.py | 2 +- 6 files changed, 55 insertions(+), 43 deletions(-) diff --git a/Readme.md b/Readme.md index 098817a275..640410bb59 100644 --- a/Readme.md +++ b/Readme.md @@ -1,9 +1,3 @@ -# Invitation to the ESPResSo Summer School 2024 - -[![CECAM Flagship School registration link](https://img.shields.io/badge/CECAM%20Flagship%20School-Register%20Now-blue?style=for-the-badge)](https://www.cecam.org/workshop-details/1324) - -The summer school "Simulating soft matter across scales" will take place on October 7-11, 2024, in Stuttgart. Registration is now open on [CECAM](https://www.cecam.org/workshop-details/1324). - # ESPResSo [![GitLab CI](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/badges/python/pipeline.svg)](https://gitlab.icp.uni-stuttgart.de/espressomd/espresso/-/commits/python) diff --git a/doc/bibliography.bib b/doc/bibliography.bib index 000642f8cf..7c4c65a616 100644 --- a/doc/bibliography.bib +++ b/doc/bibliography.bib @@ -688,13 +688,13 @@ @Book{kruger17a address = {Cham}, } -@Book{kundu01a, +@Book{kundu02a, author = {Kundu, Pijush K. and Cohen, Ira M. and Hu, Howard}, title = {Fluid Mechanics}, -year = {2001}, +year = {2002}, edition = {2nd}, publisher = {Elsevier Science}, -isbn = {9780080545585}, +isbn = {978-0-12-178251-1}, doi = {10.1016/C2009-0-20716-1}, } diff --git a/doc/tutorials/electrokinetics/electrokinetics.ipynb b/doc/tutorials/electrokinetics/electrokinetics.ipynb index 65af9f29b3..32e5f952b8 100644 --- a/doc/tutorials/electrokinetics/electrokinetics.ipynb +++ b/doc/tutorials/electrokinetics/electrokinetics.ipynb @@ -52,7 +52,7 @@ "id": "192c4793", "metadata": {}, "source": [ - "The first system that is simulated in this tutorial is the simple advection-diffusion of a drop of uncharged chemical species in a constant velocity field. To keep the computation time small, we restrict ourselves to a 2D problem, but the algorithm is also capable of solving the 3D advection-diffusion equation. Furthermore, we can also skip solving the electrostatic Poisson equation, since there are is no charged species present. The equations we solve thus reduce to\n", + "The first system that is simulated in this tutorial is the simple advection-diffusion of a drop of uncharged chemical species in a constant velocity field. To keep the computation time small, we restrict ourselves to a 2D problem, but the algorithm is also capable of solving the 3D advection-diffusion equation. Furthermore, we can also skip solving the electrostatic Poisson equation, since there are no charged species present. The equations we solve thus reduce to\n", "\n", "\\begin{equation}\n", "\\partial_{t} n = D \\Delta n - \\vec{\\nabla} \\cdot (\\vec{v} n).\n", @@ -187,7 +187,7 @@ "metadata": {}, "source": [ "# Exercise:\n", - "- Create an instance of the [`espressomd.electrokinetics.EKSpecies`]() and add it to the system with [`system.ekcontainer.add()`](https://espressomd.github.io/doc/espressomd.html#espressomd.electrokinetics.EKContainer.add). \n", + "- Create an instance of the [`espressomd.electrokinetics.EKSpecies`](https://espressomd.github.io/doc/espressomd.html#espressomd.electrokinetics.EKSpecies) and add it to the system with [`system.ekcontainer.add()`](https://espressomd.github.io/doc/espressomd.html#espressomd.electrokinetics.EKContainer.add). \n", "\n", "# Hint:\n", "- Use the variables `DIFFUSION_COEFFICIENT`, `KT` and `TAU` defined above.\n", diff --git a/doc/tutorials/lattice_boltzmann/lattice_boltzmann_theory.ipynb b/doc/tutorials/lattice_boltzmann/lattice_boltzmann_theory.ipynb index 8c9bc7af92..30fc7dde0c 100644 --- a/doc/tutorials/lattice_boltzmann/lattice_boltzmann_theory.ipynb +++ b/doc/tutorials/lattice_boltzmann/lattice_boltzmann_theory.ipynb @@ -68,7 +68,7 @@ "The LBM essentially consists of solving a fully discretized version of the linearized\n", "Boltzmann equation. The Boltzmann equation describes the time evolution of the one\n", "particle distribution function $f \\left(\\vec{x}, \\vec{p}, t\\right)$, which is the probability of finding a molecule in\n", - "a phase space volume $d^3\\vec{x}\\,d^3\\vec{p}$ at time $t$.The function $f$ is normalized so that the integral\n", + "a phase space volume $d^3\\vec{x}\\,d^3\\vec{p}$ at time $t$. The function $f$ is normalized so that the integral\n", "over the whole phase space is the total mass of the particles:\n", "\n", "$$\\int \\int f \\left(\\vec{x}, \\vec{p}, t\\right) \\,d^3\\vec{x}\\,d^3\\vec{p} = N,$$\n", @@ -121,8 +121,8 @@ "cell in every LBM step.\n", "\n", "
\n", - "\n", "
\n", + "\n", "
The 19 velocity vectors $\\vec{c_i}$ for a D3Q19 lattice. From the central grid point, the velocity vectors point towards all 18 nearest neighbours marked by filled circles. The 19th velocity vector is the rest mode (zero velocity).
\n", "
\n", "
" @@ -137,7 +137,7 @@ "The second step is the collision part, where the actual physics happens. For the LBM it is assumed that the collision process linearly relaxes the populations to the local equilibrium, thus that it is a linear (=matrix) operator \n", "acting on the populations in each LB cell. It should conserve \n", "the particle number and the momentum. At this point it is clear\n", - "why the mode space is helpful. A 19 dimensional matrix that\n", + "why the mode space is helpful. A 19-dimensional matrix that\n", "conserves the first 4 modes (with the eigenvalue 1) is diagonal in the\n", "first four rows and columns.\n", "By symmetry consideration one finds that only four independent\n", @@ -155,22 +155,22 @@ "and has the form\n", "\n", "\\begin{align*}\n", - " m^\\star_i &= \\gamma_i \\left( m_i - m_i^\\text{eq} \\right) + m_i ^\\text{eq}.\n", + " m^\\star_i &= \\omega_i \\left( m_i - m_i^\\text{eq} \\right) + m_i ^\\text{eq}.\n", "\\end{align*}\n", "\n", "Here $m^\\star_i$ is the $i$th mode after the collision.\n", "In other words: each mode is relaxed towards\n", - "its equilibrium value with a relaxation rate $\\gamma_i$.\n", + "its equilibrium value with a relaxation rate $\\omega_i$.\n", "The conserved modes are not relaxed, i.e. their relaxation rate is 1.\n", "We summarize them here:\n", "\n", "\\begin{align*}\n", - " m^\\star_i &= \\gamma_i m_i \\\\\n", - " \\gamma_1=\\dots=\\gamma_4&=1 \\\\\n", - " \\gamma_5&=\\gamma_\\text{b} \\\\\n", - " \\gamma_6=\\dots=\\gamma_{10}&=\\gamma_\\text{s} \\\\\n", - " \\gamma_{11}=\\dots=\\gamma_{16}&=\\gamma_\\text{odd} \\\\\n", - " \\gamma_{17}=\\dots = \\gamma_{19}&=\\gamma_\\text{even} \\\\\n", + " m^\\star_i &= \\omega_i m_i \\\\\n", + " \\omega_1=\\dots=\\omega_4&=1 \\\\\n", + " \\omega_5&=\\omega_\\text{b} \\\\\n", + " \\omega_6=\\dots=\\omega_{10}&=\\omega_\\text{s} \\\\\n", + " \\omega_{11}=\\dots=\\omega_{16}&=\\omega_\\text{odd} \\\\\n", + " \\omega_{17}=\\dots = \\omega_{19}&=\\omega_\\text{even} \\\\\n", "\\end{align*}\n", "\n", "To include hydrodynamic fluctuations of the fluid, \n", @@ -202,8 +202,8 @@ "to the fluctuation dissipation theorem. \n", "\n", "
\n", - "\n", "
\n", + "\n", "
The coupling scheme between fluid and particles is based on the interpolation of the fluid velocity $\\vec{u}$ from the grid nodes. This is done by trilinear interpolation. The difference between the particle velocity $\\vec{v}(t)$ and the interpolated velocity $\\vec{u}(\\vec{r},t)$ is used in the momentum exchange of the equation $\\vec{F}_H$ above.
\n", "
\n", "
" @@ -221,23 +221,24 @@ "[LBFluidWalberlaGPU](https://espressomd.github.io/doc/espressomd.html#espressomd.lb.LBFluidWalberlaGPU) in the module\n", "[espressomd.lb](https://espressomd.github.io/doc/espressomd.html#module-espressomd.lb).\n", "\n", - "The LB lattice is a cubic lattice, with a lattice constant agrid that\n", + "The LB lattice is a cubic lattice, with a lattice constant agrid in MD units that\n", "is the same in all spatial directions. The chosen box length must be an integer multiple\n", "of agrid. The LB lattice is shifted by 0.5 agrid in all directions: the node\n", "with integer coordinates $\\left(0,0,0\\right)$ is located at\n", "$\\left(0.5a,0.5a,0.5a\\right)$.\n", "The LB scheme and the MD scheme are not synchronized: in one\n", "LB time step, several MD steps may be performed. This allows to speed\n", - "up the simulations and is adjusted with the parameter tau.\n", + "up the simulations and is adjusted with the parameter tau in MD units.\n", "The LB parameter tau must be an integer multiple of the MD timestep.\n", "\n", - "Even if MD features are not used, the System parameters cell_system.skin and time_step must be set. LB steps are performed \n", + "Even if MD features are not used, the system parameters cell_system.skin and time_step must be set. LB steps are performed \n", "in regular intervals, such that the timestep $\\tau$ for LB is recovered.\n", "\n", - "Important note: all commands of the LB interface use\n", - "MD units. This is convenient, as e.g. a particular \n", - "viscosity can be set and the LB time step can be changed without\n", - "altering the viscosity. On the other hand this is a source\n", + "Important note: all commands of the LB interface use MD units.\n", + "This is convenient, as e.g. a particular viscosity can be set\n", + "and the LB time step can be changed without altering the viscosity.\n", + "In LB units, both agrid and tau are unity.\n", + "On the other hand this is a source\n", "of a plethora of mistakes: the LBM is only reliable in a certain \n", "range of parameters (in LB units) and the unit conversion\n", "may take some of them far out of this range. So remember to always\n", @@ -245,9 +246,19 @@ "\n", "One brief example: a certain velocity may be 10 in MD units.\n", "If the LB time step is 0.1 in MD units, and the lattice constant\n", - "is 1, then it corresponds to a velocity of $1\\ \\frac{a}{\\tau}$ in LB units.\n", - "This is the maximum velocity of the discrete velocity set and therefore\n", - "causes numerical instabilities like negative populations." + "is 1.0 in MD units, then it corresponds to a velocity of 1 in LB units.\n", + "More specifically, given a velocity $v = 10\\ \\ell^\\star\\cdot t^{\\star-1}$ with\n", + "$\\ell^\\star$ the reduced MD length unit and $t^\\star$ the reduced MD time unit,\n", + "and LB parameters $a = 1\\ \\ell^\\star$ and $\\tau = 0.1\\ t^\\star$ in MD units,\n", + "then the corresponding velocity in LB units is given by\n", + "$u = v \\cdot \\tau / a = 10\\ \\ell^\\star\\cdot t^{\\star-1} \\cdot 0.1\\ t^\\star / 1\\ \\ell^{\\star} = 1$.\n", + "\n", + "In practice, you would like the fluid velocity to be much smaller than the fluid\n", + "speed of sound $c_s = \\frac{1}{\\sqrt{3}}\\frac{a}{\\tau}$ in MD units, aka Mach 1,\n", + "to avoid numerical instabilities due to the breakdown of the incompressibility assumption.\n", + "It is usually good practice to avoid exceeding Mach 0.2.\n", + "At Mach 0.35 the relative error in the calculated fluid density is ~6%,\n", + "which can ultimately lead to negative populations." ] }, { diff --git a/doc/tutorials/lennard_jones/lennard_jones.ipynb b/doc/tutorials/lennard_jones/lennard_jones.ipynb index 93ac166699..cb5b500574 100644 --- a/doc/tutorials/lennard_jones/lennard_jones.ipynb +++ b/doc/tutorials/lennard_jones/lennard_jones.ipynb @@ -615,7 +615,12 @@ "id": "10510b16-bc19-4c16-a881-a3b88cc298f6", "metadata": {}, "source": [ - "### System visualization " + "### System visualization\n", + "\n", + "We will use ZnDraw to visualize and interact with the simulation box.\n", + "On the server has started, a new frame will appear in the notebook with a menu bar and various buttons.\n", + "The bottom numbers represent the number of frames and the current position in the animation.\n", + "We will soon integrate the system and update the visualizer with the new system configurations." ] }, { @@ -630,7 +635,8 @@ "radii = {0: LJ_SIG/2.0} # Particle size by type\n", "\n", "# Initializing Visualizer\n", - "vis = espressomd.zn.Visualizer(system, colors=color, radii=radii)" + "vis = espressomd.zn.Visualizer(system, colors=color, radii=radii)\n", + "vis.update()" ] }, { @@ -672,7 +678,8 @@ "* Integrate the system and measure the total and kinetic energy. Take ``N_SAMPLES`` measurements every ``STEPS_PER_SAMPLE`` integration steps. Notice that the total simulated time in LJ units is given by the product N_SAMPLES $\\times$ STEPS_PER_SAMPLE $\\times$ TIME_STEP.\n", "* Calculate the total and kinetic energies using the analysis method [system.analysis.energy()](https://espressomd.github.io/doc/espressomd.html#module-espressomd.analyze).\n", "* Use the containers ``times``, ``e_total`` and ``e_kin`` from the cell above to store the time series.\n", - "* From the simulation results, calculate the kinetic temperature $T_{\\mathrm{inst}} = 2/3 \\times E_{\\mathrm{kin}}$ / N_PART." + "* From the simulation results, calculate the kinetic temperature $T_{\\mathrm{inst}} = 2/3 \\times E_{\\mathrm{kin}}$ / N_PART.\n", + "* Call `vis.update()` at the end of each loop to update the visualizer. Click inside the ZnDraw frame and press the space bar to animate the frames." ] }, { @@ -701,11 +708,11 @@ "outputs": [], "source": [ "plt.figure(figsize=(10, 6))\n", - "plt.plot(times, T_inst, label='$T_{\\\\mathrm{inst}}$')\n", - "plt.plot(times, [TEMPERATURE] * len(times), label='$T$ set by thermostat')\n", + "plt.plot(times, T_inst, label=r\"$T_{\\mathrm{inst}}$\")\n", + "plt.plot(times, [TEMPERATURE] * len(times), label=r\"$T_{\\mathrm{thermostat}}$\")\n", "plt.legend()\n", - "plt.xlabel('t')\n", - "plt.ylabel('T')\n", + "plt.xlabel(\"Simulation time\")\n", + "plt.ylabel(\"Temperature\")\n", "plt.show()" ] }, @@ -844,8 +851,8 @@ "plt.ylim(top=1.2, bottom=-0.15)\n", "plt.legend()\n", "plt.xscale('log')\n", - "plt.xlabel('t')\n", - "plt.ylabel('total energy autocorrelation')\n", + "plt.xlabel('Simulation time')\n", + "plt.ylabel('Total energy autocorrelation')\n", "plt.show()" ] }, diff --git a/src/python/espressomd/lb.py b/src/python/espressomd/lb.py index ce38519c51..e4b870a307 100644 --- a/src/python/espressomd/lb.py +++ b/src/python/espressomd/lb.py @@ -75,7 +75,7 @@ def mach_limit(self): The relative error in the fluid density between a compressible fluid and an incompressible fluid at Mach 0.30 is less than 5% (see - *constant density assumption* in :cite:`kundu01a` chapter 16, page + *constant density assumption* in :cite:`kundu02a` chapter 16, page 663). Since the speed of sound is :math:`c_s = 1 / \\sqrt{3}` in LB velocity units in a D3Q19 lattice, the velocity limit at Mach 0.30 is :math:`v_{\\mathrm{max}} = 0.30 / \\sqrt{3} \\approx 0.17`. From 386dfb4a18d6a351133f6172d9f9dceee71c2f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Tue, 15 Oct 2024 21:26:49 +0200 Subject: [PATCH 3/3] DevOps: Adapt testsuite to new hardware --- .github/workflows/push_pull.yml | 12 ++++++------ CMakeLists.txt | 7 +++++-- cmake/espresso_unit_test.cmake | 2 +- maintainer/CI/build_cmake.sh | 20 +++++--------------- maintainer/benchmarks/CMakeLists.txt | 6 +++--- samples/drude_bmimpf6.py | 4 ++-- testsuite/python/CMakeLists.txt | 2 +- testsuite/python/resources.json | 2 +- 8 files changed, 24 insertions(+), 31 deletions(-) diff --git a/.github/workflows/push_pull.yml b/.github/workflows/push_pull.yml index 3cbd6fc2d4..7e0fce60c4 100644 --- a/.github/workflows/push_pull.yml +++ b/.github/workflows/push_pull.yml @@ -39,7 +39,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.github_token }} - options: --cpus 2 + options: --cpus 4 steps: - name: Checkout uses: actions/checkout@main @@ -55,8 +55,8 @@ jobs: - name: Build and check uses: ./.github/actions/build_and_check env: - build_procs: 2 - check_procs: 2 + build_procs: 4 + check_procs: 4 myconfig: 'maxset' with_ccache: 'true' with_cuda: 'false' @@ -78,7 +78,7 @@ jobs: credentials: username: ${{ github.actor }} password: ${{ secrets.github_token }} - options: --cpus 2 + options: --cpus 4 steps: - name: Checkout uses: actions/checkout@main @@ -94,8 +94,8 @@ jobs: - name: Build and check uses: ./.github/actions/build_and_check env: - build_procs: 2 - check_procs: 2 + build_procs: 4 + check_procs: 4 myconfig: 'maxset' with_ccache: 'true' with_cuda: 'false' diff --git a/CMakeLists.txt b/CMakeLists.txt index 06e29ace6f..d374aaa4d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -372,13 +372,16 @@ else() endif() # OpenMPI checks the number of processes against the number of CPUs -set(ESPRESSO_MPIEXEC_OVERSUBSCRIBE "") +set(ESPRESSO_MPIEXEC_PREFLAGS "") # Open MPI 4.x has a bug on NUMA archs that prevents running in singleton mode set(ESPRESSO_MPIEXEC_GUARD_SINGLETON_NUMA OFF) set(ESPRESSO_CPU_MODEL_NAME_OMPI_SINGLETON_NUMA_PATTERN "AMD (EPYC|Ryzen)") if("${ESPRESSO_MPIEXEC_VENDOR}" STREQUAL "OpenMPI") - set(ESPRESSO_MPIEXEC_OVERSUBSCRIBE "-oversubscribe") + list(APPEND ESPRESSO_MPIEXEC_PREFLAGS "--oversubscribe") + if(ESPRESSO_INSIDE_DOCKER) + list(APPEND ESPRESSO_MPIEXEC_PREFLAGS "--bind-to" "none") + endif() if(${ESPRESSO_MPIEXEC_VERSION} VERSION_LESS 5.0) if(NOT DEFINED ESPRESSO_CPU_MODEL_NAME) if(CMAKE_SYSTEM_NAME STREQUAL Linux) diff --git a/cmake/espresso_unit_test.cmake b/cmake/espresso_unit_test.cmake index c857d9bed1..daa07218b0 100644 --- a/cmake/espresso_unit_test.cmake +++ b/cmake/espresso_unit_test.cmake @@ -52,7 +52,7 @@ function(ESPRESSO_UNIT_TEST) set(TEST_NUM_PROC ${ESPRESSO_TEST_NP}) endif() espresso_set_mpiexec_tmpdir(${TEST_NAME}) - add_test(${TEST_NAME} ${MPIEXEC} ${ESPRESSO_MPIEXEC_OVERSUBSCRIBE} + add_test(${TEST_NAME} ${MPIEXEC} ${ESPRESSO_MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${TEST_NUM_PROC} ${MPIEXEC_PREFLAGS} ${ESPRESSO_MPIEXEC_TMPDIR} ${CMAKE_CURRENT_BINARY_DIR}/${TEST_NAME} ${MPIEXEC_POSTFLAGS}) diff --git a/maintainer/CI/build_cmake.sh b/maintainer/CI/build_cmake.sh index 4d4ce68d15..b155ed8c62 100755 --- a/maintainer/CI/build_cmake.sh +++ b/maintainer/CI/build_cmake.sh @@ -79,20 +79,14 @@ ci_procs=2 if [ "${GITLAB_CI}" = "true" ]; then if [[ "${OSTYPE}" == "linux-gnu"* ]]; then # Linux runner - if grep -q "i7-3820" /proc/cpuinfo; then - # communication bottleneck for more than 2 cores on Intel i7-3820 - ci_procs=2 - else - ci_procs=4 - fi + ci_procs=4 elif [[ "${OSTYPE}" == "darwin"* ]]; then # macOS runner - ci_procs=2 + ci_procs=4 fi elif [ "${GITHUB_ACTIONS}" = "true" ]; then - # GitHub Actions only provide 1 core; request 2 cores to run tests - # in parallel (OpenMPI allows oversubscription) - ci_procs=2 + # GitHub Actions provide 4 cores + ci_procs=4 else ci_procs=$(nproc) fi @@ -131,7 +125,6 @@ set_default_value with_walberla_avx false set_default_value with_stokesian_dynamics false set_default_value test_timeout 500 set_default_value hide_gpu false -set_default_value mpiexec_preflags "" if [ "${make_check_unit_tests}" = true ] || [ "${make_check_python}" = true ] || [ "${make_check_tutorials}" = true ] || [ "${make_check_samples}" = true ] || [ "${make_check_benchmarks}" = true ]; then run_checks=true @@ -166,9 +159,6 @@ if [ "${with_walberla}" = true ]; then if [ "${with_walberla_avx}" = true ]; then cmake_params="${cmake_params} -D ESPRESSO_BUILD_WITH_WALBERLA_AVX=ON" fi - # disable default OpenMPI CPU binding mechanism to avoid stale references to - # waLBerla objects when multiple LB python tests run in parallel on NUMA archs - mpiexec_preflags="${mpiexec_preflags:+$mpiexec_preflags;}--bind-to;none" fi cmake_params="${cmake_params} -D ESPRESSO_BUILD_WITH_COVERAGE=${with_coverage}" @@ -328,7 +318,7 @@ else if [ "${check_proc_particle_test}" -gt 4 ]; then check_proc_particle_test=4 fi - mpiexec -n ${check_proc_particle_test} ./pypresso "${srcdir}/testsuite/python/particle.py" || exit 1 + mpiexec -n ${check_proc_particle_test} $(mpiexec --version | grep -Pq "\\(Open(RTE| MPI)\\)" && echo "--oversubscribe --bind-to none") ./pypresso "${srcdir}/testsuite/python/particle.py" || exit 1 end "TEST" fi diff --git a/maintainer/benchmarks/CMakeLists.txt b/maintainer/benchmarks/CMakeLists.txt index ea6c0c5f48..63c04f57fb 100644 --- a/maintainer/benchmarks/CMakeLists.txt +++ b/maintainer/benchmarks/CMakeLists.txt @@ -28,9 +28,9 @@ if(EXISTS ${MPIEXEC}) OUTPUT_VARIABLE mpi_version_output ERROR_VARIABLE mpi_version_output) if(mpi_version_result EQUAL 0 AND mpi_version_output MATCHES "\\(Open(RTE| MPI)\\) ([3-9]\\.|1[0-9])") - set(ESPRESSO_MPIEXEC_OVERSUBSCRIBE "-oversubscribe") + set(ESPRESSO_MPIEXEC_PREFLAGS "--oversubscribe") else() - set(ESPRESSO_MPIEXEC_OVERSUBSCRIBE "") + set(ESPRESSO_MPIEXEC_PREFLAGS "") endif() endif() @@ -86,7 +86,7 @@ function(PYTHON_BENCHMARK) add_test( NAME ${BENCHMARK_TEST_NAME} COMMAND - ${MPIEXEC} ${ESPRESSO_MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} + ${MPIEXEC} ${ESPRESSO_MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${BENCHMARK_NUM_PROC} ${MPIEXEC_PREFLAGS} ${CMAKE_BINARY_DIR}/pypresso ${BENCHMARK_FILE} ${BENCHMARK_ARGUMENTS} ${MPIEXEC_POSTFLAGS}) diff --git a/samples/drude_bmimpf6.py b/samples/drude_bmimpf6.py index fa07f15643..86c6c5215f 100644 --- a/samples/drude_bmimpf6.py +++ b/samples/drude_bmimpf6.py @@ -107,7 +107,7 @@ # TIMESTEP fs_to_md_time = 1.0e-2 -time_step_fs = 1.0 +time_step_fs = 0.5 time_step_ns = time_step_fs * 1e-6 dt = time_step_fs * fs_to_md_time system.time_step = dt @@ -229,7 +229,7 @@ def combination_rule_sigma(rule, sig1, sig2): # ENERGY MINIMIZATION print("\n-->E minimization") print(f"Before: {system.analysis.energy()['total']:.2e}") -n_max_steps = 100000 +n_max_steps = 10000 system.integrator.set_steepest_descent(f_max=5.0, gamma=0.01, max_displacement=0.01) system.integrator.run(n_max_steps) diff --git a/testsuite/python/CMakeLists.txt b/testsuite/python/CMakeLists.txt index 2fa3fa7977..ee90256b07 100644 --- a/testsuite/python/CMakeLists.txt +++ b/testsuite/python/CMakeLists.txt @@ -92,7 +92,7 @@ function(python_test) add_test( NAME ${TEST_NAME} COMMAND - ${MPIEXEC} ${ESPRESSO_MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} + ${MPIEXEC} ${ESPRESSO_MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} ${TEST_NUM_PROC} ${MPIEXEC_PREFLAGS} ${ESPRESSO_MPIEXEC_TMPDIR} ${CMAKE_BINARY_DIR}/pypresso ${PYPRESSO_OPTIONS} ${TEST_FILE_CONFIGURED} ${TEST_ARGUMENTS} ${MPIEXEC_POSTFLAGS}) diff --git a/testsuite/python/resources.json b/testsuite/python/resources.json index 906149627d..db5acbeff3 100644 --- a/testsuite/python/resources.json +++ b/testsuite/python/resources.json @@ -8,7 +8,7 @@ "gpus": [ { "id": "0", - "slots": 4 + "slots": 8 } ] }