diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 00000000..e91ca6c4 --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,184 @@ +name: Simvue Examples + +on: + push: + tags: + - 'v*-*-rc*' + branches: + - "296-*" + workflow_dispatch: + +permissions: + contents: read + +jobs: + GeometryOptimisation: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Download BlueMira + uses: actions/checkout@v4 + with: + repository: Fusion-Power-Plant-Framework/bluemira + ref: v1.10.0 + path: blue_mira + - uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: bluemira + environment-file: blue_mira/conda/environment.yml + miniforge-version: "latest" + use-only-tar-bz2: false + miniforge-variant: Mambaforge + use-mamba: true + python-version: "3.11" + - name: Install bluemira + shell: bash -l {0} + run: pip install -e blue_mira + - name: Install Simvue + shell: bash -l {0} + run: pip install -e . + - name: Run Example + shell: bash -l {0} + run: python ./examples/GeometryOptimisation/bluemira_simvue_geometry_optimisation.py + Logging: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install Simvue + run: python3 -m pip install . + - name: Run Example + run: | + export SIMVUE_URL=${{ secrets.SIMVUE_URL }} + export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }} + python3 examples/Logging/logging-to-simvue.py --ci + OpenFOAM: + runs-on: ubuntu-latest + container: + image: openfoam/openfoam10-paraview56 + options: --user root + steps: + - uses: actions/checkout@v4 + - name: Manual Python Install + run: | + add-apt-repository ppa:deadsnakes/ppa -y + apt-get update && \ + apt-get install -y git wget curl python3.11-full + + - name: Install dependencies + run: | + python3.11 -m ensurepip --upgrade + python3.11 -m pip install ukaea-multiparser + - name: Install Package + run: python3.11 -m pip install . + - name: Run Example + shell: bash + run: | + export SIMVUE_URL=${{ secrets.SIMVUE_URL }} + export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }} + python3.11 ./examples/OpenFOAM/simvue_openfoam.py /opt/openfoam10/tutorials/incompressible/pimpleFoam/laminar/movingCone/Allrun --ci + Optuna: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install Simvue + run: python3 -m pip install . + - name: Install Dependencies + run: | + python3 -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu + python3 -m pip install -r examples/Optuna/PyTorch/requirements.txt + - name: Run Example + run: | + export SIMVUE_URL=${{ secrets.SIMVUE_URL }} + export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }} + python3.11 ./examples/Optuna/PyTorch/simvue_optuna_pytorch.py --ci + PyTorch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install Simvue + run: python3 -m pip install . + - name: Install Dependencies + run: | + python3 -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu + python3 -m pip install -r examples/PyTorch/requirements.txt + - name: Run Example + run: | + export SIMVUE_URL=${{ secrets.SIMVUE_URL }} + export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }} + python3.11 ./examples/PyTorch/main.py --ci + SU2: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Download SU2 + run: | + wget https://github.com/su2code/SU2/releases/download/v8.0.1/SU2-v8.0.1-linux64.zip + unzip SU2-v8.0.1-linux64.zip + - name: Install dependencies + run: | + python -m pip install ukaea-multiparser + python -m pip install . + - name: Run Example + run: | + export SIMVUE_URL=${{ secrets.SIMVUE_URL }} + export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }} + python examples/SU2/SU2.py bin --config tests/example_data/SU2_inv_ONERAM6.cfg --ci + TensorFlow: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install Simvue + run: python3 -m pip install . + - name: Install Dependencies + run: python3 -m pip install -r examples/Tensorflow/requirements.txt + - name: Run Example + run: | + export SIMVUE_URL=${{ secrets.SIMVUE_URL }} + export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }} + python3.11 ./examples/Tensorflow/dynamic_rnn.py --ci + + GEANT4: + runs-on: ubuntu-latest + container: + image: artemisbeta/geant4:11.2.1 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install Simvue + run: python3 -m pip install . + - name: Install Dependencies + run: python3 -m pip install -r examples/Geant4/requirements.txt + - name: Build Example + run: | + cmake -DCMAKE_PREFIX_PATH=/usr/local/share/geant4/install/4.11.2/ -Bbuild examples/Geant4/FixedTarget/ + cmake --build build + - name: Run Example + run: | + export SIMVUE_URL=${{ secrets.SIMVUE_URL }} + export SIMVUE_TOKEN=${{ secrets.SIMVUE_TOKEN }} + python examples/Geant4/geant4_simvue.py build/MaterialTesting --ci --events 10 diff --git a/.gitignore b/.gitignore index edbe47cb..1b6adc6c 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,6 @@ offline/ # VSCode .vscode/ + +# Vagrant +Vagrantfile diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..0e828d2b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "examples/Geant4/FixedTarget"] + path = examples/Geant4/FixedTarget + url = https://github.com/artemis-beta/Geant4-Example.git diff --git a/examples/Geant4/FixedTarget b/examples/Geant4/FixedTarget new file mode 160000 index 00000000..4113c4c8 --- /dev/null +++ b/examples/Geant4/FixedTarget @@ -0,0 +1 @@ +Subproject commit 4113c4c8b8f535c63f030721a0805f95f9c36123 diff --git a/examples/Geant4/geant4_simvue.py b/examples/Geant4/geant4_simvue.py new file mode 100644 index 00000000..3d3f9dfb --- /dev/null +++ b/examples/Geant4/geant4_simvue.py @@ -0,0 +1,122 @@ +""" +Geant4 Simvue +============= + +Example of repeating simulation of a proton fired at a target of beryllium +monitoring the yield of key particles of interest +""" + +import multiparser +import multiparser.parsing.file as mp_file_parse +import simvue +import uproot +import multiprocessing +import typing +import click +import pathlib +import os +import tempfile + +from particle import Particle + + +@click.command +@click.argument("g4_binary", type=click.Path(exists=True)) +@click.option("--config", type=click.Path(exists=True), default=None) +@click.option("--ci", is_flag=True, default=False) +@click.option("--momentum", type=float, default=10) +@click.option("--events", type=int, default=100) +def geant4_simvue_example( + g4_binary: str, config: typing.Optional[str], ci: bool, momentum: float, events: int +) -> None: + @mp_file_parse.file_parser + def root_file_parser( + file_name: str, *_, **__ + ) -> tuple[dict[str, typing.Any], dict[str, typing.Any]]: + with uproot.open(file_name) as root_data: + hit_data: dict[str, uproot.TBranch] + if not (hit_data := root_data.get("Hits")): + raise RuntimeError("Expected key 'Hits' in ROOT file") + + particles_of_interest = [2212, 211, 11, 22, 2112] + + all_particles = hit_data["fID"].array(library="np").tolist() + + out_data = { + Particle.from_pdgid(abs(identifier)) + .name.replace("+", "plus") + .replace("-", "minus"): [abs(i) for i in all_particles].count( + abs(identifier) + ) + for identifier in particles_of_interest + } + + return {}, out_data + + termination_trigger = multiprocessing.Event() + + with simvue.Run() as run: + run.init( + "Geant4_simvue_demo", + folder="/simvue_client_demos", + tags=[ + "Geant4", + ], + description="Geant4 fixed target scenario", + retention_period="1 hour" if ci else None, + visibility="tenant" if ci else None, + ) + + kwargs: dict[str, typing.Any] = {} + + if config: + kwargs["script"] = config + with tempfile.TemporaryDirectory() as tempd: + with multiparser.FileMonitor( + per_thread_callback=lambda metrics, *_: run.log_metrics(metrics), + exception_callback=run.log_event, + terminate_all_on_fail=False, + plain_logging=True, + flatten_data=True, + termination_trigger=termination_trigger, + ) as monitor: + monitor.track( + path_glob_exprs=[f'{pathlib.Path(tempd).joinpath("*")}'], + parser_func=root_file_parser, + static=True, + ) + monitor.run() + + for i in range(events): + if i % 10 == 0: + click.secho( + f"Running {i+1}/{events} with momentum {momentum} GeV", + bold=True, + fg="cyan", + ) + running_simulation = multiprocessing.Event() + run.add_process( + identifier=f"Geant4_simulation_{momentum}GeV_{i}", + executable=g4_binary, + momentum=momentum, + batch=True, + output=pathlib.Path(tempd).joinpath( + f"output_{momentum}GeV_{i+1}.root" + ), + completion_trigger=running_simulation + if i == events - 1 + else None, + **kwargs, + ) + + termination_trigger.set() + + for file in pathlib.Path().cwd().glob("Geant4*.err"): + os.remove(file) + + for file in pathlib.Path().cwd().glob("Geant4*.out"): + os.remove(file) + + +if __name__ in "__main__": + geant4_simvue_example() diff --git a/examples/Geant4/requirements.txt b/examples/Geant4/requirements.txt new file mode 100644 index 00000000..73394169 --- /dev/null +++ b/examples/Geant4/requirements.txt @@ -0,0 +1,5 @@ +simvue +uproot +ukaea-multiparser +click +particle diff --git a/examples/GeometryOptimisation/bluemira_simvue_geometry_optimisation.py b/examples/GeometryOptimisation/bluemira_simvue_geometry_optimisation.py index 264ed03a..28c15dae 100644 --- a/examples/GeometryOptimisation/bluemira_simvue_geometry_optimisation.py +++ b/examples/GeometryOptimisation/bluemira_simvue_geometry_optimisation.py @@ -22,6 +22,7 @@ A quick tutorial on the optimisation of geometry in bluemira """ +import os import logging from bluemira.geometry.optimisation import GeometryOptimisationProblem @@ -68,6 +69,9 @@ "optimiser.ftol_abs": ftol_abs, "optimiser.ftol_rel": ftol_rel, }, + folder="/simvue_client_demos", + visibility="tenant" if os.environ.get("CI") else None, + tags=["bluemira", "simvue_client_examples"], description="A simple GeometryOptimisationProblem, where we minimise the length of parameterised geometry using gradient-based optimisation algorithm.", ) diff --git a/examples/Logging/logging-to-simvue.py b/examples/Logging/logging-to-simvue.py index 8e1b19f1..ff6e851c 100644 --- a/examples/Logging/logging-to-simvue.py +++ b/examples/Logging/logging-to-simvue.py @@ -1,17 +1,24 @@ import logging +import click from simvue import Handler, Run -if __name__ == "__main__": - run = Run() - run.init(tags=["logging"], description="Logging test") +@click.command +@click.option("--ci", is_flag=True, default=False) +def simvue_logger_demo(ci: bool) -> None: + with Run() as run: + run.init( + tags=["logging", "simvue_client_examples"], + folder="/simvue_client_demos", + description="Logging test", + retention_period="1 hour" if ci else None, + visibility="tenant" if ci else None, + ) - logger = logging.getLogger(__name__) - logger.setLevel(logging.DEBUG) - sth = Handler(run) - logger.addHandler(sth) + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + sth = Handler(run) + logger.addHandler(sth) - logger.info("This is a Simvue logging test") - - run.close() + logger.info("This is a Simvue logging test") diff --git a/examples/OpenFOAM/Dockerfile b/examples/OpenFOAM/Dockerfile new file mode 100644 index 00000000..3f827e82 --- /dev/null +++ b/examples/OpenFOAM/Dockerfile @@ -0,0 +1,11 @@ +FROM openfoam/openfoam10-paraview56 +ARG simvue_file +USER root +RUN add-apt-repository ppa:deadsnakes/ppa -y +RUN apt-get update && \ + apt-get install -y python3.11-full +RUN useradd simvue -m +COPY . /simvue_client +RUN python3.11 -m ensurepip --upgrade +RUN python3.11 -m pip install /simvue_client +RUN python3.11 -m pip install -r /simvue_client/examples/OpenFOAM/requirements.txt diff --git a/examples/OpenFOAM/requirements.txt b/examples/OpenFOAM/requirements.txt new file mode 100644 index 00000000..18e44044 --- /dev/null +++ b/examples/OpenFOAM/requirements.txt @@ -0,0 +1 @@ +ukaea-multiparser>=1.0.1 diff --git a/examples/OpenFOAM/simvue_openfoam.py b/examples/OpenFOAM/simvue_openfoam.py index 78c88765..c092cba6 100644 --- a/examples/OpenFOAM/simvue_openfoam.py +++ b/examples/OpenFOAM/simvue_openfoam.py @@ -1,58 +1,101 @@ +""" +OpenFOAM v10 Simvue Example + +This Simvue example launches the MovingCone example within the OpenFoam10 tutorials. + +The contents of the log.PimpleFoam file are parsed using multiparser. + +To run this example within an OpenFOAM 10 Docker container +ensure you have either a Simvue config file or you have +set the values for SIMVUE_TOKEN and SIMVUE_URL and run: + +python simvue_openfoam.py /opt/openfoam10/tutorials/incompressible/pimpleFoam/laminar/movingCone/Allrun + +""" + import os import re -import sys -import time +import click +import uuid +import simvue +import multiprocessing +import multiparser +import multiparser.parsing.tail as mp_tail_parse + +from typing import Any + -from simvue import Run +@click.command +@click.argument("all_run_script", type=click.Path(exists=True)) +@click.option("--ci", is_flag=True, default=False) +def open_foam_simvue_demo(all_run_script: str, ci: bool) -> None: + """Run the Allrun file for the given simulation and parse the log.PimpleFoam content -if __name__ == "__main__": + Parameters + ---------- + all_run_script : str + path of the Allrun execution script + """ # Regular expressions - exp1 = re.compile( - "^(.+): Solving for (.+), Initial residual = (.+), Final residual = (.+), No Iterations (.+)$" - ) - exp2 = re.compile("^ExecutionTime = ([0-9.]+) s") - - # Check the log file for new entries at this interval (in secs) - polling_interval = 5 - - run = Run() - run.init(tags=["OpenFOAM"]) - - running = True - file_pos = 0 - ttime = None - metrics = {} - - while running: - # If log doesn't exist yet, wait for it - if not os.path.isfile(sys.argv[1]): - time.sleep(polling_interval) - continue - - # Read log file - with open(sys.argv[1], "r") as fh: - fh.seek(file_pos) - for line in fh.readlines(): - # Get time - match = exp2.match(line) - if match: - ttime = match.group(1) - if metrics: - run.log_metrics(metrics, time=ttime) - metrics = {} - - # Get metrics - match = exp1.match(line) - if match: - metrics["residuals.initial.%s" % match.group(2)] = match.group(3) - metrics["residuals.final.%s" % match.group(2)] = match.group(4) - - file_pos = fh.tell() - - # Check if application is still running - if os.path.isfile(".finished"): - running = False - else: - time.sleep(polling_interval) - - run.close() + + uniq_id: str = f"{uuid.uuid4()}".split("-")[0] + + @mp_tail_parse.log_parser + def custom_parser(file_content: str, **__) -> tuple[dict[str, Any], dict[str, Any]]: + exp1: re.Pattern[str] = re.compile( + "^(.+): Solving for (.+), Initial residual = (.+), Final residual = (.+), No Iterations (.+)$" + ) + exp2: re.Pattern[str] = re.compile("^ExecutionTime = ([0-9.]+) s") + metrics = {} + + for line in file_content.splitlines(): + # Get time + match = exp2.match(line) + if match: + ttime = match.group(1) + if metrics: + run.log_metrics(metrics, time=ttime) + metrics = {} + + # Get metrics + match = exp1.match(line) + if match: + metrics["residuals.initial.%s" % match.group(2)] = match.group(3) + metrics["residuals.final.%s" % match.group(2)] = match.group(4) + return {}, metrics + + log_location: str = os.path.dirname(all_run_script) + termination_trigger = multiprocessing.Event() + + with simvue.Run() as run: + run.init( + f"open_foam_demo_{uniq_id}", + folder="/simvue_client_demos", + tags=["OpenFOAM", "simvue_client_examples"], + retention_period="1 hour" if ci else None, + visibility="tenant" if ci else None, + ) + run.add_process( + identifier="OpenFOAM", + executable="/bin/sh", + script=all_run_script, + completion_callback=lambda *_, **__: termination_trigger.set(), + ) + with multiparser.FileMonitor( + per_thread_callback=lambda metrics, *_: run.log_metrics(metrics), + exception_callback=run.log_event, + terminate_all_on_fail=True, + plain_logging=True, + flatten_data=True, + interval=0.1, + termination_trigger=termination_trigger, + ) as monitor: + monitor.tail( + parser_func=custom_parser, + path_glob_exprs=[os.path.join(log_location, "log.pimpleFoam")], + ) + monitor.run() + + +if __name__ in "__main__": + open_foam_simvue_demo() diff --git a/examples/Optuna/PyTorch/simvue_optuna_pytorch.py b/examples/Optuna/PyTorch/simvue_optuna_pytorch.py index 8b99bc14..ceba3a7e 100644 --- a/examples/Optuna/PyTorch/simvue_optuna_pytorch.py +++ b/examples/Optuna/PyTorch/simvue_optuna_pytorch.py @@ -3,6 +3,7 @@ """ import os +import click import optuna import randomname @@ -19,126 +20,155 @@ DIR = os.getcwd() EPOCHS = 100 LOG_INTERVAL = 10 -N_TRAIN_EXAMPLES = BATCHSIZE * 30 -N_VALID_EXAMPLES = BATCHSIZE * 10 STUDY_NAME = "pytorch-optimization" FOLDER_NAME = randomname.get_name() -def train(optimizer, model, train_loader): - model.train() - for batch_idx, (data, target) in enumerate(train_loader): - # Limiting training data for faster epochs. - if batch_idx * BATCHSIZE >= N_TRAIN_EXAMPLES: - break - - data, target = data.view(data.size(0), -1).to(DEVICE), target.to(DEVICE) - - optimizer.zero_grad() - output = model(data) - loss = F.nll_loss(output, target) - loss.backward() - optimizer.step() - - -def validate(model, valid_loader): - # Validation of the model. - model.eval() - correct = 0 - with torch.no_grad(): - for batch_idx, (data, target) in enumerate(valid_loader): - # Limiting validation data. - if batch_idx * BATCHSIZE >= N_VALID_EXAMPLES: +@click.command +@click.option("--epochs", type=int, default=EPOCHS, show_default=True) +@click.option("--batch-size", type=int, default=BATCHSIZE, show_default=True) +@click.option("--train-examples", type=int, default=BATCHSIZE * 30, show_default=True) +@click.option("--valid-examples", type=int, default=BATCHSIZE * 10, show_default=True) +@click.option("--trials", type=int, default=100, show_default=True) +@click.option("--timeout", type=int, default=600, show_default=True) +@click.option("--ci", is_flag=True, default=False) +def run_optuna_example( + epochs: int, + batch_size: int, + train_examples: int, + valid_examples: int, + ci: bool, + trials: int, + timeout: int, +) -> None: + if ci: + batch_size = 1 + train_examples = 1 + valid_examples = 1 + epochs = 1 + trials = 1 + timeout = 30 + + def train(optimizer, model, train_loader, batch_size=batch_size): + model.train() + for batch_idx, (data, target) in enumerate(train_loader): + # Limiting training data for faster epochs. + if batch_idx * batch_size >= train_examples: break + data, target = data.view(data.size(0), -1).to(DEVICE), target.to(DEVICE) + + optimizer.zero_grad() output = model(data) - # Get the index of the max log-probability. - pred = output.argmax(dim=1, keepdim=True) - correct += pred.eq(target.view_as(pred)).sum().item() - - accuracy = correct / min(len(valid_loader.dataset), N_VALID_EXAMPLES) - - return accuracy - - -def define_model(trial): - # We optimize the number of layers, hidden units and dropout ratio in each layer. - n_layers = trial.suggest_int("n_layers", 1, 3) - layers = [] - - in_features = 28 * 28 - for i in range(n_layers): - out_features = trial.suggest_int("n_units_l{}".format(i), 4, 128) - layers.append(nn.Linear(in_features, out_features)) - layers.append(nn.ReLU()) - p = trial.suggest_float("dropout_l{}".format(i), 0.2, 0.5) - layers.append(nn.Dropout(p)) - - in_features = out_features - layers.append(nn.Linear(in_features, CLASSES)) - layers.append(nn.LogSoftmax(dim=1)) - - return nn.Sequential(*layers) - - -# Get the data loaders of FashionMNIST dataset. -train_loader = torch.utils.data.DataLoader( - datasets.FashionMNIST( - DIR, train=True, download=True, transform=transforms.ToTensor() - ), - batch_size=BATCHSIZE, - shuffle=True, -) -valid_loader = torch.utils.data.DataLoader( - datasets.FashionMNIST(DIR, train=False, transform=transforms.ToTensor()), - batch_size=BATCHSIZE, - shuffle=True, -) - - -def objective(trial): - # Generate the model. - model = define_model(trial).to(DEVICE) - - # Generate the optimizers. - optimizer_name = trial.suggest_categorical("optimizer", ["AdamW", "RMSprop", "SGD"]) - lr = trial.suggest_float("lr", 1e-5, 1e-1, log=True) - optimizer = getattr(optim, optimizer_name)(model.parameters(), lr=lr) - - # init tracking experiment. - # hyper-parameters, trial id are stored. - config = dict(trial.params) - config["trial.number"] = trial.number - from simvue import Run - - run = Run() - run.init(folder="/optuna/tests/%s" % FOLDER_NAME, metadata=config) - - # Training of the model. - for epoch in range(EPOCHS): - train(optimizer, model, train_loader) - val_accuracy = validate(model, valid_loader) - trial.report(val_accuracy, epoch) - - # report validation accuracy to wandb - run.log_metrics({"validation accuracy": val_accuracy}, step=epoch) - - # Handle pruning based on the intermediate value. - if trial.should_prune(): - run.update_metadata({"state": "pruned"}) - run.close() - raise optuna.exceptions.TrialPruned() - - # report the final validation accuracy to simvue - run.update_metadata({"final accuracy": val_accuracy, "state": "completed"}) - run.close() - - return val_accuracy - - -study = optuna.create_study( - direction="maximize", - study_name=STUDY_NAME, - pruner=optuna.pruners.MedianPruner(), -) -study.optimize(objective, n_trials=100, timeout=600) + loss = F.nll_loss(output, target) + loss.backward() + optimizer.step() + + def validate(model, valid_loader, batch_size=batch_size): + # Validation of the model. + model.eval() + correct = 0 + with torch.no_grad(): + for batch_idx, (data, target) in enumerate(valid_loader): + # Limiting validation data. + if batch_idx * batch_size >= valid_examples: + break + data, target = data.view(data.size(0), -1).to(DEVICE), target.to(DEVICE) + output = model(data) + # Get the index of the max log-probability. + pred = output.argmax(dim=1, keepdim=True) + correct += pred.eq(target.view_as(pred)).sum().item() + + accuracy = correct / min(len(valid_loader.dataset), valid_examples) + + return accuracy + + def define_model(trial): + # We optimize the number of layers, hidden units and dropout ratio in each layer. + n_layers = trial.suggest_int("n_layers", 1, 3) + layers = [] + + in_features = 28 * 28 + for i in range(n_layers): + out_features = trial.suggest_int("n_units_l{}".format(i), 4, 128) + layers.append(nn.Linear(in_features, out_features)) + layers.append(nn.ReLU()) + p = trial.suggest_float("dropout_l{}".format(i), 0.2, 0.5) + layers.append(nn.Dropout(p)) + + in_features = out_features + layers.append(nn.Linear(in_features, CLASSES)) + layers.append(nn.LogSoftmax(dim=1)) + + return nn.Sequential(*layers) + + # Get the data loaders of FashionMNIST dataset. + train_loader = torch.utils.data.DataLoader( + datasets.FashionMNIST( + DIR, train=True, download=True, transform=transforms.ToTensor() + ), + batch_size=batch_size, + shuffle=True, + ) + valid_loader = torch.utils.data.DataLoader( + datasets.FashionMNIST(DIR, train=False, transform=transforms.ToTensor()), + batch_size=batch_size, + shuffle=True, + ) + + def objective(trial): + # Generate the model. + model = define_model(trial).to(DEVICE) + + # Generate the optimizers. + optimizer_name = trial.suggest_categorical( + "optimizer", ["AdamW", "RMSprop", "SGD"] + ) + lr = trial.suggest_float("lr", 1e-5, 1e-1, log=True) + optimizer = getattr(optim, optimizer_name)(model.parameters(), lr=lr) + + # init tracking experiment. + # hyper-parameters, trial id are stored. + config = dict(trial.params) + config["trial.number"] = trial.number + from simvue import Run + + with Run() as run: + run.init( + folder="/optuna/tests/%s" % FOLDER_NAME, + metadata=config, + tags=["pytorch", "simvue_client_examples"], + retention_period="1 hour" if ci else None, + visibility="tenant" if ci else None, + ) + + # Training of the model. + for epoch in range(epochs): + train(optimizer, model, train_loader) + val_accuracy = validate(model, valid_loader) + trial.report(val_accuracy, epoch) + + # report validation accuracy to wandb + run.log_metrics({"validation accuracy": val_accuracy}, step=epoch) + + # Handle pruning based on the intermediate value. + if trial.should_prune(): + run.update_metadata({"state": "pruned"}) + run.close() + raise optuna.exceptions.TrialPruned() + + # report the final validation accuracy to simvue + run.update_metadata({"final accuracy": val_accuracy, "state": "completed"}) + + return val_accuracy + + study = optuna.create_study( + direction="maximize", + study_name=STUDY_NAME, + pruner=optuna.pruners.MedianPruner(), + ) + study.optimize(objective, n_trials=trials, timeout=timeout) + + +if __name__ in "__main__": + run_optuna_example() diff --git a/examples/PyTorch/main.py b/examples/PyTorch/main.py index 2fd55bf4..880bdb93 100644 --- a/examples/PyTorch/main.py +++ b/examples/PyTorch/main.py @@ -1,7 +1,7 @@ # Taken from https://github.com/pytorch/examples/blob/main/mnist/main.py from __future__ import print_function -import argparse +import click import torch import torch.nn as nn @@ -39,7 +39,16 @@ def forward(self, x): return output -def train(args, model, device, train_loader, optimizer, epoch, run): +def train( + dry_run: bool, + log_interval: int, + model, + device, + train_loader, + optimizer, + epoch, + run: Run, +) -> None: model.train() for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) @@ -48,7 +57,7 @@ def train(args, model, device, train_loader, optimizer, epoch, run): loss = F.nll_loss(output, target) loss.backward() optimizer.step() - if batch_idx % args.log_interval == 0: + if batch_idx % log_interval == 0: print( "Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format( epoch, @@ -61,7 +70,7 @@ def train(args, model, device, train_loader, optimizer, epoch, run): run.log_metrics( {"train.loss.%d" % epoch: float(loss.item())}, step=batch_idx ) - if args.dry_run: + if dry_run: break @@ -94,80 +103,91 @@ def test(model, device, test_loader, epoch, run): ) -def main(): - # Training settings - parser = argparse.ArgumentParser(description="PyTorch MNIST Example") - parser.add_argument( - "--batch-size", - type=int, - default=64, - metavar="N", - help="input batch size for training (default: 64)", - ) - parser.add_argument( - "--test-batch-size", - type=int, - default=1000, - metavar="N", - help="input batch size for testing (default: 1000)", - ) - parser.add_argument( - "--epochs", - type=int, - default=14, - metavar="N", - help="number of epochs to train (default: 14)", - ) - parser.add_argument( - "--lr", - type=float, - default=1.0, - metavar="LR", - help="learning rate (default: 1.0)", - ) - parser.add_argument( - "--gamma", - type=float, - default=0.7, - metavar="M", - help="Learning rate step gamma (default: 0.7)", - ) - parser.add_argument( - "--no-cuda", action="store_true", default=False, help="disables CUDA training" - ) - parser.add_argument( - "--no-mps", - action="store_true", - default=False, - help="disables macOS GPU training", - ) - parser.add_argument( - "--dry-run", - action="store_true", - default=False, - help="quickly check a single pass", - ) - parser.add_argument( - "--seed", type=int, default=1, metavar="S", help="random seed (default: 1)" - ) - parser.add_argument( - "--log-interval", - type=int, - default=10, - metavar="N", - help="how many batches to wait before logging training status", - ) - parser.add_argument( - "--save-model", - action="store_true", - default=False, - help="For Saving the current Model", - ) - args = parser.parse_args() - use_cuda = not args.no_cuda and torch.cuda.is_available() - use_mps = not args.no_mps and torch.backends.mps.is_available() - - torch.manual_seed(args.seed) +@click.command +@click.option( + "--batch-size", + type=int, + default=64, + help="input batch size for training", + show_default=True, +) +@click.option( + "--test-batch-size", + type=int, + default=1000, + help="input batch size for testing", + show_default=True, +) +@click.option( + "--epochs", + type=int, + default=14, + help="number of epochs to train", + show_default=True, +) +@click.option("--lr", type=float, default=1.0, help="learning rate", show_default=True) +@click.option( + "--gamma", + type=float, + default=0.7, + help="learning rate step gamma", + show_default=True, +) +@click.option( + "--no-cuda", + is_flag=True, + default=False, + help="disables CUDA training", + show_default=True, +) +@click.option( + "--no-mps", + is_flag=True, + default=False, + help="disables macOS GPU training", + show_default=True, +) +@click.option( + "--dry-run", + is_flag=True, + default=False, + help="quickly check a single pass", + show_default=True, +) +@click.option("--seed", type=int, default=1, help="random seed", show_default=True) +@click.option( + "--log-interval", + type=int, + default=10, + help="how many batches to wait before logging training status", + show_default=True, +) +@click.option( + "--save-model", + is_flag=True, + default=False, + help="save the current Model", + show_default=True, +) +@click.option("--ci", is_flag=True, default=False) +def simvue_pytorch_example( + batch_size: int, + test_batch_size: int, + epochs: int, + lr: float, + gamma: float, + no_cuda: bool, + no_mps: bool, + dry_run: bool, + seed: int, + log_interval: int, + save_model: bool, + ci: bool, +) -> None: + use_cuda = not no_cuda and torch.cuda.is_available() and not ci + use_mps = not no_mps and torch.backends.mps.is_available() and not ci + + torch.manual_seed(seed) if use_cuda: device = torch.device("cuda") @@ -176,12 +196,19 @@ def main(): else: device = torch.device("cpu") - train_kwargs = {"batch_size": args.batch_size} - test_kwargs = {"batch_size": args.test_batch_size} + if ci: + dry_run = True + batch_size = 1 + test_batch_size = 1 + epochs = 1 + save_model = False + + train_kwargs = {"batch_size": batch_size} + test_kwargs = {"batch_size": test_batch_size} if use_cuda: cuda_kwargs = {"num_workers": 1, "pin_memory": True, "shuffle": True} - train_kwargs.update(cuda_kwargs) - test_kwargs.update(cuda_kwargs) + train_kwupdate(cuda_kwargs) + test_kwupdate(cuda_kwargs) transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))] @@ -192,23 +219,35 @@ def main(): test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs) model = Net().to(device) - optimizer = optim.Adadelta(model.parameters(), lr=args.lr) - - scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma) + optimizer = optim.Adadelta(model.parameters(), lr=lr) - run = Run() - run.init(tags=["PyTorch"]) + scheduler = StepLR(optimizer, step_size=1, gamma=gamma) - for epoch in range(1, args.epochs + 1): - train(args, model, device, train_loader, optimizer, epoch, run) - test(model, device, test_loader, epoch, run) - scheduler.step() + with Run() as run: + run.init( + tags=["PyTorch", "simvue_client_examples"], + folder="/simvue_client_demos", + retention_period="1 hour" if ci else None, + visibility="tenant" if ci else None, + ) - if args.save_model: - run.save_file(model.state_dict(), "output", name="mnist_cnn.pt") + for epoch in range(1, epochs + 1): + train( + dry_run, + log_interval, + model, + device, + train_loader, + optimizer, + epoch, + run, + ) + test(model, device, test_loader, epoch, run) + scheduler.step() - run.close() + if save_model: + run.save_file(model.state_dict(), "output", name="mnist_cnn.pt") if __name__ == "__main__": - main() + simvue_pytorch_example() diff --git a/examples/SU2/SU2.py b/examples/SU2/SU2.py index 51740867..13dec644 100644 --- a/examples/SU2/SU2.py +++ b/examples/SU2/SU2.py @@ -1,25 +1,64 @@ -import csv import os -import sys -import time +import multiprocessing +import click +import multiparser +import requests -from simvue import Run +import multiparser.parsing.tail as mp_tail_parse +import multiparser.parsing.file as mp_file_parse -if __name__ == "__main__": +from typing import Any + +import simvue + + +@click.command +@click.argument("su2_binary_directory", type=click.Path(exists=True)) +@click.option("--config", help="URL or path of config file", default=None) +@click.option("--mesh", help="URL or path of mesh file", default=None) +@click.option("--ci", is_flag=True, default=False) +def run_su2_example( + su2_binary_directory: str, config: str | None, mesh: str | None, ci: bool +) -> None: # Name of history file to collect metrics from - HISTORY = "history.csv" + HISTORY: str = "history.csv" + + config_url = ( + config + or "https://raw.githubusercontent.com/su2code/Tutorials/master/compressible_flow/Inviscid_Bump/inv_channel.cfg" + ) - # Check the history file for new entries at this interval (in secs) - POLLING_INTERVAL = 5 + mesh_url = ( + mesh + or "https://raw.githubusercontent.com/su2code/Tutorials/master/compressible_flow/Inviscid_ONERAM6/mesh_ONERAM6_inv_ffd.su2" + ) + + config_filename: str = ( + os.path.basename(config_url) if "http" in config_url else config_url + ) + mesh_filename: str = os.path.basename(mesh_url) if "http" in mesh_url else mesh_url + + for url, file_name in zip((config_url, mesh_url), (config_filename, mesh_filename)): + if "http" not in url: + continue - # Store these input files - INPUT_FILES = ["inv_ONERAM6.cfg", "mesh_ONERAM6_inv_ffd.su2"] + req_response = requests.get(url) + + if req_response.status_code != 200: + raise RuntimeError(f"Failed to retrieve file '{url}'") + + with open(file_name, "wb") as out_f: + out_f.write(req_response.content) # Store these output files - OUTPUT_FILES = ["flow.vtk", "surface_flow.vtk", "restart_flow.dat"] + OUTPUT_FILES: list[str] = ["flow.vtk", "surface_flow.vtk", "restart_flow.dat"] + + for file_name in OUTPUT_FILES + [HISTORY]: + if os.path.exists(file_name): + os.remove(file_name) # Collect these metadata attributes from the config file - METADATA_ATTRS = [ + METADATA_ATTRS: list[str] = [ "SOLVER", "MATH_PROBLEM", "MACH_NUMBER", @@ -29,83 +68,81 @@ "FREESTREAM_TEMPERATURE", ] - # Get PID of SU2 - with open(sys.argv[1], "r") as fh: - pid = int(fh.read()) - - # Read metadata - metadata = {} - for filename in INPUT_FILES: - if filename.endswith(".cfg"): - with open(filename, "r") as cfg: - for line in cfg.readlines(): - for attr in METADATA_ATTRS: - if line.startswith(attr): - metadata[attr] = line.split("%s= " % attr)[1].strip() - - run = Run() - run.set_pid(pid) - run.init( - metadata=metadata, - tags=["SU2"], - description="SU2 tutorial https://su2code.github.io/tutorials/Inviscid_ONERAM6/", + @mp_file_parse.file_parser + def metadata_parser(file_name: str, **_) -> tuple[dict[str, Any], dict[str, Any]]: + metadata = {} + with open(file_name) as in_csv: + file_content = in_csv.read() + + for line in file_content.splitlines(): + for attr in METADATA_ATTRS: + if line.startswith(attr): + metadata[attr] = line.split("%s= " % attr)[1].strip() + return {}, metadata + + termination_trigger = multiprocessing.Event() + + environment: dict[str, str] = os.environ.copy() + environment["PATH"] = ( + f"{os.path.abspath(su2_binary_directory)}:{os.environ['PATH']}" + ) + environment["PYTHONPATH"] = ( + f"{os.path.abspath(su2_binary_directory)}{f':{pypath}' if (pypath := os.environ.get('PYTHONPATH')) else ''}" ) - # Save input files - for input_file in INPUT_FILES: - filetype = None - if input_file.endswith(".cfg"): - filetype = "text/plain" - run.save_file(input_file, "input", filetype) - - running = True - latest = [] - first = True - cols = [] - - while running: - # If history.csv doesn't exist yet, wait for it - if not os.path.isfile(HISTORY): - time.sleep(POLLING_INTERVAL) - continue + with simvue.Run() as run: + run.init( + "SU2_simvue_demo", + folder="/simvue_client_demos", + tags=[ + "SU2", + os.path.splitext(os.path.basename(config_filename))[0], + os.path.splitext(os.path.basename(mesh_filename))[0], + "simvue_client_examples", + ], + description="SU2 tutorial https://su2code.github.io/tutorials/Inviscid_ONERAM6/", + retention_period="1 hour" if ci else None, + visibility="tenant" if ci else None, + ) + run.add_process( + identifier="SU2_simulation", + executable="SU2_CFD", + script=config_filename, + env=environment, + completion_callback=lambda *_, **__: termination_trigger.set(), + ) + with multiparser.FileMonitor( + # Metrics cannot have square brackets in their names so we remove + # these before passing them to log_metrics + per_thread_callback=lambda metrics, *_: run.log_metrics( + { + key.replace("[", "_").replace("]", ""): value + for key, value in metrics.items() + } + ), + exception_callback=run.log_event, + terminate_all_on_fail=True, + plain_logging=True, + flatten_data=True, + termination_trigger=termination_trigger, + ) as monitor: + monitor.track( + path_glob_exprs=[config_filename], + parser_func=metadata_parser, + callback=lambda meta, *_: run.update_metadata(meta), + static=True, + ) + monitor.tail( + path_glob_exprs=[HISTORY], + parser_func=mp_tail_parse.record_csv, + ) + monitor.track( + path_glob_exprs=OUTPUT_FILES, + callback=lambda *_, meta: run.save_file(meta["file_name"], "output"), + parser_func=lambda *_, **__: ({}, {}), + ) + monitor.run() - # Read history.csv and get the latest rows - header = [] - with open(HISTORY, "r") as csv_file: - csv_reader = csv.reader(csv_file, delimiter=",") - new_rows = False - for row in csv_reader: - if not header: - header = [item.strip().replace('"', "") for item in row] - col = 0 - for item in header: - item = item.strip().replace('"', "") - if "rms" in item: - cols.append(col) - col += 1 - else: - if new_rows or not latest: - metrics = {} - for i in range(0, len(cols)): - data = row[cols[i]].strip().replace('"', "") - metrics[header[cols[i]]] = data - run.log_metrics(metrics) - - if row == latest: - new_rows = True - - latest = row - - # Check if application is still running - try: - os.kill(pid, 0) - except OSError: - running = False - else: - time.sleep(POLLING_INTERVAL) - - # Save output files - for output_file in OUTPUT_FILES: - run.save_file(output_file, "output") - - run.close() + +if __name__ == "__main__": + run_su2_example() diff --git a/examples/Tensorflow/dynamic_rnn.py b/examples/Tensorflow/dynamic_rnn.py index 0a5339c7..1c89e1af 100644 --- a/examples/Tensorflow/dynamic_rnn.py +++ b/examples/Tensorflow/dynamic_rnn.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function import random +import click import numpy as np @@ -13,30 +14,51 @@ # Taken from https://github.com/aymericdamien/TensorFlow-Examples/ -if __name__ == "__main__": +@click.command +@click.option( + "--classes", type=int, default=2, help="linear sequence or not", show_default=True +) +@click.option( + "--lr", type=float, default=0.001, help="learning rate", show_default=True +) +@click.option("--training-steps", type=int, default=2000, show_default=True) +@click.option("--batch-size", type=int, default=64, show_default=True) +@click.option( + "--num-units", + type=int, + default=32, + help="number of neurons for the LSTM layer", + show_default=True, +) +@click.option("--ci", is_flag=True) +def run_tensorflow_example( + classes: int, + lr: float, + training_steps: int, + batch_size: int, + ci: bool, + num_units: int, +) -> None: # Dataset parameters. - num_classes = 2 # linear sequence or not. + num_classes = classes # linear sequence or not. seq_max_len = 20 # Maximum sequence length. seq_min_len = 5 # Minimum sequence length (before padding). masking_val = -1 # -1 will represents the mask and be used to pad sequences to a common max length. max_value = 10000 # Maximum int value. - # Training Parameters - learning_rate = 0.001 - training_steps = 2000 - batch_size = 64 - - # Network Parameters - num_units = 32 # number of neurons for the LSTM layer. + if ci: + batch_size = 1 + training_steps = 1 with Run() as run: run.init( + "tensorflow_dynamic_rnn", metadata={ "dataset.num_classes": num_classes, "dataset.seq_max_len": seq_max_len, "dataset.seq_min_len": seq_min_len, "dataset.masking_val": masking_val, - "training.learning_rate": learning_rate, + "training.learning_rate": lr, "training.training_steps": training_steps, "training.batch_size": batch_size, "network.num_units": num_units, @@ -44,8 +66,12 @@ description="TensorFlow 2.0 implementation of a Recurrent Neural Network (LSTM) that performs dynamic " "computation over sequences with variable length. This example is using a toy dataset to " "classify linear sequences. The generated sequences have variable length.", + retention_period="1 hour" if ci else None, + tags=["tensorflow", "simvue_client_examples"], + folder="/simvue_client_demos", + visibility="tenant" if ci else None, ) - run.save_file("dynamic_rnn.py", "code") + run.save_file(__file__, "code") # ==================== # TOY DATA GENERATOR @@ -155,7 +181,7 @@ def accuracy(y_pred, y_true): return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1) # Adam optimizer. - optimizer = tf.optimizers.Adam(learning_rate) + optimizer = tf.optimizers.Adam(lr) # Optimization process. def run_optimization(x, y): @@ -176,7 +202,7 @@ def run_optimization(x, y): optimizer.apply_gradients(zip(gradients, trainable_variables)) # Run training for the given number of steps. - for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1): + for batch_x, batch_y in train_data.take(training_steps): # Run the optimization to update W and b values. run_optimization(batch_x, batch_y) @@ -186,4 +212,7 @@ def run_optimization(x, y): run.log_metrics({"loss": float(loss), "accuracy": float(acc)}) run.update_metadata({"loss": float(loss), "accuracy": float(acc)}) - run.close() + + +if __name__ in "__main__": + run_tensorflow_example() diff --git a/tests/example_data/SU2_inv_ONERAM6.cfg b/tests/example_data/SU2_inv_ONERAM6.cfg new file mode 100644 index 00000000..3c30cd11 --- /dev/null +++ b/tests/example_data/SU2_inv_ONERAM6.cfg @@ -0,0 +1,233 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% SU2 configuration file % +% Case description: ONERA M6 wing in inviscid, transonic flow % +% Author: Thomas D. Economon % +% Institution: Stanford University % +% Date: 2015.08.25 % +% File Version 5.0.0 "Raven" % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +% +% Physical governing equations (EULER, NAVIER_STOKES) +SOLVER= EULER +% +% Mathematical problem (DIRECT, CONTINUOUS_ADJOINT) +MATH_PROBLEM= DIRECT +% +% Restart solution (NO, YES) +RESTART_SOL= NO + +% -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% +% +% Mach number (non-dimensional, based on the free-stream values) +MACH_NUMBER= 0.8395 +% +% Angle of attack (degrees) +AOA= 3.06 +% +% Side-slip angle (degrees) +SIDESLIP_ANGLE= 0.0 +% +% Free-stream pressure (101325.0 N/m^2 by default, only for Euler equations) +FREESTREAM_PRESSURE= 101325.0 +% +% Free-stream temperature (288.15 K by default) +FREESTREAM_TEMPERATURE= 288.15 + +% ---------------------- REFERENCE VALUE DEFINITION ---------------------------% +% +% Reference origin for moment computation +REF_ORIGIN_MOMENT_X = 0.25 +REF_ORIGIN_MOMENT_Y = 0.00 +REF_ORIGIN_MOMENT_Z = 0.00 +% +% Reference length for pitching, rolling, and yaMAIN_BOX non-dimensional moment +REF_LENGTH= 1.0 +% +% Reference area for force coefficients (0 implies automatic calculation) +REF_AREA= 0 +% +% Flow non-dimensionalization (DIMENSIONAL, FREESTREAM_PRESS_EQ_ONE, +% FREESTREAM_VEL_EQ_MACH, FREESTREAM_VEL_EQ_ONE) +REF_DIMENSIONALIZATION= FREESTREAM_VEL_EQ_ONE + +% ----------------------- BOUNDARY CONDITION DEFINITION -----------------------% +% +% Marker of the Euler boundary (0 implies no marker) +MARKER_EULER= ( UPPER_SIDE, LOWER_SIDE, TIP ) +% +% Marker of the far field (0 implies no marker) +MARKER_FAR= ( XNORMAL_FACES, ZNORMAL_FACES, YNORMAL_FACE ) +% +% Marker of symmetry boundary (0 implies no marker) +MARKER_SYM= ( SYMMETRY_FACE ) +% +% Marker of the surface which is going to be plotted +MARKER_PLOTTING= ( UPPER_SIDE, LOWER_SIDE, TIP ) +% +% Marker of the surface where the functional (Cd, Cl, etc.) will be evaluated +MARKER_MONITORING= ( UPPER_SIDE, LOWER_SIDE, TIP ) + +% ------------- COMMON PARAMETERS TO DEFINE THE NUMERICAL METHOD --------------% +% +% Numerical method for spatial gradients (GREEN_GAUSS, WEIGHTED_LEAST_SQUARES) +NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES +% +% Objective function in gradient evaluation (DRAG, LIFT, SIDEFORCE, MOMENT_X, +% MOMENT_Y, MOMENT_Z, EFFICIENCY, +% EQUIVALENT_AREA, NEARFIELD_PRESSURE, +% FORCE_X, FORCE_Y, FORCE_Z, THRUST, +% TORQUE, FREE_SURFACE, TOTAL_HEATFLUX, +% MAXIMUM_HEATFLUX, INVERSE_DESIGN_PRESSURE, +% INVERSE_DESIGN_HEATFLUX) +OBJECTIVE_FUNCTION= DRAG +% +% Courant-Friedrichs-Lewy condition of the finest grid +CFL_NUMBER= 25.0 +% +% Adaptive CFL number (NO, YES) +CFL_ADAPT= YES +% +% Parameters of the adaptive CFL number (factor down, factor up, CFL min value, +% CFL max value ) +CFL_ADAPT_PARAM= ( 0.1, 2.0, 25.0, 1e10 ) +% +% Runge-Kutta alpha coefficients +RK_ALPHA_COEFF= ( 0.66667, 0.66667, 1.000000 ) +% +% Number of total iterations +ITER= 1 +% +% Linear solver for the implicit formulation (BCGSTAB, FGMRES) +LINEAR_SOLVER= FGMRES +% +% Preconditioner of the Krylov linear solver (ILU, LU_SGS, LINELET, JACOBI) +LINEAR_SOLVER_PREC= ILU +% +% Min error of the linear solver for the implicit formulation +LINEAR_SOLVER_ERROR= 1E-6 +% +% Max number of iterations of the linear solver for the implicit formulation +LINEAR_SOLVER_ITER= 10 + +% ----------------------- SLOPE LIMITER DEFINITION ----------------------------% +% +% Coefficient for the limiter +VENKAT_LIMITER_COEFF= 0.03 +% +% Coefficient for the sharp edges limiter +ADJ_SHARP_LIMITER_COEFF= 3.0 +% +% Reference coefficient (sensitivity) for detecting sharp edges. +REF_SHARP_EDGES= 3.0 +% +% Remove sharp edges from the sensitivity evaluation (NO, YES) +SENS_REMOVE_SHARP= YES + +% -------------------------- MULTIGRID PARAMETERS -----------------------------% +% +% Multi-Grid Levels (0 = no multi-grid) +MGLEVEL= 3 +% +% Multi-grid cycle (V_CYCLE, W_CYCLE, FULLMG_CYCLE) +MGCYCLE= W_CYCLE +% +% Multi-Grid PreSmoothing Level +MG_PRE_SMOOTH= ( 1, 2, 3, 3 ) +% +% Multi-Grid PostSmoothing Level +MG_POST_SMOOTH= ( 0, 0, 0, 0 ) +% +% Jacobi implicit smoothing of the correction +MG_CORRECTION_SMOOTH= ( 0, 0, 0, 0 ) +% +% Damping factor for the residual restriction +MG_DAMP_RESTRICTION= 0.9 +% +% Damping factor for the correction prolongation +MG_DAMP_PROLONGATION= 0.9 + +% -------------------- FLOW NUMERICAL METHOD DEFINITION -----------------------% +% +% Convective numerical method (JST, LAX-FRIEDRICH, CUSP, ROE, AUSM, HLLC, +% TURKEL_PREC, MSW) +CONV_NUM_METHOD_FLOW= JST +% +% 2nd and 4th order artificial dissipation coefficients +JST_SENSOR_COEFF= ( 0.5, 0.02 ) +% +% Time discretization (RUNGE-KUTTA_EXPLICIT, EULER_IMPLICIT, EULER_EXPLICIT) +TIME_DISCRE_FLOW= EULER_IMPLICIT + +% --------------------------- CONVERGENCE PARAMETERS --------------------------& +% +% Convergence criteria (CAUCHY, RESIDUAL) +CONV_FIELD= RMS_DENSITY +% +% Min value of the residual (log10 of the residual) +CONV_RESIDUAL_MINVAL= -12 +% +% Start convergence criteria at iteration number +CONV_STARTITER= 25 +% +% Number of elements to apply the criteria +CONV_CAUCHY_ELEMS= 100 +% +% Epsilon to control the series convergence +CONV_CAUCHY_EPS= 1E-10 +% + +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +% +% Mesh input file +MESH_FILENAME= mesh_ONERAM6_inv_ffd.su2 +% +% Mesh output file +MESH_OUT_FILENAME= mesh_out.su2 +% +% Restart flow input file +SOLUTION_FILENAME= solution_flow.dat +% +% Restart adjoint input file +SOLUTION_ADJ_FILENAME= solution_adj.dat +% +% Mesh input file format (SU2) +MESH_FORMAT= SU2 +% +% Output tabular format (CSV, TECPLOT) +TABULAR_FORMAT= CSV +% +% Output file convergence history +CONV_FILENAME= history +% +% Output file restart flow +RESTART_FILENAME= restart_flow.dat +% +% Output file restart adjoint +RESTART_ADJ_FILENAME= restart_adj.dat +% +% Output file flow (w/o extension) variables +VOLUME_FILENAME= flow +% +% Output file adjoint (w/o extension) variables +VOLUME_ADJ_FILENAME= adjoint +% +% Output Objective function gradient (using continuous adjoint) +GRAD_OBJFUNC_FILENAME= of_grad.dat +% +% Output file surface flow coefficient (w/o extension) +SURFACE_FILENAME= surface_flow +% +% Output file surface adjoint coefficient (w/o extension) +SURFACE_ADJ_FILENAME= surface_adjoint +% +% Writing solution frequency +OUTPUT_WRT_FREQ= 100 +% +% +% Screen output +SCREEN_OUTPUT= (INNER_ITER, WALL_TIME, RMS_DENSITY, RMS_ENERGY, LIFT, DRAG) +