diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index ab6f120..7d92881 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -1,4 +1,4 @@
-name: Build and test pymm
+name: Run pymm
on:
push:
@@ -43,9 +43,9 @@ jobs:
- name: Check code style and linting
run: |
- black --check src/ tests/ setup.py
- pylint src/ tests/ setup.py
- mypy --ignore-missing-imports src/ tests/ setup.py
+ black --check src/ tests/
+ pylint src/ tests/
+ mypy --ignore-missing-imports src/ tests/
- name: Run the tests
run: |
diff --git a/.gitignore b/.gitignore
index 08a415d..4c7cc14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -136,5 +136,6 @@ vpymm
cssr
debugging
developing
+playgroud
tests/configs/files
tests/configs/output
\ No newline at end of file
diff --git a/README.md b/README.md
index ae5b93b..c5b7241 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
[![Build Status](https://github.com/cssr-tools/pymm/actions/workflows/CI.yml/badge.svg)](https://github.com/cssr-tools/pymm/actions/workflows/CI.yml)
-
+
[![Code style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8430989.svg)](https://doi.org/10.5281/zenodo.8430989)
@@ -12,10 +12,17 @@ This repository provides a workflow to perform computational fluid dynamics (CFD
## Installation
You will first need to install
-* OpenFOAM (https://www.openfoam.com) (tested with OpenFOAM-11)
-* Gmsh (https://gmsh.info) (tested with Gmsh 4.8.4)
+* OpenFOAM (https://www.openfoam.com) (tested with OpenFOAM-12)
+* Gmsh (https://gmsh.info) (tested with Gmsh 4.13.1)
+
+To install the _pymm_ executable in an existing Python environment:
+
+```bash
+pip install git+https://github.com/cssr-tools/pymm.git
+```
+
+If you are interested in modifying the source code, then you can clone the repository and install the Python requirements in a virtual environment with the following commands:
-You will also need to install some Python packages, see ```requirements.txt``` for a complete list. You can install all the required Python packages in a virtual environment with the following commands:
```bash
# Clone the repo
git clone https://github.com/cssr-tools/pymm.git
@@ -27,21 +34,13 @@ python3 -m venv vpymm
source vpymm/bin/activate
# Upgrade pip, setuptools, and wheel
pip install --upgrade pip setuptools wheel
-# Install the pymm package (in editable mode for contributions/modifications; otherwise, pip install .)
+# Install the pymm package
pip install -e .
# For contributions/testing/linting, install the dev-requirements
pip install -r dev-requirements.txt
```
-Depending on the location where OpenFOAM is installed, then before running pymm (inside the vpymm Python environment), you need to enter the OpenFOAM environment:
-```bash
-# Check where openFOAM is installed
-echo $WM_PROJECT_DIR
-# The return value was /opt/openfoam11, then we activate the environment
-source /opt/openfoam11/etc/bashrc
-# Then, if everything went fine, typing
-gmshToFoam
-# should print the argument flags for that OpenFOAM executable.
-```
+
+See the [_OpenFOAM page_](https://openfoam.org/download/12-ubuntu/), where from OpenFOAM-12 the simulator is available via apt get.
## Running pymm
You can run _pymm_ as a single command line:
@@ -51,7 +50,7 @@ pymm -i some_input_image.png -p some_input_parameters.txt -o some_output_folder
Run `pymm --help` to see all possible command line argument options. Inside the `some_input_parameters.txt` file you provide the framework parameters such as the dimensions of the microsystem, mesh size, inlet pressure, and more. See the .txt files in the examples folder.
## Getting started
-See the [_documentation_](https://cssr-tools.github.io/pymm/introduction.html).
+See the [_examples_](https://cssr-tools.github.io/pymm/examples.html) in the [_documentation_](https://cssr-tools.github.io/pymm/introduction.html).
## Journal papers using pymm
The following is a list of journal papers in which _pymm_ is used:
@@ -61,5 +60,4 @@ The following is a list of journal papers in which _pymm_ is used:
## About pymm
The image-based Python package for computational fluid dynamics pymm is funded by [_Center for Sustainable Subsurface Resources_](https://cssr.no) [project no. 331841] and [_NORCE Norwegian Research Centre As_](https://www.norceresearch.no) [project number 101070].
-
Contributions are more than welcome using the fork and pull request approach.
\ No newline at end of file
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 7ba71be..e6a74d0 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,6 +1,7 @@
black
-pylint
+numpydoc
mypy
+pylint
pytest-cov
sphinx
sphinx-rtd-theme
\ No newline at end of file
diff --git a/docs/_images/plopm.png b/docs/_images/plopm.png
new file mode 100644
index 0000000..b7b084f
Binary files /dev/null and b/docs/_images/plopm.png differ
diff --git a/docs/_images/pycopm.gif b/docs/_images/pycopm.gif
new file mode 100644
index 0000000..40d2df9
Binary files /dev/null and b/docs/_images/pycopm.gif differ
diff --git a/docs/_images/pyopmspe11.gif b/docs/_images/pyopmspe11.gif
index 6e197bb..e0a88a5 100644
Binary files a/docs/_images/pyopmspe11.gif and b/docs/_images/pyopmspe11.gif differ
diff --git a/docs/_sources/introduction.rst.txt b/docs/_sources/introduction.rst.txt
index 69fd42b..92dec1b 100644
--- a/docs/_sources/introduction.rst.txt
+++ b/docs/_sources/introduction.rst.txt
@@ -34,4 +34,8 @@ where
Installation
------------
-See the `Github page `_.
\ No newline at end of file
+See the `Github page `_.
+
+.. tip::
+
+ See the `CI.yml `_.
\ No newline at end of file
diff --git a/docs/_sources/related.rst.txt b/docs/_sources/related.rst.txt
index c7f2189..f01a7c0 100644
--- a/docs/_sources/related.rst.txt
+++ b/docs/_sources/related.rst.txt
@@ -2,8 +2,7 @@
Related
=======
-Below are some packages following the same configuration-file-to-postprocessing-folder approach;
-check them out :).
+Below are some tools that might be of interest; check them out 🙂.
**********
pyopmspe11
@@ -23,14 +22,22 @@ pyopmnearwell
`A framework to simulate near well dynamics using OPM Flow `_.
-*******
-ad-micp
-*******
+******
+pycopm
+******
-.. image:: ./figs/ad-micp.gif
- :scale: 40%
+.. image:: ./figs/pycopm.gif
+ :scale: 60%
+
+`Simplified and flexible framework for coarsening geological models `_.
+
+*****
+plopm
+*****
+
+.. image:: ./figs/plopm.png
-`A module to study CO2 leakage remediation by microbially induced calcite precipitation (MICP) `_.
+`Quick generation of PNG figures from a simulation model given any 2D slide `_.
********
expreccs
@@ -39,4 +46,13 @@ expreccs
.. image:: ./figs/expreccs.gif
:scale: 50%
-`Expansion of ResourCes for CO2 Storage on the Horda Platform `_.
\ No newline at end of file
+`Expansion of ResourCes for CO2 Storage on the Horda Platform `_.
+
+*******
+ad-micp
+*******
+
+.. image:: ./figs/ad-micp.gif
+ :scale: 40%
+
+`A module to study CO2 leakage remediation by microbially induced calcite precipitation (MICP) `_.
\ No newline at end of file
diff --git a/docs/_static/basic.css b/docs/_static/basic.css
index 30fee9d..f316efc 100644
--- a/docs/_static/basic.css
+++ b/docs/_static/basic.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- basic theme.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/docs/_static/doctools.js b/docs/_static/doctools.js
index d06a71d..4d67807 100644
--- a/docs/_static/doctools.js
+++ b/docs/_static/doctools.js
@@ -4,7 +4,7 @@
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
diff --git a/docs/_static/language_data.js b/docs/_static/language_data.js
index 250f566..367b8ed 100644
--- a/docs/_static/language_data.js
+++ b/docs/_static/language_data.js
@@ -5,7 +5,7 @@
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -13,7 +13,7 @@
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
-/* Non-minified version is copied as a separate JS file, is available */
+/* Non-minified version is copied as a separate JS file, if available */
/**
* Porter Stemmer
diff --git a/docs/_static/searchtools.js b/docs/_static/searchtools.js
index 7918c3f..b08d58c 100644
--- a/docs/_static/searchtools.js
+++ b/docs/_static/searchtools.js
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilities for the full-text search.
*
- * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -99,7 +99,7 @@ const _displayItem = (item, searchTerms, highlightTerms) => {
.then((data) => {
if (data)
listItem.appendChild(
- Search.makeSearchSummary(data, searchTerms)
+ Search.makeSearchSummary(data, searchTerms, anchor)
);
// highlight search terms in the summary
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
@@ -116,8 +116,8 @@ const _finishSearch = (resultCount) => {
);
else
Search.status.innerText = _(
- `Search finished, found ${resultCount} page(s) matching the search query.`
- );
+ "Search finished, found ${resultCount} page(s) matching the search query."
+ ).replace('${resultCount}', resultCount);
};
const _displayNextItem = (
results,
@@ -137,6 +137,22 @@ const _displayNextItem = (
// search finished, update title and status message
else _finishSearch(resultCount);
};
+// Helper function used by query() to order search results.
+// Each input is an array of [docname, title, anchor, descr, score, filename].
+// Order the results by score (in opposite order of appearance, since the
+// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
+const _orderResultsByScoreThenName = (a, b) => {
+ const leftScore = a[4];
+ const rightScore = b[4];
+ if (leftScore === rightScore) {
+ // same score: sort alphabetically
+ const leftTitle = a[1].toLowerCase();
+ const rightTitle = b[1].toLowerCase();
+ if (leftTitle === rightTitle) return 0;
+ return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
+ }
+ return leftScore > rightScore ? 1 : -1;
+};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
@@ -160,13 +176,26 @@ const Search = {
_queued_query: null,
_pulse_status: -1,
- htmlToText: (htmlString) => {
+ htmlToText: (htmlString, anchor) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
- htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() });
+ for (const removalQuery of [".headerlink", "script", "style"]) {
+ htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
+ }
+ if (anchor) {
+ const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`);
+ if (anchorContent) return anchorContent.textContent;
+
+ console.warn(
+ `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`
+ );
+ }
+
+ // if anchor not specified or not found, fall back to main content
const docContent = htmlElement.querySelector('[role="main"]');
- if (docContent !== undefined) return docContent.textContent;
+ if (docContent) return docContent.textContent;
+
console.warn(
- "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
+ "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template."
);
return "";
},
@@ -239,16 +268,7 @@ const Search = {
else Search.deferQuery(query);
},
- /**
- * execute search (requires search index to be loaded)
- */
- query: (query) => {
- const filenames = Search._index.filenames;
- const docNames = Search._index.docnames;
- const titles = Search._index.titles;
- const allTitles = Search._index.alltitles;
- const indexEntries = Search._index.indexentries;
-
+ _parseQuery: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
@@ -284,21 +304,38 @@ const Search = {
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
- // array of [docname, title, anchor, descr, score, filename]
- let results = [];
+ return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
+ },
+
+ /**
+ * execute search (requires search index to be loaded)
+ */
+ _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => {
+ const filenames = Search._index.filenames;
+ const docNames = Search._index.docnames;
+ const titles = Search._index.titles;
+ const allTitles = Search._index.alltitles;
+ const indexEntries = Search._index.indexentries;
+
+ // Collect multiple result groups to be sorted separately and then ordered.
+ // Each is an array of [docname, title, anchor, descr, score, filename].
+ const normalResults = [];
+ const nonMainIndexResults = [];
+
_removeChildren(document.getElementById("search-progress"));
- const queryLower = query.toLowerCase();
+ const queryLower = query.toLowerCase().trim();
for (const [title, foundTitles] of Object.entries(allTitles)) {
- if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
+ if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
- let score = Math.round(100 * queryLower.length / title.length)
- results.push([
+ const score = Math.round(Scorer.title * queryLower.length / title.length);
+ const boost = titles[file] === title ? 1 : 0; // add a boost for document titles
+ normalResults.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
- score,
+ score + boost,
filenames[file],
]);
}
@@ -308,46 +345,47 @@ const Search = {
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
- for (const [file, id] of foundEntries) {
- let score = Math.round(100 * queryLower.length / entry.length)
- results.push([
+ for (const [file, id, isMain] of foundEntries) {
+ const score = Math.round(100 * queryLower.length / entry.length);
+ const result = [
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
- ]);
+ ];
+ if (isMain) {
+ normalResults.push(result);
+ } else {
+ nonMainIndexResults.push(result);
+ }
}
}
}
// lookup as object
objectTerms.forEach((term) =>
- results.push(...Search.performObjectSearch(term, objectTerms))
+ normalResults.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
- results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
+ normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
- if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
-
- // now sort the results by score (in opposite order of appearance, since the
- // display function below uses pop() to retrieve items) and then
- // alphabetically
- results.sort((a, b) => {
- const leftScore = a[4];
- const rightScore = b[4];
- if (leftScore === rightScore) {
- // same score: sort alphabetically
- const leftTitle = a[1].toLowerCase();
- const rightTitle = b[1].toLowerCase();
- if (leftTitle === rightTitle) return 0;
- return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
- }
- return leftScore > rightScore ? 1 : -1;
- });
+ if (Scorer.score) {
+ normalResults.forEach((item) => (item[4] = Scorer.score(item)));
+ nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
+ }
+
+ // Sort each group of results by score and then alphabetically by name.
+ normalResults.sort(_orderResultsByScoreThenName);
+ nonMainIndexResults.sort(_orderResultsByScoreThenName);
+
+ // Combine the result groups in (reverse) order.
+ // Non-main index entries are typically arbitrary cross-references,
+ // so display them after other results.
+ let results = [...nonMainIndexResults, ...normalResults];
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
@@ -361,7 +399,12 @@ const Search = {
return acc;
}, []);
- results = results.reverse();
+ return results.reverse();
+ },
+
+ query: (query) => {
+ const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query);
+ const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms);
// for debugging
//Search.lastresults = results.slice(); // a copy
@@ -466,14 +509,18 @@ const Search = {
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
- Object.keys(terms).forEach((term) => {
- if (term.match(escapedWord) && !terms[word])
- arr.push({ files: terms[term], score: Scorer.partialTerm });
- });
- Object.keys(titleTerms).forEach((term) => {
- if (term.match(escapedWord) && !titleTerms[word])
- arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
- });
+ if (!terms.hasOwnProperty(word)) {
+ Object.keys(terms).forEach((term) => {
+ if (term.match(escapedWord))
+ arr.push({ files: terms[term], score: Scorer.partialTerm });
+ });
+ }
+ if (!titleTerms.hasOwnProperty(word)) {
+ Object.keys(titleTerms).forEach((term) => {
+ if (term.match(escapedWord))
+ arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
+ });
+ }
}
// no match but word was a required one
@@ -496,9 +543,8 @@ const Search = {
// create the mapping
files.forEach((file) => {
- if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
- fileMap.get(file).push(word);
- else fileMap.set(file, [word]);
+ if (!fileMap.has(file)) fileMap.set(file, [word]);
+ else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word);
});
});
@@ -549,8 +595,8 @@ const Search = {
* search summary for a given text. keywords is a list
* of stemmed words.
*/
- makeSearchSummary: (htmlText, keywords) => {
- const text = Search.htmlToText(htmlText);
+ makeSearchSummary: (htmlText, keywords, anchor) => {
+ const text = Search.htmlToText(htmlText, anchor);
if (text === "") return null;
const textLower = text.toLowerCase();
diff --git a/docs/about.html b/docs/about.html
index a87f372..605e07e 100644
--- a/docs/about.html
+++ b/docs/about.html
@@ -16,7 +16,7 @@
-
+
diff --git a/docs/api.html b/docs/api.html
index 7ea315f..2d95d24 100644
--- a/docs/api.html
+++ b/docs/api.html
@@ -16,7 +16,7 @@
-
+
diff --git a/docs/configuration_file.html b/docs/configuration_file.html
index 696307a..92dad84 100644
--- a/docs/configuration_file.html
+++ b/docs/configuration_file.html
@@ -16,7 +16,7 @@
-
+
diff --git a/docs/device.html b/docs/device.html
index 3d38d95..066b7c6 100644
--- a/docs/device.html
+++ b/docs/device.html
@@ -16,7 +16,7 @@
-
+
diff --git a/docs/examples.html b/docs/examples.html
index 7d7107d..9d13a53 100644
--- a/docs/examples.html
+++ b/docs/examples.html
@@ -16,7 +16,7 @@
-
+
diff --git a/docs/genindex.html b/docs/genindex.html
index 7b920e5..5bc8fe0 100644
--- a/docs/genindex.html
+++ b/docs/genindex.html
@@ -15,7 +15,7 @@
-
+
diff --git a/docs/image.html b/docs/image.html
index 1196f9f..f901be3 100644
--- a/docs/image.html
+++ b/docs/image.html
@@ -16,7 +16,7 @@
-
+
diff --git a/docs/index.html b/docs/index.html
index 0415263..7650da6 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -16,7 +16,7 @@
-
+
@@ -116,8 +116,10 @@