Skip to content

Commit

Permalink
feat: add a command line interface (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielbdsantos authored Feb 27, 2023
1 parent 1247ba6 commit 4f9a799
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 18 deletions.
110 changes: 99 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,112 @@
# GCAT: grid convergence analysis toolkit

[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
[![black](https://github.com/gabrielbdsantos/gcat/actions/workflows/black.yaml/badge.svg?branch=master&event=push)](https://github.com/gabrielbdsantos/gcat/actions/workflows/black.yaml)

The implementation is completed. Thus, do not expect updates to the repository
anymore.

## References
## Installation


1. I. B. Celik, U. Ghia, P. J. Roache, C. J. Freitas, H. Coleman, and P. E.
Raad, Procedure for Estimation and Reporting of Uncertainty Due to
Discretization in CFD Applications,” J. Fluids Eng., vol. 130, no. 7, p.
078001, Jul. 2008, doi: [10.1115/1.2960953][1].
### Poetry (recommended)

2. P. J. Roache, Quantification of Uncertainty in Computational Fluid Dynamics,
Annu. Rev. Fluid Mech., vol. 29, no. 1, pp. 123–160, Jan. 1997, doi:
[10.1146/annurev.fluid.29.1.123][2].
Clone the repository

git clone https://github.com/gabrielbdsantos/gcat
cd gcat

Create a virtual environment (optional)

poetry env use python3

Install GCAT

poetry install --with cli

Activate it

source $(poetry env info --path)/bin/activate


### Pip

Clone the repository

git clone https://github.com/gabrielbdsantos/gcat
cd gcat

Create a virtual environment (optional)

python3 -m venv .venv --clear

Install

pip install -r requirements.txt -e .

Activate it

source .venv/bin/activate


## Quick start

1. Check the refinement ratios and representative grid sizes.

$ gcat check --n1 1000 --n2 2000 --n3 3000 --area 0.5
# Grid summary
+ ------------
N1 = 2900 elements
N2 = 1700 elements
N3 = 1000 elements
Area = 0.2 m^2

# Representative grid size
+ ------------------------
h1 = 8.304548 mm
h2 = 10.846523 mm
h3 = 14.142136 mm

# Refinement ratio
+ ----------------
r21 = 1.306094
r32 = 1.303840

2. Compute the grid convergence index

$ gcat gci --h1 8.30 --h2 10.84 --h3 14.14 --f1 1 --f2 1.02 --f3 1.08
# Grid summary
+ ---------------------------------------
h1 = 8.300000e+00 m, f1 = 1.000000e+00
h2 = 1.084000e+01 m, f2 = 1.020000e+00
h3 = 1.414000e+01 m, f3 = 1.080000e+00

# GCI (safety factor = 1.25)
+ ---------------------------------------
GCI21_fine = 1.562500e-02
GCI21_coarse = 4.687500e-02

GCI32_fine = 4.630449e-02
GCI32_coarse = 1.382163e-01

Asymptotic ratio = 1.012321


## References

3. Examining Spatial (Grid) Convergence.
https://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html (accessed
Oct. 22, 2020).
1 . I. B. Celik, U. Ghia, P. J. Roache, C. J. Freitas, H. Coleman, and P. E.
Raad, Procedure for Estimation and Reporting of Uncertainty Due to
Discretization in CFD Applications,” J. Fluids Eng., vol. 130, no. 7, p.
078001, Jul. 2008, doi: [10.1115/1.2960953][1].

2 . P. J. Roache, Quantification of Uncertainty in Computational Fluid Dynamics,
Annu. Rev. Fluid Mech., vol. 29, no. 1, pp. 123–160, Jan. 1997, doi:
[10.1146/annurev.fluid.29.1.123][2].

3 . Examining Spatial (Grid) Convergence.
https://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html (accessed
Oct. 22, 2020).

## License

Expand Down
2 changes: 1 addition & 1 deletion gcat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# coding=utf-8
"""Grid Convergence Analysis Toolkit (GCAT)."""

__version__ = "1.0.0"
__version__ = "1.1.0"

from .convergence import (
apparent_order_of_convergence,
Expand Down
6 changes: 6 additions & 0 deletions gcat/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# coding: utf-8

from .cli import app

if __name__ == "__main__":
app()
206 changes: 206 additions & 0 deletions gcat/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# coding: utf-8
"""Provide utilities for the command line interface."""

import math

import typer

from gcat.convergence import asymptotic_ratio

app = typer.Typer(
help="A simple cli for the grid convergence analysis toolkit (GCAT)",
add_completion=False,
no_args_is_help=True,
)


def MutuallyExclusiveGroup(size: int = 2, at_least_one: bool = True):
"""Create a mutually exclusive callback for typer."""
group = set()
active = set()

def callback(_: typer.Context, param: typer.CallbackParam, value: str):
group.add(param.name)

if (
value != param.default
and value is not None
and param.name not in active
):
active.add(param.name)

if len(active) > 1:
raise typer.BadParameter(
f"{param.name} is mutually exclusive with {active.pop()}"
)

if at_least_one and len(group) == size and len(active) == 0:
typer.echo(
f"Error: Expected one of the options: {[x for x in group]}"
)
raise typer.Exit(2)

return value

return callback


exclusivity_callback = MutuallyExclusiveGroup()


@app.command()
def check(
n1: int = typer.Option(
...,
help="Number of elements of the fine grid.",
metavar="",
),
n2: int = typer.Option(
...,
help="Number of elements of the medium grid.",
metavar="",
),
n3: int = typer.Option(
...,
help="Number of elements of the coarse grid.",
metavar="",
),
area: float = typer.Option(
default=0.0,
help="Area of the computational domain (in squared meters).",
show_default=False,
metavar="",
callback=exclusivity_callback,
),
volume: float = typer.Option(
default=0.0,
help="Volume of the computational domain (in cubic meters).",
show_default=False,
metavar="",
callback=exclusivity_callback,
),
) -> None:
"""Check the representative size and refinement ratios."""
# Note that area and volume are mutually exclusive. Thus, if volume is
# zero, it is a two-dimensional case. Otherwise, it will be a
# three-dimensional one.
num_dimensions: int = 2 + 1 * (volume > 0.0)

total_size = volume + area

def representative_size(
num_elements: int, total_size: float, num_dimensions: int
) -> float:
"""Compute the representative size of a given mesh."""
return math.pow((total_size / num_elements), (1 / num_dimensions))

# Compute the individual representative sizes
h1 = representative_size(n1, total_size, num_dimensions) # in meters
h2 = representative_size(n2, total_size, num_dimensions) # in meters
h3 = representative_size(n3, total_size, num_dimensions) # in meters

# Compute the refinement ratio
ratio21 = h2 / h1
ratio32 = h3 / h2

log = (
"# Grid summary",
"+ ------------",
f" N1 = {n1} elements",
f" N2 = {n2} elements",
f" N3 = {n3} elements",
f" Area = {area} m^2" if area > 0 else f" Volume = {volume} m^3",
"",
"# Representative grid size",
"+ ------------------------",
f" h1 = {h1 * 1e3:.6f} mm",
f" h2 = {h2 * 1e3:.6f} mm",
f" h3 = {h3 * 1e3:.6f} mm",
"",
"# Refinement ratio",
"+ ----------------",
f" r21 = {ratio21:.6f}",
f" r32 = {ratio32:.6f}",
)

print("\n".join([x for x in log]))


@app.command()
def gci(
h1: float = typer.Option(
...,
help="Representative grid size of the fine mesh (in mm).",
metavar="",
),
h2: float = typer.Option(
...,
help="Representative grid size of the medium mesh (in mm).",
metavar="",
),
h3: float = typer.Option(
...,
help="Representative grid size of the coarse mesh (in mm).",
metavar="",
),
f1: float = typer.Option(
...,
help="Model output for the fine mesh.",
metavar="",
),
f2: float = typer.Option(
...,
help="Model output for the medium mesh.",
metavar="",
),
f3: float = typer.Option(
...,
help="Model output for the coarse mesh.",
metavar="",
),
safety: float = typer.Option(
default=1.25,
help="Safety factor",
metavar="",
),
) -> None:
"""Compute the grid convergence index."""
from .convergence import (
apparent_order_of_convergence,
asymptotic_ratio,
gci_coarse,
gci_fine,
)

p = apparent_order_of_convergence(h1, h2, h3, f1, f2, f3)

r21 = h2 / h1
r32 = h3 / h2

gci21_fine = gci_fine(f1, f2, r21, p)
gci21_coarse = gci_coarse(f1, f2, r21, p)

gci32_fine = gci_fine(f2, f3, r32, p)
gci32_coarse = gci_coarse(f2, f3, r32, p)

r = asymptotic_ratio(gci21_fine, gci32_fine, r21, p)

log = (
"# Grid summary",
"+ ---------------------------------------",
f" h1 = {h1:.6e} m, f1 = {f1:.6e}",
f" h2 = {h2:.6e} m, f2 = {f2:.6e}",
f" h3 = {h3:.6e} m, f3 = {f3:.6e}",
"",
f"# GCI (safety factor = {safety})",
"+ ---------------------------------------",
f" GCI21_fine = {gci21_fine * safety:.6e}",
f" GCI21_coarse = {gci21_coarse * safety:.6e}",
"",
f" GCI32_fine = {gci32_fine * safety:.6e}",
f" GCI32_coarse = {gci32_coarse * safety:.6e}",
"",
f" Asymptotic ratio = {r:.6f}",
)

print("\n".join([x for x in log]))
Loading

0 comments on commit 4f9a799

Please sign in to comment.