Skip to content

Commit

Permalink
Add structured model result analysis (#31)
Browse files Browse the repository at this point in the history
This PR adds structured mesh model results analysis using the `result.Result` class, which now automatically detects if the project folder contains flexible or structured mesh results files. A clone of the basic example for structured meshes is also added  (`basic_structured.py`) to show a full structured mesh model analysis. The version number is bumped to 0.5.0 to publish all the structured mesh features added thus far.

Other changes include:
* The `relative_map_parts` argument to the `result.Result` class has been removed, as the path to the result netCDF files are now automatically resolved
* Added the `result.faces._trim_to_faces_frame` function to convert structured mesh netCDF result files into the faces dataframe.
* The `Result.edges` attribute is now optional, being populated only for flexible mesh models
* Model-file locating algorithms are now shared between the `Runner` and `Result` classes using the `_paths` module
* `result.base.TimeStepResolver` is now private, i.e. `_TimeStepResolver`
* The `result.faces._extract` decorator is now type checked
* Base classes are no longer included in the API documentation but inherited members are
  • Loading branch information
H0R5E authored Feb 22, 2022
1 parent 0def267 commit ee70c26
Show file tree
Hide file tree
Showing 27 changed files with 1,393 additions and 390 deletions.
2 changes: 1 addition & 1 deletion .conda/recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% set version = "0.4.3" %}
{% set version = "0.5.0" %}

package:
name: snl-delft3d-cec-verify
Expand Down
78 changes: 52 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
# SNL-Delft3D-CEC-Verify

SNL-Delft3D-CEC-Verify is a python package for automated testing of
[SNL-Delft3D-FM-CEC][101] which adds current energy converter (CEC) support to
the [Delft3D Flexible Mesh Suite][102]. This package is used to verify the
performance of SNL-Delft3D-FM-CEC by comparing against the 2014 flume
experiment conducted by Mycek et al.[[1]](#1).
[SNL-Delft3D-CEC][116] and [SNL-Delft3D-FM-CEC][101] which adds current energy
converter (CEC) support to the [Delft3D 4 (structured)][117] and [Delft3D
Flexible Mesh][102] suites, respectively. This package is used to verify the
performance of SNL-Delft3D-CEC and SNL-Delft3D-FM-CEC by comparing against the
2014 flume experiment conducted by Mycek et al.[[1]](#1).

## Quick Start

Expand All @@ -31,7 +32,7 @@ From a conda prompt create a named environment in which to install the
for future updates:

```
(base) > conda create -y -n snld3d --override-channels -c conda-forge -c dataonlygreater snl-delft3d-cec-verify=0.4.3
(base) > conda create -y -n snld3d --override-channels -c conda-forge -c dataonlygreater snl-delft3d-cec-verify=0.5.0
(base) > conda activate snld3d
(snld3d) > conda config --env --add channels conda-forge --add channels dataonlygreater
(snld3d) > conda config --env --set channel_priority strict
Expand All @@ -56,10 +57,10 @@ environment, type:

### Minimal Working Example

The following presents an example of running a case study, based on the Mycek
experiment, and collecting results at the turbine centre. Note that the token
`<D3D_BIN>`, should be replaced with the path to the `bin` directory of the
compiled Delft3D source code.
The following presents an example of running a case study using a flexible mesh
(`"fm"`) model, based on the Mycek experiment, and collecting results at the
turbine centre. Note that the token `<D3D_BIN>`, should be replaced with the
path to the `bin` directory of the compiled Delft3D source code.

```pycon
>>> import tempfile
Expand Down Expand Up @@ -87,6 +88,12 @@ Data variables:

```

To use a structured model in the above example, change line 3 to:

```
>>> template = Template("structured")
```

More detailed examples are provided in the section below.

## Examples
Expand All @@ -109,16 +116,16 @@ installed. To install it, type:
(snld3d) > conda install -y pypandoc pandoc=2.12
```

Currently, a compiled copy of SNL-Delft3D-FM-CEC must be available for the
examples to run. If the binaries are installed in the standard location in the
Delft3D source code (i.e. in the `src/bin` folder), simply copy the required
files for each example to the source code's `examples` directory. A list of
files required to run each example is provided at the top of the subsections
below.
Currently, a compiled copy of SNL-Delft3D-CEC or SNL-Delft3D-FM-CEC must be
available for the examples to run. If the binaries are installed in the
standard location in the Delft3D source code (i.e. in the `src/bin` folder),
simply copy the required files for each example to the source code's
`examples` directory. A list of files required to run each example is provided
at the top of the subsections below.

Alternatively, the location of SNL-Delft3D-FM-CEC binaries can specified by
setting the `D3D_BIN` environment variable, instead of copying the example
files. To set `D3D_BIN`, for example, using PowerShell:
Alternatively, the location of Delft3D binaries can specified by setting the
`D3D_BIN` environment variable, instead of copying the example files. To set
`D3D_BIN`, for example, using PowerShell:

```
(snld3d) > $env:D3D_BIN = "\path\to\SNL-Delft3D-FM-CEC\src\bin"
Expand All @@ -130,8 +137,8 @@ Required files:
+ `basic.py`
+ `reference.docx` (for conversion to Word format)

The basic example shows how to define a set of simulations with varying
parameters, run the simulations and then analyse the results.
The basic example shows how to define a set of flexible mesh models with
varying parameters, run the models and then analyse the results.

To run the example, move to the directory containing `basic.py` and then
call Python:
Expand All @@ -143,15 +150,32 @@ call Python:
If successful, the report files (and images) will be placed in a sub-directory
called `basic_report`.

### Basic Example (Structured)

Required files:
+ `basic_structured.py`
+ `reference.docx` (for conversion to Word format)

This example is identical to the [Basic Example](#basic-example), except using
a structured mesh rather than flexible. To run the example, move to the
directory containing `basic_structured.py` and then call Python:

```
(snld3d) > python basic_structured.py
```

If successful, the report files (and images) will be placed in a sub-directory
called `basic_structured_report`.

### Validation Example

Required files:
+ `validation.py`
+ `validation.bib` (for conversion to Word format)
+ `reference.docx` (for conversion to Word format)

The validation example demonstrates comparison of Delft3D with the experimental
results of Mycek et al.[[1]](#1)
The validation example demonstrates comparison of a flexible mesh Delft3D model
with the experimental results of Mycek et al.[[1]](#1)

To run the example, move to the directory containing `validation.py` and then
call Python:
Expand All @@ -170,10 +194,10 @@ Required files:
+ `validation.bib` (for conversion to Word format)
+ `reference.docx` (for conversion to Word format)

This is the first "production" example, designed to generate meaningful results.
A grid convergence study (see e.g. [[2]](#2)) is conducted for the turbine
simulation to determine the free stream and wake velocities at infinite grid
resolution. The results are then compared to the results of
This is the first "production" example, designed to generate meaningful
results. A grid convergence study (see e.g. [[2]](#2)) is conducted for a
flexible mesh model to determine the free stream and turbine wake velocities
at infinite grid resolution. The results are then compared to the results of
Mycek et al.[[1]](#1).

Note that this study takes a considerable amount of wall-clock time to
Expand Down Expand Up @@ -358,3 +382,5 @@ Retrieved 24 January 2022, from https://www.grc.nasa.gov/www/wind/valid/tutorial
[113]: https://insipid-sphinx-theme.readthedocs.io/
[114]: https://github.com/conda-forge/miniforge
[115]: https://python-semantic-release.readthedocs.io/en/latest/
[116]: https://github.com/SNL-WaterPower/SNL-Delft3D-CEC
[117]: https://www.deltares.nl/en/software/delft3d-4-suite/
4 changes: 2 additions & 2 deletions docs/_assets/gh-pages-redirect.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<title>Redirecting to latest version</title>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=./v0.4.3/index.html">
<link rel="canonical" href="https://data-only-greater.github.io/SNL-Delft3D-CEC-Verify/v0.4.3/index.html">
<meta http-equiv="refresh" content="0; url=./v0.5.0/index.html">
<link rel="canonical" href="https://data-only-greater.github.io/SNL-Delft3D-CEC-Verify/v0.5.0/index.html">
</head>
</html>
2 changes: 1 addition & 1 deletion docs/api/snl_d3d_cec_verify.copier.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ snl\_d3d\_cec\_verify.copier package
.. automodule:: snl_d3d_cec_verify.copier
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
2 changes: 1 addition & 1 deletion docs/api/snl_d3d_cec_verify.grid.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ snl\_d3d\_cec\_verify.grid package
.. automodule:: snl_d3d_cec_verify.grid
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
2 changes: 1 addition & 1 deletion docs/api/snl_d3d_cec_verify.report.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ snl\_d3d\_cec\_verify.report package
.. automodule:: snl_d3d_cec_verify.report
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
2 changes: 1 addition & 1 deletion docs/api/snl_d3d_cec_verify.result.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ snl\_d3d\_cec\_verify.result package
.. automodule:: snl_d3d_cec_verify.result
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
2 changes: 1 addition & 1 deletion docs/api/snl_d3d_cec_verify.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ snl\_d3d\_cec\_verify package
.. automodule:: snl_d3d_cec_verify
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
2 changes: 1 addition & 1 deletion docs/api/snl_d3d_cec_verify.runner.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ snl\_d3d\_cec\_verify.runner package
.. automodule:: snl_d3d_cec_verify.runner
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
2 changes: 1 addition & 1 deletion docs/api/snl_d3d_cec_verify.text.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ snl\_d3d\_cec\_verify.text package
.. automodule:: snl_d3d_cec_verify.text
:members:
:undoc-members:
:show-inheritance:
:inherited-members:
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
author = 'Mathew Topper'

# The full version, including alpha/beta/rc tags
release = '0.4.3'
release = '0.5.0'


# -- General configuration ---------------------------------------------------
Expand Down Expand Up @@ -57,7 +57,7 @@
smv_remote_whitelist = r'^(origin)$'
smv_tag_whitelist = r'^v(\d+\.\d+\.\d+)$' # r'^v(?!0.4.0|0.4.1|0.4.2)\d+\.\d+\.\d+$'
smv_released_pattern = r'^refs/tags/.*$'
smv_latest_version = 'v0.4.3'
smv_latest_version = 'v0.5.0'

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
Expand Down
93 changes: 93 additions & 0 deletions examples/basic_structured.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-

import os
import tempfile
from pathlib import Path
from collections import defaultdict

import pandas as pd
import matplotlib.pyplot as plt

from snl_d3d_cec_verify import CaseStudy, Report, Result, Runner, Template

def get_d3d_bin_path():

env = dict(os.environ)

if 'D3D_BIN' in env:
root = Path(env['D3D_BIN'].replace('"', ''))
print('D3D_BIN found')
else:
root = Path("..") / "src" / "bin"
print('D3D_BIN not found')

print(f'Setting bin folder path to {root.resolve()}')

return root.resolve()

template = Template("structured")
runner = Runner(get_d3d_bin_path())
report = Report(79, "%d %B %Y")
report_dir = Path("basic_structured_report")
report_dir.mkdir(exist_ok=True)
data = defaultdict(list)

cases = CaseStudy(discharge=[4, 5, 6, 7, 8])

for i, case in enumerate(cases):

with tempfile.TemporaryDirectory() as tmpdirname:

# Create the project and then run it
template(case, tmpdirname)
runner(tmpdirname)

# Pick up the results
result = Result(tmpdirname)
turb_ds = result.faces.extract_turbine_centre(-1, case)
turb_u = turb_ds["$u$"].values.take(0)

# Record data for table
data["discharge"].append(case.discharge)
data["u"].append(turb_u)

# Add report section with plot
report.content.add_heading(
f"Discharge: {case.discharge} (cubic meters per second)",
level=2)

fig, ax = plt.subplots()
turbz = result.faces.extract_turbine_z(-1, case)
turbz["$u$"].plot(ax=ax, x="$x$", y="$y$")
plot_name = f"discharge_case_{i}.png"
plot_path = report_dir / plot_name
plt.savefig(plot_path)

report.content.add_image(plot_name, "u-velocity (m/s)")

df = pd.DataFrame(data)
report.content.add_heading("Results", level=2)
report.content.add_table(df,
index=False,
caption="Turbine centre velocity per discharge level")

report.title = "Basic Example (Structured Grid)"
report.date = "today"

with open(report_dir / "report.md", "wt") as f:
for line in report:
f.write(line)

try:

import pypandoc

pypandoc.convert_file(f"{report_dir / 'report.md'}",
'docx',
outputfile=f"{report_dir / 'report.docx'}",
extra_args=[f'--resource-path={report_dir}',
'--reference-doc=reference.docx'])

except ImportError:

print(report)
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = SNL-Delft3D-CEC-Verify
version = 0.4.3
version = 0.5.0
author = Mathew Topper
author_email = [email protected]
description = Automated verification of SNL-Delft3D-CEC based on the 2014 Mycek experiment
Expand Down
65 changes: 65 additions & 0 deletions src/snl_d3d_cec_verify/_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Optional, Type, TypeVar
from pathlib import Path
from dataclasses import dataclass

from .types import StrOrPath


@dataclass(frozen=True)
class _BaseModelFinderDataclassMixin:
project_path: StrOrPath


class _BaseModelFinder(ABC, _BaseModelFinderDataclassMixin):

@property
@abstractmethod
def path(self) -> Optional[Path]:
pass # pragma: no cover

def is_model(self) -> bool:
if self.path is None: return False
return True


# Type variable with an upper bound of _BaseModelFinder
U = TypeVar('U', bound=_BaseModelFinder)


def get_model(project_path: StrOrPath,
*model_classes: Type[U]) -> Optional[U]:

model = None

for ModelClass in model_classes:
test_model = ModelClass(project_path)
if test_model.is_model():
model = test_model
break

return model


def find_path(project_path: StrOrPath,
ext: str,
partial: Optional[str] = None) -> Optional[Path]:

if partial is None:
file_root = "*"
else:
file_root = f"*{partial}*"

files = list(Path(project_path).glob(f"**/{file_root}{ext}"))

if len(files) > 1:
msg = f"Multiple files detected with signature '{file_root}{ext}'"
raise FileNotFoundError(msg)

if not files: return None

return files[0]
Loading

0 comments on commit ee70c26

Please sign in to comment.