diff --git a/.github/linters/.codespellrc b/.github/linters/.codespellrc index a8ac1050..11311016 100644 --- a/.github/linters/.codespellrc +++ b/.github/linters/.codespellrc @@ -1,3 +1,3 @@ [codespell] -skip = None, .git,OFsolvers,tutorial_cases,experimental_cases,build,_build,__pycache__,data_conditional_mean,Figures,assets +skip = None, .git,OFsolvers,*.pdf, tutorial_cases,experimental_cases,build,_build,__pycache__,data_conditional_mean,Figures,assets ignore-words = .github/linters/.codespell-ignore-words diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 852dc071..452df070 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.10', '3.11', '3.12'] + python-version: ['3.10', '3.13'] os: ['ubuntu-latest', 'macos-latest'] defaults: run: @@ -67,15 +67,8 @@ jobs: pip install --upgrade pip pip install . pip install pytest - - name: Test preprocess - run: | - pytest tests/preprocess - - name: Test mesh - run: | - pytest tests/meshing - - name: Test postprocessing - run: | - pytest tests/postprocess + - name: Test + run: pytest . Test-pypi-Bird: name: Test-pypi-BiRD (${{ matrix.python-version }}, ${{ matrix.os }}) @@ -176,4 +169,9 @@ jobs: cd tutorial_cases/airlift_40m bash run.sh cd ../../ + - name: Run flat panel reactor tutorial + run: | + cd tutorial_cases/FlatPanel_250L_ASU + bash run.sh + cd ../../ diff --git a/.gitignore b/.gitignore index 4e84ce28..75376bcf 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,4 @@ dmypy.json *.stl *.swp +.vim diff --git a/README.md b/README.md index 6d8301d2..c28a1a3b 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,15 @@ To cite BiRD, please use these articles on [CO2 interphase mass transfer](https: ``` -@article{hassanaly2025inverse, - title={Bayesian calibration of bubble size dynamics applied to \ce{CO2} gas fermenters}, - author={Hassanaly, Malik and Parra-Alvarez, John M. and Rahimi, Mohammad J., Municchi, Federico and Sitaraman, Hariswaran}, - journal={Chemical Engineering Research and Design}, - year={2025}, - } +@article{hassanaly2025bayesian, + title={Bayesian calibration of bubble size dynamics applied to CO2 gas fermenters}, + author={Hassanaly, Malik and Parra-Alvarez, John M and Rahimi, Mohammad J and Municchi, Federico and Sitaraman, Hariswaran}, + journal={Chemical Engineering Research and Design}, + volume={215}, + pages={312--328}, + year={2025}, + publisher={Elsevier} +} @article{rahimi2018computational, title={Computational fluid dynamics study of full-scale aerobic bioreactors: Evaluation of gas--liquid mass transfer, oxygen uptake, and dynamic oxygen distribution}, diff --git a/applications/sparger_placement_opt.py b/applications/sparger_placement_opt.py new file mode 100644 index 00000000..9074715d --- /dev/null +++ b/applications/sparger_placement_opt.py @@ -0,0 +1,17 @@ +import os +import pickle +import shutil + +import numpy as np + +from bird.preprocess.json_gen.design_io import * +from bird.preprocess.json_gen.generate_designs import * + +if __name__ == "__main__": + + generate_single_scaledup_reactor_sparger_cases( + sparger_locs=[0.3, 0.5, 1.4], + sim_id=0, + vvm=0.4, + study_folder=".", + ) diff --git a/bird/__init__.py b/bird/__init__.py index 1c16af5e..3835ca6e 100644 --- a/bird/__init__.py +++ b/bird/__init__.py @@ -5,6 +5,7 @@ from bird.version import __version__ BIRD_DIR = os.path.dirname(os.path.realpath(__file__)) +BIRD_CASE_DIR = os.path.join(BIRD_DIR, "../tutorial_cases") BIRD_MESH_DIR = os.path.join(BIRD_DIR, "meshing") BIRD_POST_DIR = os.path.join(BIRD_DIR, "postprocess") BIRD_PRE_DIR = os.path.join(BIRD_DIR, "preprocess") @@ -34,4 +35,9 @@ ) BIRD_EARLY_PRED_DATA_DIR = os.path.join(BIRD_POST_DIR, "data_early") BIRD_KLA_DATA_DIR = os.path.join(BIRD_POST_DIR, "data_kla") +BIRD_CASE_GEN_DATA_DIR = os.path.join(BIRD_PRE_DIR, "data_case_gen") BIRD_INV_DIR = os.path.join(BIRD_DIR, "inverse_modeling") + +from bird.logging_config import setup_logging + +setup_logging(level="INFO") diff --git a/bird/calibration/param_nn.py b/bird/calibration/param_nn.py index e6622cfd..4b869149 100644 --- a/bird/calibration/param_nn.py +++ b/bird/calibration/param_nn.py @@ -1,4 +1,5 @@ import argparse +import logging import os import sys import time @@ -13,6 +14,8 @@ from sklearn.preprocessing import MinMaxScaler, StandardScaler from tensorflow.keras import initializers, layers, optimizers, regularizers +logger = logging.getLogger(__name__) + def flexible_activation(x, activation): if activation is None: @@ -36,7 +39,7 @@ def flexible_activation(x, activation): elif activation.lower() == "leakyrelu": out = layers.LeakyReLU()(x) else: - sys.exit(f"ERROR: unknown activation {activation}") + raise NotImplementedError(f"unknown activation {activation}") return out @@ -185,7 +188,7 @@ def train( gradient_threshold=None, ): if gradient_threshold is not None: - print(f"INFO: clipping gradients at {gradient_threshold:.2g}") + logger.info("clipping gradients at {gradient_threshold:.2g}") # Make sure the control file for learning rate is consistent with main.py, at least at first self.prepareLog() bestLoss = None diff --git a/bird/logging_config.py b/bird/logging_config.py new file mode 100644 index 00000000..89cabf90 --- /dev/null +++ b/bird/logging_config.py @@ -0,0 +1,34 @@ +import logging +from pathlib import Path + + +class ConditionalFormatter(logging.Formatter): + def format(self, record): + """Change how to log if it is an INFO or a WARNING/ERROR""" + if record.levelno >= logging.WARNING: + self._style._fmt = ( + "%(asctime)s [%(levelname)s] %(name)s: %(message)s" + ) + else: + self._style._fmt = "%(asctime)s [%(levelname)s] bird: %(message)s" + return super().format(record) + + +def setup_logging(logfile: str | None = None, level=logging.INFO): + """Setup logging""" + + logger = logging.getLogger() + logger.setLevel(level) + + formatter = ConditionalFormatter() + + # Console handler + ch = logging.StreamHandler() + ch.setFormatter(formatter) + logger.addHandler(ch) + + # Optional file handler + if logfile: + fh = logging.FileHandler(Path(logfile)) + fh.setFormatter(formatter) + logger.addHandler(fh) diff --git a/bird/meshing/_mesh_tools.py b/bird/meshing/_mesh_tools.py index 61618b2f..c437744d 100644 --- a/bird/meshing/_mesh_tools.py +++ b/bird/meshing/_mesh_tools.py @@ -1,10 +1,14 @@ import json +import logging +import os import sys from pathlib import Path import numpy as np from ruamel.yaml import YAML +logger = logging.getLogger(__name__) + def parseJsonFile(input_filename): with open(input_filename) as f: @@ -12,7 +16,31 @@ def parseJsonFile(input_filename): return inpt +def check_for_tabs_in_yaml(file_path: str) -> None: + """ + Checks if a YAML file contains any tab characters. + Raises a ValueError if tabs found. + + Parameters + ---------- + file_path: str + path to yaml filename + """ + + with open(file_path, "r", encoding="utf-8") as f: + lines = f.readlines() + for iline, line in enumerate(lines): + if "\t" in line: + raise ValueError( + f"Tab character found on line {iline} of '{file_path}'. " + "YAML files must use spaces for indentation." + ) + + def parseYAMLFile(input_filename): + if not os.path.exists(input_filename): + raise FileNotFoundError(input_filename) + check_for_tabs_in_yaml(input_filename) yaml = YAML(typ="safe") inpt = yaml.load(Path(input_filename)) return inpt @@ -85,10 +113,8 @@ def bissection(val, stretch_fun, N1): resultmin = stretch_fun(Gmin, N1) - val resultmax = stretch_fun(Gmax, N1) - val if resultmin * resultmax > 0: - print( - "Error,the initial bounds of grading do not encompass the solution" - ) - # stop + logger.error("Initial bounds of grading do not encompass the solution") + sys.exit() for i in range(1000): Gmid = 0.5 * (Gmax + Gmin) @@ -202,8 +228,8 @@ def verticalCoarsening( length / deltaE, stretch_fun, NVert[ind] ) if iterate: - print( - f"WARNING: reduced NVert[{ind}] from {origNVert} to {NVert[ind]}" + logger.warning( + f"reduced NVert[{ind}] from {origNVert} to {NVert[ind]}" ) block_cell_minus_length[ind] = deltaE block_cell_plus_length[ind] = deltaE * gradVert[ind] @@ -225,8 +251,8 @@ def verticalCoarsening( length / deltaE, stretch_fun, NVert[ind] ) if iterate: - print( - f"WARNING: reduced NVert[{ind}] from {origNVert} to {NVert[ind]}" + logger.warning( + f"reduced NVert[{ind}] from {origNVert} to {NVert[ind]}" ) block_cell_minus_length[ind] = deltaE / gradVert[ind] block_cell_plus_length[ind] = deltaE @@ -316,8 +342,8 @@ def radialCoarsening( length / deltaE, stretch_fun, NR[ind] ) if iterate: - print( - f"WARNING: reduced NR[{ind}] from {origNR} to {NR[ind]}" + logger.warning( + f"reduced NR[{ind}] from {origNR} to {NR[ind]}" ) block_cell_minus_length[ind] = deltaE block_cell_plus_length[ind] = deltaE * gradR[ind] @@ -334,8 +360,8 @@ def radialCoarsening( length / deltaE, stretch_fun, NR[ind] ) if iterate: - print( - f"WARNING: reduced NR[{ind}] from {origNR} to {NR[ind]}" + logger.warning( + f"reduced NR[{ind}] from {origNR} to {NR[ind]}" ) block_cell_minus_length[ind] = deltaE / gradR[ind] block_cell_plus_length[ind] = deltaE @@ -359,9 +385,9 @@ def radialCoarsening( # if (gradR[last_R] > 2 or gradR[last_R] < 0.5) and abs( # ratio - 1 # ) <= 1e-12: - # print( - # "WARNING: radial smoothing had to be used because your mesh is very coarse" + # logger.warning( + # "radial smoothing had to be used because your mesh is very coarse" # ) - # print("\tIncrease NS in input file to avoid this warning") + # logger.warning("\tIncrease NS in input file to avoid this warning") return NR, gradR, minCell, maxCell diff --git a/bird/meshing/_stirred_tank_reactor.py b/bird/meshing/_stirred_tank_reactor.py index dc3f2db5..265c8b41 100644 --- a/bird/meshing/_stirred_tank_reactor.py +++ b/bird/meshing/_stirred_tank_reactor.py @@ -2,7 +2,8 @@ from pathlib import Path import numpy as np -from ruamel.yaml import YAML + +from bird.meshing._mesh_tools import parseYAMLFile class StirredTankReactor: @@ -133,11 +134,6 @@ def __init__( def from_file(cls, yamlfile): if ".yaml" not in yamlfile: yamlfile += ".yaml" - if os.path.exists(yamlfile): - yamlpath = Path(yamlfile) - else: - raise FileNotFoundError(yamlfile) - yaml = YAML(typ="safe") - in_dict = yaml.load(yamlpath) + in_dict = parseYAMLFile(yamlfile) react_dict = {**in_dict["geometry"], **in_dict["mesh"]} return cls(**react_dict) diff --git a/bird/meshing/block_cyl_mesh.py b/bird/meshing/block_cyl_mesh.py index 6a16be65..2760b2c6 100644 --- a/bird/meshing/block_cyl_mesh.py +++ b/bird/meshing/block_cyl_mesh.py @@ -1,3 +1,4 @@ +import logging import os import sys @@ -5,6 +6,8 @@ from bird.meshing._mesh_tools import * +logger = logging.getLogger(__name__) + def assemble_geom(input_file, topo_file): # inpt = parseJsonFile(input_file) @@ -106,7 +109,6 @@ def assemble_mesh(input_file, geomDict): ), 1, ) - # print(NR) NS = [NR[0] * 2] # Now figure out grading of each block for ir in range(len(R)): @@ -189,14 +191,14 @@ def assemble_mesh(input_file, geomDict): minCellR = np.amin(block_cell_length) maxCellR = np.amax(block_cell_length) - print("Vertical mesh:") - print(f"\tTotal NVert {sum(NVert)}") - print(f"\tNVert {NVert}") - print(f"\tsize min {minCellVert:.2f}mm max {maxCellVert:.2f}mm") - print("Radial mesh:") - print(f"\tTotal NR {sum(NR)}") - print(f"\tNR {NR}") - print(f"\tsize min {minCellR:.2f}mm max {maxCellR:.2f}mm") + logger.info("Vertical mesh:") + logger.info(f"\tTotal NVert {sum(NVert)}") + logger.info(f"\tNVert {NVert}") + logger.info(f"\tsize min {minCellVert:.2f}mm max {maxCellVert:.2f}mm") + logger.info("Radial mesh:") + logger.info(f"\tTotal NR {sum(NR)}") + logger.info(f"\tNR {NR}") + logger.info(f"\tsize min {minCellR:.2f}mm max {maxCellR:.2f}mm") return { "NR": NR, diff --git a/bird/meshing/stirred_tank_mesh.py b/bird/meshing/stirred_tank_mesh.py index 5b9a30b8..ba67b779 100644 --- a/bird/meshing/stirred_tank_mesh.py +++ b/bird/meshing/stirred_tank_mesh.py @@ -314,9 +314,6 @@ def write_blocks(outfile, react): outfile.write(");\n") - # print "meshz:",meshz - # print "meshr:",meshr - def write_patches(outfile, react): inhub_ci = react.inhub_circ diff --git a/bird/meshing/stirred_tank_mesh_templates/base_tank/tank_par.yaml b/bird/meshing/stirred_tank_mesh_templates/base_tank/tank_par.yaml index 3afc6376..69b86f12 100644 --- a/bird/meshing/stirred_tank_mesh_templates/base_tank/tank_par.yaml +++ b/bird/meshing/stirred_tank_mesh_templates/base_tank/tank_par.yaml @@ -1,23 +1,23 @@ geometry: - Dt: 0.26 # Tank diameter [m] - Da: 0.0866667 # Impleller tip Diameter [m] - H: 0.39 # Height of the reactor [m] - nimpellers: 1 - C: [0.0866667] # height of the center of impellers [m] - W: 0.026 # impeller blade width [m] - L: 0.013 # impeller blade length (beyond the hub) [m] - Lin: 0.013 # impeller blade length (inside the hub) - J: 0.026 # Baffle width - Wh: 0.0026 # Hub height (Width) - polyrad: 0.00866667 # Stem radius (R_shaft) - Z0: 0.0 # bottom of reactor - nbaffles: 6 # number of baffles and impeller fins + Dt: 0.26 # Tank diameter [m] + Da: 0.0866667 # Impleller tip Diameter [m] + H: 0.39 # Height of the reactor [m] + nimpellers: 1 + C: [0.0866667] # height of the center of impellers [m] + W: 0.026 # impeller blade width [m] + L: 0.013 # impeller blade length (beyond the hub) [m] + Lin: 0.013 # impeller blade length (inside the hub) + J: 0.026 # Baffle width + Wh: 0.0026 # Hub height (Width) + polyrad: 0.00866667 # Stem radius (R_shaft) + Z0: 0.0 # bottom of reactor + nbaffles: 6 # number of baffles and impeller fins mesh: - nr: 120 # mesh points per unit radial length - nz: 240 # mesh points per unit axial length - Npoly: 4 # mesh points in the polygon at the axis - Na: 6 # mesh points in the azimuthal direction + nr: 120 # mesh points per unit radial length + nz: 240 # mesh points per unit axial length + Npoly: 4 # mesh points in the polygon at the axis + Na: 6 # mesh points in the azimuthal direction diff --git a/bird/postprocess/SA_optimization/README.md b/bird/postprocess/SA_optimization/README.md new file mode 100644 index 00000000..454a45e9 --- /dev/null +++ b/bird/postprocess/SA_optimization/README.md @@ -0,0 +1,49 @@ +# Surrogate-Based Optimization with Simulated Annealing + +## Install dependencies + +``` + conda create --name bird python=3.10 + conda activate bird + git clone https://github.com/NREL/BioReactorDesign.git + cd BioReactorDesign + pip install -e .[optim] +``` + +## Examples + +Implementation of surrogate-based design optimization with Simulated Annealing (SA). It supports three surrogate models: + - Radial Basis Function Interpolator ('rbf') + - Random Forest ('rf') + - Neural Network ('nn') + +The SA optimizer operates on discrete feature values (0,1,2) with an option to restrict the number of spargers (1) to a fixed value (max_spargers). + +- Preprocessing the data (`get_csv.py`) + - reads the `configs.pkl` and `results.pkl` files from a study + - saves the configuration in `Xdata_{study_name}.csv` file + - save the qoi and qoi_error in `ydata_{study_name}.csv` file + +- Surrogate modeling and optimization (`get_optimal.py` and `get_optimal_with_constraints.py`) + - run_optimization(...) function sets up the surrogate-based optimization: + - Inputs: + - X: read from `Xdata_{study_name}.csv` file + - y: read from `ydata_{study_name}.csv` file + - model_type: type of surrogate model (default = `rbf`) + - model_type = `rbf`: Radial Basis Function + - model_type = `rf`: Random Forest + - model_type = `nn`: Neural Network + - max_spargers: maximum number of spargers (only in `get_optimal_with_constraints.py`) (default = 8) + - n_runs: number of bootstrap runs (default = 10) + - max_iters: maximum number of iterations of SA (default = 1000) + - bootstrap_size: sample size of each bootstrap (default = 100) + - For each bootstrap run, the model hyperparameters are tuned using 5-fold cross validation. + - The simulated_annealing_surrogate(...) function runs the optimization: + - If SA is too slow or fails to converge, you can change the following parameters: + - temp: maximum temperature of SA. Controls exploration. + - alpha: the rate at which temperature changes every iteration. + - It returns the optimal solutions along with the optimization logs which are used to make plots in postprocessing. + - Once the optimization is done the best solution is saved in a csv file and the following plots are made: + - Mean-CI plot of the objective function (qoi) + - Mean-CI plot of the distance of an iterate from the optimal solution (this is not done in `get_optimal_with_constraints.py`). + diff --git a/bird/postprocess/SA_optimization/get_csv.py b/bird/postprocess/SA_optimization/get_csv.py new file mode 100644 index 00000000..71040989 --- /dev/null +++ b/bird/postprocess/SA_optimization/get_csv.py @@ -0,0 +1,61 @@ +import csv +import os +import pickle as pkl + +import numpy as np + + +def get_config_result(study_fold: str = ".") -> None: + """ + Read the configs.pkl and results.pkl files from a study + Saves the configuration in Xdata_{study_fold}.csv file + Save the qoi and qoi_error in ydata_{study_fold}.csv file + + Parameters + ---------- + study_fold : str + Folder that contains the study results + + Returns + ---------- + None + + """ + # Read results + with open(os.path.join(study_fold, "results.pkl"), "rb") as f: + results = pkl.load(f) + with open(os.path.join(study_fold, "configs.pkl"), "rb") as f: + configs = pkl.load(f) + + Xdata = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64) + count = 0 + + # Save data into CSV files + xfname = os.path.join(study_fold, f"Xdata.csv") + yfname = os.path.join(study_fold, f"ydata.csv") + with open(xfname, "w", newline="") as csvfile: + writer = csv.writer(csvfile) + for sims in results: + b0 = configs[sims][0] + b1 = configs[sims][1] + b2 = configs[sims][2] + raw_data = np.concatenate((b0, b1, b2), axis=None) + writer.writerow(raw_data) + + with open(yfname, "w", newline="") as csvfile: + writer = csv.writer(csvfile) + for sims in results: + q0 = results[sims]["qoi"] + q1 = results[sims]["qoi_err"] + y_data = np.concatenate((q0, q1), axis=None) + writer.writerow(y_data) + + +if __name__ == "__main__": + studies = { + "study_scaleup_0_4vvm_3000W": r"608$m^3$ 0.4vvm 3000W", + "study_scaleup_0_1vvm_6000W": r"608$m^3$ 0.1vvm 6000W", + "study_0_4vvm_1W": r"0.00361$m^3$ 0.4vvm 1W", + } + for study in studies: + get_config_result(study) diff --git a/bird/postprocess/SA_optimization/get_optimal.py b/bird/postprocess/SA_optimization/get_optimal.py new file mode 100644 index 00000000..2aafa6f4 --- /dev/null +++ b/bird/postprocess/SA_optimization/get_optimal.py @@ -0,0 +1,323 @@ +import os +import random +import warnings + +import numpy as np +import pandas as pd +from prettyPlot.plotting import * +from sklearn.preprocessing import OneHotEncoder + +from bird.postprocess.SA_optimization.surrogate import ( + Surrogate_wrapper, + tune_nn, + tune_rbf, + tune_rf, +) + +warnings.filterwarnings("ignore") + + +def simulated_annealing_surrogate( + surrogate: Surrogate_wrapper, + dim: int = 12, + max_iters: int = 1000, + temp: float = 10.0, + alpha: float = 0.95, +) -> tuple[np.ndarray, float, list[tuple], list[np.ndarray]]: + """ + Runs the Simulated Annealing (SA) and reports the results + + Parameters + ---------- + surrogate : Surrogate_wrapper + tuned surrogate model + dim: int + dimension of the problem + max_iters: int + maximum number of iterations for SA + temp: float + parameter of Simulated Annealing (can be changed or tuned for other problems) + max temperature. It controls the exploration rate of SA + alpha: + parameter of Simulated Annealing (can be changed or tuned for other problems) + cooling rate. It determines how quickly temp decays + + Returns + ---------- + x_best: np.ndarray + optimal solution + y_best: float + optimal objective function value + trace: list[tuple] + optimization log. Updates at the end of each iteration. + trace_x: list[np.ndarray] + tracks how X changes during each iteration. + """ + + # generate random starting point + x_curr = np.random.randint(0, 3, size=dim) + + y_curr = surrogate.predict(x_curr) + x_best, y_best = x_curr.copy(), y_curr + + trace = [(0, y_best)] + trace_x = [x_curr.copy()] + + for i in range(max_iters): + # Optimization loop + + x_new = x_curr.copy() + idx = random.randint(0, dim - 1) + # perturb a random dimension to get new x + x_new[idx] = (x_new[idx] + random.choice([-1, 1])) % 3 + y_new = surrogate.predict(x_new) + + delta = y_new - y_curr + if delta < 0 or np.random.rand() < np.exp(-delta / temp): + x_curr, y_curr = x_new, y_new + if y_curr < y_best: + x_best, y_best = x_new.copy(), y_new + + trace.append((i, y_best)) + trace_x.append(x_curr.copy()) + temp *= alpha + return x_best, y_best, trace, trace_x + + +def run_optimization( + X: np.ndarray, + y: np.ndarray, + model_type: str = "rbf", + n_runs: int = 10, + max_iters: int = 1000, + bootstrap_size: int = 100, + out_folder: str = ".", +): + """ + Bootstraps data, runs optimization and postprocesses the results. + + Parameters + ---------- + X: np.ndarray + Raw design configuration + Dimension N by d, where N is the number of simulations, + and d is the number of design variables + y: np.ndarray + Raw array of quantity of interest + Dimension N by 1, where N is the number of simulations + + model_type : str + 'rbf' for RBFInterpolator + 'rf' for Random Forest + 'nn' for Neural Network + + n_runs :int + number of bootstraps + max_iters: int + maximum number of SA iterations + bootstrap_size : int + size of bootstrap samples + out_folder: str + folder where to output the results + + Returns + ---------- + """ + + all_x = [] + all_y = [] + all_traces = np.zeros((n_runs, max_iters + 1)) + all_trace_x = [] + rng = np.random.default_rng(42) + # Bootstrap the data + bootstrap_idxs = [ + rng.choice(len(X), size=bootstrap_size, replace=False) + for _ in range(n_runs) + ] + + i = 0 + for idxs in bootstrap_idxs: + # Tune the model for each bootstrap + X_sub, y_sub = X[idxs], y[idxs] + if model_type == "rbf": + params = tune_rbf(X_sub, y_sub) + elif model_type == "rf": + params = tune_rf(X_sub, y_sub) + elif model_type == "nn": + encoder = OneHotEncoder(sparse_output=False) + X_encoded = encoder.fit_transform(X_sub) + params = tune_nn(X_encoded, y_sub) + else: + raise ValueError("Invalid model_type") + + # build the surrogate model + surrogate = Surrogate_wrapper(model_type, X_sub, y_sub, params) + + # run optimization + x_best, y_best, trace, trace_x = simulated_annealing_surrogate( + surrogate, dim=X.shape[1], max_iters=max_iters + ) + + trace_y = [y for _, y in trace] + all_traces[i, :] = trace_y + all_x.append(x_best) + all_y.append(y_best) + all_trace_x.append(trace_x) + i = i + 1 + + # Compute the best y across all the bootstraps + best_index = np.argmin(all_y) + best_x = all_x[best_index] + best_y = all_y[best_index] + # Save the best solution + df = pd.DataFrame( + [ + { + **{f"x{i}": best_x[i] for i in range(len(best_x))}, + "best_y": best_y, + } + ] + ) + df.to_csv( + os.path.join( + out_folder, + f"best_bootstrap_solution_{model_type}_size_{bootstrap_size}.csv", + ), + index=False, + ) + + # Make the mean-CI plot for the objective function and save it + mean_trace = np.mean(-1 * all_traces, axis=0) + std_trace = np.std(all_traces, axis=0) + lower_bound = mean_trace - 1.96 * std_trace / np.sqrt(n_runs) + upper_bound = mean_trace + 1.96 * std_trace / np.sqrt(n_runs) + iterations = np.arange(max_iters + 1) + + plt.figure(figsize=(8, 6)) + plt.plot( + iterations, + mean_trace, + label=f"Mean Convergence {model_type.upper()}", + color="blue", + ) + plt.fill_between( + iterations, + lower_bound, + upper_bound, + color="blue", + alpha=0.3, + label="95% CI", + ) + pretty_labels( + "Iteration", + r"Predicted QOI [kg$^2$/kWh$^2$]", + fontsize=20, + title=f"Mean Convergence with 95% Confidence Interval ({model_type.upper()})", + grid=True, + fontname="Times", + ) + pretty_legend(fontsize=20, fontname="Times") + plt.savefig( + os.path.join( + out_folder, + f"Mean_Convergence_plot_{model_type}_size_{bootstrap_size}.png", + ), + dpi=300, + ) + plt.savefig( + os.path.join( + out_folder, + f"Mean_Convergence_plot_{model_type}_size_{bootstrap_size}.pdf", + ), + ) + plt.show() + + # Compute the distance of intermediate x from the optimal solution + optimal_x = np.ones(X.shape[1], dtype=int) + l1_traces = [] + for t in all_trace_x: + distances = [np.sum(np.abs(np.array(x) - optimal_x)) for x in t] + l1_traces.append(distances) + + """ compute and make the mean-CI plot for the distance between the intermdeiate + solution and the optimal solution """ + l1_traces = np.array(l1_traces) + mean_l1 = np.mean(l1_traces, axis=0) + std_l1 = np.std(l1_traces, axis=0) + lower = mean_l1 - 1.96 * std_l1 / np.sqrt(len(trace_x)) + upper = mean_l1 + 1.96 * std_l1 / np.sqrt(len(trace_x)) + iterations = np.arange(len(mean_l1)) + plt.figure(figsize=(8, 4)) + plt.plot( + iterations, + mean_l1, + label="Mean L1 Distance from Optimal", + color="darkred", + ) + plt.fill_between( + iterations, lower, upper, alpha=0.3, color="darkred", label="95% CI" + ) + pretty_labels( + "Iteration", + "L1 Distance from Optimal", + fontsize=16, + title=f"Convergence Toward Optimal Solution (L1 Norm)", + grid=True, + fontname="Times", + ) + pretty_legend(fontsize=16, fontname="Times") + plt.tight_layout() + plt.savefig( + os.path.join( + out_folder, + f"Mean_L1_distance_{model_type}_size_{bootstrap_size}.png", + ), + dpi=300, + ) + plt.savefig( + os.path.join( + out_folder, + f"Mean_L1_distance_{model_type}_size_{bootstrap_size}.pdf", + ) + ) + plt.show() + + +if __name__ == "__main__": + + # studies = ["study_scaleup_0_4vvm_3000W", "study_scaleup_0_1vvm_6000W", "study_0_4vvm_1W"] + # studies = ["study_scaleup_0_1vvm_6000W", "study_0_4vvm_1W"] + studies = ["study_scaleup_0_4vvm_3000W", "study_0_4vvm_1W"] + + for study in studies: + # Read data from the csv file. + X_raw_data = pd.read_csv(os.path.join(study, "Xdata.csv")) + y_raw_data = pd.read_csv(os.path.join(study, "ydata.csv")) + + X = X_raw_data.values + y = y_raw_data.iloc[:, :-1].values + # We want to maximize, so we minimize the opposite + y = y * -1 + + # The function will build, tune, and optimize the surrogate model and postprocess the results. + run_optimization( + X, + y, + model_type="rbf", + n_runs=10, + max_iters=1000, + bootstrap_size=150, + out_folder=study, + ) + # run_optimization( + # X, + # y, + # model_type="rf", + # n_runs=10, + # max_iters=1000, + # bootstrap_size=150, + # out_folder=study, + # ) + # run_optimization( + # X, y, model_type="nn", n_runs=10, max_iters=1000, bootstrap_size=150, out_folder=study + # ) diff --git a/bird/postprocess/SA_optimization/get_optimal_with_constraint.py b/bird/postprocess/SA_optimization/get_optimal_with_constraint.py new file mode 100644 index 00000000..b9a28af1 --- /dev/null +++ b/bird/postprocess/SA_optimization/get_optimal_with_constraint.py @@ -0,0 +1,280 @@ +import logging +import os +import random + +import numpy as np +import optuna +import pandas as pd +from prettyPlot.plotting import * +from sklearn.preprocessing import OneHotEncoder + +from bird.postprocess.SA_optimization.surrogate import ( + Surrogate_wrapper, + tune_nn, + tune_rbf, + tune_rf, +) + +logger = logging.getLogger(__name__) + + +def simulated_annealing_surrogate( + surrogate: Surrogate_wrapper, + dim: int = 12, + max_iters: int = 1000, + max_spargers: int = 8, + temp: float = 10.0, + alpha: float = 0.95, +) -> tuple[np.ndarray, float, list[tuple]]: + """ + Runs the Simulated Annealing (SA) constrained by the number of spargers and reports the results + + Parameters + ---------- + surrogate : Surrogate_wrapper + tuned surrogate model + dim: int + dimension of the problem + max_iters: int + maximum number of iterations for SA + max_spargers: int + maximum number of spargers (this is a constraint of the optimization problem) + temp: float + parameter of Simulated Annealing (can be changed or tuned for other problems) + max temperature. It controls the exploration rate of SA + alpha: + parameter of Simulated Annealing (can be changed or tuned for other problems) + cooling rate. It determines how quickly temp decays + + Returns + ---------- + x_best: np.ndarray + optimal solution + y_best: float + optimal objective function value + trace: list[tuple] + optimization log. Updates at the end of each iteration. + """ + + def is_valid(x): + # Checks if the number of spargers <= max_spargers + return np.sum(x == 1) <= max_spargers + + while True: + # generate random starting point + x_curr = np.random.randint(0, 3, size=dim) + if is_valid(x_curr): + break + + y_curr = surrogate.predict(x_curr) + x_best, y_best = x_curr.copy(), y_curr + + trace = [(0, y_best)] + + for i in range(max_iters): + # Optimization loop + + x_new = x_curr.copy() + idx = random.randint(0, dim - 1) + # perturb a random dimension to get new x + x_new[idx] = (x_new[idx] + random.choice([-1, 1])) % 3 + + if not is_valid(x_new): + trace.append((i, y_best)) + temp *= alpha + continue + + y_new = surrogate.predict(x_new) + + delta = y_new - y_curr + if delta < 0 or np.random.rand() < np.exp(-delta / temp): + x_curr, y_curr = x_new, y_new + if y_curr < y_best: + x_best, y_best = x_new.copy(), y_new + + trace.append((i, y_best)) + temp *= alpha + + return x_best, y_best, trace + + +def run_optimization( + X: np.ndarray, + y: np.ndarray, + model_type: str = "rbf", + max_spargers: int = 8, + n_runs: int = 10, + max_iters: int = 1000, + bootstrap_size: int = 100, + out_folder: str = ".", +): + """ + Bootstraps data, runs optimization and postprocesses the results. + + Parameters + ---------- + X: np.ndarray + Raw design configuration + Dimension N by d, where N is the number of simulations, + and d is the number of design variables + y: np.ndarray + Raw array of quantity of interest + Dimension N by 1, where N is the number of simulations + + model_type : str + 'rbf' for RBFInterpolator + 'rf' for Random Forest + 'nn' for Neural Network + + max_spargers: int + maximum number of spargers (this is a constraint of the optimization problem) + n_runs :int + number of bootstraps + max_iters: int + maximum number of SA iterations + bootstrap_size : int + size of bootstrap samples + out_folder: str + folder where to output the results + + Returns + ---------- + """ + + all_x = [] + all_y = [] + all_traces = np.zeros((n_runs, max_iters + 1)) + rng = np.random.default_rng(42) + # Bootstrap the data + bootstrap_idxs = [ + rng.choice(len(X), size=bootstrap_size, replace=False) + for _ in range(n_runs) + ] + + i = 0 + for idxs in bootstrap_idxs: + # Tune the model for each bootstrap + X_sub, y_sub = X[idxs], y[idxs] + if model_type == "rbf": + params = tune_rbf(X_sub, y_sub) + elif model_type == "rf": + params = tune_rf(X_sub, y_sub) + elif model_type == "nn": + encoder = OneHotEncoder(sparse_output=False) + X_encoded = encoder.fit_transform(X_sub) + params = tune_nn(X_encoded, y_sub) + else: + raise ValueError("Invalid model_type") + + # build the surrogate model + surrogate = Surrogate_wrapper(model_type, X_sub, y_sub, params) + + # run optimization + x_best, y_best, trace = simulated_annealing_surrogate( + surrogate, + dim=X.shape[1], + max_iters=max_iters, + max_spargers=max_spargers, + ) + + trace_y = [y for _, y in trace] + all_traces[i, :] = trace_y + all_x.append(x_best) + all_y.append(y_best) + i = i + 1 + + # Compute the best y across all the bootstraps + best_index = np.argmin(all_y) + best_x = all_x[best_index] + best_y = all_y[best_index] + # Save the best solution + df = pd.DataFrame( + [ + { + **{f"x{i}": best_x[i] for i in range(len(best_x))}, + "best_y": best_y, + } + ] + ) + df.to_csv( + os.path.join( + out_folder, + f"best_bootstrap_solution_{model_type}_size_{bootstrap_size}_max_spargers_{max_spargers}.csv", + ), + index=False, + ) + + logger.info(f"X = {x_best}") + logger.info(f"surrogate-predicted y = {y_best}") + + # Make the mean-CI plot for the objective function and save it + mean_trace = np.mean(-1 * all_traces, axis=0) + std_trace = np.std(all_traces, axis=0) + lower_bound = mean_trace - 1.96 * std_trace / np.sqrt(n_runs) + upper_bound = mean_trace + 1.96 * std_trace / np.sqrt(n_runs) + iterations = np.arange(max_iters + 1) + + plt.figure(figsize=(8, 4)) + plt.plot( + iterations, + mean_trace, + label=f"Mean Convergence {model_type.upper()}", + color="blue", + ) + plt.fill_between( + iterations, + lower_bound, + upper_bound, + color="blue", + alpha=0.3, + label="95% CI", + ) + pretty_labels( + "Iteration", + "Best Surrogate-Predicted Objective", + fontsize=16, + title=f"Mean Convergence with 95% Confidence Interval ({model_type.upper()})", + grid=True, + fontname="Times", + ) + pretty_legend(fontsize=16, fontname="Times") + plt.savefig( + os.path.join( + out_folder, + f"Mean_Convergence_plot_{model_type}_size_{bootstrap_size}_max_spargers_{max_spargers}.png", + ), + dpi=300, + ) + plt.savefig( + os.path.join( + out_folder, + f"Mean_Convergence_plot_{model_type}_size_{bootstrap_size}_max_spargers_{max_spargers}.pdf", + ), + ) + plt.show() + + +if __name__ == "__main__": + studies = ["study_scaleup_0_4vvm_3000W"] + + for study in studies: + for nsparg in [1, 2, 3, 4, 5, 6, 7, 8]: + X_raw_data = pd.read_csv(os.path.join(study, "Xdata.csv")) + y_raw_data = pd.read_csv(os.path.join(study, "ydata.csv")) + + X = X_raw_data.values + y = y_raw_data.iloc[:, :-1].values + # We want to maximize, so we minimize the opposite + y = y * -1 + + # The function will build, tune, and optimize the surrogate model and postprocess the results. + run_optimization( + X, + y, + model_type="rbf", + max_spargers=nsparg, + n_runs=10, + max_iters=1000, + bootstrap_size=150, + out_folder=study, + ) diff --git a/bird/postprocess/SA_optimization/plot_qoi_vs_sparg.py b/bird/postprocess/SA_optimization/plot_qoi_vs_sparg.py new file mode 100644 index 00000000..b05b85fc --- /dev/null +++ b/bird/postprocess/SA_optimization/plot_qoi_vs_sparg.py @@ -0,0 +1,13 @@ +import numpy as np +from prettyPlot.plotting import * + +nsparg = [1, 2, 3, 4, 5, 6, 7, 8] +qoi_opt = [13.485, 14.276, 14.635, 15.692, 16.072, 17.657, 19.349, 20.712] + +fig = plt.figure() +plt.plot(nsparg, qoi_opt, linewidth=3, color="k") +pretty_labels( + r"N$_{\rm sparg}$", r"Optimal QOI [kg$^2$/kWh$^2$]", 20, fontname="Times" +) +plt.savefig("marginal_gain.png") +plt.savefig("marginal_gain.pdf") diff --git a/bird/postprocess/SA_optimization/surrogate.py b/bird/postprocess/SA_optimization/surrogate.py new file mode 100644 index 00000000..0c670352 --- /dev/null +++ b/bird/postprocess/SA_optimization/surrogate.py @@ -0,0 +1,284 @@ +import random +import warnings + +import numpy as np +import optuna +import pandas as pd +from scipy.interpolate import RBFInterpolator +from sklearn.ensemble import RandomForestRegressor +from sklearn.metrics import mean_squared_error +from sklearn.model_selection import KFold, cross_val_score +from sklearn.preprocessing import OneHotEncoder +from tensorflow.keras.layers import Dense +from tensorflow.keras.models import Model as tfModel +from tensorflow.keras.models import Sequential + +warnings.filterwarnings("ignore") +import logging + +logger = logging.getLogger(__name__) + + +def check_data_shape(X: np.ndarray, y: np.ndarray) -> None: + """ + Tune the shape parameter (epsilon) of the multiquadratic RBF + + Parameters + ---------- + X : np.ndarray + Design configuration + Dimension N by d, where N is the number of simulations, + and d is the number of design variables + y: np.ndarray + Array of quantity of interest + Dimension N by 1, where N is the number of simulations + """ + + # Same number of samples + assert X.shape[0] == y.shape[0] + # Arrays are 2 dimensional + assert len(X.shape) == 2 + assert len(y.shape) == 2 + # only 1 QoI + assert y.shape[1] == 1 + logger.info(f"{X.shape[0]} sim with {X.shape[1]} design variables") + + +def tune_rbf(X: np.ndarray, y: np.ndarray) -> dict: + """ + Tune the shape parameter (epsilon) of the multiquadratic RBF + + Parameters + ---------- + X : np.ndarray + Design configuration + Dimension N by d, where N is the number of simulations, + and d is the number of design variables + y: np.ndarray + Array of quantity of interest + Dimension N by 1, where N is the number of simulations + Returns + ---------- + params: dict + dictionary of optimal parameters + + """ + + check_data_shape(X=X, y=y) + + # Tune the RBFInterpolator with optuna + # kernel - "multiquadric" (Can try other kernels) + def objective(trial): + epsilon = trial.suggest_float("epsilon", 0.1, 10.0, log=False) + try: + kf = KFold(n_splits=5, shuffle=True, random_state=42) + return np.mean( + [ + mean_squared_error( + y[test], + RBFInterpolator( + X[train], + y[train], + epsilon=epsilon, + kernel="multiquadric", + )(X[test]), + ) + for train, test in kf.split(X) + ] + ) + except: + return float("inf") + + study = optuna.create_study(direction="minimize") + study.optimize(objective, n_trials=20) + return study.best_params + + +def tune_rf(X: np.ndarray, y: np.ndarray): + """ + Tune the number of trees (n_estimators) + tree depth (max_depth) + number of samples in a leaf (min_samples_leaf) + of the RandomForestRegressor + + Parameters + ---------- + X : np.ndarray + Design configuration + Dimension N by d, where N is the number of simulations, + and d is the number of design variables + y: np.ndarray + Array of quantity of interest + Dimension N by 1, where N is the number of simulations + + Returns + ---------- + params: dict + dictionary of optimal parameters + + """ + + check_data_shape(X=X, y=y) + + # Tune the Random Forest + def objective(trial): + model = RandomForestRegressor( + n_estimators=trial.suggest_int("n_estimators", 50, 200), + max_depth=trial.suggest_int("max_depth", 3, 10), + min_samples_leaf=trial.suggest_int("min_samples_leaf", 1, 10), + random_state=42, + ) + scores = cross_val_score( + model, X, y, cv=5, scoring="neg_mean_squared_error" + ) + return -np.mean(scores) + + study = optuna.create_study(direction="minimize") + study.optimize(objective, n_trials=20) + return study.best_params + + +def tune_nn(X_encoded: np.ndarray, y: np.ndarray) -> dict: + """ + Tune the number of neurons (n_units) + number of layers (n_layers) + + in a leaf (min_samples_leaf) of the RandomForestRegressor + + Parameters + ---------- + X_encoded : np.ndarray + Design configuration + Dimension N by d, where N is the number of simulations, + and d is the number of design variables + y: np.ndarray + Array of quantity of interest + Dimension N by 1, where N is the number of simulations + + Returns + ---------- + params: dict + dictionary of optimal parameters + + """ + check_data_shape(X=X_encoded, y=y) + + # Tune the Neural Network + def objective(trial): + units = trial.suggest_int("n_units", 16, 128) + layers = trial.suggest_int("n_layers", 1, 3) + kf = KFold(n_splits=5, shuffle=True, random_state=42) + mses = [] + for train, test in kf.split(X_encoded): + model = Sequential() + model.add( + Dense( + units, activation="relu", input_shape=(X_encoded.shape[1],) + ) + ) + for _ in range(layers - 1): + model.add(Dense(units, activation="relu")) + model.add(Dense(1)) + model.compile(optimizer="adam", loss="mse") + model.fit( + X_encoded[train], + y[train], + epochs=100, + batch_size=16, + verbose=0, + ) + mses.append( + mean_squared_error( + y[test], model.predict(X_encoded[test]).flatten() + ) + ) + return np.mean(mses) + + study = optuna.create_study(direction="minimize") + study.optimize(objective, n_trials=20) + return study.best_params + + +class Surrogate_wrapper: + """ + Wrapper that builds the surrogate model and predicts the QOI value for given X + """ + + def __init__( + self, model_type: str, X: np.ndarray, y: np.ndarray, params: dict + ): + """ + Create the surroagte wrapper + + Parameters + ---------- + model_type : str + 'rbf' for RBFInterpolator + 'rf' for Random Forest + 'nn' for Neural Network + + X: np.ndarray + Raw design configuration + Dimension N by d, where N is the number of simulations, + and d is the number of design variables + y: np.ndarray + Raw array of quantity of interest + Dimension N by 1, where N is the number of simulations + params: dict + dictionary of surrogate parameters + + """ + self.model_type = model_type + self.encoder = None + + if model_type.lower() == "rbf": + self.model = RBFInterpolator( + X, y, kernel="multiquadric", epsilon=params["epsilon"] + ) + elif model_type.lower() == "rf": + self.model = RandomForestRegressor(**params, random_state=42) + self.model.fit(X, y) + elif model_type.lower() == "nn": + self.encoder = OneHotEncoder(sparse_output=False) + X_encoded = self.encoder.fit_transform(X) + self.model = self._build_nn( + X_encoded.shape[1], params["n_units"], params["n_layers"] + ) + self.model.fit(X_encoded, y, epochs=100, batch_size=16, verbose=0) + else: + raise NotImplementedError + + def _build_nn(self, input_dim: int, units: int, layers: int) -> tfModel: + """ + Builds the neural net + + Parameters + ---------- + input_dim : int + number of features + units: int + number of neurons per layer + layers: int + number of hidden layers + + Returns + ---------- + model: tfModel + """ + model = Sequential() + model.add(Dense(units, activation="relu", input_shape=(input_dim,))) + for _ in range(layers - 1): + model.add(Dense(units, activation="relu")) + model.add(Dense(1)) + model.compile(optimizer="adam", loss="mse") + return model + + def predict(self, X): + X = X.reshape(1, -1) + if self.model_type == "nn": + X = self.encoder.transform(X) + return float(self.model.predict(X)[0]) + elif self.model_type == "rf": + return float(self.model.predict(X)[0]) + else: + return float(self.model(X)[0]) diff --git a/bird/postprocess/computeQoI/compute_QOI.py b/bird/postprocess/computeQoI/compute_QOI.py index e7074d12..257774d8 100644 --- a/bird/postprocess/computeQoI/compute_QOI.py +++ b/bird/postprocess/computeQoI/compute_QOI.py @@ -1,15 +1,12 @@ import argparse -import sys - -import numpy as np - -sys.path.append("../utilities") import os import pickle +import sys -from ofio import * +import numpy as np -from bird.utilities.bubble_col_util import * +from bird.postprocess.post_quantities import * +from bird.utilities.ofio import * parser = argparse.ArgumentParser( description="Compute means QoI of OpenFOAM fields" @@ -29,15 +26,14 @@ nargs="+", help="List of variables to compute", default=[ - "GH", - "GH_height", + "gh", "d", "CO2_liq", "CO_liq", "H2_liq", - "kla_CO2", - "kla_CO", - "kla_H2", + "c_CO2_liq", + "c_CO_liq", + "c_H2_liq", ], required=False, ) @@ -109,87 +105,80 @@ def get_var( localFolder = os.path.join(case_path, time_folder) localFolder_vol = os.path.join(case_path, mesh_time_str) if name.lower() == "gh": - var, val_dict = computeGH( - localFolder, localFolder_vol, nCells, cellCentres, val_dict - ) - elif name.lower() == "gh_height": - var, val_dict = computeGH_height( - localFolder, + var, val_dict = compute_gas_holdup( + case_path, + time_folder, nCells, - cellCentres, - height_liq_base=7.0, - val_dict=val_dict, + volume_time="0", + field_dict=val_dict, ) elif name.lower() == "d": - var, val_dict = computeDiam(localFolder, nCells, cellCentres, val_dict) + var, val_dict = compute_ave_bubble_diam( + case_path, + time_folder, + nCells, + volume_time="0", + field_dict=val_dict, + ) elif name.lower() == "co2_liq": - var, val_dict = computeSpec_liq( - localFolder, + var, val_dict = compute_ave_y_liq( + case_path, + time_folder, nCells, - field_name="CO2.liquid", - key="co2_liq", - cellCentres=cellCentres, - val_dict=val_dict, + volume_time="0", + spec_name="CO2", + field_dict=val_dict, ) elif name.lower() == "co_liq": - var, val_dict = computeSpec_liq( - localFolder, + var, val_dict = compute_ave_y_liq( + case_path, + time_folder, nCells, - field_name="CO.liquid", - key="co_liq", - cellCentres=cellCentres, - val_dict=val_dict, + volume_time="0", + spec_name="CO", + field_dict=val_dict, ) elif name.lower() == "h2_liq": - var, val_dict = computeSpec_liq( - localFolder, + var, val_dict = compute_ave_y_liq( + case_path, + time_folder, nCells, - field_name="H2.liquid", - key="h2_liq", - cellCentres=cellCentres, - val_dict=val_dict, + volume_time="0", + spec_name="H2", + field_dict=val_dict, ) - elif name.lower() == "kla_co": - if "D_CO" in diff_name_list: - diff = diff_val_list[diff_name_list.index("D_CO")] - else: - diff = None - var, val_dict = computeSpec_kla( - localFolder, + elif name.lower() == "c_co2_liq": + var, val_dict = compute_ave_conc_liq( + case_path, + time_folder, nCells, - key_suffix="co", - cellCentres=cellCentres, - val_dict=val_dict, - diff=diff, + volume_time="0", + spec_name="CO2", + mol_weight=44.00995 * 1e-3, + field_dict=val_dict, ) - elif name.lower() == "kla_co2": - if "D_CO2" in diff_name_list: - diff = diff_val_list[diff_name_list.index("D_CO2")] - else: - diff = None - var, val_dict = computeSpec_kla( - localFolder, + elif name.lower() == "c_co_liq": + var, val_dict = compute_ave_conc_liq( + case_path, + time_folder, nCells, - key_suffix="co2", - cellCentres=cellCentres, - val_dict=val_dict, - diff=diff, + volume_time="0", + spec_name="CO", + mol_weight=28.01055 * 1e-3, + field_dict=val_dict, ) - elif name.lower() == "kla_h2": - if "D_H2" in diff_name_list: - diff = diff_val_list[diff_name_list.index("D_H2")] - else: - diff = None - var, val_dict = computeSpec_kla( - localFolder, + elif name.lower() == "c_h2_liq": + var, val_dict = compute_ave_conc_liq( + case_path, + time_folder, nCells, - key_suffix="h2", - cellCentres=cellCentres, - val_dict=val_dict, - diff=diff, + volume_time="0", + spec_name="H2", + mol_weight=2.01594 * 1e-3, + field_dict=val_dict, ) else: - sys.exit(f"ERROR: unknown variable {name}") + raise NotImplementedError(f"Unknown variable {name}") return var, val_dict diff --git a/bird/postprocess/conditional_mean.py b/bird/postprocess/conditional_mean.py index 19918882..e940413e 100644 --- a/bird/postprocess/conditional_mean.py +++ b/bird/postprocess/conditional_mean.py @@ -1,12 +1,15 @@ +import logging import os import pickle from prettyPlot.plotting import plt -from bird.utilities.bubble_col_util import * +from bird.postprocess.post_quantities import * from bird.utilities.mathtools import * from bird.utilities.ofio import * +logger = logging.getLogger(__name__) + def compute_cond_mean( case_path, @@ -32,65 +35,20 @@ def compute_cond_mean( fields_cond[name] = {} fields_cond_tmp[name] = {} - print(f"Case : {case_path}") + logger.info(f"Case : {case_path}") for i_ave in range(window_ave): time_folder = time_str_sorted[-i_ave - 1] - print(f"\tReading Time : {time_folder}") + logger.debug(f"\tReading Time : {time_folder}") field_file = [] for field_name in field_name_list: field_file.append(os.path.join(case_path, time_folder, field_name)) - # if os.path.isfile(d_gas_file): - # has_d = True - # else: - # has_d = False - for filename, name in zip(field_file, field_name_list): val_dict = {} - if name.lower() == "kla_co": - if "D_CO" in diff_name_list: - diff = diff_val_list[diff_name_list.index("D_CO")] - else: - diff = None - field_tmp, val_dict = computeSpec_kla_field( - os.path.join(case_path, time_folder), - nCells, - key_suffix="co", - cellCentres=cellCentres, - val_dict=val_dict, - diff=diff, - ) - elif name.lower() == "kla_co2": - if "D_CO2" in diff_name_list: - diff = diff_val_list[diff_name_list.index("D_CO2")] - else: - diff = None - field_tmp, val_dict = computeSpec_kla_field( - os.path.join(case_path, time_folder), - nCells, - key_suffix="co2", - cellCentres=cellCentres, - val_dict=val_dict, - diff=diff, - ) - elif name.lower() == "kla_h2": - if "D_H2" in diff_name_list: - diff = diff_val_list[diff_name_list.index("D_H2")] - else: - diff = None - field_tmp, val_dict = computeSpec_kla_field( - os.path.join(case_path, time_folder), - nCells, - key_suffix="h2", - cellCentres=cellCentres, - val_dict=val_dict, - diff=diff, - ) - else: - field_tmp = readOFScal(filename, nCells) - vert_axis, field_cond_tmp = conditionalAverage( - cellCentres[:, vert_ind], field_tmp, nbin=n_bins + field_tmp = readOFScal(filename, nCells)["field"] + vert_axis, field_cond_tmp = conditional_average( + cellCentres[:, vert_ind], field_tmp, nbins=n_bins ) if i_ave == 0: fields_cond[name]["val"] = field_cond_tmp / window_ave @@ -116,8 +74,8 @@ def sequencePlot( if not len(case_names) == len(folder_names): case_names = [f"test{i}" for i in range(len(folder_names))] if len(case_names) > len(symbList): - print( - f"ERROR: too many cases ({len(case_names)}), reduce number of case to {len(symbList)} or add symbols" + logger.error( + f"too many cases ({len(case_names)}), reduce number of case to {len(symbList)} or add symbols" ) sys.exit() for ic, (case_name, folder_name) in enumerate( diff --git a/bird/postprocess/data_conditional_mean/python_interface_tut.py b/bird/postprocess/data_conditional_mean/python_interface_tut.py new file mode 100644 index 00000000..59258b9e --- /dev/null +++ b/bird/postprocess/data_conditional_mean/python_interface_tut.py @@ -0,0 +1,71 @@ +# Import the relevant IO functions +from bird.utilities.ofio import * + +# Read cell centers +cell_centers = readMesh("meshCellCentres_1.obj") +print("cell centers shape = ", cell_centers.shape) + +# Read relevant fields at time 80 +co2_gas = readOF("80/CO2.gas") +alpha_gas = readOF("80/alpha.gas") +u_liq = readOF("80/U.liquid") +print("cell CO2 gas shape = ", co2_gas["field"].shape) +print("cell alpha gas shape = ", alpha_gas["field"].shape) +print("cell u liq shape = ", u_liq["field"].shape) + +# Compute conditional average of co2_gas and alpha_gas over y +from bird.utilities.mathtools import conditional_average + +y_co2_gas_cond, co2_gas_cond = conditional_average( + cell_centers[:, 1], co2_gas["field"], nbins=32 +) +y_alpha_gas_cond, alpha_gas_cond = conditional_average( + cell_centers[:, 1], alpha_gas["field"], nbins=32 +) + +# Plot +from prettyPlot.plotting import * + +fig = plt.figure() +plt.plot(y_co2_gas_cond, co2_gas_cond, color="k", label=r"$Y_{CO_2}$ [-]") +plt.plot( + y_alpha_gas_cond, alpha_gas_cond, color="b", label=r"$\alpha_{g}$ [-]" +) +pretty_labels("Y [m]", "", fontsize=20, grid=False, fontname="Times") +pretty_legend(fontname="Times") +plt.show() + +# Compute reactor quantities +from bird.postprocess.post_quantities import * + +kwargs = {"case_folder": ".", "time_folder": "80"} +gh, field_dict = compute_gas_holdup( + volume_time="1", field_dict={"cell_centers": cell_centers}, **kwargs +) +print("fields stored = ", list(field_dict.keys())) +print(f"Gas Holdup = {gh:.4g}") +sup_vel, field_dict = compute_superficial_velocity( + volume_time="1", field_dict=field_dict, **kwargs +) +print("fields stored = ", list(field_dict.keys())) +print(f"Superficial velocity = {sup_vel:.4g} m/s") +y_ave_co2, field_dict = compute_ave_y_liq( + volume_time="1", spec_name="CO2", field_dict=field_dict, **kwargs +) +print("fields stored = ", list(field_dict.keys())) +print(f"Reactor averaged YCO2 = {y_ave_co2:.4g}") +c_ave_co2, field_dict = compute_ave_conc_liq( + volume_time="1", + spec_name="CO2", + mol_weight=0.04401, + rho_val=1000, + field_dict=field_dict, + **kwargs, +) +print("fields stored = ", list(field_dict.keys())) +print(f"Reactor averaged [CO2] = {c_ave_co2:.4g} mol/m3") +diam, field_dict = compute_ave_bubble_diam( + volume_time="1", field_dict=field_dict, **kwargs +) +print("fields stored = ", list(field_dict.keys())) +print(f"Reactor averaged bubble diameter = {diam:.4g} m") diff --git a/bird/postprocess/early_pred.py b/bird/postprocess/early_pred.py index 0dae2d44..d96edf26 100644 --- a/bird/postprocess/early_pred.py +++ b/bird/postprocess/early_pred.py @@ -1,15 +1,28 @@ +import logging import os import corner +import jax import jax.numpy as jnp import jax.random as random import numpy as np +from packaging import version + +if version.parse(jax.__version__) >= version.parse("0.7.0"): + # https://github.com/pyro-ppl/numpyro/issues/2051 + import jax.experimental.pjit + from jax.extend.core.primitives import jit_p + + jax.experimental.pjit.pjit_p = jit_p + import numpyro import numpyro.distributions as dist from numpyro.infer import MCMC, NUTS from prettyPlot.plotting import * from scipy.optimize import curve_fit +logger = logging.getLogger(__name__) + def plotAllEarly(data_dict, color_files=None, chop=False, extrap=False): fig = plt.figure() @@ -95,7 +108,7 @@ def multi_data_load(data_root, tmax=600, data_files=None, color_files=None): increase_ind = increase_ind_arr[ np.argwhere(data_dict[datf]["t"][increase_ind_arr] > 10)[0][0] ][0] - print( + logger.info( f"data {datf} first time {data_dict[datf]['t'][increase_ind]:.2f}" ) data_dict[datf]["lim"] = increase_ind @@ -135,7 +148,7 @@ def fit_and_ext( bounds=bounds, ) data_dict[datf]["yextrap"] = func(data_dict[datf]["textrap"], *popt) - print(f"data {datf} coeff {popt}") + logger.info(f"data {datf} coeff {popt}") lim_ind = data_dict[datf]["lim"] data_dict[datf]["textrap"] += data_dict[datf]["t"][lim_ind] data_dict[datf]["yextrap"] += data_dict[datf]["y"][lim_ind] diff --git a/bird/postprocess/kla_utils.py b/bird/postprocess/kla_utils.py index d5d69573..28095d0d 100644 --- a/bird/postprocess/kla_utils.py +++ b/bird/postprocess/kla_utils.py @@ -1,12 +1,26 @@ +import logging + import corner +import jax import jax.numpy as jnp import jax.random as random import numpy as np +from packaging import version + +if version.parse(jax.__version__) >= version.parse("0.7.0"): + # https://github.com/pyro-ppl/numpyro/issues/2051 + import jax.experimental.pjit + from jax.extend.core.primitives import jit_p + + jax.experimental.pjit.pjit_p = jit_p + import numpyro import numpyro.distributions as dist from numpyro.infer import MCMC, NUTS from prettyPlot.plotting import * +logger = logging.getLogger(__name__) + def read_data( filename: str, time_ind: int, conc_ind: int, ind_start=0 @@ -165,7 +179,7 @@ def compute_kla( for ind_start in range(len(data_t_tmp) - 5): if max_chop is not None: max_chop -= 1 - print(f"Chopping index = {ind_start}") + logger.debug(f"Chopping index = {ind_start}") data_t, data_c = read_data(filename, time_ind, conc_ind, ind_start) # Hamiltonian Monte Carlo (HMC) with no u turn sampling (NUTS) @@ -223,10 +237,10 @@ def compute_kla( cstar_boot = [] cstar_err_boot = [] bootstrapped = True - print("Doing data bootstrapping") + logger.info("Doing data bootstrapping") for i in range(4): - print(f"\t scenario {i}") + logger.info(f"\t scenario {i}") if i == 0: data_t = data_t_tmp[ind_start + 1 :] data_c = data_c_tmp[ind_start + 1 :] @@ -361,13 +375,15 @@ def print_res_dict(res_dict: dict) -> None: bs = res_dict["bootstrapped"] - print(f"For {file} with time index: {t_ind}, concentration index: {c_ind}") + logger.info( + f"For {file} with time index: {t_ind}, concentration index: {c_ind}" + ) if bs: - print(f"\tkla = {kla:.4g} +/- {kla_err:.4g}") - print(f"\tcstar = {cs:.4g} +/- {cs_err:.4g}") - print(f"Without data bootstrap") - print(f"\tkla = {kla_nb:.4g} +/- {kla_err_nb:.4g}") - print(f"\tcstar = {cs_nb:.4g} +/- {cs_err_nb:.4g}") + logger.info(f"\tkla = {kla:.4g} +/- {kla_err:.4g}") + logger.info(f"\tcstar = {cs:.4g} +/- {cs_err:.4g}") + logger.info(f"Without data bootstrap") + logger.info(f"\tkla = {kla_nb:.4g} +/- {kla_err_nb:.4g}") + logger.info(f"\tcstar = {cs_nb:.4g} +/- {cs_err_nb:.4g}") if __name__ == "__main__": diff --git a/bird/postprocess/post_quantities.py b/bird/postprocess/post_quantities.py new file mode 100644 index 00000000..6733806a --- /dev/null +++ b/bird/postprocess/post_quantities.py @@ -0,0 +1,647 @@ +import numpy as np + +from bird.utilities.ofio import * + + +def read_field( + case_folder: str, + time_folder: str, + field_name: str, + n_cells: int | None = None, + field_dict: dict = {}, +) -> tuple: + """ + Read field at a given time and store it in dictionary for later reuse + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + field_name: str + Name of the field file to read + n_cells : int | None + Number of cells in the domain + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + field : np.ndarray | float + Field read + field_dict : dict + Dictionary of fields read + """ + + if not (field_name in field_dict) or field_dict[field_name] is None: + # Read field if it had not been read before + field_file = os.path.join(case_folder, time_folder, field_name) + field = readOF(field_file, n_cells=n_cells)["field"] + field_dict[field_name] = field + else: + # Get field from dict if it has been read before + field = field_dict[field_name] + + return field, field_dict + + +def read_cell_volume( + case_folder: str, + time_folder: str, + n_cells: int | None = None, + field_dict: dict = {}, +) -> tuple: + """ + Read volume at a given time and store it in dictionary for later reuse + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + cell_volume : np.ndarray | float + cell volume read from file + field_dict : dict + Dictionary of fields read + """ + kwargs_vol = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + try: + cell_volume, field_dict = read_field( + field_name="V", field_dict=field_dict, **kwargs_vol + ) + except FileNotFoundError: + message = f"ERROR: could not find {os.path.join(case_folder,volume_time,'V')}\n" + message += "You can generate V with\n\t" + message += f"`postProcess -func writeCellVolumes -time {time_folder} -case {case_folder}`" + sys.exit(message) + + return cell_volume, field_dict + + +def read_cell_centers( + case_folder: str, + cell_centers_file: str, + field_dict: dict = {}, +) -> tuple: + """ + Read volume at a given time and store it in dictionary for later reuse + + Parameters + ---------- + case_folder: str + Path to case folder + cell_centers_file : str + Filename of cell center data + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + cell_centers : np.ndarray + cell centers read from file + field_dict : dict + Dictionary of fields read + """ + + if ( + not ("cell_centers" in field_dict) + or field_dict["cell_centers"] is None + ): + try: + cell_centers = readMesh( + os.path.join(case_folder, cell_centers_file) + ) + field_dict["cell_centers"] = cell_centers + except FileNotFoundError: + message = f"ERROR: could not find {cell_centers_file}\n" + message += "You can generate it with\n\t" + message += f"`writeMeshObj -case {case_folder}`\n" + time_float, time_str = getCaseTimes(case_folder) + correct_path = f"meshCellCentres_{time_str[0]}.obj" + if not correct_path == cell_centers_file: + message += ( + f"And adjust the cell center file path to {correct_path}" + ) + sys.exit(message) + else: + cell_centers = field_dict["cell_centers"] + + return cell_centers, field_dict + + +def get_ind_liq( + case_folder: str | None = None, + time_folder: str | None = None, + n_cells: int | None = None, + field_dict: dict = {}, +) -> tuple: + """ + Get indices of pure liquid cells (where alpha.liquid > 0.5) + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + field_name: str + Name of the field file to read + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + ind_liq : np.ndarray | float + indices of pure liquid cells + field_dict : dict + Dictionary of fields read + """ + + kwargs = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + + # Compute indices of pure liquid + if not ("ind_liq" in field_dict) or field_dict["ind_liq"] is None: + alpha_liq, field_dict = read_field( + case_folder, + time_folder, + field_name="alpha.liquid", + n_cells=n_cells, + field_dict=field_dict, + ) + ind_liq = np.argwhere(alpha_liq > 0.5) + field_dict["ind_liq"] = ind_liq + else: + ind_liq = field_dict["ind_liq"] + + return ind_liq, field_dict + + +def get_ind_gas( + case_folder: str | None = None, + time_folder: str | None = None, + n_cells: int | None = None, + field_dict: dict = {}, +) -> tuple: + """ + Get indices of pure gas cells (where alpha.liquid <= 0.5) + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + field_name: str + Name of the field file to read + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + ind_gas : np.ndarray | float + indices of pure gas cells + field_dict : dict + Dictionary of fields read + """ + + kwargs = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + + # Compute indices of pure liquid + if not ("ind_gas" in field_dict) or field_dict["ind_gas"] is None: + alpha_liq = read_field( + case_folder, + time_folder, + field_name="alpha.liquid", + n_cells=n_cells, + field_dict=field_dict, + ) + ind_gas = np.argwhere(alpha_liq <= 0.5) + field_dict["ind_gas"] = ind_gas + else: + ind_gas = field_dict["ind_gas"] + + return ind_gas, field_dict + + +def compute_gas_holdup( + case_folder: str, + time_folder: str, + n_cells: int | None = None, + volume_time: str = "0", + field_dict: dict = {}, +) -> tuple: + """ + Calculate volume averaged gas hold up at a given time + + .. math:: + \frac{1}{V_{\rm tot}} \int_{V} (1-\alpha_{\rm liq}) dV + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + volume_time : str + Time folder to read to get the cell volumes + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + gas_holdup: float + Volume averaged gas holdup + field_dict : dict + Dictionary of fields read + """ + + # Read relevant fields + kwargs = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + kwargs_vol = { + "case_folder": case_folder, + "time_folder": volume_time, + "n_cells": n_cells, + } + alpha_liq, field_dict = read_field( + field_name="alpha.liquid", field_dict=field_dict, **kwargs + ) + cell_volume, field_dict = read_cell_volume( + field_dict=field_dict, **kwargs_vol + ) + + # Calculate + gas_holdup = np.sum((1 - alpha_liq) * cell_volume) / np.sum(cell_volume) + + return gas_holdup, field_dict + + +def compute_superficial_velocity( + case_folder: str, + time_folder: str, + n_cells: int | None = None, + volume_time: str = "0", + direction: int = 1, + cell_centers_file: str = "meshCellCentres_0.obj", + field_dict: dict = {}, +) -> tuple: + """ + Calculate superficial velocity in a given direction at a given time + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + volume_time : str + Time folder to read to get the cell volumes + direction : int + Direction along which to calculate the superficial velocity + cell_centers_file : str + Filename of cell center data + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + sup_vel: float + Superficial velocity + field_dict : dict + Dictionary of fields read + """ + + # Read relevant fields + kwargs = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + kwargs_vol = { + "case_folder": case_folder, + "time_folder": volume_time, + "n_cells": n_cells, + } + alpha_gas, field_dict = read_field( + field_name="alpha.gas", field_dict=field_dict, **kwargs + ) + U_gas, field_dict = read_field( + field_name="U.gas", field_dict=field_dict, **kwargs + ) + U_gas = U_gas[:, direction] + + cell_volume, field_dict = read_cell_volume( + field_dict=field_dict, **kwargs_vol + ) + cell_centers, field_dict = read_cell_centers( + case_folder=case_folder, + cell_centers_file=cell_centers_file, + field_dict=field_dict, + ) + + # Find all cells in the middle of the domain + max_dir = np.amax(cell_centers[:, direction]) + min_dir = np.amin(cell_centers[:, direction]) + middle_dir = (max_dir + min_dir) / 2 + tol = (max_dir - min_dir) * 0.05 + ind_middle = np.argwhere( + (cell_centers[:, direction] >= middle_dir - tol) + & (cell_centers[:, direction] <= middle_dir + tol) + ) + + # Only compute in the middle + if isinstance(alpha_gas, np.ndarray): + alpha_gas = alpha_gas[ind_middle] + if isinstance(cell_volume, np.ndarray): + cell_volume = cell_volume[ind_middle] + if isinstance(U_gas, np.ndarray): + U_gas = U_gas[ind_middle] + + # Compute + sup_vel = np.sum(U_gas * alpha_gas * cell_volume) / np.sum(cell_volume) + + return sup_vel, field_dict + + +def compute_ave_y_liq( + case_folder: str, + time_folder: str, + n_cells: int | None = None, + volume_time: str = "0", + spec_name: str = "CO2", + field_dict={}, +) -> tuple: + """ + Calculate liquid volume averaged mass fraction of a species at a given time + + .. math:: + \frac{1}{V_{\rm liq, tot}} \int_{V_{\rm liq}} Y dV_{\rm liq} + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + volume_time : str + Time folder to read to get the cell volumes + spec_name : str + Name of the species + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + liq_ave_y: float + Liquid volume averaged mass fraction + field_dict : dict + Dictionary of fields read + """ + + # Read relevant fields + kwargs = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + kwargs_vol = { + "case_folder": case_folder, + "time_folder": volume_time, + "n_cells": n_cells, + } + alpha_liq, field_dict = read_field( + field_name="alpha.liquid", field_dict=field_dict, **kwargs + ) + y_liq, field_dict = read_field( + field_name=f"{spec_name}.liquid", field_dict=field_dict, **kwargs + ) + ind_liq, field_dict = get_ind_liq(field_dict=field_dict, **kwargs) + + cell_volume, field_dict = read_cell_volume( + field_dict=field_dict, **kwargs_vol + ) + + # Only compute over the liquid + if isinstance(alpha_liq, np.ndarray): + alpha_liq = alpha_liq[ind_liq] + if isinstance(cell_volume, np.ndarray): + cell_volume = cell_volume[ind_liq] + if isinstance(y_liq, np.ndarray): + y_liq = y_liq[ind_liq] + + # Calculate + liq_ave_y = np.sum(alpha_liq * y_liq * cell_volume) / np.sum( + alpha_liq * cell_volume + ) + + return liq_ave_y, field_dict + + +def compute_ave_conc_liq( + case_folder: str, + time_folder: str, + n_cells: int | None = None, + volume_time: str = "0", + spec_name: str = "CO2", + mol_weight: float = 0.04401, + rho_val: float | None = 1000, + field_dict={}, +) -> tuple: + """ + Calculate liquid volume averaged concentration of a species at a given time + + .. math:: + \frac{1}{V_{\rm liq, tot}} \int_{V_{\rm liq}} \rho_{\rm liq} Y / W dV_{\rm liq} + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + volume_time : str + Time folder to read to get the cell volumes + spec_name : str + Name of the species + mol_weight : float + Molecular weight of species (kg/mol) + rho_val : float | None + Constant density not available from time folder (kg/m3) + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + conc_ave: float + Liquid volume averaged species concentration + field_dict : dict + Dictionary of fields read + """ + + logger.debug( + f"Computing concentration for {spec_name} with molecular weight {mol_weight:.4g} kg/mol" + ) + if rho_val is not None: + logger.debug(f"Assuming liquid density {rho_val} kg/m3") + + # Read relevant fields + kwargs = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + kwargs_vol = { + "case_folder": case_folder, + "time_folder": volume_time, + "n_cells": n_cells, + } + alpha_liq, field_dict = read_field( + field_name="alpha.liquid", field_dict=field_dict, **kwargs + ) + y_liq, field_dict = read_field( + field_name=f"{spec_name}.liquid", field_dict=field_dict, **kwargs + ) + ind_liq, field_dict = get_ind_liq(field_dict=field_dict, **kwargs) + + cell_volume, field_dict = read_cell_volume( + field_dict=field_dict, **kwargs_vol + ) + + # Density of liquid is not always printed (special case) + if not ("rho_liq" in field_dict) or field_dict["rho_liq"] is None: + if rho_val is not None: + rho_liq = rho_val + field_dict["rho_liq"] = rho_val + else: + rho_liq_file = os.path.join(case_folder, time_folder, "rhom") + rho_liq = readOFScal(rho_liq_file, n_cells)["field"] + field_dict["rho_liq"] = rho_liq + else: + rho_liq = field_dict["rho_liq"] + + # Only compute over the liquid + if isinstance(y_liq, np.ndarray): + y_liq = y_liq[ind_liq] + if isinstance(alpha_liq, np.ndarray): + alpha_liq = alpha_liq[ind_liq] + if isinstance(alpha_liq, np.ndarray): + cell_volume = cell_volume[ind_liq] + if isinstance(rho_liq, np.ndarray): + rho_liq = rho_liq[ind_liq] + + conc_loc = rho_liq * y_liq / mol_weight + + conc_ave = np.sum(conc_loc * alpha_liq * cell_volume) / np.sum( + alpha_liq * cell_volume + ) + + return conc_ave, field_dict + + +def compute_ave_bubble_diam( + case_folder: str, + time_folder: str, + n_cells: int | None = None, + volume_time: str = "0", + field_dict={}, +) -> tuple: + """ + Calculate averaged bubble diameter over the liquid volume + + .. math:: + \frac{1}{V_{\rm liq, tot}} \int_{V_{\rm liq}} D dV + + Parameters + ---------- + case_folder: str + Path to case folder + time_folder: str + Name of time folder to analyze + n_cells : int | None + Number of cells in the domain + volume_time : str + Time folder to read to get the cell volumes + field_dict : dict + Dictionary of fields used to avoid rereading the same fields to calculate different quantities + + Returns + ---------- + diam: float + Volume averaged gas holdup + field_dict : dict + Dictionary of fields read + """ + + # Read relevant fields + kwargs = { + "case_folder": case_folder, + "time_folder": time_folder, + "n_cells": n_cells, + } + kwargs_vol = { + "case_folder": case_folder, + "time_folder": volume_time, + "n_cells": n_cells, + } + alpha_liq, field_dict = read_field( + field_name="alpha.liquid", field_dict=field_dict, **kwargs + ) + d_gas, field_dict = read_field( + field_name="d.gas", field_dict=field_dict, **kwargs + ) + ind_liq, field_dict = get_ind_liq(field_dict=field_dict, **kwargs) + + cell_volume, field_dict = read_cell_volume( + field_dict=field_dict, **kwargs_vol + ) + + # Only compute over the liquid + if isinstance(d_gas, np.ndarray): + d_gas = d_gas[ind_liq] + if isinstance(alpha_liq, np.ndarray): + alpha_liq = alpha_liq[ind_liq] + if isinstance(alpha_liq, np.ndarray): + cell_volume = cell_volume[ind_liq] + + # Calculate + diam = np.sum(d_gas * alpha_liq * cell_volume) / np.sum( + alpha_liq * cell_volume + ) + + return diam, field_dict diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/CO2.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/CO2.gas new file mode 100644 index 00000000..e4165b1a --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/CO2.gas @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object CO2.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform 0; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform $f_CO2; + } + + outlet + { + //type inletOutlet; + //phi phi.gas; + //inletValue $f_CO2; + //value $f_CO2; + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/CO2.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/CO2.liquid new file mode 100644 index 00000000..4b8ea6a0 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/CO2.liquid @@ -0,0 +1,42 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object CO2.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type zeroGradient; + //type fixedValue; + //value uniform 0.0; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/H2.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/H2.gas new file mode 100644 index 00000000..9f66b2d2 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/H2.gas @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object H2.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform 0; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform $f_H2; + } + + outlet + { + //type inletOutlet; + //phi phi.gas; + //inletValue $f_H2; + //value $f_H2; + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/H2.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/H2.liquid new file mode 100644 index 00000000..65ae8d34 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/H2.liquid @@ -0,0 +1,42 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object H2.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type zeroGradient; + //type fixedValue; + //value uniform 0.0; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/N2.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/N2.gas new file mode 100644 index 00000000..c1d7225f --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/N2.gas @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object N2.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform 1; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform $f_N2; + } + + outlet + { + //type inletOutlet; + //phi phi.gas; + //inletValue $f_N2; + //value $f_N2; + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/T.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/T.gas new file mode 100644 index 00000000..bf0199a0 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/T.gas @@ -0,0 +1,44 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object T.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 300; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value $internalField; + } + + outlet + { + type inletOutlet; + phi phi.gas; + inletValue $internalField; + value $internalField; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/T.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/T.liquid new file mode 100644 index 00000000..7101ea31 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/T.liquid @@ -0,0 +1,43 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object T.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 300; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue $internalField; + value $internalField; + } + inlet + { + type fixedValue; + value $internalField; + } + defaultFaces + { + type zeroGradient; + } + +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/U.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/U.gas new file mode 100644 index 00000000..e696566f --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/U.gas @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volVectorField; + object U.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (0 0.0 0); + +#include "${FOAM_CASE}/constant/globalVars" + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + //type flowRateInletVelocity; + //massFlowRate $mflowRateGas; + //rho thermo:rho.gas; + //value $internalField; + type fixedValue; + value uniform (0 $uGasPhase 0); + } + outlet + { + type pressureInletOutletVelocity; + phi phi.gas; + value $internalField; + } + defaultFaces + { + type slip; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/U.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/U.liquid new file mode 100644 index 00000000..1879e020 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/U.liquid @@ -0,0 +1,46 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volVectorField; + object U.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform (0 0 0); + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + //type flowRateInletVelocity; + //massFlowRate $mflowRateLiq; + //rho thermo:rho.liquid; + //value $internalField; + type fixedValue; + value uniform (0 0 0); + } + outlet + { + type noSlip; + } + defaultFaces + { + type noSlip; + } + +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/Ydefault.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/Ydefault.gas new file mode 100644 index 00000000..fba2945d --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/Ydefault.gas @@ -0,0 +1,42 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object Ydefault.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform 0.0; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/Ydefault.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/Ydefault.liquid new file mode 100644 index 00000000..a5108564 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/Ydefault.liquid @@ -0,0 +1,42 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object Ydefault.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 1.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform 1.0; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alpha.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alpha.gas new file mode 100644 index 00000000..1e303fbe --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alpha.gas @@ -0,0 +1,43 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + location "0"; + object alpha.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $alphaGas; + +boundaryField +{ + inlet + { + type fixedValue; + value uniform $alphaGas; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue uniform 1; + value uniform 1; + } + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alpha.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alpha.liquid new file mode 100644 index 00000000..5c92070b --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alpha.liquid @@ -0,0 +1,40 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object alpha.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform 1; + +boundaryField +{ + inlet + { + type fixedValue; + value uniform $alphaLiq; + } + outlet + { + type fixedValue; + value uniform 0; + } + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alphat.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alphat.gas new file mode 100644 index 00000000..b867958f --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alphat.gas @@ -0,0 +1,46 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object alphat.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -1 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + defaultFaces + { + type calculated; + value $internalField; + //type compressible::alphatWallFunction; + //Prt 0.85; + //value $internalField; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alphat.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alphat.liquid new file mode 100644 index 00000000..2569c3ee --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/alphat.liquid @@ -0,0 +1,44 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object alphat.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -1 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + defaultFaces + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/epsilon.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/epsilon.gas new file mode 100644 index 00000000..707a1cda --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/epsilon.gas @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object epsilon.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -3 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $eps_inlet_gas; + +boundaryField +{ + inlet + { + type fixedValue; + value uniform $eps_inlet_gas; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + //type epsilonWallFunction; + //value $internalField; + } + + // defaultFaces + // { + // type empty; + // } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/epsilon.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/epsilon.liquid new file mode 100644 index 00000000..0a4236fd --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/epsilon.liquid @@ -0,0 +1,43 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object epsilon.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -3 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $eps_inlet_liq; + +boundaryField +{ + inlet + { + type fixedValue; + value uniform $eps_inlet_liq; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type epsilonWallFunction; + value $internalField; + } + +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/f.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/f.gas new file mode 100644 index 00000000..76ee77a9 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/f.gas @@ -0,0 +1,41 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object f.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 1.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform 1.0; //$internalField; // + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/k.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/k.gas new file mode 100644 index 00000000..4a3d44ca --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/k.gas @@ -0,0 +1,43 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object k.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -2 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $k_inlet_gas; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform $k_inlet_gas; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/k.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/k.liquid new file mode 100644 index 00000000..cde8f6c1 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/k.liquid @@ -0,0 +1,44 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object k.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -2 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $k_inlet_liq; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type fixedValue; + value uniform $k_inlet_liq; + } + + outlet + { + type zeroGradient; + } + + defaultFaces + { + type kqRWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/nut.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/nut.gas new file mode 100644 index 00000000..ba16dd4c --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/nut.gas @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object nut.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-8; + +boundaryField +{ + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + defaultFaces + { + //type nutkWallFunction; + //value $internalField; + type calculated; + value $internalField; + } + + // defaultFaces + // { + // type empty; + // } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/nut.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/nut.liquid new file mode 100644 index 00000000..1442e07f --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/nut.liquid @@ -0,0 +1,43 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object nut.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-4; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + defaultFaces + { + type nutkWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/p b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/p new file mode 100644 index 00000000..b3a295fb --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/p @@ -0,0 +1,39 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object p; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 101325; + +boundaryField +{ + inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + defaultFaces + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/p_rgh b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/p_rgh new file mode 100644 index 00000000..88ee7d80 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/0.orig/p_rgh @@ -0,0 +1,43 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object p_rgh; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 101325; + +boundaryField +{ + inlet + { + type fixedFluxPressure; + value $internalField; + } + outlet + { + type prghTotalPressure; + p0 $internalField; + U U.gas; + phi phi.gas; + rho thermo:rho.gas; + value $internalField; + } + defaultFaces + { + type fixedFluxPressure; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/Allclean b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/Allclean new file mode 100755 index 00000000..f55e0ec9 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/Allclean @@ -0,0 +1,18 @@ +#!/bin/sh +cd ${0%/*} || exit 1 # Run from this directory + +# Source tutorial clean functions +. $WM_PROJECT_DIR/bin/tools/CleanFunctions + +# Remove surface, features and solution +#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 +#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 +#rm -rf constant/polyMesh > /dev/null 2>&1 +#rm -rf processor* > /dev/null 2>&1 +rm -rf 0 +cleanCase + +#rm *.obj +#rm *.stl + +#------------------------------------------------------------------------------ diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/computeQOI.sh b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/computeQOI.sh new file mode 100644 index 00000000..3756ed7f --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/computeQOI.sh @@ -0,0 +1,13 @@ +if [ ! -f qoi.txt ]; then + # Reconstruct if needed + source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc + reconstructPar -newTimes + module load anaconda3/2023 + conda activate /projects/gas2fuels/conda_env/bird + python read_history.py -cr .. -cn local -df data + python get_qoi.py + conda deactivate +else + echo "WARNING: QOI already computed" +fi + diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/dynamicMix_util.H b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/dynamicMix_util.H new file mode 100644 index 00000000..8fa7daf7 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/dynamicMix_util.H @@ -0,0 +1,37 @@ +#include + +double gradfunMix(double V1, double V2){ + return 3.0*V2*V2 + 2.0*V1*V2 - V1*V1; +} + +double funMix(double V1, double V2, double P, double rhoL, double A){ + return V2*V2*V2 + V1*V2*V2 - V2*V1*V1 - V1*V1*V1 - 4.0*P/(rhoL * A); +} + +double findV2(double P, double rhoL, double A, double V1) { + int newton_iter = 100; + double V2 = 2*V1; + double V2_old; + double V2_new; + if (std::abs(V1) < 1e-12) { + V2=std::pow((4.0*P/rhoL/A),0.333333); + V2_new = V2; + V2_old = V2; + } else { + for (int i=0; i("thermo:rho.liquid"); + const volScalarField& alphaL = + mesh().lookupObject("alpha.liquid"); + const volVectorField& UL = + mesh().lookupObject("U.liquid"); + double pi=3.141592654; + double source_pt_x=13.807637692813548; + double source_pt_y=1.3807637692813548; + double source_pt_z=12.426873923532193; + double disk_rad=0.9665346384969483; + double disk_area=pi*disk_rad*disk_rad; + double power=7626.034346240216; + double smear_factor=3.0; + const scalar startTime = 1; + if (time.value() > startTime) + { + // Get V1 + double source_sign_factor = 1.0; + double V1 = 0; + double V2 = 0; + double rhoV; + double dist_tol = disk_rad*5; + + double dist_n; + double upV = 0; + double uprhoV = 0; + double upVvol = 0; + double downV = 0; + double downrhoV = 0; + double downVvol = 0; + double dist2; + forAll(C,i) + { + dist2 = (C[i].x()-source_pt_x)*(C[i].x()-source_pt_x); + dist2 += (C[i].y()-source_pt_y)*(C[i].y()-source_pt_y); + dist2 += (C[i].z()-source_pt_z)*(C[i].z()-source_pt_z); + + dist_n = (C[i].x()-source_pt_x); + + if (dist2 < dist_tol*dist_tol && dist_n < -dist_tol/2) { + upVvol += V[i] * alphaL[i]; + upV += V[i] * alphaL[i] * UL[i][0]; + uprhoV += V[i] * alphaL[i] * rhoL[i]; + } + if (dist2 < dist_tol*dist_tol && dist_n > dist_tol/2) { + downVvol += V[i] * alphaL[i]; + downV += V[i] * alphaL[i] * UL[i][0]; + downrhoV += V[i] * alphaL[i] * rhoL[i]; + } + } + + reduce(uprhoV, sumOp()); + reduce(downrhoV, sumOp()); + reduce(upV, sumOp()); + reduce(downV, sumOp()); + reduce(downVvol, sumOp()); + reduce(upVvol, sumOp()); + + downV /= downVvol; + upV /= upVvol; + downrhoV /= downVvol; + uprhoV /= upVvol; + + if (upV <= 0 && downV <= 0) { + source_sign_factor = -1.0; + V1 = std::abs(upV); + rhoV = uprhoV; + } else if (upV >= 0 && downV >= 0) { + source_sign_factor = 1.0; + V1 = std::abs(downV); + rhoV = downrhoV; + } else { + V1 = 0.0; + source_sign_factor = -1.0; + rhoV = uprhoV; + Foam::Info << "[BIRD:DYNMIX WARN] " << "upV = " << upV << " downV = " << downV << " for source at " << source_pt_x << ", " << source_pt_y << ", " << source_pt_z << endl; + } + Foam::Info << "[BIRD:DYNMIX INFO] V1 = " << V1 << endl; + + // Get V2 + V2 = findV2(power, rhoV, disk_area, V1); + + forAll(C,i) + { + double Thrust=0.5*rhoL[i]*(V2*V2 - V1*V1)*disk_area; + double dist2=(C[i].x()-source_pt_x)*(C[i].x()-source_pt_x); + dist2 += (C[i].y()-source_pt_y)*(C[i].y()-source_pt_y); + dist2 += (C[i].z()-source_pt_z)*(C[i].z()-source_pt_z); + double epsilon=pow(V[i],0.33333)*smear_factor; + double sourceterm=alphaL[i]*(Thrust/pow(pi,1.5)/pow(epsilon,3.0))* + exp(-dist2/(epsilon*epsilon)); + Usource[i][0] -= source_sign_factor*sourceterm*V[i]; + } + } + #}; +}; diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/g b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/g new file mode 100644 index 00000000..770a5619 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/g @@ -0,0 +1,21 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class uniformDimensionedVectorField; + location "constant"; + object g; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -2 0 0 0 0]; +value (0 -9.81 0); + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/globalVars b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/globalVars new file mode 100644 index 00000000..1a0ce724 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/globalVars @@ -0,0 +1,83 @@ +T0 300; //initial T(K) which stays constant +VVM 1.6; +//****water Liquid properties************** +CpMixLiq 4181; +muMixLiq #calc "2.414e-5 * pow(10,247.8/($T0 - 140.0))"; //viscosity (Pa.s) of water as a function of T(K) +kThermLiq 0.62; // W/m-K +rho0MixLiq 1000; // kg/m^3 +sigmaLiq 0.07; //surface tension N/m +//Wilke-Chang params for diffusion coefficient of a given solute in water (solvent) +WC_psi 2.6; +WC_M 18; // kg/kmol +WC_V_O2 25.6e-3; // m3/kmol molar volume at normal boiling temperature (Treybal 1968) +WC_V_H2 14.3e-3; +WC_V_CO2 34e-3; +WC_V_CO 30.7e-3; +WC_V_N2 31.2e-3; +WC_V_CH4 35e-3; // V_b[cm3/mol]=0.285*V_critical^1.048 (Tyn and Calus; ESTIMATING LIQUID MOLAL VOLUME; Processing, Volume 21, Issue 4, Pages 16 - 17) +//****** diffusion coeff *********** +D_H2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_H2,0.6)"; +D_CO2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_CO2,0.6)"; +D_CO #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_CO,0.6)"; +D_CH4 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_CH4,0.6)"; +D_N2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_N2,0.6)"; +//****** Henry coeff *************** +H_O2_298 0.032; +DH_O2 1700; +H_CO2_298 0.83; +DH_CO2 2400; +H_CO_298 0.023; +DH_CO 1300; +H_H2_298 0.019; +DH_H2 500; +H_CH4_298 0.032; +DH_CH4 1900; +H_N2_298 0.015; +DH_N2 1300; +He_H2 #calc "$H_H2_298 * exp($DH_H2 *(1. / $T0 - 1./298.15))"; +He_CO #calc "$H_CO_298 * exp($DH_CO *(1. / $T0 - 1./298.15))"; +He_CO2 #calc "$H_CO2_298 * exp($DH_CO2 *(1. / $T0 - 1./298.15))"; +He_CH4 #calc "$H_CH4_298 * exp($DH_CH4 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas frac************* +f_H2 0.1; +f_CO2 0.9; +f_N2 0.0; +//*******inlet gas frac************* +inletA 11.8966; +liqVol 606.514; +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$liqVol * $VVM / (60 * $inletA * $alphaGas)"; +//********************************* +LeLiqH2 #calc "$kThermLiq / $rho0MixLiq / $D_H2 / $CpMixLiq"; +LeLiqCO #calc "$kThermLiq / $rho0MixLiq / $D_CO / $CpMixLiq"; +LeLiqCO2 #calc "$kThermLiq / $rho0MixLiq / $D_CO2 / $CpMixLiq"; // = 74 +LeLiqCH4 #calc "$kThermLiq / $rho0MixLiq / $D_CH4 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_CO2*$LeLiqCO2+$f_H2*$LeLiqH2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kH2 #calc "$D_H2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrH2 #calc "$muMixLiq*$CpMixLiq / $kH2"; + +kCO #calc "$D_CO*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrCO #calc "$muMixLiq*$CpMixLiq / $kCO"; + +kCO2 #calc "$D_CO2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrCO2 #calc "$muMixLiq*$CpMixLiq / $kCO2"; + +kCH4 #calc "$D_CH4*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrCH4 #calc "$muMixLiq*$CpMixLiq / $kCH4"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.5; +intensity 0.05; +k_inlet_gas #calc "1.5 * Foam::pow(($uGasPhase), 2) * Foam::pow($intensity, 2)"; +k_inlet_liq #calc "1.5 * Foam::pow(($uGasPhase), 2) * Foam::pow($intensity, 2)"; +eps_inlet_gas #calc "pow(0.09,0.75) * Foam::pow($k_inlet_gas, 1.5) / ($l_scale * 0.07)"; +eps_inlet_liq #calc "pow(0.09,0.75) * Foam::pow($k_inlet_liq, 1.5) / ($l_scale * 0.07)"; +omega_inlet_gas #calc "pow(0.09,-0.25) * pow($k_inlet_gas,0.5) / ($l_scale * 0.07)"; +omega_inlet_liq #calc "pow(0.09,-0.25) * pow($k_inlet_liq,0.5) / ($l_scale * 0.07)"; diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/globalVars_temp b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/globalVars_temp new file mode 100644 index 00000000..fcb076a7 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/globalVars_temp @@ -0,0 +1,83 @@ +T0 300; //initial T(K) which stays constant +VVM 0.2; +//****water Liquid properties************** +CpMixLiq 4181; +muMixLiq #calc "2.414e-5 * pow(10,247.8/($T0 - 140.0))"; //viscosity (Pa.s) of water as a function of T(K) +kThermLiq 0.62; // W/m-K +rho0MixLiq 1000; // kg/m^3 +sigmaLiq 0.07; //surface tension N/m +//Wilke-Chang params for diffusion coefficient of a given solute in water (solvent) +WC_psi 2.6; +WC_M 18; // kg/kmol +WC_V_O2 25.6e-3; // m3/kmol molar volume at normal boiling temperature (Treybal 1968) +WC_V_H2 14.3e-3; +WC_V_CO2 34e-3; +WC_V_CO 30.7e-3; +WC_V_N2 31.2e-3; +WC_V_CH4 35e-3; // V_b[cm3/mol]=0.285*V_critical^1.048 (Tyn and Calus; ESTIMATING LIQUID MOLAL VOLUME; Processing, Volume 21, Issue 4, Pages 16 - 17) +//****** diffusion coeff *********** +D_H2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_H2,0.6)"; +D_CO2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_CO2,0.6)"; +D_CO #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_CO,0.6)"; +D_CH4 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_CH4,0.6)"; +D_N2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_N2,0.6)"; +//****** Henry coeff *************** +H_O2_298 0.032; +DH_O2 1700; +H_CO2_298 0.83; +DH_CO2 2400; +H_CO_298 0.023; +DH_CO 1300; +H_H2_298 0.019; +DH_H2 500; +H_CH4_298 0.032; +DH_CH4 1900; +H_N2_298 0.015; +DH_N2 1300; +He_H2 #calc "$H_H2_298 * exp($DH_H2 *(1. / $T0 - 1./298.15))"; +He_CO #calc "$H_CO_298 * exp($DH_CO *(1. / $T0 - 1./298.15))"; +He_CO2 #calc "$H_CO2_298 * exp($DH_CO2 *(1. / $T0 - 1./298.15))"; +He_CH4 #calc "$H_CH4_298 * exp($DH_CH4 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas frac************* +f_H2 0.1; +f_CO2 0.9; +f_N2 0.0; +//*******inlet gas frac************* +inletA ; +liqVol ; +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$liqVol * $VVM / (60 * $inletA * $alphaGas)"; +//********************************* +LeLiqH2 #calc "$kThermLiq / $rho0MixLiq / $D_H2 / $CpMixLiq"; +LeLiqCO #calc "$kThermLiq / $rho0MixLiq / $D_CO / $CpMixLiq"; +LeLiqCO2 #calc "$kThermLiq / $rho0MixLiq / $D_CO2 / $CpMixLiq"; // = 74 +LeLiqCH4 #calc "$kThermLiq / $rho0MixLiq / $D_CH4 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_CO2*$LeLiqCO2+$f_H2*$LeLiqH2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kH2 #calc "$D_H2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrH2 #calc "$muMixLiq*$CpMixLiq / $kH2"; + +kCO #calc "$D_CO*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrCO #calc "$muMixLiq*$CpMixLiq / $kCO"; + +kCO2 #calc "$D_CO2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrCO2 #calc "$muMixLiq*$CpMixLiq / $kCO2"; + +kCH4 #calc "$D_CH4*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrCH4 #calc "$muMixLiq*$CpMixLiq / $kCH4"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.5; +intensity 0.05; +k_inlet_gas #calc "1.5 * Foam::pow(($uGasPhase), 2) * Foam::pow($intensity, 2)"; +k_inlet_liq #calc "1.5 * Foam::pow(($uGasPhase), 2) * Foam::pow($intensity, 2)"; +eps_inlet_gas #calc "pow(0.09,0.75) * Foam::pow($k_inlet_gas, 1.5) / ($l_scale * 0.07)"; +eps_inlet_liq #calc "pow(0.09,0.75) * Foam::pow($k_inlet_liq, 1.5) / ($l_scale * 0.07)"; +omega_inlet_gas #calc "pow(0.09,-0.25) * pow($k_inlet_gas,0.5) / ($l_scale * 0.07)"; +omega_inlet_liq #calc "pow(0.09,-0.25) * pow($k_inlet_liq,0.5) / ($l_scale * 0.07)"; diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/momentumTransport.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/momentumTransport.gas new file mode 100644 index 00000000..ca916714 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/momentumTransport.gas @@ -0,0 +1,26 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object momentumTransport.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +//simulationType laminar; +simulationType RAS; +RAS +{ + model mixtureKEpsilon; + turbulence on; + printCoeff on; +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/momentumTransport.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/momentumTransport.liquid new file mode 100644 index 00000000..2063de0d --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/momentumTransport.liquid @@ -0,0 +1,27 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object momentumTransport.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +//simulationType laminar; +simulationType RAS; + +RAS +{ + model mixtureKEpsilon; + turbulence on; + printCoeffs on; +} + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties new file mode 100644 index 00000000..a3c90f5a --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties @@ -0,0 +1,295 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + object phaseProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "$FOAM_CASE/constant/globalVars" + +type interfaceCompositionPhaseChangePopulationBalanceMultiphaseSystem; + +phases (gas liquid); + +populationBalances (bubbles); + +gas +{ + type multiComponentPhaseModel;//pureIsothermalPhaseModel; + + diameterModel velocityGroup; + + velocityGroupCoeffs + { + populationBalance bubbles; + + shapeModel spherical; + + sizeGroups + ( + f1 {dSph 1.4e-3; value 0.0;} + f2 {dSph 1.8e-3; value 0.0;} + f3 {dSph 2.2e-3; value 0.0;} + f4 {dSph 2.6e-3; value 0.0;} + f5 {dSph 3e-3; value 1.0;} + f6 {dSph 3.4e-3; value 0.0;} + f7 {dSph 3.8e-3; value 0.0;} + f8 {dSph 4.2e-3; value 0.0;} + f9 {dSph 4.6e-3; value 0.0;} + f10 {dSph 5.0e-3; value 0.0;} + ); + } + + residualAlpha 1e-6; + + Sc 0.7; +} + +liquid +{ + type multiComponentPhaseModel;//pureIsothermalPhaseModel; + + diameterModel constant; + + constantCoeffs + { + d 1e-4; + } + Sc #codeStream + { + code + #{ + os << ($LeLiqMix * $CpMixLiq * $muMixLiq / $kThermLiq); + #}; + }; + + residualAlpha 1e-6; +} + +populationBalanceCoeffs +{ + bubbles + { + continuousPhase liquid; + + coalescenceModels + ( + LehrMilliesMewes{ + efficiency 4.695; + uCrit 0.08; + alphaMax 0.6; + } + ); + + binaryBreakupModels + (); + + breakupModels + ( + Laakkonen { + efficiency 13.83; + daughterSizeDistributionModel Laakkonen; + } + + ); + + driftModels + ( + densityChange{} + ); + + nucleationModels + (); + } +} + +blending +{ + default + { + type linear; + minFullyContinuousAlpha.gas 0.7; + minPartlyContinuousAlpha.gas 0.3; + minFullyContinuousAlpha.liquid 0.7; + minPartlyContinuousAlpha.liquid 0.3; + } + heatTransfer + { + type linear; + minFullyContinuousAlpha.gas 1; + minPartlyContinuousAlpha.gas 0; + minFullyContinuousAlpha.liquid 1; + minPartlyContinuousAlpha.liquid 0; + } + massTransfer + { + $heatTransfer; + } +} + +surfaceTension +( + (gas and liquid) + { + type constant; + sigma $sigmaLiq; + } +); + +interfaceCompression +(); + +aspectRatio +( + (gas in liquid) + { + type Wellek; + } +); + + +drag +( + (gas in liquid) + { + type Grace; // Need to install the model available at https://github.com/NREL/BioReactorDesign + //type IshiiZuber; + residualRe 1e-3; + swarmCorrection + { + type none; + } + } +); + +virtualMass +( + (gas in liquid) + { + type constantCoefficient; + Cvm 0.5; + } +); + +// heatTransfer +// (); + +heatTransfer.gas +( + (gas in liquid) + { + type spherical; + residualAlpha 1e-4; + } + + (liquid in gas) + { + type RanzMarshall; + residualAlpha 1e-4; + } +); + +heatTransfer.liquid +( + (gas in liquid) + { + type RanzMarshall; + residualAlpha 1e-4; + } + + (liquid in gas) + { + type spherical; + residualAlpha 1e-4; + } +); + +interfaceComposition.gas +(); + +interfaceComposition.liquid +( + (liquid and gas) + { + type Henry; + species ( CO2 H2 ); + k ( $He_CO2 $He_H2 ); + Le $LeLiqMix; + } +); + +diffusiveMassTransfer.gas +(); + +diffusiveMassTransfer.liquid +( + (gas in liquid) + { + type Higbie; // Need to install the model available at https://github.com/NREL/BioReactorDesign + //type Frossling; + Le $LeLiqMix; + } + + (liquid in gas) + { + type spherical; + Le 1.0; //not used for spherical + } +); + +phaseTransfer +(); + +lift +( + (gas in liquid) + { + type wallDamped; + + wallDamping + { + type cosine; + Cd 3.0; + } + + lift + { + type Tomiyama; + + swarmCorrection + { + type none; + } + } + } + +); + +wallLubrication +( + (gas in liquid) + { + type Antal; + Cw1 -0.01; + Cw2 0.05; + } +); + +turbulentDispersion +( + (gas in liquid) + { + type Burns; + sigma 0.9; + } +); + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties_constantd b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties_constantd new file mode 100644 index 00000000..e029df99 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties_constantd @@ -0,0 +1,261 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + object phaseProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "$FOAM_CASE/constant/globalVars" + +type interfaceCompositionPhaseChangeMultiphaseSystem; + +phases (gas liquid); + +gas +{ + type multiComponentPhaseModel;//pureIsothermalPhaseModel; + + diameterModel constant; + + constantCoeffs + { + d 3e-3; + } + residualAlpha 1e-6; + Sc 0.7; +} + +liquid +{ + type multiComponentPhaseModel;//pureIsothermalPhaseModel; + + diameterModel constant; + + constantCoeffs + { + d 1e-4; + } + Sc #codeStream + { + code + #{ + os << ($LeLiqMix * $CpMixLiq * $muMixLiq / $kThermLiq); + #}; + }; + + residualAlpha 1e-6; +} + +populationBalanceCoeffs +{ + bubbles + { + continuousPhase liquid; + + coalescenceModels + (); + + binaryBreakupModels + (); + + breakupModels + (); + + driftModels + (); + + nucleationModels + (); + } +} + +blending +{ + default + { + type linear; + minFullyContinuousAlpha.gas 0.7; + minPartlyContinuousAlpha.gas 0.3; + minFullyContinuousAlpha.liquid 0.7; + minPartlyContinuousAlpha.liquid 0.3; + } + heatTransfer + { + type linear; + minFullyContinuousAlpha.gas 1; + minPartlyContinuousAlpha.gas 0; + minFullyContinuousAlpha.liquid 1; + minPartlyContinuousAlpha.liquid 0; + } + massTransfer + { + $heatTransfer; + } +} + +surfaceTension +( + (gas and liquid) + { + type constant; + sigma $sigmaLiq; + } +); + +interfaceCompression +(); + +aspectRatio +( + (gas in liquid) + { + type Wellek; + } +); + + +drag +( + (gas in liquid) + { + type Grace; // Need to install the model available at https://github.com/NREL/BioReactorDesign + //type IshiiZuber; + residualRe 1e-3; + swarmCorrection + { + type none; + } + } +); + +virtualMass +( + (gas in liquid) + { + type constantCoefficient; + Cvm 0.5; + } +); + +// heatTransfer +// (); + +heatTransfer.gas +( + (gas in liquid) + { + type spherical; + residualAlpha 1e-4; + } + + (liquid in gas) + { + type RanzMarshall; + residualAlpha 1e-4; + } +); + +heatTransfer.liquid +( + (gas in liquid) + { + type RanzMarshall; + residualAlpha 1e-4; + } + + (liquid in gas) + { + type spherical; + residualAlpha 1e-4; + } +); + +interfaceComposition.gas +(); + +interfaceComposition.liquid +( + (liquid and gas) + { + type Henry; + species ( CO2 H2 ); + k ( $He_CO2 $He_H2 ); + Le $LeLiqMix; + } +); + +diffusiveMassTransfer.gas +(); + +diffusiveMassTransfer.liquid +( + (gas in liquid) + { + type Higbie; // Need to install the model available at https://github.com/NREL/BioReactorDesign + //type Frossling; + Le $LeLiqMix; + } + + (liquid in gas) + { + type spherical; + Le 1.0; //not used for spherical + } +); + +phaseTransfer +(); + +lift +( + (gas in liquid) + { + type wallDamped; + + wallDamping + { + type cosine; + Cd 3.0; + } + + lift + { + type Tomiyama; + + swarmCorrection + { + type none; + } + } + } + +); + +wallLubrication +( + (gas in liquid) + { + type Antal; + Cw1 -0.01; + Cw2 0.05; + } +); + +turbulentDispersion +( + (gas in liquid) + { + type Burns; + sigma 0.9; + } +); + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties_pbe b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties_pbe new file mode 100644 index 00000000..a3c90f5a --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/phaseProperties_pbe @@ -0,0 +1,295 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + object phaseProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "$FOAM_CASE/constant/globalVars" + +type interfaceCompositionPhaseChangePopulationBalanceMultiphaseSystem; + +phases (gas liquid); + +populationBalances (bubbles); + +gas +{ + type multiComponentPhaseModel;//pureIsothermalPhaseModel; + + diameterModel velocityGroup; + + velocityGroupCoeffs + { + populationBalance bubbles; + + shapeModel spherical; + + sizeGroups + ( + f1 {dSph 1.4e-3; value 0.0;} + f2 {dSph 1.8e-3; value 0.0;} + f3 {dSph 2.2e-3; value 0.0;} + f4 {dSph 2.6e-3; value 0.0;} + f5 {dSph 3e-3; value 1.0;} + f6 {dSph 3.4e-3; value 0.0;} + f7 {dSph 3.8e-3; value 0.0;} + f8 {dSph 4.2e-3; value 0.0;} + f9 {dSph 4.6e-3; value 0.0;} + f10 {dSph 5.0e-3; value 0.0;} + ); + } + + residualAlpha 1e-6; + + Sc 0.7; +} + +liquid +{ + type multiComponentPhaseModel;//pureIsothermalPhaseModel; + + diameterModel constant; + + constantCoeffs + { + d 1e-4; + } + Sc #codeStream + { + code + #{ + os << ($LeLiqMix * $CpMixLiq * $muMixLiq / $kThermLiq); + #}; + }; + + residualAlpha 1e-6; +} + +populationBalanceCoeffs +{ + bubbles + { + continuousPhase liquid; + + coalescenceModels + ( + LehrMilliesMewes{ + efficiency 4.695; + uCrit 0.08; + alphaMax 0.6; + } + ); + + binaryBreakupModels + (); + + breakupModels + ( + Laakkonen { + efficiency 13.83; + daughterSizeDistributionModel Laakkonen; + } + + ); + + driftModels + ( + densityChange{} + ); + + nucleationModels + (); + } +} + +blending +{ + default + { + type linear; + minFullyContinuousAlpha.gas 0.7; + minPartlyContinuousAlpha.gas 0.3; + minFullyContinuousAlpha.liquid 0.7; + minPartlyContinuousAlpha.liquid 0.3; + } + heatTransfer + { + type linear; + minFullyContinuousAlpha.gas 1; + minPartlyContinuousAlpha.gas 0; + minFullyContinuousAlpha.liquid 1; + minPartlyContinuousAlpha.liquid 0; + } + massTransfer + { + $heatTransfer; + } +} + +surfaceTension +( + (gas and liquid) + { + type constant; + sigma $sigmaLiq; + } +); + +interfaceCompression +(); + +aspectRatio +( + (gas in liquid) + { + type Wellek; + } +); + + +drag +( + (gas in liquid) + { + type Grace; // Need to install the model available at https://github.com/NREL/BioReactorDesign + //type IshiiZuber; + residualRe 1e-3; + swarmCorrection + { + type none; + } + } +); + +virtualMass +( + (gas in liquid) + { + type constantCoefficient; + Cvm 0.5; + } +); + +// heatTransfer +// (); + +heatTransfer.gas +( + (gas in liquid) + { + type spherical; + residualAlpha 1e-4; + } + + (liquid in gas) + { + type RanzMarshall; + residualAlpha 1e-4; + } +); + +heatTransfer.liquid +( + (gas in liquid) + { + type RanzMarshall; + residualAlpha 1e-4; + } + + (liquid in gas) + { + type spherical; + residualAlpha 1e-4; + } +); + +interfaceComposition.gas +(); + +interfaceComposition.liquid +( + (liquid and gas) + { + type Henry; + species ( CO2 H2 ); + k ( $He_CO2 $He_H2 ); + Le $LeLiqMix; + } +); + +diffusiveMassTransfer.gas +(); + +diffusiveMassTransfer.liquid +( + (gas in liquid) + { + type Higbie; // Need to install the model available at https://github.com/NREL/BioReactorDesign + //type Frossling; + Le $LeLiqMix; + } + + (liquid in gas) + { + type spherical; + Le 1.0; //not used for spherical + } +); + +phaseTransfer +(); + +lift +( + (gas in liquid) + { + type wallDamped; + + wallDamping + { + type cosine; + Cd 3.0; + } + + lift + { + type Tomiyama; + + swarmCorrection + { + type none; + } + } + } + +); + +wallLubrication +( + (gas in liquid) + { + type Antal; + Cw1 -0.01; + Cw2 0.05; + } +); + +turbulentDispersion +( + (gas in liquid) + { + type Burns; + sigma 0.9; + } +); + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/thermophysicalProperties.gas b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/thermophysicalProperties.gas new file mode 100644 index 00000000..11b1c4b9 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/thermophysicalProperties.gas @@ -0,0 +1,142 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object thermophysicalProperties.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heRhoThermo; + mixture multiComponentMixture; + transport sutherland; + thermo janaf; + equationOfState perfectGas; + specie specie; + energy sensibleInternalEnergy; + //energy sensibleEnthalpy; +} + + +species +( + H2 + CO2 + N2 +); + +defaultSpecie N2; + +CO2 +{ + specie + { + molWeight 44.00995; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.85746029 0.00441437026 -2.21481404e-06 5.23490188e-10 -4.72084164e-14 -48759.166 2.27163806 ); + lowCpCoeffs ( 2.35677352 0.00898459677 -7.12356269e-06 2.45919022e-09 -1.43699548e-13 -48371.9697 9.90105222 ); + } + transport + { + As 1.572e-06; + Ts 240; + } + elements + { + C 1; + O 2; + } +} + +water +{ + specie + { + molWeight 18.01534; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.03399249 0.00217691804 -1.64072518e-07 -9.7041987e-11 1.68200992e-14 -30004.2971 4.9667701 ); + lowCpCoeffs ( 4.19864056 -0.0020364341 6.52040211e-06 -5.48797062e-09 1.77197817e-12 -30293.7267 -0.849032208 ); + } + transport + { + As 1.512e-06; + Ts 120; + } + elements + { + H 2; + O 1; + } +} + +N2 +{ + specie + { + molWeight 28.0134; + } + thermodynamics + { + Tlow 250; + Thigh 5000; + Tcommon 1000; + highCpCoeffs ( 2.92664 0.0014879768 -5.68476e-07 1.0097038e-10 -6.753351e-15 -922.7977 5.980528 ); + lowCpCoeffs ( 3.298677 0.0014082404 -3.963222e-06 5.641515e-09 -2.444854e-12 -1020.8999 3.950372 ); + } + transport + { + As 1.512e-06; + Ts 120; + } + elements + { + N 2; + } +} + +H2 +{ + specie + { + molWeight 2.01594; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.3372792 -4.94024731e-05 4.99456778e-07 -1.79566394e-10 2.00255376e-14 -950.158922 -3.20502331 ); + lowCpCoeffs ( 2.34433112 0.00798052075 -1.9478151e-05 2.01572094e-08 -7.37611761e-12 -917.935173 0.683010238 ); + } + transport + { + As 6.362e-07; + Ts 72; + } + elements + { + H 2; + } +} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/thermophysicalProperties.liquid b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/thermophysicalProperties.liquid new file mode 100644 index 00000000..d324ec51 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/constant/thermophysicalProperties.liquid @@ -0,0 +1,108 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object thermophysicalProperties.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "$FOAM_CASE/constant/globalVars" + +thermoType +{ + type heRhoThermo; + mixture multiComponentMixture; + transport const; + thermo hConst; + equationOfState rhoConst;//rPolynomial; + specie specie; + energy sensibleInternalEnergy; + //energy sensibleEnthalpy; +} + +species +( + CO2 + water + H2 +); + +inertSpecie water; + +water +{ + specie + { + molWeight 18.0153; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrMixLiq; + } +} + +CO2 +{ + specie + { + molWeight 44.00995; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrCO2; + } +} + +H2 +{ + specie + { + molWeight 2.01594; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07;//-9402451; + } + transport + { + mu $muMixLiq; + Pr $PrH2; + } +} + + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/get_qoi.py b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/get_qoi.py new file mode 100644 index 00000000..83932c5b --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/get_qoi.py @@ -0,0 +1,183 @@ +import json +import os +import pickle as pkl + +import matplotlib as mpl +import numpy as np +from prettyPlot.plotting import * +from scipy.optimize import curve_fit + + +def get_sim_folds(path): + folds = os.listdir(path) + sim_folds = [] + for fold in folds: + if fold.startswith("loop"): + sim_folds.append(fold) + return sim_folds + + +def func(t, cstar, kla): + t = t + t0 = 0 + c0 = 0 + return (cstar - c0) * (1 - np.exp(-kla * (t - t0))) + c0 + + +def get_vl(verb=False): + filename = os.path.join("constant", "globalVars") + with open(filename, "r+") as f: + lines = f.readlines() + for line in lines: + if line.startswith("liqVol"): + vol = float(line.split()[-1][:-1]) + break + if verb: + print(f"Read liqVol = {vol}m3") + return vol + + +def get_vvm(verb=False): + filename = os.path.join("constant", "globalVars") + with open(filename, "r+") as f: + lines = f.readlines() + for line in lines: + if line.startswith("VVM"): + vvm = float(line.split()[-1][:-1]) + break + if verb: + print(f"Read VVM = {vvm} [-]") + return vvm + + +def get_As(verb=False): + filename = os.path.join("constant", "globalVars") + with open(filename, "r+") as f: + lines = f.readlines() + for line in lines: + if line.startswith("inletA"): + As = float(line.split()[-1][:-1]) + break + if verb: + print(f"Read As = {As}m2") + return As + + +def get_pmix(verb=False): + with open("system/mixers.json", "r+") as f: + data = json.load(f) + mixer_list = data["mixers"] + pmix = 0 + for mix in mixer_list: + pmix += mix["power"] / 1000 + if verb: + print(f"Read Mixing power = {pmix}kW") + return pmix + + +def get_lh(verb=False): + filename = os.path.join("system", "setFieldsDict") + with open(filename, "r+") as f: + lines = f.readlines() + for line in lines: + if "box (-1.0 -1.0 -1.0)" in line: + height = float(line.split("(")[2].split()[1]) + break + if verb: + print(f"Read Height = {height}m") + return height + + +def get_pinj(vvm, Vl, As, lh): + rhog = 1.25 # kg /m3 + Vg = Vl * vvm / (60 * As * 1) # m/s + Ptank = 101325 # Pa + # Ptank = 0 # Pa + rhoL = 1000 # kg / m3 + Pl = 101325 + rhoL * 9.8 * lh # Pa + # W + P1 = rhog * As * Vg**3 + # W + P2 = (Pl - Ptank) * As * Vg + # kg /s + MF = rhog * Vg * As + # kwh / kg + e_m = (P1 + P2) / (3600 * 1000 * MF) + + # returns kW + return (P1 + P2) * 1e-3 + + +def get_qoi(kla_co2, cs_co2, kla_h2, cs_h2, verb=False): + vvm = get_vvm(verb) + As = get_As(verb) + V_l = get_vl(verb) + liqh = get_lh(verb) + P_inj = get_pinj(vvm, V_l, As, liqh) + P_mix = get_pmix(verb) + + qoi_co2 = kla_co2 * cs_co2 * V_l * 0.04401 / (P_mix / 3600 + P_inj / 3600) + qoi_h2 = kla_h2 * cs_h2 * V_l * 0.002016 / (P_mix / 3600 + P_inj / 3600) + return qoi_co2 * qoi_h2 + + +def get_qoi_uq(kla_co2, cs_co2, kla_h2, cs_h2): + qoi = [] + for i in range(len(kla_co2)): + if i == 0: + verb = True + else: + verb = False + qoi.append(get_qoi(kla_co2[i], cs_co2[i], kla_h2[i], cs_h2[i], verb)) + qoi = np.array(qoi) + return np.mean(qoi), np.std(qoi) + + +os.makedirs("Figures", exist_ok=True) + +dataFolder = "data" +fold = "local" + +nuq = 100 +mean_cstar_co2 = np.random.uniform(14, 16.9, nuq) +mean_cstar_h2 = np.random.uniform(1.04, 1.19, nuq) + + +tmp_cs_h2 = [] +tmp_cs_co2 = [] +tmp_kla_h2 = [] +tmp_kla_co2 = [] +cs_co2 = mean_cstar_co2 +cs_h2 = mean_cstar_h2 + +a = np.load(os.path.join(dataFolder, fold, "conv.npz")) +endindex = -1 +if ( + "c_h2" in a + and "c_co2" in a + and len(a["time"][:endindex] > 0) + and (a["time"][:endindex][-1] > 95) +): + for i in range(nuq): + fitparamsH2, _ = curve_fit( + func, + np.array(a["time"][:endindex]), + np.array(a["c_h2"][:endindex]), + bounds=[(cs_h2[i] - 1e-6, 0), (cs_h2[i] + 1e-6, 1)], + ) + fitparamsCO2, _ = curve_fit( + func, + np.array(a["time"][:endindex]), + np.array(a["c_co2"][:endindex]), + bounds=[(cs_co2[i] - 1e-6, 0), (cs_co2[i] + 1e-6, 1)], + ) + tmp_kla_co2.append(fitparamsCO2[1]) + tmp_kla_h2.append(fitparamsH2[1]) + tmp_cs_h2.append(cs_h2[i]) + tmp_cs_co2.append(cs_co2[i]) + +qoi_m, qoi_s = get_qoi_uq(tmp_kla_co2, tmp_cs_co2, tmp_kla_h2, tmp_cs_h2) + + +with open("qoi.txt", "w+") as f: + f.write(f"{qoi_m},{qoi_s}\n") diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/presteps.sh b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/presteps.sh new file mode 100644 index 00000000..77fa6d6f --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/presteps.sh @@ -0,0 +1,70 @@ +# Clean case +module load conda +conda activate /projects/gas2fuels/conda_env/bird_wf +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +./Allclean + +BIRD_HOME=`python -c "import bird; print(bird.BIRD_DIR)"` + +echo PRESTEP 1 +# Generate blockmeshDict +python ${BIRD_HOME}/../applications/write_block_rect_mesh.py -i system/mesh.json -o system + +# Generate boundary stl +python ${BIRD_HOME}/../applications/write_stl_patch.py -i system/inlets_outlets.json + +# Generate mixers +python ${BIRD_HOME}/../applications/write_dynMix_fvModels.py -fs -i system/mixers.json -o constant + +echo PRESTEP 2 +# Mesh gen +blockMesh -dict system/blockMeshDict + +# Inlet BC +surfaceToPatch -tol 1e-3 inlets.stl +export newmeshdir=$(foamListTimes -latestTime) +rm -rf constant/polyMesh/ +cp -r $newmeshdir/polyMesh ./constant +rm -rf $newmeshdir +cp constant/polyMesh/boundary /tmp +sed -i -e 's/inlets\.stl/inlet/g' /tmp/boundary +cat /tmp/boundary > constant/polyMesh/boundary + +# Outlet BC +surfaceToPatch -tol 1e-3 outlets.stl +export newmeshdir=$(foamListTimes -latestTime) +rm -rf constant/polyMesh/ +cp -r $newmeshdir/polyMesh ./constant +rm -rf $newmeshdir +cp constant/polyMesh/boundary /tmp +sed -i -e 's/outlets\.stl/outlet/g' /tmp/boundary +cat /tmp/boundary > constant/polyMesh/boundary + + +# Scale +transformPoints "scale=(2.7615275385627096 2.7615275385627096 2.7615275385627096)" + + +# setup IC +cp -r 0.orig 0 +setFields + +# Setup mass flow rate +# Get inlet area +postProcess -func 'patchIntegrate(patch="inlet", field="alpha.gas")' +postProcess -func writeCellVolumes +writeMeshObj + +echo PRESTEP 3 +python writeGlobalVars.py +cp constant/phaseProperties_constantd constant/phaseProperties + +conda deactivate + +if [ -f qoi.txt ]; then + rm qoi.txt +fi +if [ -f data/local/conv.npz ]; then + rm data/local/conv.npz +fi + diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/read_history.py b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/read_history.py new file mode 100644 index 00000000..4dd134d6 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/read_history.py @@ -0,0 +1,114 @@ +import argparse +import os +import sys + +import numpy as np +from prettyPlot.plotting import plt, pretty_labels + +from bird.postprocess.post_quantities import * +from bird.utilities.ofio import * + +parser = argparse.ArgumentParser(description="Convergence of GH") +parser.add_argument( + "-cn", + "--case_name", + type=str, + metavar="", + required=True, + help="Case name", +) +parser.add_argument( + "-df", + "--data_folder", + type=str, + metavar="", + required=False, + help="data folder name", + default="data", +) + +args, unknown = parser.parse_known_args() + + +case_root = "." # "../" +case_name = args.case_name # "12_hole_sparger_snappyRefine_700rpm_opt_coeff" +case_path = "." +dataFolder = args.data_folder + +if os.path.isfile(os.path.join(dataFolder, case_name, "conv.npz")): + sys.exit("WARNING: History already created, Skipping") + +time_float_sorted, time_str_sorted = getCaseTimes(case_path, remove_zero=True) +cellCentres = readMesh(os.path.join(case_path, f"meshCellCentres_0.obj")) +nCells = len(cellCentres) + + +co2_history = np.zeros(len(time_str_sorted)) +c_co2_history = np.zeros(len(time_str_sorted)) +h2_history = np.zeros(len(time_str_sorted)) +c_h2_history = np.zeros(len(time_str_sorted)) +gh_history = np.zeros(len(time_str_sorted)) +liqvol_history = np.zeros(len(time_str_sorted)) +print(f"case_path = {case_path}") +field_dict = {} +for itime, time in enumerate(time_float_sorted): + time_folder = time_str_sorted[itime] + print(f"\tTime : {time_folder}") + if not field_dict == {}: + new_field_dict = {} + if "V" in field_dict: + new_field_dict["V"] = field_dict["V"] + field_dict = new_field_dict + gh_history[itime], field_dict = compute_gas_holdup( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + field_dict=field_dict, + ) + co2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="CO2", + field_dict=field_dict, + ) + h2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="H2", + field_dict=field_dict, + ) + c_co2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="CO2", + mol_weight=0.04401, + field_dict=field_dict, + ) + c_h2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="H2", + mol_weight=0.002016, + field_dict=field_dict, + ) + +os.makedirs(dataFolder, exist_ok=True) +os.makedirs(os.path.join(dataFolder, case_name), exist_ok=True) +np.savez( + os.path.join(dataFolder, case_name, "conv.npz"), + time=np.array(time_float_sorted), + gh=gh_history, + co2=co2_history, + h2=h2_history, + c_h2=c_h2_history, + c_co2=c_co2_history, +) diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/run.sh b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/run.sh new file mode 100644 index 00000000..99da4760 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/run.sh @@ -0,0 +1,5 @@ +birdmultiphaseEulerFoam + + + + diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/script b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/script new file mode 100755 index 00000000..efe675ff --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/script @@ -0,0 +1,14 @@ +#!/bin/bash +#SBATCH --qos=high +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=07:59:00 +#SBATCH --account=co2snow + +bash presteps.sh +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +decomposePar -fileHandler collated +srun -n 16 birdmultiphaseEulerFoam -parallel -fileHandler collated +reconstructPar -newTimes diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/script_post b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/script_post new file mode 100755 index 00000000..aabbc33e --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/script_post @@ -0,0 +1,10 @@ +#!/bin/bash +#SBATCH --qos=high +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=00:59:00 +#SBATCH --account=co2snow + +bash computeQOI.sh diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/blockMeshDict b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/blockMeshDict new file mode 100644 index 00000000..1183d262 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/blockMeshDict @@ -0,0 +1,746 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object blockMeshDict; +} + +convertToMeters 1.0; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +vertices +( +( 0.0 0.0 0.0) +( 1.0 0.0 0.0) +( 2.0 0.0 0.0) +( 3.0 0.0 0.0) +( 4.0 0.0 0.0) +( 5.0 0.0 0.0) +( 6.0 0.0 0.0) +( 7.0 0.0 0.0) +( 8.0 0.0 0.0) +( 9.0 0.0 0.0) +( 10.0 0.0 0.0) +( 0.0 1.0 0.0) +( 1.0 1.0 0.0) +( 2.0 1.0 0.0) +( 3.0 1.0 0.0) +( 4.0 1.0 0.0) +( 5.0 1.0 0.0) +( 6.0 1.0 0.0) +( 7.0 1.0 0.0) +( 8.0 1.0 0.0) +( 9.0 1.0 0.0) +( 10.0 1.0 0.0) +( 0.0 2.0 0.0) +( 1.0 2.0 0.0) +( 2.0 2.0 0.0) +( 3.0 2.0 0.0) +( 4.0 2.0 0.0) +( 5.0 2.0 0.0) +( 6.0 2.0 0.0) +( 7.0 2.0 0.0) +( 8.0 2.0 0.0) +( 9.0 2.0 0.0) +( 10.0 2.0 0.0) +( 0.0 3.0 0.0) +( 1.0 3.0 0.0) +( 2.0 3.0 0.0) +( 3.0 3.0 0.0) +( 4.0 3.0 0.0) +( 5.0 3.0 0.0) +( 6.0 3.0 0.0) +( 7.0 3.0 0.0) +( 8.0 3.0 0.0) +( 9.0 3.0 0.0) +( 10.0 3.0 0.0) +( 0.0 4.0 0.0) +( 1.0 4.0 0.0) +( 2.0 4.0 0.0) +( 3.0 4.0 0.0) +( 4.0 4.0 0.0) +( 5.0 4.0 0.0) +( 6.0 4.0 0.0) +( 7.0 4.0 0.0) +( 8.0 4.0 0.0) +( 9.0 4.0 0.0) +( 10.0 4.0 0.0) +( 0.0 5.0 0.0) +( 1.0 5.0 0.0) +( 2.0 5.0 0.0) +( 3.0 5.0 0.0) +( 4.0 5.0 0.0) +( 5.0 5.0 0.0) +( 6.0 5.0 0.0) +( 7.0 5.0 0.0) +( 8.0 5.0 0.0) +( 9.0 5.0 0.0) +( 10.0 5.0 0.0) +( 0.0 6.0 0.0) +( 1.0 6.0 0.0) +( 2.0 6.0 0.0) +( 3.0 6.0 0.0) +( 4.0 6.0 0.0) +( 5.0 6.0 0.0) +( 6.0 6.0 0.0) +( 7.0 6.0 0.0) +( 8.0 6.0 0.0) +( 9.0 6.0 0.0) +( 10.0 6.0 0.0) +( 0.0 7.0 0.0) +( 1.0 7.0 0.0) +( 2.0 7.0 0.0) +( 3.0 7.0 0.0) +( 4.0 7.0 0.0) +( 5.0 7.0 0.0) +( 6.0 7.0 0.0) +( 7.0 7.0 0.0) +( 8.0 7.0 0.0) +( 9.0 7.0 0.0) +( 10.0 7.0 0.0) +( 0.0 0.0 1.0) +( 1.0 0.0 1.0) +( 2.0 0.0 1.0) +( 3.0 0.0 1.0) +( 4.0 0.0 1.0) +( 5.0 0.0 1.0) +( 6.0 0.0 1.0) +( 7.0 0.0 1.0) +( 8.0 0.0 1.0) +( 9.0 0.0 1.0) +( 10.0 0.0 1.0) +( 0.0 1.0 1.0) +( 1.0 1.0 1.0) +( 2.0 1.0 1.0) +( 3.0 1.0 1.0) +( 4.0 1.0 1.0) +( 5.0 1.0 1.0) +( 6.0 1.0 1.0) +( 7.0 1.0 1.0) +( 8.0 1.0 1.0) +( 9.0 1.0 1.0) +( 10.0 1.0 1.0) +( 0.0 2.0 1.0) +( 1.0 2.0 1.0) +( 2.0 2.0 1.0) +( 3.0 2.0 1.0) +( 4.0 2.0 1.0) +( 5.0 2.0 1.0) +( 6.0 2.0 1.0) +( 7.0 2.0 1.0) +( 8.0 2.0 1.0) +( 9.0 2.0 1.0) +( 10.0 2.0 1.0) +( 0.0 3.0 1.0) +( 1.0 3.0 1.0) +( 2.0 3.0 1.0) +( 3.0 3.0 1.0) +( 4.0 3.0 1.0) +( 5.0 3.0 1.0) +( 6.0 3.0 1.0) +( 7.0 3.0 1.0) +( 8.0 3.0 1.0) +( 9.0 3.0 1.0) +( 10.0 3.0 1.0) +( 0.0 4.0 1.0) +( 1.0 4.0 1.0) +( 2.0 4.0 1.0) +( 3.0 4.0 1.0) +( 4.0 4.0 1.0) +( 5.0 4.0 1.0) +( 6.0 4.0 1.0) +( 7.0 4.0 1.0) +( 8.0 4.0 1.0) +( 9.0 4.0 1.0) +( 10.0 4.0 1.0) +( 0.0 5.0 1.0) +( 1.0 5.0 1.0) +( 2.0 5.0 1.0) +( 3.0 5.0 1.0) +( 4.0 5.0 1.0) +( 5.0 5.0 1.0) +( 6.0 5.0 1.0) +( 7.0 5.0 1.0) +( 8.0 5.0 1.0) +( 9.0 5.0 1.0) +( 10.0 5.0 1.0) +( 0.0 6.0 1.0) +( 1.0 6.0 1.0) +( 2.0 6.0 1.0) +( 3.0 6.0 1.0) +( 4.0 6.0 1.0) +( 5.0 6.0 1.0) +( 6.0 6.0 1.0) +( 7.0 6.0 1.0) +( 8.0 6.0 1.0) +( 9.0 6.0 1.0) +( 10.0 6.0 1.0) +( 0.0 7.0 1.0) +( 1.0 7.0 1.0) +( 2.0 7.0 1.0) +( 3.0 7.0 1.0) +( 4.0 7.0 1.0) +( 5.0 7.0 1.0) +( 6.0 7.0 1.0) +( 7.0 7.0 1.0) +( 8.0 7.0 1.0) +( 9.0 7.0 1.0) +( 10.0 7.0 1.0) +( 0.0 0.0 2.0) +( 1.0 0.0 2.0) +( 2.0 0.0 2.0) +( 3.0 0.0 2.0) +( 4.0 0.0 2.0) +( 5.0 0.0 2.0) +( 6.0 0.0 2.0) +( 7.0 0.0 2.0) +( 8.0 0.0 2.0) +( 9.0 0.0 2.0) +( 10.0 0.0 2.0) +( 0.0 1.0 2.0) +( 1.0 1.0 2.0) +( 2.0 1.0 2.0) +( 3.0 1.0 2.0) +( 4.0 1.0 2.0) +( 5.0 1.0 2.0) +( 6.0 1.0 2.0) +( 7.0 1.0 2.0) +( 8.0 1.0 2.0) +( 9.0 1.0 2.0) +( 10.0 1.0 2.0) +( 0.0 2.0 2.0) +( 1.0 2.0 2.0) +( 2.0 2.0 2.0) +( 3.0 2.0 2.0) +( 4.0 2.0 2.0) +( 5.0 2.0 2.0) +( 6.0 2.0 2.0) +( 7.0 2.0 2.0) +( 8.0 2.0 2.0) +( 9.0 2.0 2.0) +( 10.0 2.0 2.0) +( 0.0 3.0 2.0) +( 1.0 3.0 2.0) +( 2.0 3.0 2.0) +( 3.0 3.0 2.0) +( 4.0 3.0 2.0) +( 5.0 3.0 2.0) +( 6.0 3.0 2.0) +( 7.0 3.0 2.0) +( 8.0 3.0 2.0) +( 9.0 3.0 2.0) +( 10.0 3.0 2.0) +( 0.0 4.0 2.0) +( 1.0 4.0 2.0) +( 2.0 4.0 2.0) +( 3.0 4.0 2.0) +( 4.0 4.0 2.0) +( 5.0 4.0 2.0) +( 6.0 4.0 2.0) +( 7.0 4.0 2.0) +( 8.0 4.0 2.0) +( 9.0 4.0 2.0) +( 10.0 4.0 2.0) +( 0.0 5.0 2.0) +( 1.0 5.0 2.0) +( 2.0 5.0 2.0) +( 3.0 5.0 2.0) +( 4.0 5.0 2.0) +( 5.0 5.0 2.0) +( 6.0 5.0 2.0) +( 7.0 5.0 2.0) +( 8.0 5.0 2.0) +( 9.0 5.0 2.0) +( 10.0 5.0 2.0) +( 0.0 6.0 2.0) +( 1.0 6.0 2.0) +( 2.0 6.0 2.0) +( 3.0 6.0 2.0) +( 4.0 6.0 2.0) +( 5.0 6.0 2.0) +( 6.0 6.0 2.0) +( 7.0 6.0 2.0) +( 8.0 6.0 2.0) +( 9.0 6.0 2.0) +( 10.0 6.0 2.0) +( 0.0 7.0 2.0) +( 1.0 7.0 2.0) +( 2.0 7.0 2.0) +( 3.0 7.0 2.0) +( 4.0 7.0 2.0) +( 5.0 7.0 2.0) +( 6.0 7.0 2.0) +( 7.0 7.0 2.0) +( 8.0 7.0 2.0) +( 9.0 7.0 2.0) +( 10.0 7.0 2.0) +( 0.0 0.0 3.0) +( 1.0 0.0 3.0) +( 2.0 0.0 3.0) +( 3.0 0.0 3.0) +( 4.0 0.0 3.0) +( 5.0 0.0 3.0) +( 6.0 0.0 3.0) +( 7.0 0.0 3.0) +( 8.0 0.0 3.0) +( 9.0 0.0 3.0) +( 10.0 0.0 3.0) +( 0.0 1.0 3.0) +( 1.0 1.0 3.0) +( 2.0 1.0 3.0) +( 3.0 1.0 3.0) +( 4.0 1.0 3.0) +( 5.0 1.0 3.0) +( 6.0 1.0 3.0) +( 7.0 1.0 3.0) +( 8.0 1.0 3.0) +( 9.0 1.0 3.0) +( 10.0 1.0 3.0) +( 0.0 2.0 3.0) +( 1.0 2.0 3.0) +( 2.0 2.0 3.0) +( 3.0 2.0 3.0) +( 4.0 2.0 3.0) +( 5.0 2.0 3.0) +( 6.0 2.0 3.0) +( 7.0 2.0 3.0) +( 8.0 2.0 3.0) +( 9.0 2.0 3.0) +( 10.0 2.0 3.0) +( 0.0 3.0 3.0) +( 1.0 3.0 3.0) +( 2.0 3.0 3.0) +( 3.0 3.0 3.0) +( 4.0 3.0 3.0) +( 5.0 3.0 3.0) +( 6.0 3.0 3.0) +( 7.0 3.0 3.0) +( 8.0 3.0 3.0) +( 9.0 3.0 3.0) +( 10.0 3.0 3.0) +( 0.0 4.0 3.0) +( 1.0 4.0 3.0) +( 2.0 4.0 3.0) +( 3.0 4.0 3.0) +( 4.0 4.0 3.0) +( 5.0 4.0 3.0) +( 6.0 4.0 3.0) +( 7.0 4.0 3.0) +( 8.0 4.0 3.0) +( 9.0 4.0 3.0) +( 10.0 4.0 3.0) +( 0.0 5.0 3.0) +( 1.0 5.0 3.0) +( 2.0 5.0 3.0) +( 3.0 5.0 3.0) +( 4.0 5.0 3.0) +( 5.0 5.0 3.0) +( 6.0 5.0 3.0) +( 7.0 5.0 3.0) +( 8.0 5.0 3.0) +( 9.0 5.0 3.0) +( 10.0 5.0 3.0) +( 0.0 6.0 3.0) +( 1.0 6.0 3.0) +( 2.0 6.0 3.0) +( 3.0 6.0 3.0) +( 4.0 6.0 3.0) +( 5.0 6.0 3.0) +( 6.0 6.0 3.0) +( 7.0 6.0 3.0) +( 8.0 6.0 3.0) +( 9.0 6.0 3.0) +( 10.0 6.0 3.0) +( 0.0 7.0 3.0) +( 1.0 7.0 3.0) +( 2.0 7.0 3.0) +( 3.0 7.0 3.0) +( 4.0 7.0 3.0) +( 5.0 7.0 3.0) +( 6.0 7.0 3.0) +( 7.0 7.0 3.0) +( 8.0 7.0 3.0) +( 9.0 7.0 3.0) +( 10.0 7.0 3.0) +( 0.0 0.0 4.0) +( 1.0 0.0 4.0) +( 2.0 0.0 4.0) +( 3.0 0.0 4.0) +( 4.0 0.0 4.0) +( 5.0 0.0 4.0) +( 6.0 0.0 4.0) +( 7.0 0.0 4.0) +( 8.0 0.0 4.0) +( 9.0 0.0 4.0) +( 10.0 0.0 4.0) +( 0.0 1.0 4.0) +( 1.0 1.0 4.0) +( 2.0 1.0 4.0) +( 3.0 1.0 4.0) +( 4.0 1.0 4.0) +( 5.0 1.0 4.0) +( 6.0 1.0 4.0) +( 7.0 1.0 4.0) +( 8.0 1.0 4.0) +( 9.0 1.0 4.0) +( 10.0 1.0 4.0) +( 0.0 2.0 4.0) +( 1.0 2.0 4.0) +( 2.0 2.0 4.0) +( 3.0 2.0 4.0) +( 4.0 2.0 4.0) +( 5.0 2.0 4.0) +( 6.0 2.0 4.0) +( 7.0 2.0 4.0) +( 8.0 2.0 4.0) +( 9.0 2.0 4.0) +( 10.0 2.0 4.0) +( 0.0 3.0 4.0) +( 1.0 3.0 4.0) +( 2.0 3.0 4.0) +( 3.0 3.0 4.0) +( 4.0 3.0 4.0) +( 5.0 3.0 4.0) +( 6.0 3.0 4.0) +( 7.0 3.0 4.0) +( 8.0 3.0 4.0) +( 9.0 3.0 4.0) +( 10.0 3.0 4.0) +( 0.0 4.0 4.0) +( 1.0 4.0 4.0) +( 2.0 4.0 4.0) +( 3.0 4.0 4.0) +( 4.0 4.0 4.0) +( 5.0 4.0 4.0) +( 6.0 4.0 4.0) +( 7.0 4.0 4.0) +( 8.0 4.0 4.0) +( 9.0 4.0 4.0) +( 10.0 4.0 4.0) +( 0.0 5.0 4.0) +( 1.0 5.0 4.0) +( 2.0 5.0 4.0) +( 3.0 5.0 4.0) +( 4.0 5.0 4.0) +( 5.0 5.0 4.0) +( 6.0 5.0 4.0) +( 7.0 5.0 4.0) +( 8.0 5.0 4.0) +( 9.0 5.0 4.0) +( 10.0 5.0 4.0) +( 0.0 6.0 4.0) +( 1.0 6.0 4.0) +( 2.0 6.0 4.0) +( 3.0 6.0 4.0) +( 4.0 6.0 4.0) +( 5.0 6.0 4.0) +( 6.0 6.0 4.0) +( 7.0 6.0 4.0) +( 8.0 6.0 4.0) +( 9.0 6.0 4.0) +( 10.0 6.0 4.0) +( 0.0 7.0 4.0) +( 1.0 7.0 4.0) +( 2.0 7.0 4.0) +( 3.0 7.0 4.0) +( 4.0 7.0 4.0) +( 5.0 7.0 4.0) +( 6.0 7.0 4.0) +( 7.0 7.0 4.0) +( 8.0 7.0 4.0) +( 9.0 7.0 4.0) +( 10.0 7.0 4.0) +( 0.0 0.0 5.0) +( 1.0 0.0 5.0) +( 2.0 0.0 5.0) +( 3.0 0.0 5.0) +( 4.0 0.0 5.0) +( 5.0 0.0 5.0) +( 6.0 0.0 5.0) +( 7.0 0.0 5.0) +( 8.0 0.0 5.0) +( 9.0 0.0 5.0) +( 10.0 0.0 5.0) +( 0.0 1.0 5.0) +( 1.0 1.0 5.0) +( 2.0 1.0 5.0) +( 3.0 1.0 5.0) +( 4.0 1.0 5.0) +( 5.0 1.0 5.0) +( 6.0 1.0 5.0) +( 7.0 1.0 5.0) +( 8.0 1.0 5.0) +( 9.0 1.0 5.0) +( 10.0 1.0 5.0) +( 0.0 2.0 5.0) +( 1.0 2.0 5.0) +( 2.0 2.0 5.0) +( 3.0 2.0 5.0) +( 4.0 2.0 5.0) +( 5.0 2.0 5.0) +( 6.0 2.0 5.0) +( 7.0 2.0 5.0) +( 8.0 2.0 5.0) +( 9.0 2.0 5.0) +( 10.0 2.0 5.0) +( 0.0 3.0 5.0) +( 1.0 3.0 5.0) +( 2.0 3.0 5.0) +( 3.0 3.0 5.0) +( 4.0 3.0 5.0) +( 5.0 3.0 5.0) +( 6.0 3.0 5.0) +( 7.0 3.0 5.0) +( 8.0 3.0 5.0) +( 9.0 3.0 5.0) +( 10.0 3.0 5.0) +( 0.0 4.0 5.0) +( 1.0 4.0 5.0) +( 2.0 4.0 5.0) +( 3.0 4.0 5.0) +( 4.0 4.0 5.0) +( 5.0 4.0 5.0) +( 6.0 4.0 5.0) +( 7.0 4.0 5.0) +( 8.0 4.0 5.0) +( 9.0 4.0 5.0) +( 10.0 4.0 5.0) +( 0.0 5.0 5.0) +( 1.0 5.0 5.0) +( 2.0 5.0 5.0) +( 3.0 5.0 5.0) +( 4.0 5.0 5.0) +( 5.0 5.0 5.0) +( 6.0 5.0 5.0) +( 7.0 5.0 5.0) +( 8.0 5.0 5.0) +( 9.0 5.0 5.0) +( 10.0 5.0 5.0) +( 0.0 6.0 5.0) +( 1.0 6.0 5.0) +( 2.0 6.0 5.0) +( 3.0 6.0 5.0) +( 4.0 6.0 5.0) +( 5.0 6.0 5.0) +( 6.0 6.0 5.0) +( 7.0 6.0 5.0) +( 8.0 6.0 5.0) +( 9.0 6.0 5.0) +( 10.0 6.0 5.0) +( 0.0 7.0 5.0) +( 1.0 7.0 5.0) +( 2.0 7.0 5.0) +( 3.0 7.0 5.0) +( 4.0 7.0 5.0) +( 5.0 7.0 5.0) +( 6.0 7.0 5.0) +( 7.0 7.0 5.0) +( 8.0 7.0 5.0) +( 9.0 7.0 5.0) +( 10.0 7.0 5.0) +); + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +blocks +( + + //block 0 +hex (0 1 12 11 88 89 100 99 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 1 +hex (1 2 13 12 89 90 101 100 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 2 +hex (2 3 14 13 90 91 102 101 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 3 +hex (3 4 15 14 91 92 103 102 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 4 +hex (4 5 16 15 92 93 104 103 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 5 +hex (5 6 17 16 93 94 105 104 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 6 +hex (6 7 18 17 94 95 106 105 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 7 +hex (7 8 19 18 95 96 107 106 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 8 +hex (8 9 20 19 96 97 108 107 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 9 +hex (9 10 21 20 97 98 109 108 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 10 +hex (97 98 109 108 185 186 197 196 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 11 +hex (185 186 197 196 273 274 285 284 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 12 +hex (273 274 285 284 361 362 373 372 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 13 +hex (361 362 373 372 449 450 461 460 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 14 +hex (360 361 372 371 448 449 460 459 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 15 +hex (359 360 371 370 447 448 459 458 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 16 +hex (358 359 370 369 446 447 458 457 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 17 +hex (357 358 369 368 445 446 457 456 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 18 +hex (356 357 368 367 444 445 456 455 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 19 +hex (355 356 367 366 443 444 455 454 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 20 +hex (354 355 366 365 442 443 454 453 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 21 +hex (353 354 365 364 441 442 453 452 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 22 +hex (352 353 364 363 440 441 452 451 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 23 +hex (363 364 375 374 451 452 463 462 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 24 +hex (374 375 386 385 462 463 474 473 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 25 +hex (385 386 397 396 473 474 485 484 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 26 +hex (396 397 408 407 484 485 496 495 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 27 +hex (407 408 419 418 495 496 507 506 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 28 +hex (418 419 430 429 506 507 518 517 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 29 +hex (308 309 320 319 396 397 408 407 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 30 +hex (220 221 232 231 308 309 320 319 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 31 +hex (132 133 144 143 220 221 232 231 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 32 +hex (44 45 56 55 132 133 144 143 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 33 +hex (55 56 67 66 143 144 155 154 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 34 +hex (66 67 78 77 154 155 166 165 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 35 +hex (33 34 45 44 121 122 133 132 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 36 +hex (22 23 34 33 110 111 122 121 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 37 +hex (11 12 23 22 99 100 111 110 ) +( 10 10 10 ) +SimpleGrading (1 1 1) +); + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +defaultPatch +{ type wall;} + +patches +( +); diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/controlDict b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/controlDict new file mode 100644 index 00000000..6f803fd2 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/controlDict @@ -0,0 +1,67 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application birdmultiphaseEulerFoam; + +startFrom latestTime;//startTime; + +startTime 0; + +stopAt endTime; + +endTime 200; + +deltaT 0.0001; + +writeControl adjustableRunTime; + +writeInterval 2; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable yes; + +adjustTimeStep yes; + +maxCo 0.5; + +maxDeltaT 0.01; + + +functions +{ + + #includeFunc writeObjects(d.gas) + #includeFunc writeObjects(thermo:rho.gas) + #includeFunc writeObjects(thermo:rho.liquid) +} +//functions +//{ +// #includeFunc fieldAverage(U.air, U.water, alpha.air, p) +//} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/decomposeParDict b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/decomposeParDict new file mode 100755 index 00000000..f8397e73 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/decomposeParDict @@ -0,0 +1,30 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: 3.0.x | +| \\ / A nd | Web: www.OpenFOAM.org | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object decomposeParDict; +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains 16; + +method scotch; + +hierarchicalCoeffs +{ + n (4 4 1); + delta 0.001; + order xyz; +} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvConstraints b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvConstraints new file mode 100644 index 00000000..334f1c8f --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvConstraints @@ -0,0 +1,56 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + object fvConstraints; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +limitp +{ + type limitPressure; + + min 1e4; +} +limitUliq +{ + type limitVelocity; + active yes; + U U.liquid; + selectionMode all; + max 1e1; +} +limitUgas +{ + type limitVelocity; + active yes; + U U.gas; + selectionMode all; + max 2e1; +} +limitTgas +{ + type limitTemperature; + selectionMode all; + min 290; + max 310; + phase gas; +} +limitTliq +{ + type limitTemperature; + selectionMode all; + min 290; + max 310; + phase liquid; +} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvSchemes b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvSchemes new file mode 100644 index 00000000..52e6e13a --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvSchemes @@ -0,0 +1,70 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default Euler; +} + +gradSchemes +{ + default Gauss linear; + limited cellLimited Gauss linear 1; +} + +divSchemes +{ + default none; + + "div\(phi,alpha.*\)" Gauss vanLeer; + + "div\(phir,alpha.*,alpha.*\)" Gauss vanLeer; + + "div\(alphaRhoPhi.*,U.*\)" Gauss limitedLinearV 1; + "div\(phi.*,U.*\)" Gauss limitedLinearV 1; + "div\(alphaRhoPhi.*,Yi\)" Gauss limitedLinear 1; + "div\(alphaRhoPhi.*,(h|e).*\)" Gauss limitedLinear 1; + "div\(alphaRhoPhi.*,(K|k|epsilon|omega).*\)" Gauss limitedLinear 1; + "div\(alphaPhi.*,f.*\)" Gauss limitedLinear 1; + "div\(alphaRhoPhi.*,\(p\|thermo:rho.*\)\)" Gauss limitedLinear 1; + + "div\(phim,(k|epsilon)m\)" Gauss upwind; + "div\(\(\(\(alpha.*\*thermo:rho.*\)*nuEff.*\)*dev2\(T\(grad\(U.*\)\)\)\)\)" Gauss linear; +} + +laplacianSchemes +{ + default Gauss linear corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default uncorrected; +} + +wallDist +{ + method Poisson; + nRequired true; +} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvSolution b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvSolution new file mode 100644 index 00000000..2e69fdfa --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/fvSolution @@ -0,0 +1,120 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +solvers +{ + "alpha.*" + { + nAlphaCorr 2; + nAlphaSubCycles 5; + } + + bubbles + { + nCorr 1; + tolerance 1e-4; + scale true; + solveOnFinalIterOnly true; + sourceUpdateInterval 1; + } + + p_rgh + { + solver GAMG; + smoother DIC; + tolerance 1e-7; + relTol 0; + } + + p_rghFinal + { + $p_rgh; + relTol 0; + } + + "(k|omega).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-7; + relTol 0; + minIter 1; + } + + "(e|h).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 0; + minIter 0; + maxIter 3; + } + + "f.*" + { + solver PBiCGStab; + preconditioner DILU; + tolerance 1e-6; + relTol 0; + } + + "Yi.*" + { + solver PBiCGStab; + preconditioner DILU; + tolerance 1e-12; + relTol 0; + residualAlpha 1e-8; + } + + "U.*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-5; + relTol 0; + minIter 1; + } + + yPsi + { + solver PCG; + preconditioner DIC; + tolerance 1e-10; + relTol 0; + } + +} + +PIMPLE +{ + nOuterCorrectors 3; + nCorrectors 1; + nNonOrthogonalCorrectors 0; + +} + +relaxationFactors +{ + equations + { + ".*" 1; + } +} + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/inlets_outlets.json b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/inlets_outlets.json new file mode 100644 index 00000000..2b53da73 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/inlets_outlets.json @@ -0,0 +1,28 @@ +{ + "Geometry": { + "OverallDomain": { + "x" : {"nblocks": 10, "size_per_block": 1.0}, + "y" : {"nblocks": 11, "size_per_block": 1.0}, + "z" : {"nblocks": 5, "size_per_block": 1.0} + }, + "Fluids": [ + [ [0,0,0], [9,0,0] ], + [ [9,0,0], [9,0,4] ], + [ [9,0,4], [0,0,4] ], + [ [0,1,4], [0,4,4] ], + [ [0,4,4], [0,10,4] ], + [ [0,4,4], [0,4,0] ], + [ [0,4,0], [0,10,0] ], + [ [0,4,0], [0,1,0] ] + ] + }, + "inlets": [ + {"branch_id": 0, "type": "circle", "frac_space": 0.2222222222222222, "normal_dir": 1, "radius": 0.4, "nelements": 50, "block_pos": "bottom"}, + {"branch_id": 0, "type": "circle", "frac_space": 0.5, "radius": 0.4, "normal_dir": 1,"nelements": 50, "block_pos": "bottom"}, + {"branch_id": 0, "type": "circle", "frac_space": 0.7777777777777778, "radius": 0.4, "normal_dir": 1,"nelements": 50, "block_pos": "bottom"} + ], + "outlets": [ + {"branch_id": 6, "type": "circle", "frac_space": 1, "normal_dir": 1, "radius": 0.4, "nelements": 50, "block_pos": "top"}, + {"branch_id": 4, "type": "circle", "frac_space": 1, "normal_dir": 1, "radius": 0.4, "nelements": 50, "block_pos": "top"} + ] +} diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/mesh.json b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/mesh.json new file mode 100644 index 00000000..29841d7e --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/mesh.json @@ -0,0 +1,26 @@ +{ + "Meshing": { + "Blockwise": { + "x" : 10, + "y" : 10, + "z" : 10 + } + }, + "Geometry": { + "OverallDomain": { + "x" : {"nblocks": 10, "size_per_block": 1.0}, + "y" : {"nblocks": 11, "size_per_block": 1.0}, + "z" : {"nblocks": 5, "size_per_block": 1.0} + }, + "Fluids": [ + [ [0,0,0], [9,0,0] ], + [ [9,0,0], [9,0,4] ], + [ [9,0,4], [0,0,4] ], + [ [0,1,4], [0,4,4] ], + [ [0,4,4], [0,10,4] ], + [ [0,4,4], [0,4,0] ], + [ [0,4,0], [0,10,0] ], + [ [0,4,0], [0,1,0] ] + ] + } +} diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/mixers.json b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/mixers.json new file mode 100644 index 00000000..b6224fb7 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/mixers.json @@ -0,0 +1,29 @@ +{ + "Meshing": { + "Blockwise": { + "x" : 10, + "y" : 10, + "z" : 10 + } + }, + "Geometry": { + "OverallDomain": { + "x" : {"nblocks": 10, "size_per_block": 1.0, "rescale": 2.7615275385627096}, + "y" : {"nblocks": 11, "size_per_block": 1.0, "rescale": 2.7615275385627096}, + "z" : {"nblocks": 5, "size_per_block": 1.0, "rescale": 2.7615275385627096} + }, + "Fluids": [ + [ [0,0,0], [9,0,0] ], + [ [9,0,0], [9,0,4] ], + [ [9,0,4], [0,0,4] ], + [ [0,1,4], [0,4,4] ], + [ [0,4,4], [0,10,4] ], + [ [0,4,4], [0,4,0] ], + [ [0,4,0], [0,10,0] ], + [ [0,4,0], [0,1,0] ] + ] + }, + "mixers": [ + {"branch_id": 2, "frac_space": 0.5, "start_time": 4, "power": 1500, "sign": "+"} + ] +} diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/setFieldsDict b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/setFieldsDict new file mode 100644 index 00000000..89a797b9 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/system/setFieldsDict @@ -0,0 +1,37 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: dev + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object setFieldsDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +defaultFieldValues +( + volScalarFieldValue alpha.gas 0.99 + volScalarFieldValue alpha.liquid 0.01 +); + +regions +( + boxToCell + { + box (-1.0 -1.0 -1.0) (552.3 11.046 552.3); + fieldValues + ( + volScalarFieldValue alpha.gas 0.01 + volScalarFieldValue alpha.liquid 0.99 + ); + } +); + + +// ************************************************************************* // diff --git a/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/writeGlobalVars.py b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/writeGlobalVars.py new file mode 100644 index 00000000..3445bdb8 --- /dev/null +++ b/bird/preprocess/data_case_gen/loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup/writeGlobalVars.py @@ -0,0 +1,49 @@ +import os + +import numpy as np + +from bird.utilities.ofio import * + + +def writeGvars(inletA, liqVol): + filename_tmp = os.path.join("constant", "globalVars_temp") + with open(filename_tmp, "r+") as f: + lines = f.readlines() + filename = os.path.join("constant", "globalVars") + with open(filename, "w+") as f: + for line in lines: + if line.startswith("inletA"): + f.write(f"inletA\t{inletA:g};\n") + elif line.startswith("liqVol"): + f.write(f"liqVol\t{liqVol:g};\n") + else: + f.write(line) + + +def readInletArea(): + filename = os.path.join( + "postProcessing", + "patchIntegrate(patch=inlet,field=alpha.gas)", + "0", + "surfaceFieldValue.dat", + ) + with open(filename, "r+") as f: + lines = f.readlines() + return float(lines[4].split()[-1]) + + +def getLiqVol(): + cellCentres = readMesh(os.path.join(".", f"meshCellCentres_0.obj")) + volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres))[ + "field" + ] + alpha_field = readOFScal( + os.path.join("0", "alpha.liquid"), len(cellCentres) + )["field"] + return np.sum(volume_field * alpha_field) + + +if __name__ == "__main__": + A = readInletArea() + V = getLiqVol() + writeGvars(A, V) diff --git a/bird/preprocess/dynamic_mixer/mixer.py b/bird/preprocess/dynamic_mixer/mixer.py index 1cc9b8b5..bb959f80 100644 --- a/bird/preprocess/dynamic_mixer/mixer.py +++ b/bird/preprocess/dynamic_mixer/mixer.py @@ -1,5 +1,9 @@ +import logging + import numpy as np +logger = logging.getLogger(__name__) + class Mixer: def __init__(self): @@ -80,7 +84,7 @@ def check_status(self, blocks=None): ): self.ready = False else: - print( + logger.info( f"\n\tpos({self.x:.2g}, {self.y:.2g}, {self.z:.2g})" + f"\n\tnormal_dir {self.normal_dir}" + f"\n\trad {self.rad:.2g}" @@ -90,6 +94,6 @@ def check_status(self, blocks=None): + f"\n\tstart_time {self.start_time:.2g}" ) if blocks is not None: - print(f"\tbranch = {blocks}") + logger.info(f"\tbranch = {blocks}") self.ready = True diff --git a/bird/preprocess/inhomogeneousBC/util/fromMomtoPdf.py b/bird/preprocess/inhomogeneousBC/util/fromMomtoPdf.py index b1c2bddb..b6ed1a15 100644 --- a/bird/preprocess/inhomogeneousBC/util/fromMomtoPdf.py +++ b/bird/preprocess/inhomogeneousBC/util/fromMomtoPdf.py @@ -1,8 +1,11 @@ +import logging import sys import numpy as np from scipy.optimize import minimize +logger = logging.getLogger(__name__) + def bounded_constraint(x): xend = 1 - np.sum(x) @@ -64,16 +67,16 @@ def opt(meanTar, stdTar, diam): return res -def get_f_vals(meanTar, stdTar, diam, verb=True): +def get_f_vals(meanTar, stdTar, diam): if meanTar < np.amin(diam) or meanTar > np.amax(diam): - sys.exit( - f"ERROR: mean target {meanTar} out of bounds [{np.amin(diam)}, {np.amax(diam)}]" + logger.error( + f"mean target {meanTar} out of bounds [{np.amin(diam)}, {np.amax(diam)}]" ) + sys.exit() tol = 10 irep = 0 while tol > 0.01: - if verb: - print(f"Rep = {irep}, tol={tol}") + logger.debug(f"Rep = {irep}, tol={tol}") res = opt(meanTar, stdTar, diam) x = np.zeros(len(res.x) + 1) x[:-1] = res.x @@ -89,21 +92,17 @@ def get_f_vals(meanTar, stdTar, diam, verb=True): ) irep += 1 if irep > 100: - sys.exit( - "ERROR: optimization fail, typically occurs because the population balance domain is too tight" + logger.error( + "optimization fail, typically occurs because the population balance domain is too tight" ) - if verb: - print("meanTar = ", meanTar) - print("stdTar = ", stdTar) - print("x = ", x) - print("mean = ", np.sum(x * diam)) - print( - "std = ", - np.sqrt( - np.sum(np.clip(x, a_min=0, a_max=None) * (diam - meanTar) ** 2) - ), - ) + logger.debug(f"meanTar = {meanTar}") + logger.debug(f"stdTar = {stdTar}") + logger.debug(f"x = {x}") + logger.debug(f"mean = {np.sum(x * diam)}") + logger.debug( + f"std = {np.sqrt(np.sum(np.clip(x, a_min=0, a_max=None) * (diam - meanTar) ** 2))}" + ) return x diff --git a/bird/preprocess/json_gen/design_io.py b/bird/preprocess/json_gen/design_io.py new file mode 100644 index 00000000..d6ed2c72 --- /dev/null +++ b/bird/preprocess/json_gen/design_io.py @@ -0,0 +1,85 @@ +import json + + +def generate_stl_patch(filename, bc_dict, geom_dict): + final_dict = {} + final_dict["Geometry"] = geom_dict["Geometry"] + for patch in bc_dict: + final_dict[patch] = bc_dict[patch] + with open(filename, "w+") as f: + json.dump(final_dict, f, indent=2) + + +def generate_dynamic_mixer(filename, mixers_list, geom_dict): + final_dict = {} + final_dict["Meshing"] = geom_dict["Meshing"] + final_dict["Geometry"] = geom_dict["Geometry"] + final_dict["mixers"] = mixers_list + with open(filename, "w+") as f: + json.dump(final_dict, f, indent=2) + + +def make_default_geom_dict_from_file(filename, rescale=2.7615275385627096): + with open(filename, "r+") as f: + geom_dict = json.load(f) + if "rescale" not in geom_dict["Geometry"]["OverallDomain"]["x"]: + geom_dict["Geometry"]["OverallDomain"]["x"]["rescale"] = rescale + geom_dict["Geometry"]["OverallDomain"]["y"]["rescale"] = rescale + geom_dict["Geometry"]["OverallDomain"]["z"]["rescale"] = rescale + assert "Meshing" in geom_dict + assert "Geometry" in geom_dict + return geom_dict + + +if __name__ == "__main__": + bc_dict = {} + bc_dict["inlets"] = [] + bc_dict["outlets"] = [] + tmp_dict = {} + tmp_dict["type"] = "circle" + tmp_dict["centx"] = 5.0 + tmp_dict["centy"] = 0.0 + tmp_dict["centz"] = 0.5 + tmp_dict["radius"] = 0.4 + tmp_dict["normal_dir"] = 1 + tmp_dict["nelements"] = 50 + bc_dict["inlets"].append(tmp_dict) + tmp_dict = {} + tmp_dict["type"] = "circle" + tmp_dict["centx"] = 2.5 + tmp_dict["centy"] = 0.0 + tmp_dict["centz"] = 0.5 + tmp_dict["radius"] = 0.4 + tmp_dict["normal_dir"] = 1 + tmp_dict["nelements"] = 50 + bc_dict["inlets"].append(tmp_dict) + tmp_dict = {} + tmp_dict["type"] = "circle" + tmp_dict["centx"] = 7.5 + tmp_dict["centy"] = 0.0 + tmp_dict["centz"] = 0.5 + tmp_dict["radius"] = 0.4 + tmp_dict["normal_dir"] = 1 + tmp_dict["nelements"] = 50 + bc_dict["inlets"].append(tmp_dict) + + tmp_dict = {} + tmp_dict["type"] = "circle" + tmp_dict["centx"] = 0.5 + tmp_dict["centy"] = 5.0 + tmp_dict["centz"] = 0.5 + tmp_dict["radius"] = 0.4 + tmp_dict["normal_dir"] = 1 + tmp_dict["nelements"] = 50 + bc_dict["outlets"].append(tmp_dict) + tmp_dict = {} + tmp_dict["type"] = "circle" + tmp_dict["centx"] = 0.5 + tmp_dict["centy"] = 5.0 + tmp_dict["centz"] = 0.5 + tmp_dict["radius"] = 0.4 + tmp_dict["normal_dir"] = 1 + tmp_dict["nelements"] = 50 + bc_dict["outlets"].append(tmp_dict) + + generate_stl_patch("test.json", bc_dict) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py new file mode 100644 index 00000000..7345b030 --- /dev/null +++ b/bird/preprocess/json_gen/generate_designs.py @@ -0,0 +1,583 @@ +import os +import pickle +import shutil +import sys + +import numpy as np + +from bird import BIRD_CASE_DIR +from bird.preprocess.json_gen.design_io import * + + +def id2simfolder(sim_id: int) -> str: + """ + Generates simulation folder name from simulation index + + Parameters + ---------- + sim_id: int + Simulation index + + Returns + ---------- + sim_folder : str + Simulation folder name + """ + sim_folder = f"Sim_{sim_id:04}" + return sim_folder + + +def compare_config(config1, config2): + same = True + for key in config1: + if np.linalg.norm(config1[key] - config2[key]) > 1e-6: + same = False + return same + return same + + +def check_config(config): + success = False + inlet_exist = False + for key in config: + if len(np.argwhere(config[key] == 1)) > 0: + inlet_exist = True + break + if inlet_exist: + success = True + else: + success = False + return success + + +def save_config_dict(filename, config_dict): + with open(filename, "wb") as f: + pickle.dump(config_dict, f) + + +def load_config_dict(filename): + with open(filename, "rb") as f: + config_dict = pickle.load(f) + return config_dict + + +def write_script_start(filename, n): + with open(filename, "w+") as f: + for i in range(n): + sim_folder = id2simfolder(i) + f.write(f"cd {sim_folder}\n") + f.write(f"sbatch script\n") + f.write(f"cd ..\n") + + +def write_script_post(filename, n): + with open(filename, "w+") as f: + for i in range(n): + sim_folder = id2simfolder(i) + f.write(f"cd {sim_folder}\n") + f.write(f"sbatch script_post\n") + f.write(f"cd ..\n") + + +def write_prep(filename, n): + with open(filename, "w+") as f: + f.write("prep () {\n") + f.write(f"\tcd $1\n") + f.write(f"\treconstructPar -newTimes\n") + f.write(f"\tcd ..\n") + f.write("}\n") + f.write(f"\n") + f.write( + f"source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc\n" + ) + for i in range(n): + sim_folder = id2simfolder(i) + f.write(f"prep {sim_folder}\n") + + +def overwrite_vvm(case_folder, vvm): + list_dir = os.listdir(case_folder) + if not "constant" in list_dir: + sys.exit( + f"ERROR: {case_folder} is likely not a case folder, could not find constant/" + ) + else: + filename = os.path.join(case_folder, "constant", "globalVars_temp") + filename_write = os.path.join( + case_folder, "constant", "globalVars_temp2" + ) + with open(filename, "r+") as f: + lines = f.readlines() + with open(filename_write, "w+") as f: + for line in lines: + if line.startswith("VVM"): + f.write(f"VVM\t{vvm};\n") + else: + f.write(line) + shutil.copy( + os.path.join(case_folder, "constant", "globalVars_temp2"), + os.path.join(case_folder, "constant", "globalVars_temp"), + ) + os.remove(os.path.join(case_folder, "constant", "globalVars_temp2")) + + +def overwrite_bubble_size_model(case_folder, constantD=False): + list_dir = os.listdir(case_folder) + if not "constant" in list_dir: + sys.exit( + f"ERROR: {case_folder} is likely not a case folder, could not find constant/" + ) + else: + filename = os.path.join(case_folder, "presteps.sh") + filename_write = os.path.join(case_folder, "presteps2.sh") + with open(filename, "r+") as f: + lines = f.readlines() + with open(filename_write, "w+") as f: + for line in lines: + if line.startswith("cp constant/phaseProperties"): + if constantD: + f.write( + "cp constant/phaseProperties_constantd constant/phaseProperties\n" + ) + else: + f.write( + "cp constant/phaseProperties_pbe constant/phaseProperties\n" + ) + else: + f.write(line) + shutil.copy( + os.path.join(case_folder, "presteps2.sh"), + os.path.join(case_folder, "presteps.sh"), + ) + os.remove(os.path.join(case_folder, "presteps2.sh")) + + +def generate_small_reactor_cases( + config_dict, + branchcom_spots, + vvm, + power, + constantD, + study_folder, + template_folder="loop_reactor_pbe_dynmix_nonstat_headbranch", +): + if not os.path.isabs(template_folder): + + template_folder = os.path.join( + f"{BIRD_CASE_DIR}", f"{template_folder}" + ) + + geom_dict = make_default_geom_dict_from_file( + os.path.join(f"{template_folder}", "system", "mesh.json"), + rescale=0.05, + ) + try: + shutil.rmtree(study_folder) + except: + pass + os.makedirs(study_folder) + for sim_id in config_dict: + sim_folder = id2simfolder(sim_id) + shutil.copytree( + f"{template_folder}", + os.path.join(f"{study_folder}", sim_folder), + ) + bc_dict = {} + bc_dict["inlets"] = [] + bc_dict["outlets"] = [] + bc_dict["outlets"].append( + { + "branch_id": 6, + "type": "circle", + "frac_space": 1, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "top", + } + ) + bc_dict["outlets"].append( + { + "branch_id": 4, + "type": "circle", + "frac_space": 1, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "top", + } + ) + for branch in config_dict: + if branch in [0, 1, 2]: + ind = np.argwhere(config_dict[sim_id][branch] == 1) + if len(ind) > 0: + ind = list(ind[:, 0]) + for iind in ind: + bc_dict["inlets"].append( + { + "branch_id": branch, + "type": "circle", + "frac_space": branchcom_spots[branch][iind], + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom", + } + ) + generate_stl_patch( + os.path.join( + study_folder, sim_folder, "system", "inlets_outlets.json" + ), + bc_dict, + geom_dict, + ) + + mix_list = [] + for branch in config_dict: + if branch in [0, 1, 2]: + ind = np.argwhere(config_dict[sim_id][branch] == 0) + if len(ind) > 0: + ind = list(ind[:, 0]) + for iind in ind: + if branch == 0: + sign = "+" + else: + sign = "-" + mix_list.append( + { + "branch_id": branch, + "frac_space": branchcom_spots[branch][iind], + "start_time": 1, + "power": power, + "sign": sign, + } + ) + generate_dynamic_mixer( + os.path.join(study_folder, sim_folder, "system", "mixers.json"), + mix_list, + geom_dict, + ) + overwrite_vvm( + case_folder=os.path.join(study_folder, sim_folder), vvm=vvm + ) + overwrite_bubble_size_model( + case_folder=os.path.join(study_folder, sim_folder), + constantD=constantD, + ) + + geom_dict = make_default_geom_dict_from_file( + os.path.join(f"{template_folder}", "system", "mesh.json"), + rescale=0.05, + ) + + +def generate_scaledup_reactor_cases( + config_dict, + branchcom_spots, + vvm, + power, + constantD, + study_folder, + template_folder="loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", +): + + if not os.path.isabs(template_folder): + template_folder = os.path.join( + f"{BIRD_CASE_DIR}", f"{template_folder}" + ) + + geom_dict = make_default_geom_dict_from_file( + os.path.join(f"{template_folder}", "system", "mesh.json") + ) + try: + shutil.rmtree(study_folder) + except: + pass + os.makedirs(study_folder) + for sim_id in config_dict: + sim_folder = id2simfolder(sim_id) + shutil.copytree( + f"{template_folder}", + os.path.join(f"{study_folder}", sim_folder), + ) + bc_dict = {} + bc_dict["inlets"] = [] + bc_dict["outlets"] = [] + bc_dict["outlets"].append( + { + "branch_id": 6, + "type": "circle", + "frac_space": 1, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "top", + } + ) + bc_dict["outlets"].append( + { + "branch_id": 4, + "type": "circle", + "frac_space": 1, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "top", + } + ) + for branch in config_dict: + if branch in [0, 1, 2]: + ind = np.argwhere(config_dict[sim_id][branch] == 1) + if len(ind) > 0: + ind = list(ind[:, 0]) + for iind in ind: + bc_dict["inlets"].append( + { + "branch_id": branch, + "type": "circle", + "frac_space": branchcom_spots[branch][iind], + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom", + } + ) + generate_stl_patch( + os.path.join( + study_folder, sim_folder, "system", "inlets_outlets.json" + ), + bc_dict, + geom_dict, + ) + + mix_list = [] + for branch in config_dict: + if branch in [0, 1, 2]: + ind = np.argwhere(config_dict[sim_id][branch] == 0) + if len(ind) > 0: + ind = list(ind[:, 0]) + for iind in ind: + if branch == 0: + sign = "+" + else: + sign = "-" + mix_list.append( + { + "branch_id": branch, + "frac_space": branchcom_spots[branch][iind], + "start_time": 3, + "power": power, + "sign": sign, + } + ) + generate_dynamic_mixer( + os.path.join(study_folder, sim_folder, "system", "mixers.json"), + mix_list, + geom_dict, + ) + overwrite_vvm( + case_folder=os.path.join(study_folder, sim_folder), vvm=vvm + ) + overwrite_bubble_size_model( + case_folder=os.path.join(study_folder, sim_folder), + constantD=constantD, + ) + + +def check_sparger_config( + sparger_locs: list[float], + n_spargers: int | None, + sparger_spacing: float, + edge_spacing: float, + n_branches: int, + bypass_sparger_spacing: bool, +) -> None: + """ + Check realizability of the sparger placement configuration + + Parameters + ---------- + sparger_locs : list[float] + Location of every sparger along the loop reactor coordinate [-] + There are 3 branches. Spargers can be placed anywhere + between edge_spacing and (1-edge_spacing) fractions of the branch + Each sparger locations must be between 0 and 3*1=3 + n_spargers : int|None + Number of spargers + sparger_spacing : float + Spacing between two spargers [-] + edge_spacing : float + Spacing required between any sparger and the edges of the branches [-] + n_branches : int + Number of loop reactor branches + bypass_sparger_spacing: bool + If true, allow an overlap of spargers + """ + + # Check that number of spargers is consistent + if n_spargers is None: + n_spargers = len(sparger_locs) + else: + assert n_spargers == len(sparger_locs) + assert n_spargers >= 1 + + # Basis check on the number of branches + assert n_branches > 0 + + # Check that locations of spargers is consistent + # There are n_branches branches. Spargers can be placed anywhere + # between edge_spacing and (1-edge_spacing) fractions of the branch + # Each sparger locations must be between 0 and n_branches*1=n_branches + assert edge_spacing > 0 + assert edge_spacing < 1 + assert all(np.array(sparger_locs) >= 0) + assert all(np.array(sparger_locs) <= float(n_branches)) + for ibranch in range(n_branches): + if ibranch == 0: + assert not np.any(np.array(sparger_locs) < edge_spacing) + if ibranch == n_branches - 1: + assert not np.any( + np.array(sparger_locs) > float(n_branches) - edge_spacing + ) + assert not np.any( + (np.array(sparger_locs) > float(ibranch) + 1.0 - edge_spacing) + & (np.array(sparger_locs) < float(ibranch) + 1.0 + edge_spacing) + ) + + # Check that spargers are sufficiently spaced out + assert sparger_spacing >= 0 + if not bypass_sparger_spacing: + assert all(np.diff(np.sort(np.array(sparger_locs))) >= sparger_spacing) + + +def generate_single_scaledup_reactor_sparger_cases( + sparger_locs: list[float], + n_spargers: int | None = None, + sparger_spacing: float = 0.15, + edge_spacing: float = 0.2, + n_branches: int = 3, + sim_id: int = 0, + constantD: bool = True, + vvm: float = 0.4, + study_folder: str = ".", + template_folder: str = "loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", + bypass_sparger_spacing: bool = False, +): + """ + Generates loop reactor case with desired sparger placement configuration + + Parameters + ---------- + sparger_locs : list[float] + Location of every sparger along the loop reactor coordinate [-] + n_spargers : int|None + Number of spargers + sparger_spacing : float + Spacing between two spargers [-] + edge_spacing : float + Spacing required between any sparger and the edges of the branches [-] + n_branches : int + Number of loop reactor branches + sim_id : int + Index identifier of the simulation + constantD : bool + If true, use constant bubble diameter + If false, use population balance + vvm : float + VVM value [-] + study_folder : str + Where to generate the case + template_folder: str + The case template to start from + bypass_sparger_spacing: bool + If true, allow an overlap of spargers + """ + + # Sanity checks + check_sparger_config( + sparger_locs=sparger_locs, + n_spargers=n_spargers, + sparger_spacing=sparger_spacing, + edge_spacing=edge_spacing, + n_branches=n_branches, + bypass_sparger_spacing=bypass_sparger_spacing, + ) + + # Find on which branch is each sparger + branch_id = [int(entry) for entry in sparger_locs] + + # Case generation + if not os.path.isabs(template_folder): + + template_folder = os.path.join( + f"{BIRD_CASE_DIR}", f"{template_folder}" + ) + geom_dict = make_default_geom_dict_from_file( + os.path.join(f"{template_folder}", "system", "mesh.json") + ) + + # Start from template + sim_folder = id2simfolder(sim_id) + shutil.copytree( + f"{template_folder}", + os.path.join(f"{study_folder}", sim_folder), + ) + + bc_dict = {} + bc_dict["inlets"] = [] + bc_dict["outlets"] = [] + bc_dict["outlets"].append( + { + "branch_id": 6, + "type": "circle", + "frac_space": 1, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "top", + } + ) + bc_dict["outlets"].append( + { + "branch_id": 4, + "type": "circle", + "frac_space": 1, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "top", + } + ) + + for branch, loc in zip(branch_id, sparger_locs): + bc_dict["inlets"].append( + { + "branch_id": branch, + "type": "circle", + "frac_space": loc - branch, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom", + } + ) + + generate_stl_patch( + os.path.join( + study_folder, sim_folder, "system", "inlets_outlets.json" + ), + bc_dict, + geom_dict, + ) + + mix_list = [] + generate_dynamic_mixer( + os.path.join(study_folder, sim_folder, "system", "mixers.json"), + mix_list, + geom_dict, + ) + overwrite_vvm(case_folder=os.path.join(study_folder, sim_folder), vvm=vvm) + overwrite_bubble_size_model( + case_folder=os.path.join(study_folder, sim_folder), + constantD=constantD, + ) diff --git a/bird/preprocess/stl_patch/stl_bc.py b/bird/preprocess/stl_patch/stl_bc.py index a14584fa..066eab6d 100644 --- a/bird/preprocess/stl_patch/stl_bc.py +++ b/bird/preprocess/stl_patch/stl_bc.py @@ -1,4 +1,5 @@ import json +import logging import sys import numpy as np @@ -7,6 +8,8 @@ from bird.meshing._mesh_tools import parseJsonFile from bird.preprocess.stl_patch.stl_shapes import * +logger = logging.getLogger(__name__) + def check_input(input_dict): assert isinstance(input_dict, dict) @@ -41,9 +44,9 @@ def write_boundaries(input_dict): check_input(input_dict) for boundary_name in input_dict.keys(): if not boundary_name == "Geometry": - print(f"Making {boundary_name}") + logger.info(f"Making {boundary_name}") boundary_mesh = get_all_vert_faces(input_dict, boundary_name) - print(f"\tArea {boundary_mesh.area} m2") + logger.info(f"\tArea {boundary_mesh.area} m2") boundary_mesh.save(f"{boundary_name}.stl") diff --git a/bird/preprocess/stl_patch/stl_mesh.py b/bird/preprocess/stl_patch/stl_mesh.py index 7a568c1c..a66cd86e 100644 --- a/bird/preprocess/stl_patch/stl_mesh.py +++ b/bird/preprocess/stl_patch/stl_mesh.py @@ -1,7 +1,11 @@ +import logging + import numpy as np import stl from scipy.spatial import Delaunay +logger = logging.getLogger(__name__) + class STLMesh: def __init__( @@ -59,20 +63,20 @@ def update(self): and not self.status["normal_dir"] and self.status["planar"] ): - print("\t\tUpdating normal_dir to") + logger.debug("\t\tUpdating normal_dir to") for i in range(3): A = np.where(self.vertices[:, i] == self.vertices[0, i])[0] if len(A) == self.vertices.shape[0]: self.normal_dir = i self.status["normal_dir"] = True - print(f"\t\t\t{i}") + logger.debug(f"\t\t\t{i}") break if ( self.status["vertices"] and not self.status["faces"] and self.status["planar"] ): - print("\t\tUpdating faces") + logger.debug("\t\tUpdating faces") points = np.zeros((self.vertices.shape[0], 2)) count = 0 for i in range(3): @@ -84,7 +88,7 @@ def update(self): self.status["faces"] = True if self.status["vertices"] and self.status["faces"]: - print("\t\tUpdating area") + logger.debug("\t\tUpdating area") self.calc_area() self.status["area"] = True diff --git a/bird/preprocess/stl_patch/stl_shapes.py b/bird/preprocess/stl_patch/stl_shapes.py index e9cc1fec..3a6c0a0b 100644 --- a/bird/preprocess/stl_patch/stl_shapes.py +++ b/bird/preprocess/stl_patch/stl_shapes.py @@ -1,4 +1,5 @@ import json +import logging import sys import numpy as np @@ -6,9 +7,11 @@ from bird.meshing.block_rect_mesh import from_block_rect_to_seg from bird.preprocess.stl_patch.stl_mesh import STLMesh +logger = logging.getLogger(__name__) + def make_polygon(rad, nvert, center, normal_dir): - print( + logger.debug( f"\tMaking polygon at ({center[0]:.4g}, {center[1]:.4g}, {center[2]:.4g})" ) theta = 2 * np.pi / nvert @@ -26,7 +29,7 @@ def make_polygon(rad, nvert, center, normal_dir): def make_rectangle(w, h, center, normal_dir): - print( + logger.debug( f"\tMaking rectangle at ({center[0]:.4g}, {center[1]:.4g}, {center[2]:.4g})" ) # Define vertices @@ -49,7 +52,7 @@ def make_rectangle(w, h, center, normal_dir): def make_circle(radius, center, normal_dir, npts=3): - print( + logger.debug( f"\tMaking circle at ({center[0]:.4g}, {center[1]:.4g}, {center[2]:.4g})" ) vertices = np.zeros((npts + 1, 3)) @@ -69,8 +72,7 @@ def make_circle(radius, center, normal_dir, npts=3): def make_spider(centerRad, nArms, widthArms, lengthArms, center, normal_dir): globalArea = 0 if nArms < 2: - print("ERROR: nArms must be >= 2") - print(f"Got nArms = {nArms}") + logger.error(f"nArms ({nArms}) must be >= 2") sys.exit() if nArms == 2: nVertPol = 4 @@ -82,8 +84,9 @@ def make_spider(centerRad, nArms, widthArms, lengthArms, center, normal_dir): vertices = center_mesh.vertices maxWidth = np.linalg.norm((vertices[1, :] - vertices[0, :])) if widthArms > maxWidth: - print("ERROR: arm width will make arms overlap") - print("Either increase center radius or reduce arm width") + error_msg = "arm width will make arms overlap" + error_msg += "\nEither increase center radius or reduce arm width" + logger.error(error_msg) sys.exit() arm_mesh_list = [] diff --git a/bird/utilities/bubble_col_util.py b/bird/utilities/bubble_col_util.py deleted file mode 100644 index b2d21795..00000000 --- a/bird/utilities/bubble_col_util.py +++ /dev/null @@ -1,244 +0,0 @@ -import numpy as np - -from bird.utilities.ofio import * - - -def readFromDict(val_dict, key, read_func=None, path=None, nCells=None): - if key not in val_dict: - field = read_func(path, nCells) - val_dict[key] = field - else: - field = val_dict[key] - return field, val_dict - - -def check_indLiq(ind_liq, cellCentres): - height_liq = cellCentres[ind_liq, 1] - ind_to_remove = np.argwhere(height_liq > 9.5) - if len(ind_to_remove) > 0: - ind_liq_copy = ind_liq.copy() - n_remove = len(ind_to_remove) - print(f"ind liq found to be at high heights {n_remove} times") - ind_to_remove = list(ind_liq[ind_to_remove[:, 0]][:, 0]) - ind_liq_copy = list(set(list(ind_liq[:, 0])) - set(ind_to_remove)) - assert len(ind_liq_copy) == len(ind_liq) - n_remove - ind_liq = np.reshape(np.array(ind_liq_copy), (-1, 1)) - return ind_liq - - -def check_indHeight(ind_height, cellCentres): - height_liq = cellCentres[ind_height, 1] - ind_to_remove = np.argwhere(height_liq < 6) - if len(ind_to_remove) > 0: - ind_height_copy = ind_height.copy() - n_remove = len(ind_to_remove) - print(f"ind height found to be at low heights {n_remove} times") - ind_to_remove = list(ind_height[ind_to_remove[:, 0]][:, 0]) - ind_height_copy = list( - set(list(ind_height_copy[:, 0])) - set(ind_to_remove) - ) - assert len(ind_height_copy) == len(ind_height) - n_remove - ind_height = np.reshape(np.array(ind_height_copy), (-1, 1)) - return ind_height - - -def indLiqFromDict(val_dict, localFolder, nCells, cellCentres): - if "ind_liq" not in val_dict: - alpha_gas, val_dict = readFromDict( - val_dict=val_dict, - key="alpha_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "alpha.gas"), - nCells=nCells, - ) - ind_liq = np.argwhere(alpha_gas < 0.8)[:, 0] - ind_liq = check_indLiq(ind_liq, cellCentres) - val_dict["ind_liq"] = ind_liq - else: - ind_liq = val_dict["ind_liq"] - - return ind_liq, val_dict - - -def computeGH(localFolder, localFolder_vol, nCells, cellCentres, val_dict={}): - alpha_gas, val_dict = readFromDict( - val_dict=val_dict, - key="alpha_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "alpha.gas"), - nCells=nCells, - ) - volume, val_dict = readFromDict( - val_dict=val_dict, - key="volume", - read_func=readOFScal, - path=os.path.join(localFolder_vol, "V"), - nCells=nCells, - ) - ind_liq, val_dict = indLiqFromDict( - val_dict, localFolder, nCells, cellCentres - ) - - holdup = np.sum(alpha_gas[ind_liq] * volume[ind_liq]) / np.sum( - volume[ind_liq] - ) - return holdup, val_dict - - -def computeGH_height( - localFolder, nCells, cellCentres, height_liq_base, val_dict={} -): - alpha_gas, val_dict = readFromDict( - val_dict=val_dict, - key="alpha_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "alpha.gas"), - nCells=nCells, - ) - - tol = 1e-3 - tol_max = 0.1 - nFound = 0 - iteration = 0 - while nFound <= 10 and tol < tol_max: - ind_height = np.argwhere(abs(alpha_gas - 0.8) < tol) - ind_height = check_indHeight(ind_height, cellCentres) - nFound = len(ind_height) - tol *= 1.1 - tol = np.clip(tol, a_min=None, a_max=0.2) - iteration += 1 - - if iteration > 1: - print(f"\tChanged GH tol to {tol:.2g}") - - height_liq = np.mean(cellCentres[ind_height, 1]) - holdup = height_liq / height_liq_base - 1 - - return holdup, val_dict - - -def computeDiam(localFolder, nCells, cellCentres, val_dict={}): - d_gas, val_dict = readFromDict( - val_dict=val_dict, - key="d_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "d.gas"), - nCells=nCells, - ) - ind_liq, val_dict = indLiqFromDict( - val_dict, localFolder, nCells, cellCentres - ) - - diam = np.mean(d_gas[ind_liq]) - - return diam, val_dict - - -def computeSpec_liq( - localFolder, nCells, field_name, key, cellCentres, val_dict={} -): - species, val_dict = readFromDict( - val_dict=val_dict, - key=key, - read_func=readOFScal, - path=os.path.join(localFolder, field_name), - nCells=nCells, - ) - ind_liq, val_dict = indLiqFromDict( - val_dict, localFolder, nCells, cellCentres - ) - - species = np.mean(species[ind_liq]) - - return species, val_dict - - -def computeSpec_kla_field( - localFolder, nCells, key_suffix, cellCentres, val_dict={}, diff=None -): - if "slip_vel" not in val_dict: - u_gas, val_dict = readFromDict( - val_dict=val_dict, - key="u_gas", - read_func=readOFVec, - path=os.path.join(localFolder, "U.gas"), - nCells=nCells, - ) - u_liq, val_dict = readFromDict( - val_dict=val_dict, - key="u_liq", - read_func=readOFVec, - path=os.path.join(localFolder, "U.liquid"), - nCells=nCells, - ) - slipvel = np.linalg.norm(u_liq - u_gas, axis=1) - val_dict["slipvel"] = slipvel - - rho_gas, val_dict = readFromDict( - val_dict=val_dict, - key="rho_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "thermo:rho.gas"), - nCells=nCells, - ) - if "D_" + key_suffix not in val_dict: - if diff is None: - T_gas, val_dict = readFromDict( - val_dict=val_dict, - key="T_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "T.gas"), - nCells=nCells, - ) - mu = 1.67212e-06 * np.sqrt(T_gas) / (1 + 170.672 / T_gas) - D = mu / rho_gas / 0.7 - else: - D = np.ones(rho_gas.shape) * diff - val_dict["D_" + key_suffix] = D - else: - D = val_dict["D_" + key_suffix] - - d_gas, val_dict = readFromDict( - val_dict=val_dict, - key="d_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "d.gas"), - nCells=nCells, - ) - if "Sh_" + key_suffix not in val_dict: - # Sh = 1.12*np.sqrt(rho_gas*slipvel*d_gas/(D*0.7*rho_gas))*np.sqrt(0.7) - Sh = ( - 2.0 - + 0.552 - * np.sqrt(rho_gas * slipvel * d_gas / (D * 0.7 * rho_gas)) - * 0.8889 - ) - val_dict["Sh_" + key_suffix] = Sh - else: - Sh = val_dict["Sh_" + key_suffix] - - alpha_gas, val_dict = readFromDict( - val_dict=val_dict, - key="alpha_gas", - read_func=readOFScal, - path=os.path.join(localFolder, "alpha.gas"), - nCells=nCells, - ) - - kla = Sh * 6 * alpha_gas / d_gas / d_gas * D - - return kla, val_dict - - -def computeSpec_kla( - localFolder, nCells, key_suffix, cellCentres, val_dict={}, diff=None -): - kla, val_dict = computeSpec_kla_field( - localFolder, nCells, key_suffix, cellCentres, val_dict, diff - ) - - ind_liq, val_dict = indLiqFromDict( - val_dict, localFolder, nCells, cellCentres - ) - - return np.mean(kla[ind_liq]), val_dict diff --git a/bird/utilities/folderManagement.py b/bird/utilities/folderManagement.py index 380a1497..38adf933 100644 --- a/bird/utilities/folderManagement.py +++ b/bird/utilities/folderManagement.py @@ -1,6 +1,9 @@ +import logging import os import re +logger = logging.getLogger(__name__) + def makeRecursiveFolder(path): folder_list = path.split("/") @@ -22,8 +25,9 @@ def getManyFolders(rootFolder, prefix="flat_donut"): for entry in fold_tmp: num = re.findall(r"\d+", entry) if len(num) > 1: - print(f"WARNING: Cannot find num of folder {entry}.") - print("Do not trust the spearman stat") + msg = f"Cannot find num of folder {entry}." + msg += "\nDo not trust the spearman stat" + logger.warning(msg) else: fold_num.append(int(num[0])) diff --git a/bird/utilities/label_plot.py b/bird/utilities/label_plot.py index f19012ed..17e50ceb 100644 --- a/bird/utilities/label_plot.py +++ b/bird/utilities/label_plot.py @@ -1,5 +1,9 @@ +import logging + from prettyPlot.plotting import plt, pretty_labels, pretty_legend +logger = logging.getLogger(__name__) + def label_conv(input_string): if input_string.lower() == "width": @@ -52,5 +56,5 @@ def label_conv(input_string): elif input_string.lower() == "gh_height": return "Height-based gas holdup" else: - print(input_string) + logger.info(input_string) return input_string diff --git a/bird/utilities/mathtools.py b/bird/utilities/mathtools.py index e75cfec3..6788ba2e 100644 --- a/bird/utilities/mathtools.py +++ b/bird/utilities/mathtools.py @@ -1,25 +1,72 @@ +import logging +import sys + import numpy as np +logger = logging.getLogger(__name__) + + +def conditional_average( + x: np.ndarray, y: np.ndarray, nbins: int = 32 +) -> tuple: + """ + Compute a 1D conditional average of y with respect to x + The conditional average is distributed to neighbors of the binned array when needed -def conditionalAverage(x, y, nbin): + Parameters + ---------- + x: np.ndarray + 1D array with respect to which conditional averaged is performed + y : np.ndarray + 1D array conditioned + nbins: int + Number of bins through x + + Returns + ---------- + x_cond: np.ndarray + The binned array of values conditioned againsts + y_cond: np.ndarray + The conditional averages at each bin + """ + # Check the shape of input arrays + try: + assert len(x.shape) <= 2 + assert len(y.shape) <= 2 + if len(x.shape) == 2: + assert x.shape[1] == 1 + if len(y.shape) == 2: + assert y.shape[1] == 1 + except AssertionError: + error_msg = "conditional average of tensors is ambiguous" + error_msg += f"\nx shape = {x.shape}" + error_msg += f"\ny shape = {y.shape}" + logger.error(error_msg) + sys.exit() + if len(x.shape) == 2: + x = x[:, 0] + if len(y.shape) == 2: + y = y[:, 0] try: assert len(x) == len(y) except AssertionError: - print("conditional average x and y have different dimension") - print("dim x = ", len(x)) - print("dim y = ", len(y)) + error_msg = "conditional average x and y have different dimension" + error_msg += f"\ndim x = {len(x)}" + error_msg += f"\ndim y = {len(y)}" + logger.error(error_msg) sys.exit() + # Bin conditional space mag = np.amax(x) - np.amin(x) x_bin = np.linspace( - np.amin(x) - mag / (2 * nbin), np.amax(x) + mag / (2 * nbin), nbin + np.amin(x) - mag / (2 * nbins), np.amax(x) + mag / (2 * nbins), nbins ) - weight = np.zeros(nbin) - weightVal = np.zeros(nbin) - asum = np.zeros(nbin) - bsum = np.zeros(nbin) - avalsum = np.zeros(nbin) - bvalsum = np.zeros(nbin) + weight = np.zeros(nbins) + weightVal = np.zeros(nbins) + asum = np.zeros(nbins) + bsum = np.zeros(nbins) + avalsum = np.zeros(nbins) + bvalsum = np.zeros(nbins) inds = np.digitize(x, x_bin) a = abs(y - x_bin[inds - 1]) @@ -28,7 +75,8 @@ def conditionalAverage(x, y, nbin): a = a / c b = b / c - for i in range(nbin): + # Conditional average at each bin + for i in range(nbins): asum[i] = np.sum(a[np.argwhere(inds == i)]) bsum[i] = np.sum(b[np.argwhere(inds == i + 1)]) avalsum[i] = np.sum( @@ -40,4 +88,8 @@ def conditionalAverage(x, y, nbin): weight = asum + bsum weightVal = avalsum + bvalsum - return x_bin, weightVal / (weight) + # Assemble output + x_cond = x_bin + y_cond = weightVal / (weight) + + return x_cond, y_cond diff --git a/bird/utilities/ofio.py b/bird/utilities/ofio.py index a8cd223f..fd2f1aaf 100644 --- a/bird/utilities/ofio.py +++ b/bird/utilities/ofio.py @@ -1,89 +1,413 @@ +import logging import os +import re import sys import numpy as np +logger = logging.getLogger(__name__) -def readMesh(file): - A = np.loadtxt(file, usecols=(1, 2, 3)) - return A +def readMesh(filename: str) -> np.ndarray: + """ + Reads cell center location from meshCellCentres_X.obj -def readOFScal(file, nCells, nHeader=None): - # Check that the field is not a uniform field - try: - f = open(file, "r") - for i in range(20): + Parameters + ---------- + filename: str + meshCellCentres_X.obj filename + + returns + ---------- + cell_centers: np.ndarray + Array (N,3) representing the cell centers (N is number of cells) + + """ + assert "meshCellCentres" in filename + assert ".obj" in filename + cell_centers = np.loadtxt(filename, usecols=(1, 2, 3)) + return cell_centers + + +def ofvec2arr(vec: str) -> np.ndarray: + """ + Converts a vector written as a string into a numpy array + + Parameters + ---------- + vec: str + Vector written as string + Must start with "(" + Must end with ")" + + returns + ---------- + vec_array: np.ndarray + Array (3,) representing the vector + + + """ + vec = vec.strip() + assert vec[0] == "(" + assert vec[-1] == ")" + + vec_list = vec[1:-1].split() + vec_array = np.array([float(entry) for entry in vec_list]) + return vec_array + + +def is_comment(line: str) -> bool: + """ + Checks if line is a comment + + Parameters + ---------- + line: str + Line of file + + returns + ---------- + is_comment: bool + True if line is a comment + False if line is not a comment + """ + is_comment = False + + sline = line.strip() + if sline.startswith("//"): + is_comment = True + elif sline.startswith("/*"): + is_comment = True + return is_comment + + +def read_meta_data(filename: str, mode: str | None = None) -> dict: + """ + Read meta data from field outputted by OpenFOAM in ASCII format + + Parameters + ---------- + filename: str + Field filename + mode: str | None + If "scalar", expects a scalar field + If "vector", expects a vector field + If None, obtained from field header + + returns + ---------- + meta_data: dict + Dictionary that contain info about the scalar field + ============= ===================================================== + Key Description (type) + ============= ===================================================== + name Name of the field (*str*) + n_cells Number of computational cells (*int*) + uniform Whether the field is uniform (*bool*) + uniform_value Uniform value if uniform field (*float* | *np.ndarray*) + type "vector" or "scalar" (*str*) + ============= ===================================================== + """ + meta_data = {} + meta_data["type"] = mode + + # Read meta data + with open(filename, "r") as f: + header_done = False + iline = 0 + while not header_done: line = f.readline() - f.close() - lineList = line.split() - if len(lineList) == 3 and lineList[1] == "uniform": - # Uniform field - val = float(lineList[2][:-1]) - Array = val * np.ones(nCells) + if not is_comment(line): + # Get field type + if (line.strip().startswith("class")) and (";" in line): + sline = line.strip().split() + field_type = sline[1][:-1] + if field_type == "volVectorField": + field_type = "vector" + elif field_type == "volScalarField": + field_type = "scalar" + else: + raise NotImplementedError + if mode is not None: + assert field_type == mode + meta_data["type"] = field_type + # Get field name + if (line.strip().startswith("object")) and (";" in line): + sline = line.strip().split() + field_name = sline[1][:-1] + meta_data["name"] = field_name + + # Check if uniform + if line.strip().startswith("internalField"): + if "nonuniform" in line: + meta_data["uniform"] = False + iline += 1 + # read until no comments + comment = True + while comment: + count_line = f.readline().strip() + if not is_comment(count_line): + comment = False + try: + n_cells = int(count_line) + meta_data["n_cells"] = n_cells + header_done = True + except ValueError: + raise ValueError( + f"Expected integer number of cells on line {iline}, got: '{count_line}'" + ) + elif "uniform" in line: + meta_data["uniform"] = True + sline = line.split() + if meta_data["type"] == "scalar": + unif_value = float(sline[-1].strip(";")) + elif meta_data["type"] == "vector": + unif_value = ofvec2arr(sline[-1].strip(";")) + else: + raise NotImplementedError( + f"Mode {mode} is unknown" + ) + meta_data["uniform_value"] = unif_value + header_done = True + + if len(line) == 0 and (not header_done): + raise ValueError( + f"File {filename} ends before meta-data found" + ) + + return meta_data + + +def readOFScal( + filename: str, + n_cells: int | None = None, + n_header: int | None = None, + meta_data: dict | None = None, +) -> dict: + """ + Read a scalar field outputted by OpenFOAM in ASCII format + + Parameters + ---------- + filename: str + Field filename + n_cells : int | None + Number of computational cells in the domain + n_header : int | None + Number of header lines + meta_data : dict | None + meta data dictionary + If None, it is read from filename + + returns + ---------- + data: dict + Dictionary that contain info about the scalar field + ======== ===================================================== + Key Description (type) + ======== ===================================================== + field For nonuniform fields, array of size the number of cells (*np.ndarray*). + For uniform fields with a specified n_cells, + array of size the number of cells (*np.ndarray*). + For uniform fields, a scalar value (*float*) + name Name of the scalar field (*str*) + n_cells Number of computational cells (*int*) + n_header Number of header lines (*int*) + ======== ===================================================== + """ + field = None + + if meta_data is None: + # Read meta data + meta_data = read_meta_data(filename, mode="scalar") + + # Set field + if meta_data["uniform"]: + if n_cells is None: + field = meta_data["uniform_value"] else: - # Not a uniform field - f = open(file, "r") - if nHeader is None: - # Find header - lines = f.readlines() - for iline, line in enumerate(lines[:-2]): - if str(nCells) in lines[iline] and "(" in lines[iline + 1]: + field = meta_data["uniform_value"] * np.ones(n_cells) + else: + n_cells = meta_data["n_cells"] + + # Get header size + if n_header is None: + oldline = "" + newline = "" + iline = 0 + eof = False + with open(filename, "r") as f: + while iline < 100 and (not eof): + line = f.readline() + if len(line) == 0: + eof = True + oldline = newline + newline = line + if str(n_cells) in oldline and "(" in newline: + n_header = iline + 1 break - nHeader = iline + 2 - f.close() - Array = np.loadtxt(file, skiprows=nHeader, max_rows=nCells) - except Exception as err: - print("Issue when reading %s" % file) - print(err) - sys.exit() - return Array + iline += 1 + else: + raise ValueError( + "Could not find a sequence {n_cells} and '(' in file ({filename})" + ) + # Rapid field read + try: + field = np.loadtxt(filename, skiprows=n_header, max_rows=n_cells) + except Exception as err: + logger.error(f"Issue when reading {filename}") + print(err) + sys.exit() -def ofvec2arr(vec): - vec_list = vec[1:-1].split() - vec_float = [float(entry) for entry in vec_list] - return np.array(vec_float) + return { + "field": field, + "name": meta_data["name"], + "n_cells": n_cells, + "n_header": n_header, + } -def readOFVec(file, nCells, nHeader=None): - # Check that the field is not a uniform field - try: - f = open(file, "r") - for i in range(20): - line = f.readline() - f.close() - lineList = line.split() - if len(lineList) == 3 and lineList[1] == "uniform": - # Uniform field - raise NotImplementedError - val = ofvec2arr(lineList[2][:-1]) - Array = val * np.ones(nCells) +def readOFVec( + filename: str, + n_cells: int | None = None, + n_header: int | None = None, + meta_data: dict | None = None, +) -> dict: + """ + Read a vector field outputted by OpenFOAM in ASCII format + + Parameters + ---------- + filename: str + Vector field filename + n_cells : int | None + Number of computational cells in the domain + n_header : int | None + Number of header lines + meta_data : dict | None + meta data dictionary + If None, it is read from filename + + returns + ---------- + data: dict + Dictionary that contain info about the scalar field + ======== ===================================================== + Key Description (type) + ======== ===================================================== + field For nonuniform fields, array of size the number of cells by 3 (*np.ndarray*). + For uniform fields with a specified n_cells, + array of size the number of cells by 3 (*np.ndarray*). + For uniform fields, a scalar value (*float*) + name Name of the field (*str*) + n_cells Number of computational cells (*int*) + n_header Number of header lines (*int*) + ======== ===================================================== + """ + field = None + + if meta_data is None: + # Read meta data + meta_data = read_meta_data(filename, mode="vector") + + # Set field + if meta_data["uniform"]: + if n_cells is None: + field = meta_data["uniform_value"] else: - # Not a uniform field - f = open(file, "r") - if nHeader is None: - # Find header - lines = f.readlines() - for iline, line in enumerate(lines[:-2]): - if str(nCells) in lines[iline] and "(" in lines[iline + 1]: + field = meta_data["uniform_value"] * np.ones((n_cells, 3)) + else: + n_cells = meta_data["n_cells"] + if n_header is None: + oldline = "" + newline = "" + iline = 0 + eof = False + with open(filename, "r") as f: + while iline < 100 and (not eof): + line = f.readline() + if len(line) == 0: + eof = True + oldline = newline + newline = line + if str(n_cells) in oldline and "(" in newline: + n_header = iline + 1 break - nHeader = iline + 2 - f.close() - Array = np.loadtxt( - file, dtype=tuple, skiprows=nHeader, max_rows=nCells + iline += 1 + else: + raise ValueError( + "Could not find a sequence {n_cells} and '(' in file ({filename})" + ) + + try: + field = np.loadtxt( + filename, dtype=tuple, skiprows=n_header, max_rows=n_cells ) - for i in range(nCells): - Array[i, 0] = float(Array[i, 0][1:]) - Array[i, 1] = float(Array[i, 1]) - Array[i, 2] = float(Array[i, 2][:-1]) - Array = np.array(Array).astype(float) - except Exception as err: - print("Issue when reading %s" % file) - print(err) - sys.exit() + for i in range(n_cells): + field[i, 0] = float(field[i, 0][1:]) + field[i, 1] = float(field[i, 1]) + field[i, 2] = float(field[i, 2][:-1]) + field = np.array(field).astype(float) + + except Exception as err: + logger.error(f"Issue when reading {filename}") + print(err) + sys.exit() + + return { + "field": field, + "name": meta_data["name"], + "n_cells": n_cells, + "n_header": n_header, + } + + +def readOF( + filename: str, + n_cells: int | None = None, + n_header: int | None = None, + meta_data: dict | None = None, +) -> dict: + """ + Read an OpenFOAM field outputted in ASCII format - return Array + Parameters + ---------- + filename: str + Field filename + n_cells : int | None + Number of computational cells in the domain + n_header : int | None + Number of header lines + meta_data : dict | None + meta data dictionary + If None, it is read from filename + + + returns + ---------- + data: dict + Dictionary that contain info about the scalar field + ======== ===================================================== + Key Description (type) + ======== ===================================================== + field For nonuniform fields, array of size the number of cells (*np.ndarray*). + For uniform fields with a specified n_cells, + array of size the number of cells (*np.ndarray*). + For uniform fields, a scalar value (*float*) + name Name of the field (*str*) + n_cells Number of computational cells (*int*) + n_header Number of header lines (*int*) + ======== ===================================================== + """ + if meta_data is None: + # Read meta data + meta_data = read_meta_data(filename) + if meta_data["type"] == "scalar": + return readOFScal(filename=filename, meta_data=meta_data) + if meta_data["type"] == "vector": + return readOFVec(filename=filename, meta_data=meta_data) def readSizeGroups(file): @@ -129,7 +453,25 @@ def readSizeGroups(file): return sizeGroup, binGroup -def getCaseTimes(casePath, remove_zero=False): +def getCaseTimes(casePath: str, remove_zero: bool = False) -> tuple: + """ + Get list of all time folders from an OpenFOAM case + + Parameters + ---------- + casePath: str + Path to case folder + remove_zero : bool + Whether to remove zero from the time folder list + + returns + ---------- + time_float_sorted: list[float] + List of time folder values in ascending order + time_str_sorted: list[str] + List of time folder names in ascending order + + """ # Read Time times_tmp = os.listdir(casePath) # remove non floats @@ -140,7 +482,7 @@ def getCaseTimes(casePath, remove_zero=False): if abs(a) < 1e-12: _ = times_tmp.pop(i) except ValueError: - print(f"{entry} not a time folder, removing") + logger.debug(f"{entry} not a time folder, removing") a = times_tmp.pop(i) # print('removed ', a) time_float = [float(entry) for entry in times_tmp] @@ -152,8 +494,234 @@ def getCaseTimes(casePath, remove_zero=False): return time_float_sorted, time_str_sorted -def getMeshTime(casePath): +def getMeshTime(casePath: str) -> str: + """ + Get the time at which the mesh was printed + + Parameters + ---------- + casePath: str + Path to case folder + + Returns + ---------- + time_mesh: str + The name of the time at which "meshFaceCentresXXX" was created + """ + files_tmp = os.listdir(casePath) for entry in files_tmp: - if entry.startswith("meshFaceCentres"): - return entry[16:-4] + if "meshFaceCentres" in entry: + time_mesh = entry[16:-4] + return time_mesh + + +def remove_comments(text: str) -> str: + """ + Remove C++-style comments (// and /*) from the input and markers like #{ #} + + Parameters + ---------- + text: str + Raw input text containing comments + + Returns + ---------- + text: str + Text with all comments removed + """ + + # text = re.sub( + # r"/\*.*?\*/", "", text, flags=re.DOTALL + # ) # Remove /* */ comments + # text_unc = re.sub(r"//.*", "", text) # Remove // comments + + text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL) + text = re.sub(r"//.*", "", text) + text = re.sub(r"#\{", "{", text) + text = re.sub(r"#\};", "}", text) + text = re.sub(r"#codeStream", "", text) + return text + + +def tokenize(text: str) -> list[str]: + """ + Add spaces around special characters (brace and semicolons) to make them separate tokens + + Parameters + ---------- + text: str + The cleaned (comment-free) OpenFOAM-style text. + + Returns: + ---------- + token_list: list[str] + List of tokens. + """ + text = re.sub(r"([{}();])", r" \1 ", text) + token_list = text.split() + return token_list + + +def parse_tokens(tokens: list[str]) -> dict: + """ + Parse OpenFOAM tokens into a nested Python dictionary. + Special handling for `code { ... }` blocks to be stored as raw strings. + + Parameters + ---------- + tokens: list[str] + A list of tokens produced by `tokenize`. + + Returns + ---------- + parsed: dict + A nested dictionary that represents the OpenFOAM dictionary. + """ + + def parse_block(index: int) -> tuple: + result = {} + while index < len(tokens): + token = tokens[index] + if token == "}": + return result, index + 1 + elif token == "{": + raise SyntaxError("Unexpected '{'") + + key = token + index += 1 + + # key followed by dictionary + if index < len(tokens) and tokens[index] == "{": + index += 1 + if key == "code": + code_lines = [] + while tokens[index] != "}": + code_lines.append(tokens[index]) + index += 1 + index += 1 + if index < len(tokens) and tokens[index] == ";": + index += 1 + result[key] = " ".join(code_lines).strip() + else: + subdict, index = parse_block(index) + result[key] = subdict + + # key followed by list + elif index < len(tokens) and tokens[index] == "(": + index += 1 + + # Peek to check if it's a dict-list (starts with '(' then '{') + if tokens[index] == "(": + dictlist = {} + while tokens[index] != ")": + if tokens[index] != "(": + raise SyntaxError( + f"Expected '(' for label in dict-list, got {tokens[index]}" + ) + # Read full label (e.g., "(gas and liquid)") + label_tokens = [] + while tokens[index] != ")": + label_tokens.append(tokens[index]) + index += 1 + label_tokens.append(tokens[index]) # include ')' + index += 1 + label = " ".join(label_tokens) + + if tokens[index] != "{": + raise SyntaxError( + f"Expected '{{' after label {label}" + ) + index += 1 + subdict, index = parse_block(index) + dictlist[label] = subdict + index += 1 # skip final ')' + if index < len(tokens) and tokens[index] == ";": + index += 1 + result[key] = dictlist + else: + # Standard list + lst = [] + while tokens[index] != ")": + lst.append(tokens[index]) + index += 1 + index += 1 + if index < len(tokens) and tokens[index] == ";": + index += 1 + result[key] = lst + + # key followed by scalar + elif index < len(tokens): + value = tokens[index] + index += 1 + if index < len(tokens) and tokens[index] == ";": + index += 1 + result[key] = value + + return result, index + + parsed, _ = parse_block(0) + return parsed + + +def parse_openfoam_dict(filename: str) -> dict: + """ + Parse OpenFOAM dictionary into a python dictionary + + Parameters + ---------- + filename: str + OpenFOAM dictionary filename + + Returns + ---------- + dict_of: dict + A Python dictionary representing the structure of the OpenFOAM dictionary. + """ + with open(filename, "r+") as f: + text = f.read() + text = remove_comments(text) + tokens = tokenize(text) + foam_dict = parse_tokens(tokens) + return foam_dict + + +def write_openfoam_dict(d: dict, filename: str, indent: int = 0) -> None: + """ + Save a Python dictionary back to an OpenFOAM-style file. + + Parameters + ---------- + d: dict + Python dictionary to save + filename: str + The file that will contain the saved dictionary + indent: int + Number of indentation space + """ + + lines = [] + + indent_str = " " * indent + + for key, value in d.items(): + if isinstance(value, dict): + lines.append(f"{indent_str}{key}") + lines.append(f"{indent_str}{{") + lines.extend(write_openfoam_dict(value, indent + 4)) + lines.append(f"{indent_str}}}") + elif isinstance(value, list): + lines.append(f"{indent_str}{key}") + lines.append(f"{indent_str}(") + for item in value: + lines.append(f"{indent_str} {item}") + lines.append(f"{indent_str});") + else: + lines.append(f"{indent_str}{key} {value};") + + with open(filename, "w") as f: + lines = write_openfoam_dict(foam_dict) + f.write("\n".join(lines)) + f.write( + "\n\n// ************************************************************************* //\n" + ) diff --git a/bird/utilities/stl_plotting.py b/bird/utilities/stl_plotting.py index 6f120894..19fdaabb 100644 --- a/bird/utilities/stl_plotting.py +++ b/bird/utilities/stl_plotting.py @@ -3,7 +3,6 @@ def plotSTL(stl_file): - import matplotlib.pyplot as plt from mpl_toolkits import mplot3d from stl import mesh diff --git a/bird/version.py b/bird/version.py index 75d8e719..c4338ae1 100644 --- a/bird/version.py +++ b/bird/version.py @@ -1,3 +1,3 @@ """Bio reactor design version""" -__version__ = "0.0.19" +__version__ = "0.0.27" diff --git a/docs/assets/cond_mean_tut.png b/docs/assets/cond_mean_tut.png new file mode 100644 index 00000000..564d02e9 Binary files /dev/null and b/docs/assets/cond_mean_tut.png differ diff --git a/docs/assets/overlap_patches.png b/docs/assets/overlap_patches.png new file mode 100644 index 00000000..133d1d79 Binary files /dev/null and b/docs/assets/overlap_patches.png differ diff --git a/docs/source/contribute.rst b/docs/source/contribute.rst index 54c1fd74..147e399c 100644 --- a/docs/source/contribute.rst +++ b/docs/source/contribute.rst @@ -1,7 +1,7 @@ Contributing ===== -We welcome pull requests from anybody! +We welcome pull requests from anyone! Formatting ------------ @@ -16,8 +16,33 @@ You can automatically enforce the formatting guidelines with bash fixFormat.sh -Test +Tests ------------ -Please ensure your contribution passes the tests in ``tests`` +Please ensure your contribution passes the tests in the CI (``.github/worklows/ci.yml``) +To run the unit tests +.. code-block:: console + + conda activate bird + pip install pytest + BIRD_HOME=`python -c "import bird; print(bird.BIRD_DIR)"` + cd ${BIRD_HOME}/../ + pytest . + +To run the regression tests + +.. code-block:: console + + source /etc/rc + conda activate bird + pip install pytest + BIRD_HOME=`python -c "import bird; print(bird.BIRD_DIR)"` + cd ${BIRD_HOME}/../tutorial_cases + bash run_all.sh + +Demonstrating and documenting your contribution +------------ +We prefer the use of docstrings and type hinting. A good example to follow are functions in ``bird/utilities/ofio.py``. + +If you add a new capability, please make sure to add relevant unit tests in the ``tests/`` folder. A good example to follow are tests ``tests/io`` diff --git a/docs/source/index.rst b/docs/source/index.rst index 7f315c91..747210fd 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -26,8 +26,10 @@ We provide a solver ``birdmultiphaseEulerFoam`` that contains custom models adde quickstart meshing preprocess - postprocess + uq + python_interface tutorials contribute references + troubleshoot acknowledgments diff --git a/docs/source/preprocess.rst b/docs/source/preprocess.rst index db6e79f8..eb67a45d 100644 --- a/docs/source/preprocess.rst +++ b/docs/source/preprocess.rst @@ -41,6 +41,24 @@ Edit the json files that are read when generating the mesh. In the case ``tutori } +What if the STL patches overlap? +^^^^^^^^^^^^^^^ + +If STL patches are defined such that there is an overlap between patches, the final patch will be the union of the overlapping patches. +In case of an overlap, the final patch will be therefore smaller than without an overlap. +An example of this behavior is shown below for a U-loop reactor. + +In this case, the inlet (highlighted in red) contain 2 circular spargers. On the left, the two spargers contain an overlap and on the right they are disjoint. The inlet patch surface area is 13% smaller on the left than the right, but both simulations successfully run. + +.. figure:: ../assets/overlap_patches.png + :width: 95% + :align: center + :name: fig-stl-patch + :target: ../assets/overlap_patches.png + :alt: Overlapping STL patch + + + Related tutorials ^^^^^^^^^^^^^^^ diff --git a/docs/source/pythonInterface_tut.rst b/docs/source/pythonInterface_tut.rst new file mode 100644 index 00000000..38390400 --- /dev/null +++ b/docs/source/pythonInterface_tut.rst @@ -0,0 +1,146 @@ +Post processing with the python interface to OpenFOAM +===== + +This tutorial demonstrates how to post process a case with the python interface to OpenFOAM provided in BiRD. + +The tutorial assumes you have created and activated the ``bird`` environment and setup path variables as + +.. code-block:: console + + conda activate bird + BIRD_HOME=`python -c "import bird; print(bird.BIRD_DIR)"` + DCM_CASE=${BIRD_HOME}/postprocess/data_conditional_mean/ + cd $DCM_CASE + +The case and the data correspond to a coflowing bubble column. + +This tutorial is code-along, meaning to that you can execute the commands listed in the code-blocks. You can also execute the entirety of the tutorial in ``$DCM_CASE/python_interface_tut.py`` + +Reading fields +------------ + +The mesh manipulation tools are located in ``bird.utilities.ofio`` + +Before starting to manipulate the fields with the python interface, we need mesh descriptor object files, in particular ``meshCellCentres_1.obj``. This file contains the coordinates of the cell centers which is the location where the internal fields values are. The folder ``$DCM_CASE`` contains pre-generated mesh descriptor object files. The user can generate these files with ``writeMeshObj -case {case_folder}``. This command will not work for this tutorial as it would require activating your OpenFOAM environment and would require a ``constant`` and ``system`` folders that we did not add because of space constraints. + +To read the cells centers + +.. code-block:: python + + from bird.utilities.ofio import * + cell_centers = readMesh("meshCellCentres_1.obj") + +``cell_centers`` is a (N,3) numpy array that contains the cell center coordinates (N is the number of cells in the domain) + + +If we want to read the gas volume fraction ``alpha.gas``, the mass fraction of CO2 in the gas phase ``CO2.gas``, and the liquid velocity at time ``80/`` we can run + +.. code-block:: python + + co2_gas = readOF("80/CO2.gas") + alpha_gas = readOF("80/alpha.gas") + u_liq = readOF("80/U.liquid") + print("cell CO2 gas shape = ", co2_gas["field"].shape) + print("cell alpha gas shape = ", alpha_gas["field"].shape) + print("cell u liq shape = ", u_liq["field"].shape) + +The function ``readOF`` generates a dictionary of values and automatically detects whether the field is a vector or a scalar field. + + + +Plot conditional means +------------ + +With cells center locations and the internal fields, one can use python functions to post process the case. For example, we might want to compute conditional averages over a spatial direction, say y + +.. code-block:: python + + from bird.utilities.mathtools import conditional_average + + y_co2_gas_cond, co2_gas_cond = conditional_average( + cell_centers[:, 1], co2_gas["field"], nbins=32 + ) + y_alpha_gas_cond, alpha_gas_cond = conditional_average( + cell_centers[:, 1], alpha_gas["field"], nbins=32 + ) + + from prettyPlot.plotting import * + fig = plt.figure() + plt.plot(y_co2_gas_cond, co2_gas_cond, color="k", label=r"$Y_{CO_2}$ [-]") + plt.plot( + y_alpha_gas_cond, alpha_gas_cond, color="b", label=r"$\alpha_{g}$ [-]" + ) + pretty_labels("Y [m]", "", fontsize=20, grid=False, fontname="Times") + pretty_legend(fontname="Times") + plt.show() + + +This will show the following plot + + +.. container:: figures-cond-mean-tut + + .. figure:: ../assets/cond_mean_tut.png + :width: 70% + :align: center + :alt: Height-conditional mean + + +Compute reactor properties +------------ + +The python interface is also useful to compute reactor averaged properties. We usually like to compute volume averaged properties, which requires access to the cell volume. A cell volume field ``V`` can be written using OpenFOAM utilities (``postProcess -func writeCellVolumes -time {time_folder} -case {case_folder}``). Running this command would again require activating the OpenFOAM environment and we already provide a volume field in the ``1/`` folder here. + +A typical example is that one would want to compute at time 80 +1. gas hold up (``gh``) +2. superficial velocity (``sup_vel``) +3. reactor volume averaged mass fraction of CO2 in the liquid phase (``y_ave_co2``) +4. reactor volume averaged concentration of CO2 in the liquid phase (``c_ave_co2``) +5. Reactor averaged bubble diameter (``diam``) + +Several of these quantities, will require reading and processing the same fields. For example, both ``y_ave_co2`` and ``c_ave_co2`` require to read ``CO2.liquid``. To avoid re-reading the same fields, we store the fields in ``field_dict`` that allows to reuse fields when possible. + +.. code-block:: python + + from bird.postprocess.post_quantities import * + + kwargs = {"case_folder": ".", "time_folder": "80"} + gh, field_dict = compute_gas_holdup( + volume_time="1", field_dict={"cell_centers": cell_centers}, **kwargs + ) + print("fields stored = ", list(field_dict.keys())) + print(f"Gas Holdup = {gh:.4g}") + sup_vel, field_dict = compute_superficial_velocity( + volume_time="1", field_dict=field_dict, **kwargs + ) + print("fields stored = ", list(field_dict.keys())) + print(f"Superficial velocity = {sup_vel:.4g} m/s") + y_ave_co2, field_dict = compute_ave_y_liq( + volume_time="1", spec_name="CO2", field_dict=field_dict, **kwargs + ) + print("fields stored = ", list(field_dict.keys())) + print(f"Reactor averaged YCO2 = {y_ave_co2:.4g}") + c_ave_co2, field_dict = compute_ave_conc_liq( + volume_time="1", + spec_name="CO2", + mol_weight=0.04401, + rho_val=1000, + field_dict=field_dict, + **kwargs, + ) + print("fields stored = ", list(field_dict.keys())) + print(f"Reactor averaged [CO2] = {c_ave_co2:.4g} mol/m3") + diam, field_dict = compute_ave_bubble_diam( + volume_time="1", field_dict=field_dict, **kwargs + ) + print("fields stored = ", list(field_dict.keys())) + print(f"Reactor averaged bubble diameter = {diam:.4g} m") + + + + + + + + + diff --git a/docs/source/python_interface.rst b/docs/source/python_interface.rst new file mode 100644 index 00000000..9f721c50 --- /dev/null +++ b/docs/source/python_interface.rst @@ -0,0 +1,68 @@ +Python interface to OpenFOAM +===== + +We provide a simple python interface for reading OpenFOAM case results and dictionaries. +A more comprehensive interface is available through ``pyFoam`` although we found it difficult to use and without recent support, which motivated the implementation of a new python interface. + + +Read fields +------------ + +Internal scalar and vector fields +~~~~~~~~~~~~~~~~~~~~ + +Currently internal scalar and vector fields can be read using the python interface. In particular, note that +#. 1. We do not support reading tensor fields for now. +#. 2. We do not support reading boundary fields for now. +#. 3. We only read reconstructed files + +We are open to implementing 1. and 2. if need be. Implementing 3. is possible but will be more involved. + +The main function interface to read openFOAM fields is ``readOF`` in ``bird.utilities.ofio`` + +The function only reads fields written in ASCII format and decided based on the header whether the field is a scalar or a vector. In the case of scalar, ``readOF`` returns a (N,) numpy array, where N is the number of computational cells. In the case of a vector, ``readOF`` returns a (N,3) numpy array. + +If a uniform field is read, the number of cells may not be available from the field file and the function returns a float (equal to the uniform internal field value). If a numpy array is needed, the user can specify the number of cells in the field as an input. + +Reuse instead of re-read +~~~~~~~~~~~~~~~~~~~~ + +The ``read_field`` function uses ``readOF`` and takes a dictionary ``field_dict`` as input which is used to avoid reading multiple times the same field. For example, if one wants to compute the reactor-averaged concentration of a species, and then the reactor-averaged mass fraction of a species, the same mass fraction field will be used in both cases. As fields are read, ``field_dict`` will store the mass fraction field and recognize that the same field is needed. + +It is up to the user to reinitialize ``field_dict``. For example, if the reactor-averaged mass fraction needs to be computed at time ``1`` and then at time ``2``, the user needs to pass an empty dictionary (or nothing) to ``read_field`` before reading the fields at time ``2``. Otherwise, ``read_field`` will assume that the mass fraction field is the same between time ``1`` and time ``2``. + + +Read mesh-related fields +~~~~~~~~~~~~~~~~~~~~ + +We rely on OpenFOAM utilities to provide mesh-based fields. The results of the OpenFOAM utilities can still be processed using ``bird`` functions. + + +Reading cell volumes +^^^^^^^^^^^^^^^ + +A cell volume field can be generated using the following OpenFOAM command ``postProcess -func writeCellVolumes -time {time_folder} -case {case_folder}`` +It will generate a file ``{time_folder}/V`` which can be read with the ``readOF`` function of ``bird``. +This workflow is used in ``bird.postprocess.post_quantitities``, for example in the ``compute_gas_holdup`` function. + + +Reading cell centers +^^^^^^^^^^^^^^^ + +A mesh object file can be generated with the OpenFOAM command ``writeMeshObj -case {case_folder}`` +The file can then be read with the function ``readMesh`` from ``bird.utilities.ofio``. +Again, this is used in ``bird.postprocess.post_quantities`` in the ``compute_superficial_velocity`` function. + + + + +Read dictionaries +------------ + +We provide a function ``parse_openfoam_dict`` in ``bird.utilities.ofio`` that can parse OpenFOAM dictionaries. The function requires a lot of special characters handling but works for processing basic dictionaries needed to manage OpenFOAM cases (``controlDict``, ``setFieldsDict``, ``phaseProperties``, ``thermophysicalProperties``, ``momentumTransport``, ...) + + +Generate cases +------------ + +(to be added based on the reactor optimization work) diff --git a/docs/source/references.rst b/docs/source/references.rst index 5b325880..051a2b7f 100644 --- a/docs/source/references.rst +++ b/docs/source/references.rst @@ -9,10 +9,13 @@ To cite BiRD, please use these articles on `CO2 interphase mass transfer FOAM FATAL ERROR: + [6] Unknown dragModelType type Grace + + +This may mean that you are using ``multiphaseEulerFoam`` instead of ``birdmultiphaseEulerFoam`` + + + +BDOFoam does not compile +------------ + +``birdmultiphaseEulerFoam`` requires OpenFOAM 9 but ``bdoFoam`` requires OpenFOAM 6. +Compiling ``bdoFoam`` also requires a little more work that ``birdmultiphaseEulerFoam``. +Detailed step are discussed `here `_ + +In the future, we will make sure that ``bdoFoam`` works with OpenFOAM 9. diff --git a/docs/source/tutorials.rst b/docs/source/tutorials.rst index 1d19efcf..0d37751a 100644 --- a/docs/source/tutorials.rst +++ b/docs/source/tutorials.rst @@ -7,9 +7,20 @@ Tutorials ********* .. toctree:: - :maxdepth: 3 - :caption: Tutorials contents: + :maxdepth: 1 + :caption: Bioreactor cases bubbleColumn.rst + +.. toctree:: + :maxdepth: 2 + :caption: Post processing OpenFOAM cases + + pythonInterface_tut.rst + +.. toctree:: + :maxdepth: 1 + :caption: Bayesian calibration of models + calibration_normal_beta.rst calibration_bsd.rst diff --git a/docs/source/postprocess.rst b/docs/source/uq.rst similarity index 79% rename from docs/source/postprocess.rst rename to docs/source/uq.rst index d5656943..142c682b 100644 --- a/docs/source/postprocess.rst +++ b/docs/source/uq.rst @@ -1,4 +1,4 @@ -Postprocess +Uncertainty quantification ===== @@ -28,29 +28,6 @@ Generates :alt: Uncertainty-aware early prediction -Plot conditional means ------------- - -.. code-block:: console - - python applications/compute_conditional_mean.py -f bird/postprocess/data_conditional_mean -avg 2 - -Generates (among others) - -.. container:: figures-cond-mean - - .. figure:: ../assets/gh_cond_mean.png - :width: 70% - :align: center - :alt: Height-conditional gas holdup - - .. figure:: ../assets/co2g_cond_mean.png - :width: 70% - :align: center - :alt: Height-conditional CO2 gas fraction - - - Compute kLa with uncertainty estimates ------------ diff --git a/experimental_cases/deckwer17/Allclean b/experimental_cases/deckwer17/Allclean index f4c6f3db..dc2f77db 100755 --- a/experimental_cases/deckwer17/Allclean +++ b/experimental_cases/deckwer17/Allclean @@ -1,15 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions - -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase -#rm system/blockMeshDict +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi + +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt + #------------------------------------------------------------------------------ diff --git a/experimental_cases/deckwer17/run.sh b/experimental_cases/deckwer17/run.sh index 599e5a5b..7c667d34 100755 --- a/experimental_cases/deckwer17/run.sh +++ b/experimental_cases/deckwer17/run.sh @@ -1,3 +1,4 @@ +#!/bin/bash if ! type "blockMesh" &> /dev/null; then echo " could not be found" echo "OpenFoam is likely not installed, skipping run" @@ -6,6 +7,11 @@ else ./Allclean fi +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + if ! type "python" &> /dev/null; then echo " could not be found" echo "Skipping Mesh generation" diff --git a/experimental_cases/deckwer17/system/controlDict b/experimental_cases/deckwer17/system/controlDict index 61296ae9..6942e6d0 100644 --- a/experimental_cases/deckwer17/system/controlDict +++ b/experimental_cases/deckwer17/system/controlDict @@ -13,7 +13,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime; diff --git a/experimental_cases/deckwer19/Allclean b/experimental_cases/deckwer19/Allclean index e9225d17..dc2f77db 100755 --- a/experimental_cases/deckwer19/Allclean +++ b/experimental_cases/deckwer19/Allclean @@ -1,15 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions - -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase -rm system/blockMeshDict +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi + +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt + #------------------------------------------------------------------------------ diff --git a/experimental_cases/deckwer19/run.sh b/experimental_cases/deckwer19/run.sh index 599e5a5b..4bb7ca5f 100755 --- a/experimental_cases/deckwer19/run.sh +++ b/experimental_cases/deckwer19/run.sh @@ -1,3 +1,8 @@ +#!/bin/bash +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + if ! type "blockMesh" &> /dev/null; then echo " could not be found" echo "OpenFoam is likely not installed, skipping run" diff --git a/experimental_cases/deckwer19/system/controlDict b/experimental_cases/deckwer19/system/controlDict index 61296ae9..6942e6d0 100644 --- a/experimental_cases/deckwer19/system/controlDict +++ b/experimental_cases/deckwer19/system/controlDict @@ -13,7 +13,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime; diff --git a/experimental_cases/disengagement/bubble_column_pbe_20L/Allclean b/experimental_cases/disengagement/bubble_column_pbe_20L/Allclean index f55e0ec9..dc2f77db 100755 --- a/experimental_cases/disengagement/bubble_column_pbe_20L/Allclean +++ b/experimental_cases/disengagement/bubble_column_pbe_20L/Allclean @@ -1,18 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase +# Remove 0 +[ -d "0" ] && rm -rf 0 -#rm *.obj -#rm *.stl +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt #------------------------------------------------------------------------------ diff --git a/experimental_cases/disengagement/bubble_column_pbe_20L/presteps.sh b/experimental_cases/disengagement/bubble_column_pbe_20L/presteps.sh index 63e1b33b..f7db7b17 100755 --- a/experimental_cases/disengagement/bubble_column_pbe_20L/presteps.sh +++ b/experimental_cases/disengagement/bubble_column_pbe_20L/presteps.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Clean case module load conda conda activate bird @@ -5,6 +7,12 @@ module load openfoam/9-craympich #source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc ./Allclean + +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + BIRD_HOME="/home/fmunicch/BioReactorDesign/bird" diff --git a/experimental_cases/disengagement/bubble_column_pbe_20L/system/controlDict b/experimental_cases/disengagement/bubble_column_pbe_20L/system/controlDict index f46a636b..0f3246f2 100644 --- a/experimental_cases/disengagement/bubble_column_pbe_20L/system/controlDict +++ b/experimental_cases/disengagement/bubble_column_pbe_20L/system/controlDict @@ -14,7 +14,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime;//startTime; diff --git a/experimental_cases/disengagement/bubble_column_pbe_20L/writeGlobalVars.py b/experimental_cases/disengagement/bubble_column_pbe_20L/writeGlobalVars.py index 0594eccc..3445bdb8 100644 --- a/experimental_cases/disengagement/bubble_column_pbe_20L/writeGlobalVars.py +++ b/experimental_cases/disengagement/bubble_column_pbe_20L/writeGlobalVars.py @@ -34,10 +34,12 @@ def readInletArea(): def getLiqVol(): cellCentres = readMesh(os.path.join(".", f"meshCellCentres_0.obj")) - volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres)) + volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres))[ + "field" + ] alpha_field = readOFScal( os.path.join("0", "alpha.liquid"), len(cellCentres) - ) + )["field"] return np.sum(volume_field * alpha_field) diff --git a/papers/co2_model/cases/breakup_17_mesh1_opt/system/controlDict b/papers/co2_model/cases/breakup_17_mesh1_opt/system/controlDict index f62759d0..fce220d3 100644 --- a/papers/co2_model/cases/breakup_17_mesh1_opt/system/controlDict +++ b/papers/co2_model/cases/breakup_17_mesh1_opt/system/controlDict @@ -13,7 +13,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime; diff --git a/papers/co2_model/cases/breakup_19_mesh1_opt/system/controlDict b/papers/co2_model/cases/breakup_19_mesh1_opt/system/controlDict index f62759d0..fce220d3 100644 --- a/papers/co2_model/cases/breakup_19_mesh1_opt/system/controlDict +++ b/papers/co2_model/cases/breakup_19_mesh1_opt/system/controlDict @@ -13,7 +13,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime; diff --git a/setup.py b/setup.py index 4eaa7bd2..d98a2918 100644 --- a/setup.py +++ b/setup.py @@ -29,18 +29,11 @@ "License :: OSI Approved :: BSD License", "Natural Language :: English", "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ], package_data={ "": [ "*requirements.txt", - "*.json", - "*.yaml", - "*.csv", - "*.dat", - "data_conditional_mean", - "data_kla", ] }, extras_require={ @@ -50,6 +43,10 @@ "scikit-learn", "tf2jax", ], + "optim": [ + "optuna", + "pandas", + ], }, project_urls={ "Documentation": "https://nrel.github.io/BioReactorDesign/", diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/io/test_case.py b/tests/io/test_case.py new file mode 100644 index 00000000..cbb55537 --- /dev/null +++ b/tests/io/test_case.py @@ -0,0 +1,28 @@ +import os +from pathlib import Path + +import numpy as np + +from bird.utilities.ofio import getCaseTimes + + +def test_case_time(): + """ + Test for listing all time folders in a case + """ + caseFolder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + # Read non uniform field + time_float, time_str = getCaseTimes(caseFolder) + assert np.linalg.norm(np.array(time_float) - np.array([1, 79, 80])) < 1e-6 + assert time_str == ["1", "79", "80"] + + +if __name__ == "__main__": + test_case_time() diff --git a/tests/io/test_read_foam_dict.py b/tests/io/test_read_foam_dict.py new file mode 100644 index 00000000..6b539d92 --- /dev/null +++ b/tests/io/test_read_foam_dict.py @@ -0,0 +1,114 @@ +import os +from pathlib import Path + +import numpy as np + +from bird.utilities.ofio import parse_openfoam_dict + + +def test_read_phaseProperties(): + """ + Test for reading content of `constant/phaseProperties` + """ + const_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "tutorial_cases", + "loop_reactor_mixing", + "constant", + ) + # Read non uniform field + foam_dict = parse_openfoam_dict( + filename=os.path.join(const_folder, "phaseProperties") + ) + + assert foam_dict["phases"] == ["gas", "liquid"] + assert foam_dict["gas"]["constantCoeffs"]["d"] == "3e-3" + assert ( + foam_dict["liquid"]["Sc"]["code"] + == "os << ( $LeLiqMix * $CpMixLiq * $muMixLiq / $kThermLiq ) ;" + ) + assert ( + foam_dict["diffusiveMassTransfer.liquid"]["( gas in liquid )"]["type"] + == "Higbie" + ) + assert ( + foam_dict["lift"]["( gas in liquid )"]["lift"]["swarmCorrection"][ + "type" + ] + == "none" + ) + + +def test_read_thermophysicalProperties(): + """ + Test for reading content of `constant/thermophysicalProperties` + """ + const_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "tutorial_cases", + "loop_reactor_mixing", + "constant", + ) + # Read non uniform field + foam_dict = parse_openfoam_dict( + filename=os.path.join(const_folder, "thermophysicalProperties.gas") + ) + + assert foam_dict["species"] == ["H2", "CO2", "N2"] + assert ( + foam_dict["CO2"]["thermodynamics"]["highCpCoeffs"][0] == "3.85746029" + ) + assert len(foam_dict["CO2"]["thermodynamics"]["highCpCoeffs"]) == 7 + + +def test_read_momentumTransport(): + """ + Test for reading content of `constant/momentumTransport` + """ + const_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "tutorial_cases", + "loop_reactor_mixing", + "constant", + ) + # Read non uniform field + foam_dict = parse_openfoam_dict( + filename=os.path.join(const_folder, "momentumTransport.gas") + ) + + assert foam_dict["simulationType"] == "RAS" + assert foam_dict["RAS"]["turbulence"] == "on" + + +def test_read_controlDict(): + """ + Test for reading content of `system/controlDict` + """ + syst_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "tutorial_cases", + "loop_reactor_mixing", + "system", + ) + # Read non uniform field + foam_dict = parse_openfoam_dict( + filename=os.path.join(syst_folder, "controlDict") + ) + + assert foam_dict["writeControl"] == "adjustableRunTime" + assert foam_dict["maxCo"] == "0.5" + + +if __name__ == "__main__": + test_read_phaseProperties() + test_read_thermophysicalProperties() + test_read_momentumTransport() + test_read_controlDict() diff --git a/tests/io/test_read_foam_fields.py b/tests/io/test_read_foam_fields.py new file mode 100644 index 00000000..b293fc66 --- /dev/null +++ b/tests/io/test_read_foam_fields.py @@ -0,0 +1,118 @@ +import os +from pathlib import Path + +import numpy as np + +from bird.utilities.ofio import readOF, readOFScal, readOFVec + + +def test_read_nonunif_scal(): + """ + Test for reading non uniform scalarField + """ + caseFolder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + # Read non uniform field + data_dict = readOFScal(filename=os.path.join(caseFolder, "79", "CO2.gas")) + assert abs(data_dict["field"][0] - 0.616955) < 1e-6 + assert abs(data_dict["field"][-1] - 0.625389) < 1e-6 + assert abs(data_dict["n_cells"] - 137980) < 1e-6 + assert abs(data_dict["field"].shape[0] - 137980) < 1e-6 + assert data_dict["name"] == "CO2.gas" + # Read non uniform field with flexible interface + data_dict = readOF(filename=os.path.join(caseFolder, "79", "CO2.gas")) + assert abs(data_dict["field"][0] - 0.616955) < 1e-6 + assert abs(data_dict["field"][-1] - 0.625389) < 1e-6 + assert abs(data_dict["n_cells"] - 137980) < 1e-6 + assert abs(data_dict["field"].shape[0] - 137980) < 1e-6 + assert data_dict["name"] == "CO2.gas" + + +def test_read_unif_scal(): + """ + Test for reading uniform scalarField + """ + caseFolder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + # Read non uniform field + data_dict = readOFScal(filename=os.path.join(caseFolder, "79", "f.gas")) + assert abs(data_dict["field"] - 1) < 1e-6 + assert data_dict["n_cells"] is None + # Read non uniform field with flexible interface + data_dict = readOF(filename=os.path.join(caseFolder, "79", "f.gas")) + assert abs(data_dict["field"] - 1) < 1e-6 + assert data_dict["n_cells"] is None + # Read non uniform field with prespecified cell number + data_dict = readOFScal( + filename=os.path.join(caseFolder, "79", "f.gas"), n_cells=100 + ) + assert np.shape(data_dict["field"]) == (100,) + assert np.linalg.norm(data_dict["field"] - 1) < 1e-6 + assert data_dict["n_cells"] == 100 + assert data_dict["name"] == "f.gas" + + +def test_read_nonunif_vec(): + """ + Test for reading non uniform vectorField + """ + caseFolder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + # Read non uniform field + data_dict = readOFVec(filename=os.path.join(caseFolder, "79", "U.gas")) + assert ( + np.linalg.norm( + data_dict["field"][0, :] - [0.140018, 1.20333, 0.127566] + ) + < 1e-6 + ) + assert ( + np.linalg.norm( + data_dict["field"][-1, :] - [0.0409271, 0.176052, 0.0302899] + ) + < 1e-6 + ) + assert abs(data_dict["n_cells"] - 137980) < 1e-6 + assert abs(data_dict["field"].shape[0] - 137980) < 1e-6 + assert data_dict["name"] == "U.gas" + # Read non uniform field with flexible interface + data_dict = readOF(filename=os.path.join(caseFolder, "79", "U.gas")) + assert ( + np.linalg.norm( + data_dict["field"][0, :] - [0.140018, 1.20333, 0.127566] + ) + < 1e-6 + ) + assert ( + np.linalg.norm( + data_dict["field"][-1, :] - [0.0409271, 0.176052, 0.0302899] + ) + < 1e-6 + ) + assert abs(data_dict["n_cells"] - 137980) < 1e-6 + assert abs(data_dict["field"].shape[0] - 137980) < 1e-6 + assert data_dict["name"] == "U.gas" + + +if __name__ == "__main__": + test_read_nonunif_scal() + test_read_unif_scal() + test_read_nonunif_vec() diff --git a/tests/meshing/test_block_cyl_mesh.py b/tests/meshing/test_block_cyl_mesh.py index 1847dbc8..394da6ce 100644 --- a/tests/meshing/test_block_cyl_mesh.py +++ b/tests/meshing/test_block_cyl_mesh.py @@ -1,9 +1,9 @@ import os import sys +from pathlib import Path import numpy as np -from bird import BIRD_BLOCK_CYL_MESH_TEMP_DIR from bird.meshing.block_cyl_mesh import ( assemble_geom, assemble_mesh, @@ -19,6 +19,14 @@ def base_mesh(input_file, topo_file, output_folder): def test_side_sparger(): + BIRD_BLOCK_CYL_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_cyl_mesh_templates", + ) input_file = os.path.join( BIRD_BLOCK_CYL_MESH_TEMP_DIR, "sideSparger/input.json" ) @@ -30,6 +38,14 @@ def test_side_sparger(): def test_flat_donut(): + BIRD_BLOCK_CYL_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_cyl_mesh_templates", + ) input_file = os.path.join( BIRD_BLOCK_CYL_MESH_TEMP_DIR, "flatDonut/input.json" ) @@ -41,6 +57,14 @@ def test_flat_donut(): def test_base_column(): + BIRD_BLOCK_CYL_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_cyl_mesh_templates", + ) input_file = os.path.join( BIRD_BLOCK_CYL_MESH_TEMP_DIR, "baseColumn/input.json" ) @@ -52,6 +76,14 @@ def test_base_column(): def test_base_column_refine(): + BIRD_BLOCK_CYL_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_cyl_mesh_templates", + ) input_file = os.path.join( BIRD_BLOCK_CYL_MESH_TEMP_DIR, "baseColumn_refineSparg/input.json" ) @@ -63,6 +95,14 @@ def test_base_column_refine(): def test_base_column_projected(): + BIRD_BLOCK_CYL_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_cyl_mesh_templates", + ) input_file = os.path.join( BIRD_BLOCK_CYL_MESH_TEMP_DIR, "baseColumn_projected/input.json" ) @@ -74,6 +114,14 @@ def test_base_column_projected(): def test_multiring(): + BIRD_BLOCK_CYL_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_cyl_mesh_templates", + ) input_file = os.path.join( BIRD_BLOCK_CYL_MESH_TEMP_DIR, "multiRing_simple/input.json" ) @@ -85,6 +133,14 @@ def test_multiring(): def test_multiring_coarse(): + BIRD_BLOCK_CYL_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_cyl_mesh_templates", + ) input_file = os.path.join( BIRD_BLOCK_CYL_MESH_TEMP_DIR, "multiRing_coarse/input.json" ) @@ -93,3 +149,7 @@ def test_multiring_coarse(): ) output_folder = "system_tmp" base_mesh(input_file, topo_file, output_folder) + + +if __name__ == "__main__": + test_multiring_coarse() diff --git a/tests/meshing/test_block_rect_mesh.py b/tests/meshing/test_block_rect_mesh.py index 9cc0f00b..e3deb9b5 100644 --- a/tests/meshing/test_block_rect_mesh.py +++ b/tests/meshing/test_block_rect_mesh.py @@ -1,9 +1,9 @@ import os import sys +from pathlib import Path import numpy as np -from bird import BIRD_BLOCK_RECT_MESH_TEMP_DIR from bird.meshing.block_rect_mesh import ( assemble_geom, assemble_mesh, @@ -19,16 +19,32 @@ def base_mesh(input_file, output_folder): def test_loop_reactor(): + BIRD_BLOCK_RECT_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_rect_mesh_templates", + ) input_file = os.path.join( - BIRD_BLOCK_RECT_MESH_TEMP_DIR, "loopReactor/input.json" + BIRD_BLOCK_RECT_MESH_TEMP_DIR, "loopReactor", "input.json" ) output_folder = "system_tmp" base_mesh(input_file, output_folder) def test_subblock_reactor(): + BIRD_BLOCK_RECT_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "block_rect_mesh_templates", + ) input_file = os.path.join( - BIRD_BLOCK_RECT_MESH_TEMP_DIR, "sub_blocks/input.json" + BIRD_BLOCK_RECT_MESH_TEMP_DIR, "sub_blocks", "input.json" ) output_folder = "system_tmp" base_mesh(input_file, output_folder) diff --git a/tests/meshing/test_stirred_tank.py b/tests/meshing/test_stirred_tank.py index 73d23a46..2dacfc36 100644 --- a/tests/meshing/test_stirred_tank.py +++ b/tests/meshing/test_stirred_tank.py @@ -1,10 +1,10 @@ import argparse import os import sys +from pathlib import Path import numpy as np -from bird import BIRD_STIRRED_TANK_MESH_TEMP_DIR from bird.meshing.stirred_tank_mesh import ( get_reactor_geom, write_blocks, @@ -16,6 +16,14 @@ def test_stirred_tank(): + BIRD_STIRRED_TANK_MESH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "meshing", + "stirred_tank_mesh_templates", + ) inp = os.path.join( BIRD_STIRRED_TANK_MESH_TEMP_DIR, "base_tank", "tank_par.yaml" ) diff --git a/tests/postprocess/test_cond_mean.py b/tests/postprocess/test_cond_mean.py index c453b99a..fafca77f 100644 --- a/tests/postprocess/test_cond_mean.py +++ b/tests/postprocess/test_cond_mean.py @@ -1,4 +1,5 @@ import os +from pathlib import Path from prettyPlot.plotting import plt, pretty_labels @@ -10,7 +11,14 @@ def test_compute_cond(): - caseFolder = os.path.join("bird", "postprocess", "data_conditional_mean") + caseFolder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) fields_list = [ "CO.gas", "CO.liquid", @@ -31,3 +39,7 @@ def test_compute_cond(): plot_name = sequencePlot(cond, [caseFolder], field_name) pretty_labels(plot_name, "y [m]", 14) plt.close() + + +if __name__ == "__main__": + test_compute_cond() diff --git a/tests/postprocess/test_early_pred.py b/tests/postprocess/test_early_pred.py index f7899163..7339b92a 100644 --- a/tests/postprocess/test_early_pred.py +++ b/tests/postprocess/test_early_pred.py @@ -1,4 +1,6 @@ -from bird import BIRD_EARLY_PRED_DATA_DIR +import os +from pathlib import Path + from bird.postprocess.early_pred import ( bayes_fit, fit_and_ext, @@ -9,12 +11,18 @@ def test_fit(): + BIRD_EARLY_PRED_DATA_DIR = os.path.join( + Path(__file__).parent, "..", "..", "bird", "postprocess", "data_early" + ) data_dict, color_files = multi_data_load(BIRD_EARLY_PRED_DATA_DIR) data_dict = fit_and_ext(data_dict) plotAllEarly(data_dict, color_files=color_files, chop=True, extrap=True) def test_bayes_fit(): + BIRD_EARLY_PRED_DATA_DIR = os.path.join( + Path(__file__).parent, "..", "..", "bird", "postprocess", "data_early" + ) data_dict, color_files = multi_data_load(BIRD_EARLY_PRED_DATA_DIR) bayes_fit(data_dict) plotAllEarly_uq(data_dict, color_files=color_files) diff --git a/tests/postprocess/test_kla.py b/tests/postprocess/test_kla.py index c28f3fba..66ee5e80 100644 --- a/tests/postprocess/test_kla.py +++ b/tests/postprocess/test_kla.py @@ -1,8 +1,8 @@ import os +from pathlib import Path import pytest -from bird import BIRD_KLA_DATA_DIR from bird.postprocess.kla_utils import compute_kla, print_res_dict @@ -14,6 +14,9 @@ ], ) def test_kla(bootstrap, max_chop): + BIRD_KLA_DATA_DIR = os.path.join( + Path(__file__).parent, "..", "..", "bird", "postprocess", "data_kla" + ) res_dict = compute_kla( os.path.join(BIRD_KLA_DATA_DIR, "volume_avg.dat"), time_ind=0, diff --git a/tests/postprocess/test_post_quantities.py b/tests/postprocess/test_post_quantities.py new file mode 100644 index 00000000..56b36625 --- /dev/null +++ b/tests/postprocess/test_post_quantities.py @@ -0,0 +1,153 @@ +import os +from pathlib import Path + +from prettyPlot.plotting import plt, pretty_labels + +from bird.postprocess.post_quantities import * + + +def test_compute_gh(): + """ + Test for gas holdup calculation + """ + case_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + kwargs = {"case_folder": case_folder, "n_cells": None, "volume_time": "1"} + field_dict = {} + gh, field_dict = compute_gas_holdup( + time_folder="1", field_dict=field_dict, **kwargs + ) + field_dict = {} + gh, field_dict = compute_gas_holdup( + time_folder="79", field_dict=field_dict, **kwargs + ) + + +def test_compute_diam(): + """ + Test for bubble diameter calculation + """ + case_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + kwargs = {"case_folder": case_folder, "n_cells": None, "volume_time": "1"} + field_dict = {} + diam, field_dict = compute_ave_bubble_diam( + time_folder="1", field_dict=field_dict, **kwargs + ) + field_dict = {} + diam, field_dict = compute_ave_bubble_diam( + time_folder="79", field_dict=field_dict, **kwargs + ) + + +def test_compute_superficial_velocity(): + """ + Test for superficial velocity calculation + """ + case_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + kwargs = { + "case_folder": case_folder, + "n_cells": None, + "volume_time": "1", + "direction": 1, + "cell_centers_file": "meshCellCentres_1.obj", + } + field_dict = {} + sup_vel, field_dict = compute_superficial_velocity( + time_folder="79", field_dict=field_dict, **kwargs + ) + + +def test_ave_y_liq(): + """ + Test for liquid volume averaged species mass fraction + """ + case_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + kwargs = { + "time_folder": "79", + "case_folder": case_folder, + "n_cells": None, + "volume_time": "1", + } + field_dict = {} + ave_y_co2, field_dict = compute_ave_y_liq( + spec_name="CO2", field_dict=field_dict, **kwargs + ) + ave_y_co, field_dict = compute_ave_y_liq( + spec_name="CO", field_dict=field_dict, **kwargs + ) + ave_y_h2, field_dict = compute_ave_y_liq( + spec_name="H2", field_dict=field_dict, **kwargs + ) + + +def test_ave_conc_liq(): + """ + Test for liquid volume averaged species concentration + """ + case_folder = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "postprocess", + "data_conditional_mean", + ) + kwargs = { + "time_folder": "79", + "case_folder": case_folder, + "n_cells": None, + "volume_time": "1", + } + field_dict = {} + ave_conc_co2, field_dict = compute_ave_conc_liq( + spec_name="CO2", + mol_weight=44.00995 * 1e-3, + field_dict=field_dict, + **kwargs, + ) + ave_conc_co, field_dict = compute_ave_conc_liq( + spec_name="CO", + mol_weight=28.01055 * 1e-3, + field_dict=field_dict, + **kwargs, + ) + ave_conc_h2, field_dict = compute_ave_conc_liq( + spec_name="H2", + mol_weight=2.01594 * 1e-3, + field_dict=field_dict, + **kwargs, + ) + + +if __name__ == "__main__": + test_compute_superficial_velocity() + test_compute_gh() + test_ave_y_liq() + test_ave_conc_liq() diff --git a/tests/preprocess/test_case_gen.py b/tests/preprocess/test_case_gen.py new file mode 100644 index 00000000..dc83b37b --- /dev/null +++ b/tests/preprocess/test_case_gen.py @@ -0,0 +1,123 @@ +import os +import pickle +import shutil +from pathlib import Path + +import numpy as np + +from bird.preprocess.json_gen.design_io import * +from bird.preprocess.json_gen.generate_designs import * + + +def test_continuous_loop(): + + BIRD_CASE_GEN_DATA_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "data_case_gen", + ) + generate_single_scaledup_reactor_sparger_cases( + sparger_locs=[0.3, 0.5, 1.4], + sim_id=0, + vvm=0.4, + study_folder=".", + template_folder=os.path.join( + BIRD_CASE_GEN_DATA_DIR, + "loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", + ), + ) + + generate_single_scaledup_reactor_sparger_cases( + sparger_locs=[0.3, 0.35], + sim_id=0, + vvm=0.4, + study_folder="sparger_overlap", + template_folder=os.path.join( + BIRD_CASE_GEN_DATA_DIR, + "loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", + ), + bypass_sparger_spacing=True, + ) + + +def test_discrete_loop(): + + BIRD_CASE_GEN_DATA_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "data_case_gen", + ) + + def optimization_setup(): + # spots on the branches where we can place sparger or mixers + branchcom_spots = {} + branchcom_spots[0] = np.linspace(0.2, 0.8, 4) + branchcom_spots[1] = np.linspace(0.2, 0.8, 3) + branchcom_spots[2] = np.linspace(0.2, 0.8, 4) + # branches where the sparger and mixers are placed + branches_com = [0, 1, 2] + return branchcom_spots, branches_com + + def random_sample(branches_com, branchcom_spots, config_dict={}): + config = {} + # choices = ["mix", "sparger", "none"] + choices_com = [0, 1, 2] + for branch in branches_com: + config[branch] = np.random.choice( + choices_com, size=len(branchcom_spots[branch]) + ) + + existing = False + new_config_key = 0 + for old_key_conf in config_dict: + if compare_config(config_dict[old_key_conf], config): + existing = True + print("FOUND SAME CONFIG") + return config_dict + new_config_key = old_key_conf + 1 + + if check_config(config): + config_dict[new_config_key] = config + + return config_dict + + branchcom_spots, branches_com = optimization_setup() + n_sim = 20 + config_dict = {} + for i in range(n_sim): + config_dict = random_sample( + branches_com, branchcom_spots, config_dict=config_dict + ) + + vvm_l = [0.1, 0.4] + pow_l = [3000, 6000] + + for vvm_v in vvm_l: + vvm_str = str(vvm_v).replace(".", "_") + for pow_v in pow_l: + study_folder = f"study_scaleup_{vvm_str}vvm_{pow_v}W" + generate_scaledup_reactor_cases( + config_dict, + branchcom_spots, + vvm=vvm_v, + power=pow_v, + constantD=True, + study_folder=study_folder, + template_folder=os.path.join( + BIRD_CASE_GEN_DATA_DIR, + "loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", + ), + ) + write_script_start(f"{study_folder}/many_scripts_start", n_sim) + write_script_post(f"{study_folder}/many_scripts_post", n_sim) + write_prep(f"{study_folder}/prep.sh", n_sim) + save_config_dict(f"{study_folder}/configs.pkl", config_dict) + save_config_dict( + f"{study_folder}/branchcom_spots.pkl", branchcom_spots + ) diff --git a/tests/preprocess/test_dynamic_mixer.py b/tests/preprocess/test_dynamic_mixer.py index 47668528..b2722c2d 100644 --- a/tests/preprocess/test_dynamic_mixer.py +++ b/tests/preprocess/test_dynamic_mixer.py @@ -1,13 +1,22 @@ import os +from pathlib import Path import numpy as np -from bird import BIRD_PRE_DYNMIX_TEMP_DIR from bird.meshing._mesh_tools import parseJsonFile from bird.preprocess.dynamic_mixer.mixing_fvModels import * def test_expl_list(): + BIRD_PRE_DYNMIX_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "dynamic_mixer", + "mixing_template", + ) input_dict = parseJsonFile( os.path.join(BIRD_PRE_DYNMIX_TEMP_DIR, "expl_list", "mixers.json") ) @@ -15,6 +24,15 @@ def test_expl_list(): def test_loop_list(): + BIRD_PRE_DYNMIX_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "dynamic_mixer", + "mixing_template", + ) input_dict = parseJsonFile( os.path.join( BIRD_PRE_DYNMIX_TEMP_DIR, "loop_reactor_list", "mixers.json" diff --git a/tests/preprocess/test_stl_patch.py b/tests/preprocess/test_stl_patch.py index 6ed831cf..ff6f326e 100644 --- a/tests/preprocess/test_stl_patch.py +++ b/tests/preprocess/test_stl_patch.py @@ -1,14 +1,25 @@ import os +from pathlib import Path import numpy as np +from prettyPlot.plotting import pretty_labels -from bird import BIRD_PRE_PATCH_TEMP_DIR from bird.meshing._mesh_tools import parseJsonFile from bird.preprocess.stl_patch.stl_bc import write_boundaries -from bird.utilities.stl_plotting import plotSTL, pretty_labels +from bird.utilities.stl_plotting import plotSTL def test_spider_sparger(): + BIRD_PRE_PATCH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "stl_patch", + "bc_patch_mesh_template", + ) + input_dict = parseJsonFile( os.path.join(BIRD_PRE_PATCH_TEMP_DIR, "spider_spg/inlets_outlets.json") ) @@ -19,6 +30,15 @@ def test_spider_sparger(): def test_loop_reactor(): + BIRD_PRE_PATCH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "stl_patch", + "bc_patch_mesh_template", + ) input_dict = parseJsonFile( os.path.join( BIRD_PRE_PATCH_TEMP_DIR, "loop_reactor_expl/inlets_outlets.json" @@ -31,6 +51,15 @@ def test_loop_reactor(): def test_loop_reactor_branch(): + BIRD_PRE_PATCH_TEMP_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "stl_patch", + "bc_patch_mesh_template", + ) input_dict = parseJsonFile( os.path.join( BIRD_PRE_PATCH_TEMP_DIR, "loop_reactor_branch/inlets_outlets.json" @@ -43,4 +72,7 @@ def test_loop_reactor_branch(): if __name__ == "__main__": + from prettyPlot.plotting import plt + test_spider_sparger() + plt.show() diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/T.gas b/tutorial_cases/FlatPanel_250L_ASU/0.orig/T.gas new file mode 100644 index 00000000..1d515f11 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/T.gas @@ -0,0 +1,44 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object T.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 308; + +boundaryField +{ + "wall_.*" + { + type zeroGradient; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue $internalField; + value $internalField; + } + inlet + { + type fixedValue; + value $internalField; + } + frontAndBackPlanes + { + type empty; + } +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/T.liquid b/tutorial_cases/FlatPanel_250L_ASU/0.orig/T.liquid new file mode 100644 index 00000000..2d4e3a4d --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/T.liquid @@ -0,0 +1,44 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object T.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 308; + +boundaryField +{ + "wall_.*" + { + type zeroGradient; + } + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue $internalField; + value $internalField; + } + inlet + { + type fixedValue; + value $internalField; + } + frontAndBackPlanes + { + type empty; + } +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/U.gas b/tutorial_cases/FlatPanel_250L_ASU/0.orig/U.gas new file mode 100644 index 00000000..808205dc --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/U.gas @@ -0,0 +1,40 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format binary; + class volVectorField; + object U.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (0 0.1 0); + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + outlet + { + type pressureInletOutletVelocity; + phi phi.gas; + value $internalField; + } + "wall_.*" + { + type fixedValue; + value uniform (0 0 0); + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/U.liquid b/tutorial_cases/FlatPanel_250L_ASU/0.orig/U.liquid new file mode 100644 index 00000000..84b3317a --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/U.liquid @@ -0,0 +1,40 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format binary; + class volVectorField; + object U.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (0 0 0); + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + outlet + { + type pressureInletOutletVelocity; + phi phi.liquid; + value $internalField; + } + "wall_.*" + { + type fixedValue; + value uniform (0 0 0); + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/alpha.gas.orig b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alpha.gas.orig new file mode 100644 index 00000000..e827a9ed --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alpha.gas.orig @@ -0,0 +1,41 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + location "0"; + object alpha.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + inlet + { + type fixedValue; + value uniform 0.5; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue uniform 1; + value uniform 1; + } + "wall_.*" + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/alpha.liquid.orig b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alpha.liquid.orig new file mode 100644 index 00000000..c2531311 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alpha.liquid.orig @@ -0,0 +1,40 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object alpha.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 1; + +boundaryField +{ + inlet + { + type fixedValue; + value uniform 0.5; + } + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue uniform 0; + value uniform 0; + } + "wall_.*" + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/alphat.gas b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alphat.gas new file mode 100644 index 00000000..8d53c794 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alphat.gas @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object alphat.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -1 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + "wall_.*" + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/alphat.liquid b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alphat.liquid new file mode 100644 index 00000000..4e503479 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/alphat.liquid @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object alphat.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -1 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + "wall_.*" + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilon.gas b/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilon.gas new file mode 100644 index 00000000..24d4f17e --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilon.gas @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object epsilon.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -3 0 0 0 0]; + +internalField uniform 1.5e-4; + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + + outlet + { + type inletOutlet; + phi phi.gas; + inletValue $internalField; + value $internalField; + } + + "wall_.*" + { + type epsilonWallFunction; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilon.liquid b/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilon.liquid new file mode 100644 index 00000000..c307061f --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilon.liquid @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object epsilon.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -3 0 0 0 0]; + +internalField uniform 1.5e-4; + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue $internalField; + value $internalField; + } + + "wall_.*" + { + type epsilonWallFunction; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilonm b/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilonm new file mode 100644 index 00000000..cc6f87e6 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/epsilonm @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object epsilonm; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -3 0 0 0 0]; + +internalField uniform 1.5e-4; + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + + outlet + { + type inletOutlet; + phi phim; + inletValue $internalField; + value $internalField; + } + + "wall_.*" + { + type zeroGradient; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/k.gas b/tutorial_cases/FlatPanel_250L_ASU/0.orig/k.gas new file mode 100644 index 00000000..0a844f7a --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/k.gas @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object k.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -2 0 0 0 0]; + +internalField uniform 3.75e-5; + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + + outlet + { + type inletOutlet; + phi phi.gas; + inletValue $internalField; + value $internalField; + } + + "wall_.*" + { + type kqRWallFunction; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/k.liquid b/tutorial_cases/FlatPanel_250L_ASU/0.orig/k.liquid new file mode 100644 index 00000000..86ac25af --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/k.liquid @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object k.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -2 0 0 0 0]; + +internalField uniform 3.75e-5; + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue $internalField; + value $internalField; + } + + "wall_.*" + { + type kqRWallFunction; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/km b/tutorial_cases/FlatPanel_250L_ASU/0.orig/km new file mode 100644 index 00000000..6d8fb00f --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/km @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object km; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -2 0 0 0 0]; + +internalField uniform 3.75e-5; + +boundaryField +{ + inlet + { + type fixedValue; + value $internalField; + } + + outlet + { + type inletOutlet; + phi phim; + inletValue $internalField; + value $internalField; + } + + "wall_.*" + { + type zeroGradient; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/nut.gas b/tutorial_cases/FlatPanel_250L_ASU/0.orig/nut.gas new file mode 100644 index 00000000..d2208625 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/nut.gas @@ -0,0 +1,46 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object nut.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-8; + +boundaryField +{ + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + "wall_.*" + { + type nutkWallFunction; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/nut.liquid b/tutorial_cases/FlatPanel_250L_ASU/0.orig/nut.liquid new file mode 100644 index 00000000..f08faba7 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/nut.liquid @@ -0,0 +1,46 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object nut.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-8; + +boundaryField +{ + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + "wall_.*" + { + type nutkWallFunction; + value $internalField; + } + + defaultFaces + { + type empty; + } +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/p b/tutorial_cases/FlatPanel_250L_ASU/0.orig/p new file mode 100644 index 00000000..46b3fa42 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/p @@ -0,0 +1,39 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object p; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 1e5; + +boundaryField +{ + inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + "wall_.*" + { + type calculated; + value $internalField; + } +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/tutorial_cases/FlatPanel_250L_ASU/0.orig/p_rgh b/tutorial_cases/FlatPanel_250L_ASU/0.orig/p_rgh new file mode 100644 index 00000000..c66435ba --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/0.orig/p_rgh @@ -0,0 +1,40 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class volScalarField; + object p_rgh; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 1e5; + +boundaryField +{ + inlet + { + type fixedFluxPressure; + value $internalField; + } + outlet + { + type prghPressure; + p $internalField; + value $internalField; + } + "wall_.*" + { + type fixedFluxPressure; + value $internalField; + } +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/tutorial_cases/FlatPanel_250L_ASU/Allclean b/tutorial_cases/FlatPanel_250L_ASU/Allclean new file mode 100755 index 00000000..dc2f77db --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/Allclean @@ -0,0 +1,24 @@ +#!/bin/sh +cd ${0%/*} || exit 1 # Run from this directory + +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi + +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt + +#------------------------------------------------------------------------------ diff --git a/tutorial_cases/FlatPanel_250L_ASU/AllmassT b/tutorial_cases/FlatPanel_250L_ASU/AllmassT new file mode 100644 index 00000000..84a47c33 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/AllmassT @@ -0,0 +1,18 @@ +#!/bin/sh +cd ${0%/*} || exit 1 # Run from this directory + +# Source tutorial run functions +. $WM_PROJECT_DIR/bin/tools/RunFunctions + +mv constant/NofvModels constant/fvModels + +echo -e " - Manipulate default specie" +sed 's|^defaultSpecie.*|defaultSpecie air;|' -i constant/thermophysicalProperties.gas + +echo -e " - Manipulate controlDict" +sed 's|^endTime.*|endTime 160;|' -i system/controlDict +sed 's|^deltaT.*|deltaT 0.0001;|' -i system/controlDict +sed 's|^adjustTimeStep.*|adjustTimeStep no;//yes;//|' -i system/controlDict + +## runApplication $(getApplication) +#------------------------------------------------------------------------------ diff --git a/tutorial_cases/FlatPanel_250L_ASU/README.md b/tutorial_cases/FlatPanel_250L_ASU/README.md new file mode 100644 index 00000000..c3c0d170 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/README.md @@ -0,0 +1,15 @@ +### Flat Panel reactor + +``` +@inproceedings{parra2022fluid, + title={Fluid-Dynamic Simulation of a Flat-Panel Bioreactor with Emphasis on Mixing, Mass Transfer and Inorganic CO 2 Chemistry}, + author={Parra-Alvarez, John and Lua, Mauro and Laurens, Lieve and Sitaraman, Hari and Stickel, Jonathan and Mark, Heinnickel and Troy, Paddock}, + booktitle={2022 AIChE Annual Meeting}, + year={2022}, + organization={AIChE} +} +``` + +Single core exec + +1. `bash run.sh` diff --git a/tutorial_cases/FlatPanel_250L_ASU/constant/g b/tutorial_cases/FlatPanel_250L_ASU/constant/g new file mode 100644 index 00000000..2c53688b --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/constant/g @@ -0,0 +1,21 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class uniformDimensionedVectorField; + location "constant"; + object g; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -2 0 0 0 0]; +value (0 -9.81 0); + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/constant/momentumTransport.gas b/tutorial_cases/FlatPanel_250L_ASU/constant/momentumTransport.gas new file mode 100644 index 00000000..e73147a8 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/constant/momentumTransport.gas @@ -0,0 +1,27 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object momentumTransport.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +simulationType RAS; + +RAS +{ + model mixtureKEpsilon; // continuousGasKEpsilon; + + turbulence on; + printCoeffs on; +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/constant/momentumTransport.liquid b/tutorial_cases/FlatPanel_250L_ASU/constant/momentumTransport.liquid new file mode 100644 index 00000000..f3205aa6 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/constant/momentumTransport.liquid @@ -0,0 +1,27 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object momentumTransport.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +simulationType RAS; + +RAS +{ + model mixtureKEpsilon; // LaheyKEpsilon; + + turbulence on; + printCoeffs on; +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/constant/phaseProperties b/tutorial_cases/FlatPanel_250L_ASU/constant/phaseProperties new file mode 100644 index 00000000..3d165947 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/constant/phaseProperties @@ -0,0 +1,170 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object phaseProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +type basicMultiphaseSystem; + +phases (gas liquid); + +gas +{ + type purePhaseModel; + diameterModel isothermal; + isothermalCoeffs + { + d0 3e-3; + p0 1e5; + } + + residualAlpha 1e-6; +} + +liquid +{ + type purePhaseModel; + diameterModel constant; + constantCoeffs + { + d 1e-4; + } + + residualAlpha 1e-6; +} + +blending +{ + default + { + type linear; + minFullyContinuousAlpha.gas 0.7; + minPartlyContinuousAlpha.gas 0.3; + minFullyContinuousAlpha.liquid 0.7; + minPartlyContinuousAlpha.liquid 0.3; + } + + drag + { + type linear; + minFullyContinuousAlpha.gas 0.7; + minPartlyContinuousAlpha.gas 0.5; + minFullyContinuousAlpha.liquid 0.7; + minPartlyContinuousAlpha.liquid 0.5; + } +} + +surfaceTension +( + (gas and liquid) + { + type constant; + sigma 0.07; + } +); + +aspectRatio +( + (gas in liquid) + { + type constant; + E0 1.0; + } + + (liquid in gas) + { + type constant; + E0 1.0; + } +); + +drag +( + (gas in liquid) + { + type SchillerNaumann; + residualRe 1e-3; + swarmCorrection + { + type none; + } + } + + (liquid in gas) + { + type SchillerNaumann; + residualRe 1e-3; + swarmCorrection + { + type none; + } + } + + (gas and liquid) + { + type segregated; + m 0.5; + n 8; + swarmCorrection + { + type none; + } + } +); + +virtualMass +( + (gas in liquid) + { + type constantCoefficient; + Cvm 0.5; + } + + (liquid in gas) + { + type constantCoefficient; + Cvm 0.5; + } +); + +heatTransfer +( + (gas in liquid) + { + type RanzMarshall; + residualAlpha 1e-4; + } + + (liquid in gas) + { + type RanzMarshall; + residualAlpha 1e-4; + } +); + +phaseTransfer +(); + +lift +(); + +wallLubrication +(); + +turbulentDispersion +(); + +interfaceCompression +(); + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/constant/thermophysicalProperties.gas b/tutorial_cases/FlatPanel_250L_ASU/constant/thermophysicalProperties.gas new file mode 100644 index 00000000..28d92618 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/constant/thermophysicalProperties.gas @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object thermophysicalProperties.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heRhoThermo; + mixture pureMixture; + transport const; + thermo hConst; + equationOfState perfectGas; + specie specie; + energy sensibleInternalEnergy; +} + +mixture +{ + specie + { + molWeight 28.9; + } + thermodynamics + { + Cp 1007; + Hf 0; + } + transport + { + mu 1.84e-05; + Pr 0.7; + } +} + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/constant/thermophysicalProperties.liquid b/tutorial_cases/FlatPanel_250L_ASU/constant/thermophysicalProperties.liquid new file mode 100644 index 00000000..afb88b7e --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/constant/thermophysicalProperties.liquid @@ -0,0 +1,51 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "constant"; + object thermophysicalProperties.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heRhoThermo; + mixture pureMixture; + transport const; + thermo eConst; + equationOfState rPolynomial; + specie specie; + energy sensibleInternalEnergy; +} + +mixture +{ + specie + { + molWeight 18; + } + equationOfState + { + C (0.001278 -2.1055e-06 3.9689e-09 4.3772e-13 -2.0225e-16); + } + thermodynamics + { + Cv 4195; + Hf 0; + } + transport + { + mu 3.645e-4; + Pr 2.289; + } +} + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/presteps.sh b/tutorial_cases/FlatPanel_250L_ASU/presteps.sh new file mode 100644 index 00000000..d1a0cf4c --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/presteps.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + +cp -r 0.orig 0 +m4 ./system/panel.m4 > ./system/blockMeshDict +blockMesh +setFields diff --git a/tutorial_cases/FlatPanel_250L_ASU/run.sh b/tutorial_cases/FlatPanel_250L_ASU/run.sh new file mode 100644 index 00000000..ea9ed020 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash +bash presteps.sh +birdmultiphaseEulerFoam diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/blockMeshDict b/tutorial_cases/FlatPanel_250L_ASU/system/blockMeshDict new file mode 100644 index 00000000..d2203a4e --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/blockMeshDict @@ -0,0 +1,110 @@ +//--------------------------------*- C++ -*---------------------------------- +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object blockMeshDict; +} +// ************************************ + + + + + + +convertToMeters 1; + + //wall-sparge distance + // sparger diameter + // total lenght + // total depth +//define(H, 0.9144) //total height + //total height + + + + + + +vertices +( + ( 0 0 0 ) // Vertex block0_0 = 0 + ( 1.1176 0 0 ) // Vertex block0_1 = 1 + ( 1.1176 1.1 0 ) // Vertex block0_2 = 2 + ( 0 1.1 0 ) // Vertex block0_3 = 3 + ( 0.02925 0 0.02925 ) // Vertex block0_4 = 4 + ( 1.08835 0 0.02925 ) // Vertex block0_5 = 5 + ( 1.08835 1.1 0.02925 ) // Vertex block0_6 = 6 + ( 0.02925 1.1 0.02925 ) // Vertex block0_7 = 7 + ( 0.02925 0 0.03425) // Vertex block0_8 = 8 + ( 1.08835 0 0.03425) // Vertex block0_9 = 9 + ( 1.08835 1.1 0.03425) // Vertex block0_10 = 10 + ( 0.02925 1.1 0.03425) // Vertex block0_11 = 11 + ( 0 0 0.0635 ) // Vertex block0_12 = 12 + ( 1.1176 0 0.0635 ) // Vertex block0_13 = 13 + ( 1.1176 1.1 0.0635 ) // Vertex block0_14 = 14 + ( 0 1.1 0.0635 ) // Vertex block0_15 = 15 + +); + +blocks +( + //block 0 + hex ( 0 1 2 3 4 5 6 7 ) (100 100 10) simpleGrading (1 1 1) + //block 1 + hex ( 5 1 2 6 9 13 14 10) ( 10 100 10 ) simpleGrading (1 1 1) + //block 2 + hex ( 8 9 10 11 12 13 14 15) ( 100 100 10) simpleGrading (1 1 1) + //block 3 + hex ( 0 4 7 3 12 8 11 15 ) ( 10 100 10 ) simpleGrading (1 1 1) + //block 4 + hex (4 5 6 7 8 9 10 11) ( 100 100 10 ) simpleGrading (1 1 1) +); + +edges +( +); + +patches +( + patch inlet + ( + ( 4 5 9 8 ) + ) + + patch outlet + ( + ( 3 2 6 7 ) + ( 11 10 14 15 ) + ( 3 7 11 15 ) + ( 2 14 10 6 ) + ( 7 6 10 11) + ) + + wall wall_sides + ( + ( 0 3 15 12 ) + ( 1 13 14 2 ) + ) + + wall wall_frontback + ( + ( 12 15 14 13 ) + ( 0 1 2 3 ) + ) + + wall wall_botttom + ( + ( 0 1 5 4 ) + ( 8 9 13 12 ) + ( 0 4 8 12 ) + ( 1 13 9 5) + ) +); + +mergePatchPairs +( +); + diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/controlDict b/tutorial_cases/FlatPanel_250L_ASU/system/controlDict new file mode 100644 index 00000000..baa693c3 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/controlDict @@ -0,0 +1,59 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application multiphaseEulerFoam; + +startFrom startTime; + +startTime 0; + +stopAt writeNow;//endTime; + +endTime 100; + +deltaT 0.0005; + +writeControl adjustableRunTime; + +writeInterval 0.5; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable yes; + +adjustTimeStep yes;//no; + +maxCo 0.3; + +maxDeltaT 1; + +functions +{ + #includeFunc fieldAverage(U.gas, U.liquid, alpha.gas, p) +} + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/controlDict~ b/tutorial_cases/FlatPanel_250L_ASU/system/controlDict~ new file mode 100644 index 00000000..9e045ee7 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/controlDict~ @@ -0,0 +1,59 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application multiphaseEulerFoam; + +startFrom startTime; + +startTime 0; + +stopAt endTime; + +endTime 100; + +deltaT 0.0005; + +writeControl runTime; + +writeInterval 1; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable yes; + +adjustTimeStep no; + +maxCo 0.5; + +maxDeltaT 1; + +functions +{ + #includeFunc fieldAverage(U.gas, U.liquid, alpha.gas, p) +} + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/decomposeParDict b/tutorial_cases/FlatPanel_250L_ASU/system/decomposeParDict new file mode 100644 index 00000000..a4095548 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/decomposeParDict @@ -0,0 +1,45 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: 2.4.0 | +| \\ / A nd | Web: www.OpenFOAM.org | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains 32; // running the case + +method hierarchical; + +simpleCoeffs +{ + n ( 2 16 1 ); + delta 0.001; +} + +hierarchicalCoeffs +{ + n ( 2 16 1 ); + delta 0.001; + order xyz; +} + +manualCoeffs +{ + dataFile ""; +} + +distributed no; + +roots ( ); + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/fvConstraints b/tutorial_cases/FlatPanel_250L_ASU/system/fvConstraints new file mode 100644 index 00000000..dbf1d468 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/fvConstraints @@ -0,0 +1,23 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + object fvConstraints; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +limitp +{ + type limitPressure; + + min 1e4; +} + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/fvSchemes b/tutorial_cases/FlatPanel_250L_ASU/system/fvSchemes new file mode 100644 index 00000000..21904d1e --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/fvSchemes @@ -0,0 +1,65 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default Euler; +} + +gradSchemes +{ + default Gauss linear; +} + +divSchemes +{ + default none; + + div(phi,alpha.gas) Gauss vanLeer; + div(phi,alpha.liquid) Gauss vanLeer; + div(phir,alpha.liquid,alpha.gas) Gauss vanLeer; + div(phir,alpha.gas,alpha.liquid) Gauss vanLeer; + + "div\(alphaRhoPhi.*,U.*\)" Gauss limitedLinearV 1; + "div\(phi.*,U.*\)" Gauss limitedLinearV 1; + + "div\(alphaRhoPhi.*,(h|e).*\)" Gauss limitedLinear 1; + "div\(alphaRhoPhi.*,K.*\)" Gauss limitedLinear 1; + "div\(alphaRhoPhi.*,\(p\|thermo:rho.*\)\)" Gauss limitedLinear 1; + + "div\(alphaRhoPhi.*,(k|epsilon).*\)" Gauss limitedLinear 1; + "div\(phim,(k|epsilon)m\)" Gauss limitedLinear 1; + + "div\(\(\(\(alpha.*\*thermo:rho.*\)*nuEff.*\)\*dev2\(T\(grad\(U.*\)\)\)\)\)" Gauss linear; +} + +laplacianSchemes +{ + default Gauss linear uncorrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default uncorrected; +} + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/fvSolution b/tutorial_cases/FlatPanel_250L_ASU/system/fvSolution new file mode 100644 index 00000000..f080b7ff --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/fvSolution @@ -0,0 +1,84 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +solvers +{ + "alpha.*" + { + nAlphaCorr 1; + nAlphaSubCycles 2; + } + + p_rgh + { + solver GAMG; + smoother DIC; + tolerance 1e-8; + relTol 0; + } + + p_rghFinal + { + $p_rgh; + relTol 0; + } + + "U.*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-7; + relTol 0; + minIter 1; + } + + "e.*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-7; + relTol 0; + minIter 1; + } + + "(k|epsilon).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-7; + relTol 0; + minIter 1; + } +} + +PIMPLE +{ + nOuterCorrectors 3; + nCorrectors 1; + nNonOrthogonalCorrectors 0; + +} + +relaxationFactors +{ + equations + { + ".*" 1; + } +} + + +// ************************************************************************* // diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/panel.m4 b/tutorial_cases/FlatPanel_250L_ASU/system/panel.m4 new file mode 100644 index 00000000..ba92dc02 --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/panel.m4 @@ -0,0 +1,110 @@ +//--------------------------------*- C++ -*---------------------------------- +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object blockMeshDict; +} +// ************************************ +changecom(//)changequote([,]) +define(calc, [esyscmd(perl -e 'printf ($1)')]) +define(calcint, [esyscmd(perl -e 'printf int($1)')]) +define(VCOUNT, 0) +define(vlabel, [[// ]Vertex $1 = VCOUNT define($1, VCOUNT)define([VCOUNT], incr(VCOUNT))]) + +convertToMeters 1; + +define(h, 0.02925) //wall-sparge distance +define(b, 0.005) // sparger diameter +define(L, 1.1176) // total lenght +define(D, 0.0635) // total depth +//define(H, 0.9144) //total height +define(H, 1.1) //total height +define(Lmh, calc(L-h)) +define(hpb, calc(h+b)) +define(NPX, 50) +define(NPZ, 5) +define(NPY, 50) + +vertices +( + ( 0 0 0 ) vlabel(block0_0) + ( L 0 0 ) vlabel(block0_1) + ( L H 0 ) vlabel(block0_2) + ( 0 H 0 ) vlabel(block0_3) + ( h 0 h ) vlabel(block0_4) + ( Lmh 0 h ) vlabel(block0_5) + ( Lmh H h ) vlabel(block0_6) + ( h H h ) vlabel(block0_7) + ( h 0 hpb) vlabel(block0_8) + ( Lmh 0 hpb) vlabel(block0_9) + ( Lmh H hpb) vlabel(block0_10) + ( h H hpb) vlabel(block0_11) + ( 0 0 D ) vlabel(block0_12) + ( L 0 D ) vlabel(block0_13) + ( L H D ) vlabel(block0_14) + ( 0 H D ) vlabel(block0_15) + +); + +blocks +( + //block 0 + hex ( block0_0 block0_1 block0_2 block0_3 block0_4 block0_5 block0_6 block0_7 ) (NPX NPY NPZ) simpleGrading (1 1 1) + //block 1 + hex ( block0_5 block0_1 block0_2 block0_6 block0_9 block0_13 block0_14 block0_10) ( NPZ NPY NPZ ) simpleGrading (1 1 1) + //block 2 + hex ( block0_8 block0_9 block0_10 block0_11 block0_12 block0_13 block0_14 block0_15) ( NPX NPY NPZ) simpleGrading (1 1 1) + //block 3 + hex ( block0_0 block0_4 block0_7 block0_3 block0_12 block0_8 block0_11 block0_15 ) ( NPZ NPY NPZ ) simpleGrading (1 1 1) + //block 4 + hex (block0_4 block0_5 block0_6 block0_7 block0_8 block0_9 block0_10 block0_11) ( NPX NPY NPZ ) simpleGrading (1 1 1) +); + +edges +( +); + +patches +( + patch inlet + ( + ( block0_4 block0_5 block0_9 block0_8 ) + ) + + patch outlet + ( + ( block0_3 block0_2 block0_6 block0_7 ) + ( block0_11 block0_10 block0_14 block0_15 ) + ( block0_3 block0_7 block0_11 block0_15 ) + ( block0_2 block0_14 block0_10 block0_6 ) + ( block0_7 block0_6 block0_10 block0_11) + ) + + wall wall_sides + ( + ( block0_0 block0_3 block0_15 block0_12 ) + ( block0_1 block0_13 block0_14 block0_2 ) + ) + + wall wall_frontback + ( + ( block0_12 block0_15 block0_14 block0_13 ) + ( block0_0 block0_1 block0_2 block0_3 ) + ) + + wall wall_botttom + ( + ( block0_0 block0_1 block0_5 block0_4 ) + ( block0_8 block0_9 block0_13 block0_12 ) + ( block0_0 block0_4 block0_8 block0_12 ) + ( block0_1 block0_13 block0_9 block0_5) + ) +); + +mergePatchPairs +( +); + diff --git a/tutorial_cases/FlatPanel_250L_ASU/system/setFieldsDict b/tutorial_cases/FlatPanel_250L_ASU/system/setFieldsDict new file mode 100644 index 00000000..ce17d73c --- /dev/null +++ b/tutorial_cases/FlatPanel_250L_ASU/system/setFieldsDict @@ -0,0 +1,37 @@ +/*--------------------------------*- C++ -*----------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | Website: https://openfoam.org + \\ / A nd | Version: 9 + \\/ M anipulation | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + format ascii; + class dictionary; + location "system"; + object setFieldsDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +defaultFieldValues +( + volScalarFieldValue alpha.gas 1 + volScalarFieldValue alpha.liquid 0 +); + +regions +( + boxToCell + { + box (0 0 0) (1.2 0.914 0.1); + fieldValues + ( + volScalarFieldValue alpha.gas 0 + volScalarFieldValue alpha.liquid 1 + ); + } +); + + +// ************************************************************************* // diff --git a/tutorial_cases/airlift_40m/Allclean b/tutorial_cases/airlift_40m/Allclean new file mode 100755 index 00000000..dc2f77db --- /dev/null +++ b/tutorial_cases/airlift_40m/Allclean @@ -0,0 +1,24 @@ +#!/bin/sh +cd ${0%/*} || exit 1 # Run from this directory + +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi + +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt + +#------------------------------------------------------------------------------ diff --git a/tutorial_cases/airlift_40m/presteps.sh b/tutorial_cases/airlift_40m/presteps.sh index 06433b60..9e615bf8 100644 --- a/tutorial_cases/airlift_40m/presteps.sh +++ b/tutorial_cases/airlift_40m/presteps.sh @@ -1,3 +1,8 @@ +#!/bin/bash +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + m4 system/conc_cylinder_mesh.m4 > system/blockMeshDict rm -rf 0 cp -r 0.org 0 diff --git a/tutorial_cases/airlift_40m/run.sh b/tutorial_cases/airlift_40m/run.sh index f79cc8d4..ea9ed020 100644 --- a/tutorial_cases/airlift_40m/run.sh +++ b/tutorial_cases/airlift_40m/run.sh @@ -1,2 +1,3 @@ -. ./presteps.sh +#!/bin/bash +bash presteps.sh birdmultiphaseEulerFoam diff --git a/tutorial_cases/airlift_40m/submitjob b/tutorial_cases/airlift_40m/submitjob deleted file mode 100644 index 9f34f963..00000000 --- a/tutorial_cases/airlift_40m/submitjob +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# #SBATCH --qos=high -#SBATCH --job-name=bio-airlift -#SBATCH --partition=standard -#SBATCH --nodes=4 -#SBATCH --ntasks-per-node=36 -#SBATCH --time=48:00:00 -#SBATCH --account=bpms -#SBATCH --output=log.out - -module purge -ml PrgEnv-cray -source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc - -module purge -module load openmpi/1.10.7/gcc-7.3.0 -module load gcc -source /projects/bpms/openfoam/OpenFOAM-dev/etc/bashrc -. ./presteps.sh -srun -n 144 multiphaseEulerFoam -parallel diff --git a/tutorial_cases/bdofoam_cases/nonreact/Allclean b/tutorial_cases/bdofoam_cases/nonreact/Allclean old mode 100644 new mode 100755 index b44622ae..e9221067 --- a/tutorial_cases/bdofoam_cases/nonreact/Allclean +++ b/tutorial_cases/bdofoam_cases/nonreact/Allclean @@ -1,11 +1,26 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +[ -d "fluentInterface" ] && rm -rf "rm -f fluentInterface" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt rm -f constant/polyMesh/boundary -rm -rf fluentInterface -cleanCase -# ----------------------------------------------------------------- end-of-file +#------------------------------------------------------------------------------ diff --git a/tutorial_cases/bdofoam_cases/react/Allclean b/tutorial_cases/bdofoam_cases/react/Allclean old mode 100644 new mode 100755 index b44622ae..e9221067 --- a/tutorial_cases/bdofoam_cases/react/Allclean +++ b/tutorial_cases/bdofoam_cases/react/Allclean @@ -1,11 +1,26 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +[ -d "fluentInterface" ] && rm -rf "rm -f fluentInterface" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt rm -f constant/polyMesh/boundary -rm -rf fluentInterface -cleanCase -# ----------------------------------------------------------------- end-of-file +#------------------------------------------------------------------------------ diff --git a/tutorial_cases/bubble_column_20L/Allclean b/tutorial_cases/bubble_column_20L/Allclean index f55e0ec9..dc2f77db 100755 --- a/tutorial_cases/bubble_column_20L/Allclean +++ b/tutorial_cases/bubble_column_20L/Allclean @@ -1,18 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase +# Remove 0 +[ -d "0" ] && rm -rf 0 -#rm *.obj -#rm *.stl +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt #------------------------------------------------------------------------------ diff --git a/tutorial_cases/bubble_column_20L/presteps.sh b/tutorial_cases/bubble_column_20L/presteps.sh index 6b2d824f..1ac2a84c 100755 --- a/tutorial_cases/bubble_column_20L/presteps.sh +++ b/tutorial_cases/bubble_column_20L/presteps.sh @@ -1,9 +1,17 @@ +#!/bin/bash + # Clean case -module load anaconda3/2023 +module load conda conda activate /projects/gas2fuels/conda_env/bird source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc ./Allclean + +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + BIRD_HOME=`python -c "import bird; print(bird.BIRD_DIR)"` diff --git a/tutorial_cases/bubble_column_20L/run.sh b/tutorial_cases/bubble_column_20L/run.sh index 56d304e8..4a76e6ad 100755 --- a/tutorial_cases/bubble_column_20L/run.sh +++ b/tutorial_cases/bubble_column_20L/run.sh @@ -1,6 +1,13 @@ +#!/bin/bash + # Clean case ./Allclean +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + echo PRESTEP 1 # Generate blockmeshDict python ../../applications/write_block_cyl_mesh.py -i system/mesh.json -t system/topology.json -o system diff --git a/tutorial_cases/bubble_column_20L/system/controlDict b/tutorial_cases/bubble_column_20L/system/controlDict index 61d4e04a..ef313841 100644 --- a/tutorial_cases/bubble_column_20L/system/controlDict +++ b/tutorial_cases/bubble_column_20L/system/controlDict @@ -14,7 +14,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime;//startTime; diff --git a/tutorial_cases/bubble_column_20L/writeGlobalVars.py b/tutorial_cases/bubble_column_20L/writeGlobalVars.py index 0594eccc..3445bdb8 100644 --- a/tutorial_cases/bubble_column_20L/writeGlobalVars.py +++ b/tutorial_cases/bubble_column_20L/writeGlobalVars.py @@ -34,10 +34,12 @@ def readInletArea(): def getLiqVol(): cellCentres = readMesh(os.path.join(".", f"meshCellCentres_0.obj")) - volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres)) + volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres))[ + "field" + ] alpha_field = readOFScal( os.path.join("0", "alpha.liquid"), len(cellCentres) - ) + )["field"] return np.sum(volume_field * alpha_field) diff --git a/tutorial_cases/loop_reactor_mixing/Allclean b/tutorial_cases/loop_reactor_mixing/Allclean index 6c023512..dc2f77db 100755 --- a/tutorial_cases/loop_reactor_mixing/Allclean +++ b/tutorial_cases/loop_reactor_mixing/Allclean @@ -1,19 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase +# Remove 0 +[ -d "0" ] && rm -rf 0 -rm *.obj -rm *.stl -rm *.txt +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt #------------------------------------------------------------------------------ diff --git a/tutorial_cases/loop_reactor_mixing/get_qoi.py b/tutorial_cases/loop_reactor_mixing/get_qoi.py index f36f6f3c..7f3897ed 100644 --- a/tutorial_cases/loop_reactor_mixing/get_qoi.py +++ b/tutorial_cases/loop_reactor_mixing/get_qoi.py @@ -150,8 +150,8 @@ def get_qoi_uq(kla_co2, cs_co2, kla_h2, cs_h2): nuq = 100 # mean_cstar_co2 = np.random.uniform(12.6, 13.3, nuq) # mean_cstar_h2 = np.random.uniform(0.902, 0.96, nuq) -mean_cstar_co2 = np.random.uniform(11.9, 13.4, nuq) -mean_cstar_h2 = np.random.uniform(0.884, 0.943, nuq) +mean_cstar_co2 = np.random.uniform(14, 16.9, nuq) +mean_cstar_h2 = np.random.uniform(1.04, 1.19, nuq) tmp_cs_h2 = [] diff --git a/tutorial_cases/loop_reactor_mixing/presteps.sh b/tutorial_cases/loop_reactor_mixing/presteps.sh index e111fe88..fde6546f 100644 --- a/tutorial_cases/loop_reactor_mixing/presteps.sh +++ b/tutorial_cases/loop_reactor_mixing/presteps.sh @@ -1,9 +1,16 @@ +#!/bin/bash + # Clean case -module load anaconda3/2023 +module load conda conda activate /projects/gas2fuels/conda_env/bird source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc ./Allclean +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + echo PRESTEP 1 # Generate blockmeshDict python /projects/gas2fuels/BioReactorDesign/applications/write_block_rect_mesh.py -i system/mesh.json -o system @@ -14,8 +21,8 @@ python /projects/gas2fuels/BioReactorDesign/applications/write_stl_patch.py -i s #python ../../../applications/write_stl_patch.py -i system/inlets_outlets.json # Generate mixers -python /projects/gas2fuels/BioReactorDesign/applications/write_dynMix_fvModels_force_sign.py -i system/mixers.json -o constant -#python ../../../applications/write_dynMix_fvModels_force_sign.py -i system/mixers.json -o constant +python /projects/gas2fuels/BioReactorDesign/applications/write_dynMix_fvModels.py -fs -i system/mixers.json -o constant +#python ../../../applications/write_dynMix_fvModels.py -fs -i system/mixers.json -o constant echo PRESTEP 2 # Mesh gen diff --git a/tutorial_cases/loop_reactor_mixing/read_history.py b/tutorial_cases/loop_reactor_mixing/read_history.py index 264711f8..4dd134d6 100644 --- a/tutorial_cases/loop_reactor_mixing/read_history.py +++ b/tutorial_cases/loop_reactor_mixing/read_history.py @@ -5,158 +5,9 @@ import numpy as np from prettyPlot.plotting import plt, pretty_labels +from bird.postprocess.post_quantities import * from bird.utilities.ofio import * - -def compute_gas_holdup(caseFolder, timeFolder, nCells, field_dict={}): - if not ("alpha_liq" in field_dict) or field_dict["alpha_liq"] is None: - alpha_liq_file = os.path.join(caseFolder, timeFolder, "alpha.liquid") - alpha_liq = readOFScal(alpha_liq_file, nCells) - # print("reading alpha_liq") - field_dict["alpha_liq"] = alpha_liq - if not ("volume" in field_dict) or field_dict["volume"] is None: - volume_file = os.path.join(caseFolder, "0", "V") - volume = readOFScal(volume_file, nCells) - # print("reading Volume") - field_dict["volume"] = volume - alpha_liq = field_dict["alpha_liq"] - volume = field_dict["volume"] - holdup = np.sum((1 - alpha_liq) * volume) / np.sum(volume) - return holdup, field_dict - - -def co2liq(caseFolder, timeFolder, nCells, field_dict={}): - if not ("alpha_liq" in field_dict) or field_dict["alpha_liq"] is None: - alpha_liq_file = os.path.join(caseFolder, timeFolder, "alpha.liquid") - alpha_liq = readOFScal(alpha_liq_file, nCells) - # print("reading alpha_liq") - field_dict["alpha_liq"] = alpha_liq - if not ("co2_liq" in field_dict) or field_dict["co2_liq"] is None: - co2_liq_file = os.path.join(caseFolder, timeFolder, "CO2.liquid") - co2_liq = readOFScal(co2_liq_file, nCells) - # print("computing co2 liq") - field_dict["co2_liq"] = co2_liq - if not ("volume" in field_dict) or field_dict["volume"] is None: - volume_file = os.path.join(caseFolder, "0", "V") - volume = readOFScal(volume_file, nCells) - # print("reading Volume") - field_dict["volume"] = volume - if not ("indliq" in field_dict) or field_dict["indliq"] is None: - alpha_liq = field_dict["alpha_liq"] - indliq = np.argwhere(alpha_liq > 0.5) - # print("computing indliq") - field_dict["indliq"] = indliq - volume = field_dict["volume"] - indliq = field_dict["indliq"] - alpha_liq = field_dict["alpha_liq"] - co2_liq = field_dict["co2_liq"] - met = np.sum( - alpha_liq[indliq] * co2_liq[indliq] * volume[indliq] - ) / np.sum(volume[indliq]) - return met, field_dict - - -def cliq(caseFolder, timeFolder, nCells, field_dict={}): - if not ("alpha_liq" in field_dict) or field_dict["alpha_liq"] is None: - alpha_liq_file = os.path.join(caseFolder, timeFolder, "alpha.liquid") - alpha_liq = readOFScal(alpha_liq_file, nCells) - # print("reading alpha_liq") - field_dict["alpha_liq"] = alpha_liq - if not ("rho_liq" in field_dict) or field_dict["rho_liq"] is None: - rho_liq_file = os.path.join(caseFolder, timeFolder, "rhom") - rho_liq = readOFScal(rho_liq_file, nCells) - field_dict["rho_liq"] = rho_liq - if not ("co2_liq" in field_dict) or field_dict["co2_liq"] is None: - co2_liq_file = os.path.join(caseFolder, timeFolder, "CO2.liquid") - co2_liq = readOFScal(co2_liq_file, nCells) - # print("computing co2 liq") - field_dict["co2_liq"] = co2_liq - if not ("h2_liq" in field_dict) or field_dict["h2_liq"] is None: - h2_liq_file = os.path.join(caseFolder, timeFolder, "H2.liquid") - h2_liq = readOFScal(h2_liq_file, nCells) - # print("computing h2 liq") - field_dict["h2_liq"] = h2_liq - if not ("volume" in field_dict) or field_dict["volume"] is None: - volume_file = os.path.join(caseFolder, "0", "V") - volume = readOFScal(volume_file, nCells) - # print("reading Volume") - field_dict["volume"] = volume - if not ("indliq" in field_dict) or field_dict["indliq"] is None: - alpha_liq = field_dict["alpha_liq"] - indliq = np.argwhere(alpha_liq > 0.5) - # print("computing indliq") - field_dict["indliq"] = indliq - - volume = field_dict["volume"] - indliq = field_dict["indliq"] - alpha_liq = field_dict["alpha_liq"] - co2_liq = field_dict["co2_liq"] - h2_liq = field_dict["h2_liq"] - rho_liq = field_dict["rho_liq"] - - # c_h2 = rho_liq[indliq] * alpha_liq[indliq] * h2_liq[indliq] / 0.002016 - # c_co2 = rho_liq[indliq] * alpha_liq[indliq] * co2_liq[indliq] / 0.04401 - - c_h2 = 1000 * alpha_liq[indliq] * h2_liq[indliq] / 0.002016 - c_co2 = 1000 * alpha_liq[indliq] * co2_liq[indliq] / 0.04401 - - c_h2 = np.sum(c_h2 * volume[indliq]) / np.sum(volume[indliq]) - c_co2 = np.sum(c_co2 * volume[indliq]) / np.sum(volume[indliq]) - - return c_co2, c_h2, field_dict - - -def h2liq(caseFolder, timeFolder, nCells, field_dict={}): - if not ("alpha_liq" in field_dict) or field_dict["alpha_liq"] is None: - alpha_liq_file = os.path.join(caseFolder, timeFolder, "alpha.liquid") - alpha_liq = readOFScal(alpha_liq_file, nCells) - # print("reading alpha_liq") - field_dict["alpha_liq"] = alpha_liq - if not ("h2_liq" in field_dict) or field_dict["h2_liq"] is None: - h2_liq_file = os.path.join(caseFolder, timeFolder, "H2.liquid") - h2_liq = readOFScal(h2_liq_file, nCells) - # print("computing h2 liq") - field_dict["h2_liq"] = h2_liq - if not ("volume" in field_dict) or field_dict["volume"] is None: - volume_file = os.path.join(caseFolder, "0", "V") - volume = readOFScal(volume_file, nCells) - # print("reading Volume") - field_dict["volume"] = volume - if not ("indliq" in field_dict) or field_dict["indliq"] is None: - alpha_liq = field_dict["alpha_liq"] - indliq = np.argwhere(alpha_liq > 0.5) - # print("computing indliq") - field_dict["indliq"] = indliq - volume = field_dict["volume"] - indliq = field_dict["indliq"] - alpha_liq = field_dict["alpha_liq"] - h2_liq = field_dict["h2_liq"] - met = np.sum(alpha_liq[indliq] * h2_liq[indliq] * volume[indliq]) / np.sum( - volume[indliq] - ) - return met, field_dict - - -def vol_liq(caseFolder, timeFolder, nCells, field_dict={}): - if not ("alpha_liq" in field_dict) or field_dict["alpha_liq"] is None: - alpha_liq_file = os.path.join(caseFolder, timeFolder, "alpha.liquid") - alpha_liq = readOFScal(alpha_liq_file, nCells) - # print("reading alpha_liq") - field_dict["alpha_liq"] = alpha_liq - if not ("volume" in field_dict) or field_dict["volume"] is None: - volume_file = os.path.join(caseFolder, "0", "V") - volume = readOFScal(volume_file, nCells) - # print("reading Volume") - field_dict["volume"] = volume - volume = field_dict["volume"] - alpha_liq = field_dict["alpha_liq"] - indliq = np.argwhere(alpha_liq > 0.0) - liqvol = np.sum(alpha_liq[indliq] * volume[indliq]) / np.sum( - volume[indliq] - ) - return liqvol, field_dict - - parser = argparse.ArgumentParser(description="Convergence of GH") parser.add_argument( "-cn", @@ -205,26 +56,51 @@ def vol_liq(caseFolder, timeFolder, nCells, field_dict={}): print(f"\tTime : {time_folder}") if not field_dict == {}: new_field_dict = {} - if "volume" in field_dict: - new_field_dict["volume"] = field_dict["volume"] + if "V" in field_dict: + new_field_dict["V"] = field_dict["V"] field_dict = new_field_dict gh_history[itime], field_dict = compute_gas_holdup( - case_path, time_str_sorted[itime], nCells, field_dict + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + field_dict=field_dict, ) - co2_history[itime], field_dict = co2liq( - case_path, time_str_sorted[itime], nCells, field_dict + co2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="CO2", + field_dict=field_dict, ) - h2_history[itime], field_dict = h2liq( - case_path, time_str_sorted[itime], nCells, field_dict + h2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="H2", + field_dict=field_dict, ) - liqvol_history[itime], field_dict = vol_liq( - case_path, time_str_sorted[itime], nCells, field_dict + c_co2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="CO2", + mol_weight=0.04401, + field_dict=field_dict, ) - c_co2_history[itime], c_h2_history[itime], field_dict = cliq( - case_path, time_str_sorted[itime], nCells, field_dict + c_h2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + nCells, + volume_time="0", + spec_name="H2", + mol_weight=0.002016, + field_dict=field_dict, ) - os.makedirs(dataFolder, exist_ok=True) os.makedirs(os.path.join(dataFolder, case_name), exist_ok=True) np.savez( @@ -233,7 +109,6 @@ def vol_liq(caseFolder, timeFolder, nCells, field_dict={}): gh=gh_history, co2=co2_history, h2=h2_history, - vol_liq=liqvol_history, c_h2=c_h2_history, c_co2=c_co2_history, ) diff --git a/tutorial_cases/loop_reactor_mixing/run.sh b/tutorial_cases/loop_reactor_mixing/run.sh index c910f3c3..19458495 100644 --- a/tutorial_cases/loop_reactor_mixing/run.sh +++ b/tutorial_cases/loop_reactor_mixing/run.sh @@ -1,9 +1,15 @@ +#!/bin/bash # Clean case #module load anaconda3/2023 #conda activate /projects/gas2fuels/conda_env/bird #source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc ./Allclean +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + echo PRESTEP 1 # Generate blockmeshDict python ../../applications/write_block_rect_mesh.py -i system/mesh.json -o system diff --git a/tutorial_cases/loop_reactor_mixing/script b/tutorial_cases/loop_reactor_mixing/script index 090e5c05..efe675ff 100755 --- a/tutorial_cases/loop_reactor_mixing/script +++ b/tutorial_cases/loop_reactor_mixing/script @@ -10,5 +10,5 @@ bash presteps.sh source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc decomposePar -fileHandler collated -srun -n 16 multiphaseEulerFoam -parallel -fileHandler collated +srun -n 16 birdmultiphaseEulerFoam -parallel -fileHandler collated reconstructPar -newTimes diff --git a/tutorial_cases/loop_reactor_mixing/system/controlDict b/tutorial_cases/loop_reactor_mixing/system/controlDict index d8c42afd..204a8cbe 100644 --- a/tutorial_cases/loop_reactor_mixing/system/controlDict +++ b/tutorial_cases/loop_reactor_mixing/system/controlDict @@ -14,7 +14,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime;//startTime; diff --git a/tutorial_cases/loop_reactor_mixing/writeGlobalVars.py b/tutorial_cases/loop_reactor_mixing/writeGlobalVars.py index 0594eccc..3445bdb8 100644 --- a/tutorial_cases/loop_reactor_mixing/writeGlobalVars.py +++ b/tutorial_cases/loop_reactor_mixing/writeGlobalVars.py @@ -34,10 +34,12 @@ def readInletArea(): def getLiqVol(): cellCentres = readMesh(os.path.join(".", f"meshCellCentres_0.obj")) - volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres)) + volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres))[ + "field" + ] alpha_field = readOFScal( os.path.join("0", "alpha.liquid"), len(cellCentres) - ) + )["field"] return np.sum(volume_field * alpha_field) diff --git a/tutorial_cases/loop_reactor_reacting/Allclean b/tutorial_cases/loop_reactor_reacting/Allclean index 371d82a8..dc2f77db 100755 --- a/tutorial_cases/loop_reactor_reacting/Allclean +++ b/tutorial_cases/loop_reactor_reacting/Allclean @@ -1,17 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase -rm *.obj -rm *.stl +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt #------------------------------------------------------------------------------ diff --git a/tutorial_cases/loop_reactor_reacting/run.sh b/tutorial_cases/loop_reactor_reacting/run.sh index b408d12f..1749dd3f 100644 --- a/tutorial_cases/loop_reactor_reacting/run.sh +++ b/tutorial_cases/loop_reactor_reacting/run.sh @@ -1,7 +1,13 @@ +#!/bin/bash # Clean case ./Allclean +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + # Generate blockmeshDict python ../../applications/write_block_rect_mesh.py -i system/mesh.json -o system diff --git a/tutorial_cases/loop_reactor_reacting/system/controlDict b/tutorial_cases/loop_reactor_reacting/system/controlDict index 5e782597..99054d1c 100644 --- a/tutorial_cases/loop_reactor_reacting/system/controlDict +++ b/tutorial_cases/loop_reactor_reacting/system/controlDict @@ -14,7 +14,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime;//startTime; diff --git a/tutorial_cases/loop_reactor_reacting/writeGlobalVars.py b/tutorial_cases/loop_reactor_reacting/writeGlobalVars.py index 0594eccc..3445bdb8 100644 --- a/tutorial_cases/loop_reactor_reacting/writeGlobalVars.py +++ b/tutorial_cases/loop_reactor_reacting/writeGlobalVars.py @@ -34,10 +34,12 @@ def readInletArea(): def getLiqVol(): cellCentres = readMesh(os.path.join(".", f"meshCellCentres_0.obj")) - volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres)) + volume_field = readOFScal(os.path.join("0", "V"), len(cellCentres))[ + "field" + ] alpha_field = readOFScal( os.path.join("0", "alpha.liquid"), len(cellCentres) - ) + )["field"] return np.sum(volume_field * alpha_field) diff --git a/tutorial_cases/runall.sh b/tutorial_cases/runall.sh new file mode 100644 index 00000000..c58bc65f --- /dev/null +++ b/tutorial_cases/runall.sh @@ -0,0 +1,53 @@ +#Compile solver + +conda activate bird +BIRD_HOME=`python -c "import bird; print(bird.BIRD_DIR)"` +cd ${BIRD_HOME}/../OFsolvers/birdmultiphaseEulerFoam +export WM_COMPILE_OPTION=Debug +./Allwmake +cd ../../ + +# Run all tests + +## Run deckwer17 PBE +cd experimental_cases/deckwer17 +bash run.sh +cd ../../ +## Run deckwer17 constantD +cd experimental_cases/deckwer17 +cp constant/phaseProperties_constantd constant/phaseProperties +bash run.sh +cd ../../ +## Run deckwer19 PBE +cd experimental_cases/deckwer19 +bash run.sh +cd ../../ +## Run side sparger tutorial +cd tutorial_cases/side_sparger +bash run.sh +cd ../../ +## Run bubble column tutorial +cd tutorial_cases/bubble_column_20L +bash run.sh +cd ../../ +## Run stirred-tank tutorial +cd tutorial_cases/stirred_tank +bash run.sh +cd ../../ +## Run reactive loop reactor tutorial +cd tutorial_cases/loop_reactor_reacting +bash run.sh +cd ../../ +## Run mixing loop reactor tutorial +cd tutorial_cases/loop_reactor_mixing +bash run.sh +cd ../../ +## Run airlift reactor tutorial +cd tutorial_cases/airlift_40m +bash run.sh +cd ../../ +## Run flat panel reactor tutorial +cd tutorial_cases/FlatPanel_250L_ASU +bash run.sh +cd ../../ + diff --git a/tutorial_cases/side_sparger/Allclean b/tutorial_cases/side_sparger/Allclean index b00a24a7..dc2f77db 100755 --- a/tutorial_cases/side_sparger/Allclean +++ b/tutorial_cases/side_sparger/Allclean @@ -1,16 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi + +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase -#rm system/blockMeshDict -rm *.obj #------------------------------------------------------------------------------ diff --git a/tutorial_cases/side_sparger/run.sh b/tutorial_cases/side_sparger/run.sh index 8f029538..b88b1c3d 100755 --- a/tutorial_cases/side_sparger/run.sh +++ b/tutorial_cases/side_sparger/run.sh @@ -1,3 +1,4 @@ +#!/bin/bash if ! type "blockMesh" &> /dev/null; then echo " could not be found" echo "OpenFoam is likely not installed, skipping run" @@ -6,6 +7,11 @@ else ./Allclean fi +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + if ! type "python" &> /dev/null; then echo " could not be found" echo "Skipping Mesh generation" diff --git a/tutorial_cases/side_sparger/system/controlDict b/tutorial_cases/side_sparger/system/controlDict index 812bdde3..9cf2ac1b 100644 --- a/tutorial_cases/side_sparger/system/controlDict +++ b/tutorial_cases/side_sparger/system/controlDict @@ -13,7 +13,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime; diff --git a/tutorial_cases/side_sparger/system/controlDict.first b/tutorial_cases/side_sparger/system/controlDict.first index 48186072..ff0bf772 100644 --- a/tutorial_cases/side_sparger/system/controlDict.first +++ b/tutorial_cases/side_sparger/system/controlDict.first @@ -13,7 +13,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime; diff --git a/tutorial_cases/side_sparger/system/controlDict.second b/tutorial_cases/side_sparger/system/controlDict.second index eee3a5d6..9e0d2eeb 100644 --- a/tutorial_cases/side_sparger/system/controlDict.second +++ b/tutorial_cases/side_sparger/system/controlDict.second @@ -13,7 +13,7 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -application multiphaseEulerFoam; +application birdmultiphaseEulerFoam; startFrom latestTime; diff --git a/tutorial_cases/stirred_tank/Allclean b/tutorial_cases/stirred_tank/Allclean index e9225d17..dc2f77db 100755 --- a/tutorial_cases/stirred_tank/Allclean +++ b/tutorial_cases/stirred_tank/Allclean @@ -1,15 +1,24 @@ #!/bin/sh cd ${0%/*} || exit 1 # Run from this directory -# Source tutorial clean functions -. $WM_PROJECT_DIR/bin/tools/CleanFunctions - -# Remove surface, features and solution -#rm -rf constant/extendedFeatureEdgeMesh > /dev/null 2>&1 -#rm -f constant/triSurface/*.eMesh > /dev/null 2>&1 -#rm -rf constant/polyMesh > /dev/null 2>&1 -#rm -rf processor* > /dev/null 2>&1 -rm -rf 0 -cleanCase -rm system/blockMeshDict +if [ -n "$WM_PROJECT_DIR" ]; then + . $WM_PROJECT_DIR/bin/tools/CleanFunctions + cleanCase +else + echo "WARNING: could not run cleanCase, OpenFOAM env not found" +fi + +# Remove 0 +[ -d "0" ] && rm -rf 0 + +# rm -f constant/triSurface/*.eMesh +# [ -d "constant/extendedFeatureEdgeMesh" ] && rm -rf "constant/extendedFeatureEdgeMesh" +[ -d "constant/polyMesh" ] && rm -rf "constant/polyMesh" +[ -d "dynamicCode" ] && rm -rf "dynamicCode" +[ -d "processor*" ] && rm -rf "processor*" +# rm -f constant/fvModels +rm -f *.obj +rm -f *.stl +rm -f *.txt + #------------------------------------------------------------------------------ diff --git a/tutorial_cases/stirred_tank/run.sh b/tutorial_cases/stirred_tank/run.sh index 307e7b84..6e4b240b 100644 --- a/tutorial_cases/stirred_tank/run.sh +++ b/tutorial_cases/stirred_tank/run.sh @@ -1,3 +1,4 @@ +#!/bin/bash if ! type "blockMesh" &> /dev/null; then echo " could not be found" echo "OpenFoam is likely not installed, skipping run" @@ -6,6 +7,11 @@ else ./Allclean fi +set -e # Exit on any error +# Define what to do on error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + if ! type "python" &> /dev/null; then echo " could not be found" echo "Skipping Mesh generation"