From 1deecfe64a63de5770ad5deff3199c1242580e19 Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 17:15:25 -0600 Subject: [PATCH 01/37] Add SwirlMixer class --- bird/preprocess/dynamic_mixer/mixer.py | 109 +++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/bird/preprocess/dynamic_mixer/mixer.py b/bird/preprocess/dynamic_mixer/mixer.py index 524ff43c..607ae9f4 100644 --- a/bird/preprocess/dynamic_mixer/mixer.py +++ b/bird/preprocess/dynamic_mixer/mixer.py @@ -93,3 +93,112 @@ def check_status(self, blocks=None): logger.info(f"\tbranch = {blocks}") self.ready = True + + +class SwirlMixer: + """Actuator-disk mixer with optional swirl, used by the ``ball`` source. + + Unlike :class:`Mixer` (the legacy ``pancake`` source), momentum is deposited + over a ball of physical radius ``R`` and the drive is set by a power number + ``Np`` and tip speed ``Vtip`` rather than a raw power. ``sigma`` is the swirl + fraction; ``sign`` is the axial push direction and ``swirl_sign`` the + (independent) rotation sense. + """ + + def __init__(self): + self.x = None + self.y = None + self.z = None + self.normal_dir = None + self.R = None # physical mixer radius [m] + self.Vtip = 1.5 # tip speed [m/s] + self.Np = 6.0 # power number [-] + self.sigma = 0.35 # swirl fraction [-] + self.power = None # mixer power P [W], only used when power=from_P + self.sign = None # axial push sign, "+" / "-" + self.swirl_sign = "+" # rotation sense, "+" / "-" + self.start_time = 1.0 + self.ready = False + + def _read_common(self, mixer_dict: dict) -> None: + """Read the per-mixer keys""" + if "Vtip" in mixer_dict: + self.Vtip = mixer_dict["Vtip"] + if "Np" in mixer_dict: + self.Np = mixer_dict["Np"] + if "sigma" in mixer_dict: + self.sigma = mixer_dict["sigma"] + if "power" in mixer_dict: + self.power = mixer_dict["power"] + if "sign" in mixer_dict: + self.sign = mixer_dict["sign"] + if "swirl_sign" in mixer_dict: + self.swirl_sign = mixer_dict["swirl_sign"] + if "start_time" in mixer_dict: + self.start_time = mixer_dict["start_time"] + + def update_from_expl_dict(self, mixer_dict: dict) -> None: + """Populate from an explicit mixer dict (absolute position and radius).""" + if "x" in mixer_dict: + self.x = mixer_dict["x"] + if "y" in mixer_dict: + self.y = mixer_dict["y"] + if "z" in mixer_dict: + self.z = mixer_dict["z"] + if "normal_dir" in mixer_dict: + self.normal_dir = mixer_dict["normal_dir"] + if "radius" in mixer_dict: + # explicit mode: radius is absolute [m] + self.R = mixer_dict["radius"] + self._read_common(mixer_dict) + self.check_status() + + def update_from_loop_dict(self, mixer_dict: dict, geom_dict: dict) -> None: + """Populate from a loop mixer dict. + + :param mixer_dict: mixer entry with ``branch_id``, ``frac_space`` and, + optionally, ``radius`` as a fraction of the branch cross-section. + :param geom_dict: output of ``from_block_rect_to_seg`` (``segments`` and + ``blocksize``). + """ + segment = geom_dict["segments"][mixer_dict["branch_id"]] + pos = segment["start"] + mixer_dict["frac_space"] * segment["conn"] + self.x = float(pos[0]) + self.y = float(pos[1]) + self.z = float(pos[2]) + self.normal_dir = segment["normal_dir"] + # radius is a fraction of the branch cross-section (as for spargers): + # R = frac * mean of the two block sizes transverse to the axis. + bx, by, bz = geom_dict["blocksize"] + transverse = {0: (by, bz), 1: (bx, bz), 2: (bx, by)}[self.normal_dir] + frac = mixer_dict.get("radius", 0.4) + self.R = frac * 0.5 * (transverse[0] + transverse[1]) + self._read_common(mixer_dict) + self.check_status(blocks=segment["blocks"]) + + def check_status(self, blocks=None) -> None: + """Log the resolved mixer and set ``ready`` if all fields are present.""" + if ( + self.x is None + or self.y is None + or self.z is None + or self.normal_dir is None + or self.R is None + or self.sign not in ("+", "-") + or self.swirl_sign not in ("+", "-") + ): + self.ready = False + else: + logger.info( + f"\n\tpos({self.x:.2g}, {self.y:.2g}, {self.z:.2g})" + + f"\n\tnormal_dir {self.normal_dir}" + + f"\n\tR {self.R:.2g}" + + f"\n\tVtip {self.Vtip:.2g}" + + f"\n\tNp {self.Np:.2g}" + + f"\n\tsigma {self.sigma:.2g}" + + f"\n\tsign {self.sign} swirl_sign {self.swirl_sign}" + + f"\n\tstart_time {self.start_time:.2g}" + ) + if blocks is not None: + logger.info(f"\tbranch = {blocks}") + self.ready = True From b9d3df293fa081f3c83f6cee79ee05f0ae688eee Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 17:23:23 -0600 Subject: [PATCH 02/37] rename to actuator class, swirl is decided later --- bird/preprocess/dynamic_mixer/mixer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bird/preprocess/dynamic_mixer/mixer.py b/bird/preprocess/dynamic_mixer/mixer.py index 607ae9f4..2a4ff555 100644 --- a/bird/preprocess/dynamic_mixer/mixer.py +++ b/bird/preprocess/dynamic_mixer/mixer.py @@ -95,7 +95,7 @@ def check_status(self, blocks=None): self.ready = True -class SwirlMixer: +class ActuatorMixer: """Actuator-disk mixer with optional swirl, used by the ``ball`` source. Unlike :class:`Mixer` (the legacy ``pancake`` source), momentum is deposited From 13ad8c3a2d73335383dba5710cd64d92bac0b4dc Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 17:35:33 -0600 Subject: [PATCH 03/37] ball-source fvModels writer --- bird/preprocess/dynamic_mixer/io_fvModels.py | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/bird/preprocess/dynamic_mixer/io_fvModels.py b/bird/preprocess/dynamic_mixer/io_fvModels.py index 414dfdf6..4e01d7d5 100644 --- a/bird/preprocess/dynamic_mixer/io_fvModels.py +++ b/bird/preprocess/dynamic_mixer/io_fvModels.py @@ -326,6 +326,204 @@ def write_mixer_force_sign(mixer, output_folder): f.write("\t\t}\n") +def write_preamble_ball(output_folder): + """Write the FoamFile header + codedSource preamble for the ``ball`` source. + + The Newton solve is inlined in each mixer block (see ``write_mixer_ball``), + so no external ``dynamicMix_util.H`` is needed. + """ + with open(os.path.join(output_folder, "fvModels"), "w+") as f: + f.write("FoamFile\n") + f.write("{\n") + f.write("\tversion 2.0;\n") + f.write("\tformat ascii;\n") + f.write("\tclass dictionary;\n") + f.write('\tlocation "constant";\n') + f.write("\tobject fvModels;\n") + f.write("}\n\n") + f.write("codedSource\n") + f.write("{\n") + f.write("\ttype\tcoded;\n") + f.write("\tselectionMode\tall;\n") + f.write("\tfield\tU.liquid;\n") + f.write("\tname\tsourceTime;\n\n") + f.write("\tcodeInclude\n") + f.write("\t#{\n") + f.write("\t\t#include \n") + f.write("\t\t#include \n") + f.write("\t#};\n\n") + f.write("\tcodeAddAlphaRhoSup\n") + f.write("\t#{\n") + f.write("\t\tconst Time& time = mesh().time();\n") + f.write("\t\tconst scalarField& V = mesh().V();\n") + f.write("\t\tvectorField& Usource = eqn.source();\n") + f.write("\t\tconst vectorField& C = mesh().C();\n") + f.write("\t\tconst volScalarField& rhoL =\n") + f.write( + '\t\t\tmesh().lookupObject("thermo:rho.liquid");\n' + ) + f.write("\t\tconst volScalarField& alphaL =\n") + f.write('\t\t\tmesh().lookupObject("alpha.liquid");\n') + f.write("\t\tconst volVectorField& UL =\n") + f.write('\t\t\tmesh().lookupObject("U.liquid");\n') + f.write("\t\tconst double pi = 3.14159265358979;\n") + + +def write_mixer_ball( + mixer, output_folder, power_mode="from_Np_Vtip", momentum_mode="axial" +): + """Append one ``ball`` mixer block to ``fvModels``. + + :param mixer: a ready :class:`~bird.preprocess.dynamic_mixer.mixer.ActuatorMixer`. + :param power_mode: ``"from_P"`` (drive by ``mixer.power``) or + ``"from_Np_Vtip"`` (drive by ``mixer.Np`` and ``mixer.Vtip``). + :param momentum_mode: ``"axial"`` (thrust only) or ``"axial_and_swirl"`` + (thrust + tangential source using ``mixer.sigma``). + """ + if power_mode not in ("from_P", "from_Np_Vtip"): + raise ValueError(f"unknown power_mode {power_mode!r}") + if momentum_mode not in ("axial", "axial_and_swirl"): + raise ValueError(f"unknown momentum_mode {momentum_mode!r}") + if power_mode == "from_P" and mixer.power is None: + raise ValueError("power_mode 'from_P' requires 'power' in the mixer") + + nd = int(mixer.normal_dir) + dn = ["dx", "dy", "dz"][nd] + # theta_hat = n_hat x r_hat, per axis: (component index, numerator expr) + tan = { + 0: [(1, "-dz"), (2, "dy")], + 1: [(0, "dz"), (2, "-dx")], + 2: [(0, "-dy"), (1, "dx")], + }[nd] + push_ax = "1.0" if mixer.sign == "+" else "-1.0" + push_th = "1.0" if mixer.swirl_sign == "+" else "-1.0" + swirl = momentum_mode == "axial_and_swirl" + + if power_mode == "from_P": + rhs = f"4.0*{mixer.power}/(rhoM*area)" + else: + rhs = f"16.0*{mixer.Np}*Vtip*Vtip*Vtip/pow(pi,4.0)" + swirl_F = f" + {mixer.sigma}*(V1+V2)*Vtip*Vtip" if swirl else "" + swirl_dF = f" + {mixer.sigma}*Vtip*Vtip" if swirl else "" + + with open(os.path.join(output_folder, "fvModels"), "a+") as f: + f.write("\t\t// ===== ball mixer =====\n") + f.write("\t\t{\n") + f.write(f"\t\t\tconst double Rmix = {mixer.R};\n") + f.write("\t\t\tconst double area = pi*Rmix*Rmix;\n") + f.write(f"\t\t\tconst double Vtip = {mixer.Vtip};\n") + if swirl: + f.write(f"\t\t\tconst double sigma = {mixer.sigma};\n") + f.write(f"\t\t\tconst double startT = {mixer.start_time};\n") + f.write( + f"\t\t\tconst double px = {mixer.x}, py = {mixer.y}, pz = {mixer.z};\n" + ) + f.write("\t\t\tif (time.value() > startT)\n") + f.write("\t\t\t{\n") + # --- sense V1 and rho over the upstream half-ball --- + f.write("\t\t\t\tscalar sV = 0.0, sVU = 0.0, sVrho = 0.0;\n") + f.write("\t\t\t\tforAll(C, i)\n") + f.write("\t\t\t\t{\n") + f.write( + "\t\t\t\t\tconst double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz;\n" + ) + f.write("\t\t\t\t\tconst double d2 = dx*dx + dy*dy + dz*dz;\n") + f.write(f"\t\t\t\t\tif (d2 <= Rmix*Rmix && {push_ax}*{dn} < 0.0)\n") + f.write("\t\t\t\t\t{\n") + f.write("\t\t\t\t\t\tconst double w = V[i]*alphaL[i];\n") + f.write(f"\t\t\t\t\t\tsV += w; sVU += w*UL[i][{nd}]; sVrho += w*rhoL[i];\n") + f.write("\t\t\t\t\t}\n") + f.write("\t\t\t\t}\n") + f.write("\t\t\t\treduce(sV, sumOp());\n") + f.write("\t\t\t\treduce(sVU, sumOp());\n") + f.write("\t\t\t\treduce(sVrho, sumOp());\n") + f.write(f"\t\t\t\tdouble V1 = (sV>1e-30) ? {push_ax}*(sVU/sV) : 0.0;\n") + f.write("\t\t\t\tif (V1 < 0.0) V1 = 0.0;\n") + f.write("\t\t\t\tconst double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0;\n") + # --- Newton solve for V2 --- + f.write(f"\t\t\t\tconst double rhs = {rhs};\n") + f.write( + "\t\t\t\tdouble V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs));\n" + ) + f.write("\t\t\t\tfor (int it = 0; it < 100; ++it)\n") + f.write("\t\t\t\t{\n") + f.write( + f"\t\t\t\t\tconst double F = (V2-V1)*(V2+V1)*(V2+V1){swirl_F} - rhs;\n" + ) + f.write( + f"\t\t\t\t\tconst double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1{swirl_dF};\n" + ) + f.write("\t\t\t\t\tconst double dV = F/dF;\n") + f.write("\t\t\t\t\tV2 -= dV;\n") + f.write("\t\t\t\t\tif (std::abs(dV) < 1e-10) break;\n") + f.write("\t\t\t\t}\n") + f.write("\t\t\t\tconst double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1);\n") + if swirl: + f.write( + "\t\t\t\tconst double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip;\n" + ) + # --- pass 1: normalisation sums over the ball --- + if swirl: + f.write("\t\t\t\tscalar Sax = 0.0, Sth = 0.0;\n") + else: + f.write("\t\t\t\tscalar Sax = 0.0;\n") + f.write("\t\t\t\tforAll(C, i)\n") + f.write("\t\t\t\t{\n") + f.write( + "\t\t\t\t\tconst double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz;\n" + ) + f.write("\t\t\t\t\tconst double d2 = dx*dx + dy*dy + dz*dz;\n") + f.write("\t\t\t\t\tif (d2 <= Rmix*Rmix)\n") + f.write("\t\t\t\t\t{\n") + f.write( + "\t\t\t\t\t\tconst double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i]));\n" + ) + f.write("\t\t\t\t\t\tconst double g = std::exp(-d2/(epsi*epsi));\n") + f.write("\t\t\t\t\t\tSax += alphaL[i]*g*V[i];\n") + if swirl: + f.write(f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n") + f.write("\t\t\t\t\t\tSth += alphaL[i]*g*rr*V[i];\n") + f.write("\t\t\t\t\t}\n") + f.write("\t\t\t\t}\n") + f.write("\t\t\t\treduce(Sax, sumOp());\n") + if swirl: + f.write("\t\t\t\treduce(Sth, sumOp());\n") + # --- pass 2: apply --- + f.write("\t\t\t\tforAll(C, i)\n") + f.write("\t\t\t\t{\n") + f.write( + "\t\t\t\t\tconst double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz;\n" + ) + f.write("\t\t\t\t\tconst double d2 = dx*dx + dy*dy + dz*dz;\n") + f.write("\t\t\t\t\tif (d2 <= Rmix*Rmix)\n") + f.write("\t\t\t\t\t{\n") + f.write( + "\t\t\t\t\t\tconst double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i]));\n" + ) + f.write("\t\t\t\t\t\tconst double g = std::exp(-d2/(epsi*epsi));\n") + f.write("\t\t\t\t\t\tif (Sax > 1e-30)\n") + f.write("\t\t\t\t\t\t{\n") + f.write("\t\t\t\t\t\t\tconst double fax = Tax/Sax*alphaL[i]*g;\n") + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += {push_ax}*fax*V[i];\n") + f.write("\t\t\t\t\t\t}\n") + if swirl: + f.write(f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n") + f.write("\t\t\t\t\t\tif (rr > 1e-3*Rmix && Sth > 1e-30)\n") + f.write("\t\t\t\t\t\t{\n") + f.write("\t\t\t\t\t\t\tconst double fth = Qsw/Sth*alphaL[i]*g;\n") + f.write( + f"\t\t\t\t\t\t\tUsource[i][{tan[0][0]}] += {push_th}*fth*V[i]*(({tan[0][1]})/rr);\n" + ) + f.write( + f"\t\t\t\t\t\t\tUsource[i][{tan[1][0]}] += {push_th}*fth*V[i]*(({tan[1][1]})/rr);\n" + ) + f.write("\t\t\t\t\t\t}\n") + f.write("\t\t\t\t\t}\n") + f.write("\t\t\t\t}\n") + f.write("\t\t\t}\n") + f.write("\t\t}\n") + + def write_end(output_folder): with open(os.path.join(output_folder, "fvModels"), "a+") as f: f.write("\t#};\n") From 8a712045cce1cc9f967943b1dda9d2eaff08f472 Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 17:36:06 -0600 Subject: [PATCH 04/37] format --- bird/preprocess/dynamic_mixer/io_fvModels.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/bird/preprocess/dynamic_mixer/io_fvModels.py b/bird/preprocess/dynamic_mixer/io_fvModels.py index 4e01d7d5..e2077c17 100644 --- a/bird/preprocess/dynamic_mixer/io_fvModels.py +++ b/bird/preprocess/dynamic_mixer/io_fvModels.py @@ -431,15 +431,21 @@ def write_mixer_ball( f.write(f"\t\t\t\t\tif (d2 <= Rmix*Rmix && {push_ax}*{dn} < 0.0)\n") f.write("\t\t\t\t\t{\n") f.write("\t\t\t\t\t\tconst double w = V[i]*alphaL[i];\n") - f.write(f"\t\t\t\t\t\tsV += w; sVU += w*UL[i][{nd}]; sVrho += w*rhoL[i];\n") + f.write( + f"\t\t\t\t\t\tsV += w; sVU += w*UL[i][{nd}]; sVrho += w*rhoL[i];\n" + ) f.write("\t\t\t\t\t}\n") f.write("\t\t\t\t}\n") f.write("\t\t\t\treduce(sV, sumOp());\n") f.write("\t\t\t\treduce(sVU, sumOp());\n") f.write("\t\t\t\treduce(sVrho, sumOp());\n") - f.write(f"\t\t\t\tdouble V1 = (sV>1e-30) ? {push_ax}*(sVU/sV) : 0.0;\n") + f.write( + f"\t\t\t\tdouble V1 = (sV>1e-30) ? {push_ax}*(sVU/sV) : 0.0;\n" + ) f.write("\t\t\t\tif (V1 < 0.0) V1 = 0.0;\n") - f.write("\t\t\t\tconst double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0;\n") + f.write( + "\t\t\t\tconst double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0;\n" + ) # --- Newton solve for V2 --- f.write(f"\t\t\t\tconst double rhs = {rhs};\n") f.write( @@ -481,7 +487,9 @@ def write_mixer_ball( f.write("\t\t\t\t\t\tconst double g = std::exp(-d2/(epsi*epsi));\n") f.write("\t\t\t\t\t\tSax += alphaL[i]*g*V[i];\n") if swirl: - f.write(f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n") + f.write( + f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n" + ) f.write("\t\t\t\t\t\tSth += alphaL[i]*g*rr*V[i];\n") f.write("\t\t\t\t\t}\n") f.write("\t\t\t\t}\n") @@ -507,7 +515,9 @@ def write_mixer_ball( f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += {push_ax}*fax*V[i];\n") f.write("\t\t\t\t\t\t}\n") if swirl: - f.write(f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n") + f.write( + f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n" + ) f.write("\t\t\t\t\t\tif (rr > 1e-3*Rmix && Sth > 1e-30)\n") f.write("\t\t\t\t\t\t{\n") f.write("\t\t\t\t\t\t\tconst double fth = Qsw/Sth*alphaL[i]*g;\n") From 7f8e3e55b77be462dd9e1ce8989150f8aebc3096 Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 17:43:35 -0600 Subject: [PATCH 05/37] machinery to call the right mixer --- .../dynamic_mixer/mixing_fvModels.py | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/bird/preprocess/dynamic_mixer/mixing_fvModels.py b/bird/preprocess/dynamic_mixer/mixing_fvModels.py index 6a4e99e6..1c23ca07 100644 --- a/bird/preprocess/dynamic_mixer/mixing_fvModels.py +++ b/bird/preprocess/dynamic_mixer/mixing_fvModels.py @@ -1,6 +1,6 @@ from bird.meshing.block_rect_mesh import from_block_rect_to_seg from bird.preprocess.dynamic_mixer.io_fvModels import * -from bird.preprocess.dynamic_mixer.mixer import Mixer +from bird.preprocess.dynamic_mixer.mixer import ActuatorMixer, Mixer def check_input(input_dict): @@ -26,6 +26,12 @@ def check_input(input_dict): def write_fvModel(input_dict, output_folder=".", force_sign=False): + # Switch on the volumetric source: "ball" (new, exact-conservation + # actuator-disk) vs "pancake" (legacy, default). The legacy path below is + # left byte-for-byte unchanged. + if input_dict.get("volumetric_source", "pancake") == "ball": + write_fvModel_ball(input_dict, output_folder=output_folder) + return mix_type = check_input(input_dict) write_preamble(output_folder) if "loop" in mix_type: @@ -51,3 +57,28 @@ def write_fvModel(input_dict, output_folder=".", force_sign=False): write_mixer(mixer, output_folder) write_end(output_folder) + + +def write_fvModel_ball(input_dict, output_folder="."): + """Write the ``ball`` (actuator-disk) fvModels. + + Reads the top-level ``power`` (``from_P`` / ``from_Np_Vtip``) and + ``momentum_source`` (``axial`` / ``axial_and_swirl``) modes; both default to + the new full model. Each mixer is an + :class:`~bird.preprocess.dynamic_mixer.mixer.ActuatorMixer`. + """ + mix_type = check_input(input_dict) + power_mode = input_dict.get("power", "from_Np_Vtip") + momentum_mode = input_dict.get("momentum_source", "axial_and_swirl") + write_preamble_ball(output_folder) + if "loop" in mix_type: + geom_dict = from_block_rect_to_seg(input_dict["Geometry"]) + for imix, mtype in enumerate(mix_type): + mixer = ActuatorMixer() + if mtype == "expl": + mixer.update_from_expl_dict(input_dict["mixers"][imix]) + elif mtype == "loop": + mixer.update_from_loop_dict(input_dict["mixers"][imix], geom_dict) + if mixer.ready: + write_mixer_ball(mixer, output_folder, power_mode, momentum_mode) + write_end(output_folder) From b6681fdda23e5be8f612e0d9596831a0794f1c6f Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 17:51:40 -0600 Subject: [PATCH 06/37] new tutorial for the new mixer --- .github/workflows/ci.yml | 7 +- .../loop_reactor_mixing_swirl/0.orig/CO2.gas | 47 + .../0.orig/CO2.liquid | 42 + .../loop_reactor_mixing_swirl/0.orig/H2.gas | 47 + .../0.orig/H2.liquid | 42 + .../loop_reactor_mixing_swirl/0.orig/N2.gas | 47 + .../loop_reactor_mixing_swirl/0.orig/T.gas | 46 + .../loop_reactor_mixing_swirl/0.orig/T.liquid | 45 + .../loop_reactor_mixing_swirl/0.orig/U.gas | 47 + .../loop_reactor_mixing_swirl/0.orig/U.liquid | 46 + .../0.orig/Ydefault.gas | 42 + .../0.orig/Ydefault.liquid | 42 + .../0.orig/alpha.gas | 43 + .../0.orig/alpha.liquid | 40 + .../0.orig/alphat.gas | 46 + .../0.orig/alphat.liquid | 44 + .../0.orig/epsilon.gas | 48 + .../0.orig/epsilon.liquid | 43 + .../loop_reactor_mixing_swirl/0.orig/f.gas | 41 + .../loop_reactor_mixing_swirl/0.orig/k.gas | 43 + .../loop_reactor_mixing_swirl/0.orig/k.liquid | 44 + .../loop_reactor_mixing_swirl/0.orig/nut.gas | 48 + .../0.orig/nut.liquid | 43 + .../loop_reactor_mixing_swirl/0.orig/p | 39 + .../loop_reactor_mixing_swirl/0.orig/p_rgh | 43 + .../loop_reactor_mixing_swirl/Allclean | 24 + .../loop_reactor_mixing_swirl/README.md | 27 + .../loop_reactor_mixing_swirl/computeQOI.sh | 13 + .../constant/fvModels | 353 ++++++ .../loop_reactor_mixing_swirl/constant/g | 21 + .../constant/globalVars | 83 ++ .../constant/globalVars_temp | 83 ++ .../constant/momentumTransport.gas | 26 + .../constant/momentumTransport.liquid | 27 + .../constant/phaseProperties | 261 ++++ .../constant/phaseProperties_constantd | 261 ++++ .../constant/phaseProperties_pbe | 295 +++++ .../constant/thermophysicalProperties.gas | 142 +++ .../constant/thermophysicalProperties.liquid | 108 ++ .../loop_reactor_mixing_swirl/get_qoi.py | 199 ++++ .../loop_reactor_mixing_swirl/presteps.sh | 81 ++ .../loop_reactor_mixing_swirl/read_history.py | 104 ++ .../loop_reactor_mixing_swirl/run.sh | 72 ++ .../loop_reactor_mixing_swirl/script | 14 + .../loop_reactor_mixing_swirl/script_post | 10 + .../system/blockMeshDict | 1050 +++++++++++++++++ .../system/controlDict | 66 ++ .../system/decomposeParDict | 30 + .../system/fvConstraints | 56 + .../system/fvSchemes | 70 ++ .../system/fvSolution | 120 ++ .../system/inlets_outlets.json | 177 +++ .../system/mesh.json | 26 + .../system/mixers.json | 175 +++ .../system/setFieldsDict | 37 + .../writeGlobalVars.py | 42 + tutorial_cases/runall.sh | 6 +- 57 files changed, 5122 insertions(+), 2 deletions(-) create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/CO2.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/CO2.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/H2.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/H2.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/N2.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/U.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/U.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/Ydefault.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/Ydefault.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/alpha.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/alpha.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/alphat.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/alphat.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/epsilon.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/epsilon.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/f.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/k.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/k.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/nut.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/nut.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/p create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/0.orig/p_rgh create mode 100755 tutorial_cases/loop_reactor_mixing_swirl/Allclean create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/README.md create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/computeQOI.sh create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/g create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars_temp create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/momentumTransport.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/momentumTransport.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties_constantd create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties_pbe create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/thermophysicalProperties.gas create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/constant/thermophysicalProperties.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/get_qoi.py create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/presteps.sh create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/read_history.py create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/run.sh create mode 100755 tutorial_cases/loop_reactor_mixing_swirl/script create mode 100755 tutorial_cases/loop_reactor_mixing_swirl/script_post create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/blockMeshDict create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/controlDict create mode 100755 tutorial_cases/loop_reactor_mixing_swirl/system/decomposeParDict create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/fvConstraints create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/fvSchemes create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/fvSolution create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/inlets_outlets.json create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/mesh.json create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/mixers.json create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/system/setFieldsDict create mode 100644 tutorial_cases/loop_reactor_mixing_swirl/writeGlobalVars.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5247c979..d8efcf29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -245,7 +245,12 @@ jobs: - name: Run mixing loop reactor tutorial run: | cd tutorial_cases/loop_reactor_mixing - bash run.sh + bash run.sh + cd ../../ + - name: Run mixing loop reactor with swirl tutorial + run: | + cd tutorial_cases/loop_reactor_mixing_swirl + bash run.sh cd ../../ - name: Run airlift reactor tutorial run: | diff --git a/tutorial_cases/loop_reactor_mixing_swirl/0.orig/CO2.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/CO2.gas new file mode 100644 index 00000000..e4165b1a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/CO2.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/CO2.liquid new file mode 100644 index 00000000..4b8ea6a0 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/H2.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/H2.gas new file mode 100644 index 00000000..9f66b2d2 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/H2.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/H2.liquid new file mode 100644 index 00000000..65ae8d34 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/N2.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/N2.gas new file mode 100644 index 00000000..c1d7225f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.gas new file mode 100644 index 00000000..1202c340 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.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 T.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.liquid new file mode 100644 index 00000000..d6c1836a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/T.liquid @@ -0,0 +1,45 @@ +/*--------------------------------*- 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]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/U.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/U.gas new file mode 100644 index 00000000..e696566f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/U.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/U.liquid new file mode 100644 index 00000000..1879e020 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/Ydefault.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/Ydefault.gas new file mode 100644 index 00000000..fba2945d --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/Ydefault.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/Ydefault.liquid new file mode 100644 index 00000000..a5108564 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alpha.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alpha.gas new file mode 100644 index 00000000..1e303fbe --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alpha.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alpha.liquid new file mode 100644 index 00000000..5c92070b --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alphat.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alphat.gas new file mode 100644 index 00000000..b867958f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alphat.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/alphat.liquid new file mode 100644 index 00000000..2569c3ee --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/epsilon.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/epsilon.gas new file mode 100644 index 00000000..707a1cda --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/epsilon.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/epsilon.liquid new file mode 100644 index 00000000..0a4236fd --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/f.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/f.gas new file mode 100644 index 00000000..76ee77a9 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/k.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/k.gas new file mode 100644 index 00000000..4a3d44ca --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/k.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/k.liquid new file mode 100644 index 00000000..cde8f6c1 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/nut.gas b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/nut.gas new file mode 100644 index 00000000..ba16dd4c --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/nut.liquid b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/nut.liquid new file mode 100644 index 00000000..1442e07f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/p b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/p new file mode 100644 index 00000000..b3a295fb --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/0.orig/p_rgh b/tutorial_cases/loop_reactor_mixing_swirl/0.orig/p_rgh new file mode 100644 index 00000000..88ee7d80 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/Allclean b/tutorial_cases/loop_reactor_mixing_swirl/Allclean new file mode 100755 index 00000000..dc2f77db --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/loop_reactor_mixing_swirl/README.md b/tutorial_cases/loop_reactor_mixing_swirl/README.md new file mode 100644 index 00000000..794e422d --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/README.md @@ -0,0 +1,27 @@ +### Loop reactor with actuator-disk mixers (with swirl) + +Same 608 m3 loop reactor as `loop_reactor_mixing`, but the 4 mixers use the +new `ball` actuator-disk momentum source instead of the legacy `pancake` one. + +The model is selected by three top-level keys in `system/mixers.json`: + +- `"volumetric_source": "ball"` — momentum deposited over a ball of radius `R` + (a fraction of the branch cross-section), with exact momentum/torque + conservation. +- `"power": "from_Np_Vtip"` — the mixer power is derived from the power + number `Np` and tip speed `Vtip` (rather than a prescribed `P`). +- `"momentum_source": "axial_and_swirl"` — adds a tangential (swirl) source on + top of the axial thrust, set by the swirl fraction `sigma`. + +Per-mixer inputs: `radius` (fraction of the branch cross-section, as for the +spargers), `Vtip` [m/s], `Np`, `sigma`, `sign` (axial push) and `swirl_sign` +(rotation sense). With `Np = 6` and `Vtip = 1.5` m/s the derived power is +~3.2 kW per mixer at this scale. + +Unlike `loop_reactor_mixing`, no `constant/dynamicMix_util.H` is needed: the +Newton solve for the post-mixer velocity is inlined in the generated +`constant/fvModels`. + +Single core exec + +1. `bash run.sh` diff --git a/tutorial_cases/loop_reactor_mixing_swirl/computeQOI.sh b/tutorial_cases/loop_reactor_mixing_swirl/computeQOI.sh new file mode 100644 index 00000000..3756ed7f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels b/tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels new file mode 100644 index 00000000..8bad277c --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels @@ -0,0 +1,353 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object fvModels; +} + +codedSource +{ + type coded; + selectionMode all; + field U.liquid; + name sourceTime; + + codeInclude + #{ + #include + #include + #}; + + codeAddAlphaRhoSup + #{ + const Time& time = mesh().time(); + const scalarField& V = mesh().V(); + vectorField& Usource = eqn.source(); + const vectorField& C = mesh().C(); + const volScalarField& rhoL = + mesh().lookupObject("thermo:rho.liquid"); + const volScalarField& alphaL = + mesh().lookupObject("alpha.liquid"); + const volVectorField& UL = + mesh().lookupObject("U.liquid"); + const double pi = 3.14159265358979; + // ===== ball mixer ===== + { + const double Rmix = 1.1046110154250839; + const double area = pi*Rmix*Rmix; + const double Vtip = 1.5; + const double sigma = 0.35; + const double startT = 3; + const double px = 11.32226290810711, py = 1.3807637692813548, pz = 1.3807637692813548; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && 1.0*dx < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][0]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? 1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double rhs = 16.0*6*Vtip*Vtip*Vtip/pow(pi,4.0); + double V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs)); + for (int it = 0; it < 100; ++it) + { + const double F = (V2-V1)*(V2+V1)*(V2+V1) + 0.35*(V1+V2)*Vtip*Vtip - rhs; + const double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1 + 0.35*Vtip*Vtip; + const double dV = F/dF; + V2 -= dV; + if (std::abs(dV) < 1e-10) break; + } + const double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1); + const double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip; + scalar Sax = 0.0, Sth = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + Sax += alphaL[i]*g*V[i]; + const double rr = std::sqrt(d2-(dx)*(dx)); + Sth += alphaL[i]*g*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Sth, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fax = Tax/Sax*alphaL[i]*g; + Usource[i][0] += 1.0*fax*V[i]; + } + const double rr = std::sqrt(d2-(dx)*(dx)); + if (rr > 1e-3*Rmix && Sth > 1e-30) + { + const double fth = Qsw/Sth*alphaL[i]*g; + Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + } + } + } + } + } + // ===== ball mixer ===== + { + const double Rmix = 1.1046110154250839; + const double area = pi*Rmix*Rmix; + const double Vtip = 1.5; + const double sigma = 0.35; + const double startT = 3; + const double px = 16.293012477519987, py = 1.3807637692813548, pz = 1.3807637692813548; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && 1.0*dx < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][0]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? 1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double rhs = 16.0*6*Vtip*Vtip*Vtip/pow(pi,4.0); + double V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs)); + for (int it = 0; it < 100; ++it) + { + const double F = (V2-V1)*(V2+V1)*(V2+V1) + 0.35*(V1+V2)*Vtip*Vtip - rhs; + const double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1 + 0.35*Vtip*Vtip; + const double dV = F/dF; + V2 -= dV; + if (std::abs(dV) < 1e-10) break; + } + const double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1); + const double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip; + scalar Sax = 0.0, Sth = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + Sax += alphaL[i]*g*V[i]; + const double rr = std::sqrt(d2-(dx)*(dx)); + Sth += alphaL[i]*g*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Sth, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fax = Tax/Sax*alphaL[i]*g; + Usource[i][0] += 1.0*fax*V[i]; + } + const double rr = std::sqrt(d2-(dx)*(dx)); + if (rr > 1e-3*Rmix && Sth > 1e-30) + { + const double fth = Qsw/Sth*alphaL[i]*g; + Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + } + } + } + } + } + // ===== ball mixer ===== + { + const double Rmix = 1.1046110154250839; + const double area = pi*Rmix*Rmix; + const double Vtip = 1.5; + const double sigma = 0.35; + const double startT = 3; + const double px = 21.263762046932865, py = 1.3807637692813548, pz = 1.3807637692813548; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && 1.0*dx < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][0]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? 1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double rhs = 16.0*6*Vtip*Vtip*Vtip/pow(pi,4.0); + double V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs)); + for (int it = 0; it < 100; ++it) + { + const double F = (V2-V1)*(V2+V1)*(V2+V1) + 0.35*(V1+V2)*Vtip*Vtip - rhs; + const double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1 + 0.35*Vtip*Vtip; + const double dV = F/dF; + V2 -= dV; + if (std::abs(dV) < 1e-10) break; + } + const double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1); + const double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip; + scalar Sax = 0.0, Sth = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + Sax += alphaL[i]*g*V[i]; + const double rr = std::sqrt(d2-(dx)*(dx)); + Sth += alphaL[i]*g*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Sth, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fax = Tax/Sax*alphaL[i]*g; + Usource[i][0] += 1.0*fax*V[i]; + } + const double rr = std::sqrt(d2-(dx)*(dx)); + if (rr > 1e-3*Rmix && Sth > 1e-30) + { + const double fth = Qsw/Sth*alphaL[i]*g; + Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + } + } + } + } + } + // ===== ball mixer ===== + { + const double Rmix = 1.1046110154250839; + const double area = pi*Rmix*Rmix; + const double Vtip = 1.5; + const double sigma = 0.35; + const double startT = 3; + const double px = 16.293012477519987, py = 1.3807637692813548, pz = 12.426873923532193; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dx < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][0]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double rhs = 16.0*6*Vtip*Vtip*Vtip/pow(pi,4.0); + double V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs)); + for (int it = 0; it < 100; ++it) + { + const double F = (V2-V1)*(V2+V1)*(V2+V1) + 0.35*(V1+V2)*Vtip*Vtip - rhs; + const double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1 + 0.35*Vtip*Vtip; + const double dV = F/dF; + V2 -= dV; + if (std::abs(dV) < 1e-10) break; + } + const double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1); + const double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip; + scalar Sax = 0.0, Sth = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + Sax += alphaL[i]*g*V[i]; + const double rr = std::sqrt(d2-(dx)*(dx)); + Sth += alphaL[i]*g*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Sth, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fax = Tax/Sax*alphaL[i]*g; + Usource[i][0] += -1.0*fax*V[i]; + } + const double rr = std::sqrt(d2-(dx)*(dx)); + if (rr > 1e-3*Rmix && Sth > 1e-30) + { + const double fth = Qsw/Sth*alphaL[i]*g; + Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + } + } + } + } + } + #}; +}; diff --git a/tutorial_cases/loop_reactor_mixing_swirl/constant/g b/tutorial_cases/loop_reactor_mixing_swirl/constant/g new file mode 100644 index 00000000..770a5619 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars b/tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars new file mode 100644 index 00000000..c0dce472 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars @@ -0,0 +1,83 @@ +T0 300; //initial T(K) which stays constant +VVM 0.4; +//****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 15.8621; +liqVol 608.198; +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/tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars_temp b/tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars_temp new file mode 100644 index 00000000..dfddd649 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/constant/globalVars_temp @@ -0,0 +1,83 @@ +T0 300; //initial T(K) which stays constant +VVM 0.4; +//****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/tutorial_cases/loop_reactor_mixing_swirl/constant/momentumTransport.gas b/tutorial_cases/loop_reactor_mixing_swirl/constant/momentumTransport.gas new file mode 100644 index 00000000..ca916714 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/constant/momentumTransport.liquid b/tutorial_cases/loop_reactor_mixing_swirl/constant/momentumTransport.liquid new file mode 100644 index 00000000..2063de0d --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties b/tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties new file mode 100644 index 00000000..e029df99 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties @@ -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/tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties_constantd b/tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties_constantd new file mode 100644 index 00000000..e029df99 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties_pbe b/tutorial_cases/loop_reactor_mixing_swirl/constant/phaseProperties_pbe new file mode 100644 index 00000000..a3c90f5a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/constant/thermophysicalProperties.gas b/tutorial_cases/loop_reactor_mixing_swirl/constant/thermophysicalProperties.gas new file mode 100644 index 00000000..11b1c4b9 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/constant/thermophysicalProperties.liquid b/tutorial_cases/loop_reactor_mixing_swirl/constant/thermophysicalProperties.liquid new file mode 100644 index 00000000..d324ec51 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/get_qoi.py b/tutorial_cases/loop_reactor_mixing_swirl/get_qoi.py new file mode 100644 index 00000000..7f3897ed --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/get_qoi.py @@ -0,0 +1,199 @@ +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_kla_co2 = kla_co2 * cs_co2 * V_l * 0.04401 + qoi_kla_h2 = kla_h2 * cs_h2 * V_l * 0.002016 + + qoi_co2 = qoi_kla_co2 / (P_mix / 3600 + P_inj / 3600) + qoi_h2 = qoi_kla_h2 / (P_mix / 3600 + P_inj / 3600) + return qoi_co2 * qoi_h2, qoi_kla_co2 * qoi_kla_h2 + + +def get_qoi_uq(kla_co2, cs_co2, kla_h2, cs_h2): + qoi = [] + qoi_kla = [] + for i in range(len(kla_co2)): + if i == 0: + verb = True + else: + verb = False + qoi_tmp, qoi_kla_tmp = get_qoi( + kla_co2[i], cs_co2[i], kla_h2[i], cs_h2[i], verb + ) + qoi.append(qoi_tmp) + qoi_kla.append(qoi_kla_tmp) + qoi = np.array(qoi) + qoi_kla = np.array(qoi_kla) + return np.mean(qoi), np.std(qoi), np.mean(qoi_kla), np.std(qoi_kla) + + +os.makedirs("Figures", exist_ok=True) + +dataFolder = "data" +fold = "local" + +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(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, qoi_kla_m, qoi_kla_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") + +with open("qoi_kla.txt", "w+") as f: + f.write(f"{qoi_kla_m},{qoi_kla_s}\n") diff --git a/tutorial_cases/loop_reactor_mixing_swirl/presteps.sh b/tutorial_cases/loop_reactor_mixing_swirl/presteps.sh new file mode 100644 index 00000000..bfcff75f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/presteps.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +# Clean case +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 +#python ../../../applications/write_block_rect_mesh.py -i system/mesh.json -o system + +# Generate boundary stl +python /projects/gas2fuels/BioReactorDesign/applications/write_stl_patch.py -i system/inlets_outlets.json +#python ../../../applications/write_stl_patch.py -i system/inlets_outlets.json + +# Generate mixers +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 + +# Generate species thermo properties +python /projects/gas2fuels/BioReactorDesign//applications/write_species_thermo_prop.py -cf . + +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_pbe 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/tutorial_cases/loop_reactor_mixing_swirl/read_history.py b/tutorial_cases/loop_reactor_mixing_swirl/read_history.py new file mode 100644 index 00000000..c27eae94 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/read_history.py @@ -0,0 +1,104 @@ +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 = get_case_times( + case_path, remove_zero=True +) +cell_centers, _ = read_cell_centers(".") +nCells = len(cell_centers) + + +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], + field_dict=field_dict, + ) + co2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + species_name="CO2", + field_dict=field_dict, + ) + h2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + species_name="H2", + field_dict=field_dict, + ) + c_co2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + species_name="CO2", + field_dict=field_dict, + ) + c_h2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + species_name="H2", + 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/tutorial_cases/loop_reactor_mixing_swirl/run.sh b/tutorial_cases/loop_reactor_mixing_swirl/run.sh new file mode 100644 index 00000000..25251599 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/run.sh @@ -0,0 +1,72 @@ +#!/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 + +# Generate boundary stl +python ../../applications/write_stl_patch.py -i system/inlets_outlets.json + +# Generate mixers +python ../../applications/write_dynMix_fvModels.py -fs -i system/mixers.json -o constant + +# Generate species thermo properties +python ../../applications/write_species_thermo_prop.py -cf . + +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_pbe constant/phaseProperties + +#conda deactivate + +echo RUN +birdmultiphaseEulerFoam diff --git a/tutorial_cases/loop_reactor_mixing_swirl/script b/tutorial_cases/loop_reactor_mixing_swirl/script new file mode 100755 index 00000000..efe675ff --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/script_post b/tutorial_cases/loop_reactor_mixing_swirl/script_post new file mode 100755 index 00000000..aabbc33e --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/system/blockMeshDict b/tutorial_cases/loop_reactor_mixing_swirl/system/blockMeshDict new file mode 100644 index 00000000..0bb60950 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/system/blockMeshDict @@ -0,0 +1,1050 @@ +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 8.0 0.0) +( 1.0 8.0 0.0) +( 2.0 8.0 0.0) +( 3.0 8.0 0.0) +( 4.0 8.0 0.0) +( 5.0 8.0 0.0) +( 6.0 8.0 0.0) +( 7.0 8.0 0.0) +( 8.0 8.0 0.0) +( 9.0 8.0 0.0) +( 10.0 8.0 0.0) +( 0.0 9.0 0.0) +( 1.0 9.0 0.0) +( 2.0 9.0 0.0) +( 3.0 9.0 0.0) +( 4.0 9.0 0.0) +( 5.0 9.0 0.0) +( 6.0 9.0 0.0) +( 7.0 9.0 0.0) +( 8.0 9.0 0.0) +( 9.0 9.0 0.0) +( 10.0 9.0 0.0) +( 0.0 10.0 0.0) +( 1.0 10.0 0.0) +( 2.0 10.0 0.0) +( 3.0 10.0 0.0) +( 4.0 10.0 0.0) +( 5.0 10.0 0.0) +( 6.0 10.0 0.0) +( 7.0 10.0 0.0) +( 8.0 10.0 0.0) +( 9.0 10.0 0.0) +( 10.0 10.0 0.0) +( 0.0 11.0 0.0) +( 1.0 11.0 0.0) +( 2.0 11.0 0.0) +( 3.0 11.0 0.0) +( 4.0 11.0 0.0) +( 5.0 11.0 0.0) +( 6.0 11.0 0.0) +( 7.0 11.0 0.0) +( 8.0 11.0 0.0) +( 9.0 11.0 0.0) +( 10.0 11.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 8.0 1.0) +( 1.0 8.0 1.0) +( 2.0 8.0 1.0) +( 3.0 8.0 1.0) +( 4.0 8.0 1.0) +( 5.0 8.0 1.0) +( 6.0 8.0 1.0) +( 7.0 8.0 1.0) +( 8.0 8.0 1.0) +( 9.0 8.0 1.0) +( 10.0 8.0 1.0) +( 0.0 9.0 1.0) +( 1.0 9.0 1.0) +( 2.0 9.0 1.0) +( 3.0 9.0 1.0) +( 4.0 9.0 1.0) +( 5.0 9.0 1.0) +( 6.0 9.0 1.0) +( 7.0 9.0 1.0) +( 8.0 9.0 1.0) +( 9.0 9.0 1.0) +( 10.0 9.0 1.0) +( 0.0 10.0 1.0) +( 1.0 10.0 1.0) +( 2.0 10.0 1.0) +( 3.0 10.0 1.0) +( 4.0 10.0 1.0) +( 5.0 10.0 1.0) +( 6.0 10.0 1.0) +( 7.0 10.0 1.0) +( 8.0 10.0 1.0) +( 9.0 10.0 1.0) +( 10.0 10.0 1.0) +( 0.0 11.0 1.0) +( 1.0 11.0 1.0) +( 2.0 11.0 1.0) +( 3.0 11.0 1.0) +( 4.0 11.0 1.0) +( 5.0 11.0 1.0) +( 6.0 11.0 1.0) +( 7.0 11.0 1.0) +( 8.0 11.0 1.0) +( 9.0 11.0 1.0) +( 10.0 11.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 8.0 2.0) +( 1.0 8.0 2.0) +( 2.0 8.0 2.0) +( 3.0 8.0 2.0) +( 4.0 8.0 2.0) +( 5.0 8.0 2.0) +( 6.0 8.0 2.0) +( 7.0 8.0 2.0) +( 8.0 8.0 2.0) +( 9.0 8.0 2.0) +( 10.0 8.0 2.0) +( 0.0 9.0 2.0) +( 1.0 9.0 2.0) +( 2.0 9.0 2.0) +( 3.0 9.0 2.0) +( 4.0 9.0 2.0) +( 5.0 9.0 2.0) +( 6.0 9.0 2.0) +( 7.0 9.0 2.0) +( 8.0 9.0 2.0) +( 9.0 9.0 2.0) +( 10.0 9.0 2.0) +( 0.0 10.0 2.0) +( 1.0 10.0 2.0) +( 2.0 10.0 2.0) +( 3.0 10.0 2.0) +( 4.0 10.0 2.0) +( 5.0 10.0 2.0) +( 6.0 10.0 2.0) +( 7.0 10.0 2.0) +( 8.0 10.0 2.0) +( 9.0 10.0 2.0) +( 10.0 10.0 2.0) +( 0.0 11.0 2.0) +( 1.0 11.0 2.0) +( 2.0 11.0 2.0) +( 3.0 11.0 2.0) +( 4.0 11.0 2.0) +( 5.0 11.0 2.0) +( 6.0 11.0 2.0) +( 7.0 11.0 2.0) +( 8.0 11.0 2.0) +( 9.0 11.0 2.0) +( 10.0 11.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 8.0 3.0) +( 1.0 8.0 3.0) +( 2.0 8.0 3.0) +( 3.0 8.0 3.0) +( 4.0 8.0 3.0) +( 5.0 8.0 3.0) +( 6.0 8.0 3.0) +( 7.0 8.0 3.0) +( 8.0 8.0 3.0) +( 9.0 8.0 3.0) +( 10.0 8.0 3.0) +( 0.0 9.0 3.0) +( 1.0 9.0 3.0) +( 2.0 9.0 3.0) +( 3.0 9.0 3.0) +( 4.0 9.0 3.0) +( 5.0 9.0 3.0) +( 6.0 9.0 3.0) +( 7.0 9.0 3.0) +( 8.0 9.0 3.0) +( 9.0 9.0 3.0) +( 10.0 9.0 3.0) +( 0.0 10.0 3.0) +( 1.0 10.0 3.0) +( 2.0 10.0 3.0) +( 3.0 10.0 3.0) +( 4.0 10.0 3.0) +( 5.0 10.0 3.0) +( 6.0 10.0 3.0) +( 7.0 10.0 3.0) +( 8.0 10.0 3.0) +( 9.0 10.0 3.0) +( 10.0 10.0 3.0) +( 0.0 11.0 3.0) +( 1.0 11.0 3.0) +( 2.0 11.0 3.0) +( 3.0 11.0 3.0) +( 4.0 11.0 3.0) +( 5.0 11.0 3.0) +( 6.0 11.0 3.0) +( 7.0 11.0 3.0) +( 8.0 11.0 3.0) +( 9.0 11.0 3.0) +( 10.0 11.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 8.0 4.0) +( 1.0 8.0 4.0) +( 2.0 8.0 4.0) +( 3.0 8.0 4.0) +( 4.0 8.0 4.0) +( 5.0 8.0 4.0) +( 6.0 8.0 4.0) +( 7.0 8.0 4.0) +( 8.0 8.0 4.0) +( 9.0 8.0 4.0) +( 10.0 8.0 4.0) +( 0.0 9.0 4.0) +( 1.0 9.0 4.0) +( 2.0 9.0 4.0) +( 3.0 9.0 4.0) +( 4.0 9.0 4.0) +( 5.0 9.0 4.0) +( 6.0 9.0 4.0) +( 7.0 9.0 4.0) +( 8.0 9.0 4.0) +( 9.0 9.0 4.0) +( 10.0 9.0 4.0) +( 0.0 10.0 4.0) +( 1.0 10.0 4.0) +( 2.0 10.0 4.0) +( 3.0 10.0 4.0) +( 4.0 10.0 4.0) +( 5.0 10.0 4.0) +( 6.0 10.0 4.0) +( 7.0 10.0 4.0) +( 8.0 10.0 4.0) +( 9.0 10.0 4.0) +( 10.0 10.0 4.0) +( 0.0 11.0 4.0) +( 1.0 11.0 4.0) +( 2.0 11.0 4.0) +( 3.0 11.0 4.0) +( 4.0 11.0 4.0) +( 5.0 11.0 4.0) +( 6.0 11.0 4.0) +( 7.0 11.0 4.0) +( 8.0 11.0 4.0) +( 9.0 11.0 4.0) +( 10.0 11.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) +( 0.0 8.0 5.0) +( 1.0 8.0 5.0) +( 2.0 8.0 5.0) +( 3.0 8.0 5.0) +( 4.0 8.0 5.0) +( 5.0 8.0 5.0) +( 6.0 8.0 5.0) +( 7.0 8.0 5.0) +( 8.0 8.0 5.0) +( 9.0 8.0 5.0) +( 10.0 8.0 5.0) +( 0.0 9.0 5.0) +( 1.0 9.0 5.0) +( 2.0 9.0 5.0) +( 3.0 9.0 5.0) +( 4.0 9.0 5.0) +( 5.0 9.0 5.0) +( 6.0 9.0 5.0) +( 7.0 9.0 5.0) +( 8.0 9.0 5.0) +( 9.0 9.0 5.0) +( 10.0 9.0 5.0) +( 0.0 10.0 5.0) +( 1.0 10.0 5.0) +( 2.0 10.0 5.0) +( 3.0 10.0 5.0) +( 4.0 10.0 5.0) +( 5.0 10.0 5.0) +( 6.0 10.0 5.0) +( 7.0 10.0 5.0) +( 8.0 10.0 5.0) +( 9.0 10.0 5.0) +( 10.0 10.0 5.0) +( 0.0 11.0 5.0) +( 1.0 11.0 5.0) +( 2.0 11.0 5.0) +( 3.0 11.0 5.0) +( 4.0 11.0 5.0) +( 5.0 11.0 5.0) +( 6.0 11.0 5.0) +( 7.0 11.0 5.0) +( 8.0 11.0 5.0) +( 9.0 11.0 5.0) +( 10.0 11.0 5.0) +); + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +blocks +( + + //block 0 +hex (0 1 12 11 132 133 144 143 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 1 +hex (1 2 13 12 133 134 145 144 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 2 +hex (2 3 14 13 134 135 146 145 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 3 +hex (3 4 15 14 135 136 147 146 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 4 +hex (4 5 16 15 136 137 148 147 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 5 +hex (5 6 17 16 137 138 149 148 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 6 +hex (6 7 18 17 138 139 150 149 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 7 +hex (7 8 19 18 139 140 151 150 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 8 +hex (8 9 20 19 140 141 152 151 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 9 +hex (9 10 21 20 141 142 153 152 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 10 +hex (141 142 153 152 273 274 285 284 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 11 +hex (273 274 285 284 405 406 417 416 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 12 +hex (405 406 417 416 537 538 549 548 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 13 +hex (537 538 549 548 669 670 681 680 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 14 +hex (536 537 548 547 668 669 680 679 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 15 +hex (535 536 547 546 667 668 679 678 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 16 +hex (534 535 546 545 666 667 678 677 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 17 +hex (533 534 545 544 665 666 677 676 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 18 +hex (532 533 544 543 664 665 676 675 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 19 +hex (531 532 543 542 663 664 675 674 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 20 +hex (530 531 542 541 662 663 674 673 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 21 +hex (529 530 541 540 661 662 673 672 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 22 +hex (528 529 540 539 660 661 672 671 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 23 +hex (539 540 551 550 671 672 683 682 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 24 +hex (550 551 562 561 682 683 694 693 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 25 +hex (561 562 573 572 693 694 705 704 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 26 +hex (572 573 584 583 704 705 716 715 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 27 +hex (583 584 595 594 715 716 727 726 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 28 +hex (594 595 606 605 726 727 738 737 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 29 +hex (605 606 617 616 737 738 749 748 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 30 +hex (616 617 628 627 748 749 760 759 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 31 +hex (627 628 639 638 759 760 771 770 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 32 +hex (638 639 650 649 770 771 782 781 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 33 +hex (440 441 452 451 572 573 584 583 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 34 +hex (308 309 320 319 440 441 452 451 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 35 +hex (176 177 188 187 308 309 320 319 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 36 +hex (44 45 56 55 176 177 188 187 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 37 +hex (55 56 67 66 187 188 199 198 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 38 +hex (66 67 78 77 198 199 210 209 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 39 +hex (77 78 89 88 209 210 221 220 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 40 +hex (88 89 100 99 220 221 232 231 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 41 +hex (99 100 111 110 231 232 243 242 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 42 +hex (110 111 122 121 242 243 254 253 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 43 +hex (33 34 45 44 165 166 177 176 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 44 +hex (22 23 34 33 154 155 166 165 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 45 +hex (11 12 23 22 143 144 155 154 ) +( 10 10 10 ) +SimpleGrading (1 1 1) +); + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +defaultPatch +{ type wall;} + +patches +( +); diff --git a/tutorial_cases/loop_reactor_mixing_swirl/system/controlDict b/tutorial_cases/loop_reactor_mixing_swirl/system/controlDict new file mode 100644 index 00000000..f4665ed8 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/system/controlDict @@ -0,0 +1,66 @@ +/*--------------------------------*- 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 writeNow;//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) + #includeFunc writeObjects(thermo:mu.liquid) + #includeFunc writeObjects(thermo:mu.gas) + #includeFunc fieldAverage(U.air, U.water, alpha.air, p) +} + + +// ************************************************************************* // diff --git a/tutorial_cases/loop_reactor_mixing_swirl/system/decomposeParDict b/tutorial_cases/loop_reactor_mixing_swirl/system/decomposeParDict new file mode 100755 index 00000000..f8397e73 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/system/fvConstraints b/tutorial_cases/loop_reactor_mixing_swirl/system/fvConstraints new file mode 100644 index 00000000..334f1c8f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/system/fvSchemes b/tutorial_cases/loop_reactor_mixing_swirl/system/fvSchemes new file mode 100644 index 00000000..52e6e13a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/system/fvSolution b/tutorial_cases/loop_reactor_mixing_swirl/system/fvSolution new file mode 100644 index 00000000..2e69fdfa --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/system/inlets_outlets.json b/tutorial_cases/loop_reactor_mixing_swirl/system/inlets_outlets.json new file mode 100644 index 00000000..a083d47f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/system/inlets_outlets.json @@ -0,0 +1,177 @@ +{ + "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 + ] + ] + ] + }, + "inlets": [ + { + "branch_id": 0, + "type": "circle", + "frac_space": 0.2, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom" + }, + { + "branch_id": 1, + "type": "circle", + "frac_space": 0.2, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom" + }, + { + "branch_id": 1, + "type": "circle", + "frac_space": 0.8, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom" + }, + { + "branch_id": 2, + "type": "circle", + "frac_space": 0.8, + "normal_dir": 1, + "radius": 0.4, + "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" + } + ] +} \ No newline at end of file diff --git a/tutorial_cases/loop_reactor_mixing_swirl/system/mesh.json b/tutorial_cases/loop_reactor_mixing_swirl/system/mesh.json new file mode 100644 index 00000000..29841d7e --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/system/mixers.json b/tutorial_cases/loop_reactor_mixing_swirl/system/mixers.json new file mode 100644 index 00000000..4c88041b --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/system/mixers.json @@ -0,0 +1,175 @@ +{ + "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": 0, + "frac_space": 0.4, + "start_time": 3, + "sign": "+", + "swirl_sign": "+", + "radius": 0.4, + "Vtip": 1.5, + "Np": 6, + "sigma": 0.35 + }, + { + "branch_id": 0, + "frac_space": 0.6000000000000001, + "start_time": 3, + "sign": "+", + "swirl_sign": "+", + "radius": 0.4, + "Vtip": 1.5, + "Np": 6, + "sigma": 0.35 + }, + { + "branch_id": 0, + "frac_space": 0.8, + "start_time": 3, + "sign": "+", + "swirl_sign": "+", + "radius": 0.4, + "Vtip": 1.5, + "Np": 6, + "sigma": 0.35 + }, + { + "branch_id": 2, + "frac_space": 0.4, + "start_time": 3, + "sign": "-", + "swirl_sign": "+", + "radius": 0.4, + "Vtip": 1.5, + "Np": 6, + "sigma": 0.35 + } + ], + "volumetric_source": "ball", + "power": "from_Np_Vtip", + "momentum_source": "axial_and_swirl" +} \ No newline at end of file diff --git a/tutorial_cases/loop_reactor_mixing_swirl/system/setFieldsDict b/tutorial_cases/loop_reactor_mixing_swirl/system/setFieldsDict new file mode 100644 index 00000000..89a797b9 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/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/tutorial_cases/loop_reactor_mixing_swirl/writeGlobalVars.py b/tutorial_cases/loop_reactor_mixing_swirl/writeGlobalVars.py new file mode 100644 index 00000000..e64d69e3 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_swirl/writeGlobalVars.py @@ -0,0 +1,42 @@ +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", + ) + return read_surface_field_value(filename) + + +def getLiqVol(): + volume_field, _ = read_cell_volumes(".") + alpha_field, _ = read_field(".", "0", field_name="alpha.liquid") + return np.sum(volume_field * alpha_field) + + +if __name__ == "__main__": + A = readInletArea() + V = getLiqVol() + writeGvars(A, V) diff --git a/tutorial_cases/runall.sh b/tutorial_cases/runall.sh index c58bc65f..894373a2 100644 --- a/tutorial_cases/runall.sh +++ b/tutorial_cases/runall.sh @@ -40,7 +40,11 @@ bash run.sh cd ../../ ## Run mixing loop reactor tutorial cd tutorial_cases/loop_reactor_mixing -bash run.sh +bash run.sh +cd ../../ +## Run mixing loop reactor with swirl tutorial +cd tutorial_cases/loop_reactor_mixing_swirl +bash run.sh cd ../../ ## Run airlift reactor tutorial cd tutorial_cases/airlift_40m From 69652eeff8db6cc7e2954c40d8ce25a36f9f1aa2 Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 17:59:31 -0600 Subject: [PATCH 07/37] add actuator mixer test --- tests/preprocess/test_dynamic_mixer.py | 109 +++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/tests/preprocess/test_dynamic_mixer.py b/tests/preprocess/test_dynamic_mixer.py index 335f7f81..4e105c15 100644 --- a/tests/preprocess/test_dynamic_mixer.py +++ b/tests/preprocess/test_dynamic_mixer.py @@ -4,6 +4,8 @@ import numpy as np +from bird.meshing.block_rect_mesh import from_block_rect_to_seg +from bird.preprocess.dynamic_mixer.mixer import ActuatorMixer from bird.preprocess.dynamic_mixer.mixing_fvModels import * from bird.utilities.parser import parse_json @@ -54,3 +56,110 @@ def test_loop_list(): # Output to temporary directory and delete when done with tempfile.TemporaryDirectory() as tmpdirname: write_fvModel(input_dict, output_folder=tmpdirname, force_sign=True) + + +def test_ActuatorMixer(): + geom = { + "OverallDomain": { + a: {"size_per_block": 1.0, "rescale": 2.76} + for a in ("x", "y", "z") + }, + "Fluids": [[[0, 0, 0], [9, 0, 0]]], + } + g = from_block_rect_to_seg(geom) + + # loop mode: radius is a fraction of the branch cross-section + m = ActuatorMixer() + m.update_from_loop_dict( + { + "branch_id": 0, + "frac_space": 0.4, + "radius": 0.4, + "sign": "+", + "swirl_sign": "+", + "Vtip": 1.5, + "Np": 6, + "sigma": 0.35, + "start_time": 3, + }, + g, + ) + assert m.ready + assert m.normal_dir == 0 + assert abs(m.R - 0.4 * 2.76) < 1e-9 # frac * mean transverse block size + assert (m.Vtip, m.Np, m.sigma) == (1.5, 6, 0.35) + assert m.sign == "+" and m.swirl_sign == "+" + # position = segment start + frac*conn, block size = 2.76 + assert abs(m.x - (0.5 * 2.76 + 0.4 * 9 * 2.76)) < 1e-6 + assert abs(m.y - 0.5 * 2.76) < 1e-6 + + # explicit mode: radius is absolute + m2 = ActuatorMixer() + m2.update_from_expl_dict( + { + "x": 0.1, + "y": 0.2, + "z": 0.3, + "normal_dir": 1, + "radius": 0.05, + "sign": "-", + } + ) + assert m2.ready and abs(m2.R - 0.05) < 1e-12 and m2.normal_dir == 1 + + # missing sign leaves the mixer not ready + m3 = ActuatorMixer() + m3.update_from_expl_dict( + {"x": 0.1, "y": 0.2, "z": 0.3, "normal_dir": 1, "radius": 0.05} + ) + assert not m3.ready + + +def test_write_fvModel_ball(): + base = { + "Meshing": {"Blockwise": {"x": 10, "y": 10, "z": 10}}, + "Geometry": { + "OverallDomain": { + a: {"nblocks": 10, "size_per_block": 1.0, "rescale": 2.76} + for a in ("x", "y", "z") + }, + "Fluids": [[[0, 0, 0], [9, 0, 0]]], + }, + "volumetric_source": "ball", + "mixers": [ + { + "branch_id": 0, + "frac_space": 0.5, + "radius": 0.4, + "sign": "+", + "swirl_sign": "+", + "Vtip": 1.5, + "Np": 6, + "sigma": 0.35, + "power": 3000, + "start_time": 1, + } + ], + } + + # swirl endpoint: from_Np_Vtip + axial_and_swirl + d = dict(base, power="from_Np_Vtip", momentum_source="axial_and_swirl") + with tempfile.TemporaryDirectory() as tmpdirname: + write_fvModel(d, output_folder=tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + assert "// ===== ball mixer =====" in txt + assert "dynamicMix_util" not in txt # Newton solve is inlined + assert "16.0*6" in txt and "pow(pi,4.0)" in txt # Np/Vtip drive + assert "Qsw" in txt # swirl torque present + # exact conservation: runtime-summed normalisers + assert "reduce(Sax, sumOp());" in txt + assert "reduce(Sth, sumOp());" in txt + assert "Tax/Sax" in txt and "Qsw/Sth" in txt + + # axial + from_P endpoint + d = dict(base, power="from_P", momentum_source="axial") + with tempfile.TemporaryDirectory() as tmpdirname: + write_fvModel(d, output_folder=tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + assert "4.0*3000/(rhoM*area)" in txt # P drive + assert "Qsw" not in txt and "Sth" not in txt # no swirl From 19eed37c1c33d2261a9bb86dc18e5af962cc74e8 Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 18:53:32 -0600 Subject: [PATCH 08/37] actuator-disk power helper and mixer-model keys for case generation --- bird/preprocess/dynamic_mixer/mixer.py | 15 +++++++++++++++ bird/preprocess/json_gen/design_io.py | 11 ++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/bird/preprocess/dynamic_mixer/mixer.py b/bird/preprocess/dynamic_mixer/mixer.py index 2a4ff555..fb4cec3a 100644 --- a/bird/preprocess/dynamic_mixer/mixer.py +++ b/bird/preprocess/dynamic_mixer/mixer.py @@ -1,6 +1,21 @@ +import math + from bird import logger +def actuator_disk_power( + Np: float, Vtip: float, R: float, rho: float = 1000.0 +) -> float: + """Power [W] drawn by a ``from_Np_Vtip`` actuator-disk mixer. + + ``P = Np * rho * Vtip**3 * D**2 / pi**3`` with ``D = 2R`` (see the ball + mixer derivation). Used at case-generation time to record the derived + power for the efficiency QoI. + """ + D = 2.0 * R + return Np * rho * Vtip**3 * D**2 / math.pi**3 + + class Mixer: def __init__(self): self.x = None diff --git a/bird/preprocess/json_gen/design_io.py b/bird/preprocess/json_gen/design_io.py index aaaa7a47..7846bbff 100644 --- a/bird/preprocess/json_gen/design_io.py +++ b/bird/preprocess/json_gen/design_io.py @@ -10,8 +10,17 @@ def generate_stl_patch(filename, bc_dict, geom_dict): json.dump(final_dict, f, indent=2) -def generate_dynamic_mixer(filename, mixers_list, geom_dict): +def generate_dynamic_mixer(filename, mixers_list, geom_dict, model=None): + """Write a mixers.json. + + :param model: optional dict of top-level model selectors written verbatim + (e.g. ``{"volumetric_source": "ball", "power": "from_Np_Vtip", + "momentum_source": "axial_and_swirl"}``). ``None`` keeps the legacy + pancake output. + """ final_dict = {} + if model is not None: + final_dict.update(model) final_dict["Meshing"] = geom_dict["Meshing"] final_dict["Geometry"] = geom_dict["Geometry"] final_dict["mixers"] = mixers_list From 699bb0a3e2734bf84cafaa18ea22a4953bab3b3f Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 19:00:19 -0600 Subject: [PATCH 09/37] improve design sweep gen --- bird/preprocess/json_gen/generate_designs.py | 254 ++++++++++++++++++- 1 file changed, 253 insertions(+), 1 deletion(-) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index 862de36a..1bb011a3 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -5,7 +5,12 @@ import numpy as np -from bird import BIRD_CASE_DIR, logger +from bird import BIRD_CASE_DIR, BIRD_DIR, logger +from bird.meshing.block_rect_mesh import from_block_rect_to_seg +from bird.preprocess.dynamic_mixer.mixer import ( + ActuatorMixer, + actuator_disk_power, +) from bird.preprocess.json_gen.design_io import * @@ -581,3 +586,250 @@ def generate_single_scaledup_reactor_sparger_cases( case_folder=os.path.join(study_folder, sim_folder), constantD=constantD, ) + + +def overwrite_scale(case_folder, scale): + """Rewrite the ``transformPoints`` scale in presteps.sh to `scale`.""" + filename = os.path.join(case_folder, "presteps.sh") + with open(filename, "r+") as f: + lines = f.readlines() + with open(filename, "w+") as f: + for line in lines: + if line.strip().startswith("transformPoints"): + f.write(f'transformPoints "scale=({scale} {scale} {scale})"\n') + else: + f.write(line) + + +def overwrite_ncores(case_folder, n): + """Rewrite ``numberOfSubdomains`` in system/decomposeParDict to `n`.""" + filename = os.path.join(case_folder, "system", "decomposeParDict") + with open(filename, "r+") as f: + lines = f.readlines() + with open(filename, "w+") as f: + for line in lines: + if line.strip().startswith("numberOfSubdomains"): + f.write(f"numberOfSubdomains {n};\n") + else: + f.write(line) + + +def write_script_single( + case_folder, + account="gas2fuels", + cores=4, + solver="birdmultiphaseEulerFoam", +): + """Write a per-case SLURM script (``script_single``) running one case.""" + ofbashrc = ( + "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" + ) + with open(os.path.join(case_folder, "script_single"), "w+") as f: + f.write("#!/bin/bash\n") + f.write("#SBATCH --qos=high\n") + f.write("#SBATCH --job-name=lev_single\n") + f.write("#SBATCH --nodes=1\n") + f.write(f"#SBATCH --ntasks-per-node={cores}\n") + f.write("#SBATCH --time=07:59:00\n") + f.write(f"#SBATCH --account={account}\n\n") + f.write("bash presteps.sh\n") + f.write(f"source {ofbashrc}\n") + f.write("decomposePar -fileHandler collated\n") + f.write(f"srun -n {cores} {solver} -parallel -fileHandler collated\n") + f.write("reconstructPar -newTimes\n") + + +def write_pack_scripts( + study_folder, + sim_ids, + sims_per_node=26, + cores_per_sim=4, + account="gas2fuels", + solver="birdmultiphaseEulerFoam", +): + """Write node-packing scripts (Option A): pack_XXX bundles + submit_all.sh. + + Each bundle runs up to `sims_per_node` cases concurrently on one node, each + via ``srun --exclusive -n cores_per_sim`` (so `sims_per_node*cores_per_sim` + cores are used per node). + """ + ofbashrc = ( + "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" + ) + bundles = [ + sim_ids[i : i + sims_per_node] + for i in range(0, len(sim_ids), sims_per_node) + ] + pack_names = [] + for b, bundle in enumerate(bundles): + pack_name = f"pack_{b:03}" + pack_names.append(pack_name) + with open(os.path.join(study_folder, pack_name), "w+") as f: + f.write("#!/bin/bash\n") + f.write("#SBATCH --qos=high\n") + f.write(f"#SBATCH --job-name=lev_{pack_name}\n") + f.write("#SBATCH --nodes=1\n") + f.write("#SBATCH --exclusive\n") + f.write("#SBATCH --time=07:59:00\n") + f.write(f"#SBATCH --account={account}\n\n") + f.write(f"source {ofbashrc}\n\n") + f.write("run_sim () {\n") + f.write('\tcd "$1"\n') + f.write("\tbash presteps.sh > log.presteps 2>&1\n") + f.write( + "\tdecomposePar -fileHandler collated > log.decompose 2>&1\n" + ) + f.write( + f"\tsrun --exclusive -n {cores_per_sim} {solver} -parallel" + " -fileHandler collated > log.solve 2>&1\n" + ) + f.write("\treconstructPar -newTimes > log.reconstruct 2>&1\n") + f.write("\tcd ..\n") + f.write("}\n\n") + for sim_id in bundle: + f.write(f"run_sim {id2simfolder(sim_id)} &\n") + f.write("wait\n") + with open(os.path.join(study_folder, "submit_all.sh"), "w+") as f: + for pack_name in pack_names: + f.write(f"sbatch {pack_name}\n") + + +def generate_leveled_reactor_cases( + config_dict, + branchcom_spots, + scale, + n_sim, + study_folder, + mixer_params, + vvm=0.4, + constantD=True, + start_time=3, + template_folder="loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", + account="gas2fuels", + cores_per_sim=4, + sims_per_node=26, +): + """Generate one scale level of the actuator-disk (ball) design sweep. + + One template drives every level; the level `scale` is applied both to the + mixers.json rescale (mixer positions) and to presteps.sh transformPoints. + Uses the first `n_sim` designs of `config_dict`, so ``Sim_i`` is the same + design at every level. `mixer_params` holds Np/Vtip/sigma/radius/swirl_sign. + """ + if not os.path.isabs(template_folder): + template_folder = os.path.join( + BIRD_DIR, "preprocess", "data_case_gen", template_folder + ) + geom_dict = make_default_geom_dict_from_file( + os.path.join(template_folder, "system", "mesh.json") + ) + # mesh.json has no rescale; force the level scale (matched by transformPoints) + for a in ("x", "y", "z"): + geom_dict["Geometry"]["OverallDomain"][a]["rescale"] = scale + seg_geom = from_block_rect_to_seg(geom_dict["Geometry"]) + model = { + "volumetric_source": "ball", + "power": "from_Np_Vtip", + "momentum_source": "axial_and_swirl", + } + + try: + shutil.rmtree(study_folder) + except FileNotFoundError: + pass + Path(study_folder).mkdir(parents=True, exist_ok=True) + + sim_ids = sorted(config_dict)[:n_sim] + for sim_id in sim_ids: + sim_folder = id2simfolder(sim_id) + case = os.path.join(study_folder, sim_folder) + shutil.copytree(template_folder, case) + + bc_dict = {"inlets": [], "outlets": []} + for br in (6, 4): + bc_dict["outlets"].append( + { + "branch_id": br, + "type": "circle", + "frac_space": 1, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "top", + } + ) + for branch in (0, 1, 2): + for iind in np.argwhere(config_dict[sim_id][branch] == 1)[:, 0]: + 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(case, "system", "inlets_outlets.json"), + bc_dict, + geom_dict, + ) + + mix_list = [] + for branch in (0, 1, 2): + for iind in np.argwhere(config_dict[sim_id][branch] == 0)[:, 0]: + sign = "+" if branch == 0 else "-" + frac = branchcom_spots[branch][iind] + # derive this mixer's power from Np/Vtip and its (scaled) radius + probe = ActuatorMixer() + probe.update_from_loop_dict( + { + "branch_id": branch, + "frac_space": frac, + "radius": mixer_params["radius"], + }, + seg_geom, + ) + power = actuator_disk_power( + mixer_params["Np"], mixer_params["Vtip"], probe.R + ) + mix_list.append( + { + "branch_id": branch, + "frac_space": float(frac), + "start_time": start_time, + "sign": sign, + "swirl_sign": mixer_params["swirl_sign"], + "radius": mixer_params["radius"], + "Vtip": mixer_params["Vtip"], + "Np": mixer_params["Np"], + "sigma": mixer_params["sigma"], + "power": power, + } + ) + generate_dynamic_mixer( + os.path.join(case, "system", "mixers.json"), + mix_list, + geom_dict, + model=model, + ) + overwrite_vvm(case_folder=case, vvm=vvm) + overwrite_scale(case_folder=case, scale=scale) + overwrite_ncores(case_folder=case, n=cores_per_sim) + overwrite_bubble_size_model(case_folder=case, constantD=constantD) + write_script_single(case, account=account, cores=cores_per_sim) + + write_pack_scripts( + study_folder, + sim_ids, + sims_per_node=sims_per_node, + cores_per_sim=cores_per_sim, + account=account, + ) + write_prep(os.path.join(study_folder, "prep.sh"), n_sim) + save_config_dict(os.path.join(study_folder, "configs.pkl"), config_dict) + save_config_dict( + os.path.join(study_folder, "branchcom_spots.pkl"), branchcom_spots + ) From a5f9e89aed8e8e74b6a755a3699ed40518bb27bd Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 29 Jul 2026 19:17:30 -0600 Subject: [PATCH 10/37] adjust path --- .../presteps.sh | 3 ++- bird/preprocess/json_gen/generate_designs.py | 8 ++------ 2 files changed, 4 insertions(+), 7 deletions(-) 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 index 77fa6d6f..a3c05e46 100644 --- 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 @@ -1,6 +1,7 @@ # Clean case module load conda -conda activate /projects/gas2fuels/conda_env/bird_wf +#conda activate /projects/gas2fuels/conda_env/bird_wf +conda activate /projects/gas2fuels/conda_env/bird_mixer source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc ./Allclean diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index 1bb011a3..df100b18 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -621,9 +621,7 @@ def write_script_single( solver="birdmultiphaseEulerFoam", ): """Write a per-case SLURM script (``script_single``) running one case.""" - ofbashrc = ( - "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" - ) + ofbashrc = "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" with open(os.path.join(case_folder, "script_single"), "w+") as f: f.write("#!/bin/bash\n") f.write("#SBATCH --qos=high\n") @@ -653,9 +651,7 @@ def write_pack_scripts( via ``srun --exclusive -n cores_per_sim`` (so `sims_per_node*cores_per_sim` cores are used per node). """ - ofbashrc = ( - "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" - ) + ofbashrc = "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" bundles = [ sim_ids[i : i + sims_per_node] for i in range(0, len(sim_ids), sims_per_node) From 0963bc173cb927c51bb84eb65e847db44c1fa907 Mon Sep 17 00:00:00 2001 From: Malik Date: Thu, 30 Jul 2026 09:52:36 -0600 Subject: [PATCH 11/37] remove te default qos high option --- bird/preprocess/json_gen/generate_designs.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index df100b18..a73720a8 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -624,7 +624,6 @@ def write_script_single( ofbashrc = "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" with open(os.path.join(case_folder, "script_single"), "w+") as f: f.write("#!/bin/bash\n") - f.write("#SBATCH --qos=high\n") f.write("#SBATCH --job-name=lev_single\n") f.write("#SBATCH --nodes=1\n") f.write(f"#SBATCH --ntasks-per-node={cores}\n") @@ -637,6 +636,18 @@ def write_script_single( f.write("reconstructPar -newTimes\n") +def write_script_post_single(case_folder, account="gas2fuels"): + """Write a per-case post-processing SLURM script (``script_post_single``).""" + with open(os.path.join(case_folder, "script_post_single"), "w+") as f: + f.write("#!/bin/bash\n") + f.write("#SBATCH --job-name=lev_post\n") + f.write("#SBATCH --nodes=1\n") + f.write("#SBATCH --ntasks-per-node=1\n") + f.write("#SBATCH --time=00:59:00\n") + f.write(f"#SBATCH --account={account}\n\n") + f.write("bash computeQOI.sh\n") + + def write_pack_scripts( study_folder, sim_ids, @@ -662,7 +673,6 @@ def write_pack_scripts( pack_names.append(pack_name) with open(os.path.join(study_folder, pack_name), "w+") as f: f.write("#!/bin/bash\n") - f.write("#SBATCH --qos=high\n") f.write(f"#SBATCH --job-name=lev_{pack_name}\n") f.write("#SBATCH --nodes=1\n") f.write("#SBATCH --exclusive\n") @@ -816,6 +826,7 @@ def generate_leveled_reactor_cases( overwrite_ncores(case_folder=case, n=cores_per_sim) overwrite_bubble_size_model(case_folder=case, constantD=constantD) write_script_single(case, account=account, cores=cores_per_sim) + write_script_post_single(case, account=account) write_pack_scripts( study_folder, From 6f47fe0c98e9580fa85b11ce1ff2d04e424f4a16 Mon Sep 17 00:00:00 2001 From: Malik Date: Thu, 30 Jul 2026 09:55:23 -0600 Subject: [PATCH 12/37] fix _readOFScal NameError, we have a public functions for that --- .../writeGlobalVars.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 index 2b42b8e8..e64d69e3 100644 --- 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 @@ -31,11 +31,8 @@ def readInletArea(): def getLiqVol(): - cell_centers, _ = read_cell_centers(".") volume_field, _ = read_cell_volumes(".") - alpha_field = _readOFScal( - os.path.join("0", "alpha.liquid"), len(cell_centers) - )["field"] + alpha_field, _ = read_field(".", "0", field_name="alpha.liquid") return np.sum(volume_field * alpha_field) From b096c07ff4ca7427244e2c953cebfd72b66d4d28 Mon Sep 17 00:00:00 2001 From: Malik Date: Fri, 31 Jul 2026 08:20:33 -0600 Subject: [PATCH 13/37] update template file with the right qoi --- .../computeQOI.sh | 4 +- .../get_qoi.py | 31 ++++-- bird/preprocess/json_gen/generate_designs.py | 103 +++++++++++++++++- 3 files changed, 121 insertions(+), 17 deletions(-) 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 index 3756ed7f..800e45d4 100644 --- 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 @@ -2,8 +2,8 @@ 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 + module load conda + conda activate /projects/gas2fuels/conda_env/bird_mixer/ python read_history.py -cr .. -cn local -df data python get_qoi.py conda deactivate 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 index 83932c5b..aa9b4a0a 100644 --- 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 @@ -89,7 +89,7 @@ def get_lh(verb=False): def get_pinj(vvm, Vl, As, lh): - rhog = 1.25 # kg /m3 + rhog = 0.593333 # kg /m3 Vg = Vl * vvm / (60 * As * 1) # m/s Ptank = 101325 # Pa # Ptank = 0 # Pa @@ -116,21 +116,30 @@ def get_qoi(kla_co2, cs_co2, kla_h2, cs_h2, verb=False): 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 + qoi_kla_co2 = kla_co2 * cs_co2 * V_l * 0.04401 + qoi_kla_h2 = kla_h2 * cs_h2 * V_l * 0.002016 + + qoi_co2 = qoi_kla_co2 / (P_mix / 3600 + P_inj / 3600) + qoi_h2 = qoi_kla_h2 / (P_mix / 3600 + P_inj / 3600) + return qoi_co2 * qoi_h2, qoi_kla_co2 * qoi_kla_h2 def get_qoi_uq(kla_co2, cs_co2, kla_h2, cs_h2): qoi = [] + qoi_kla = [] 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_tmp, qoi_kla_tmp = get_qoi( + kla_co2[i], cs_co2[i], kla_h2[i], cs_h2[i], verb + ) + qoi.append(qoi_tmp) + qoi_kla.append(qoi_kla_tmp) qoi = np.array(qoi) - return np.mean(qoi), np.std(qoi) + qoi_kla = np.array(qoi_kla) + return np.mean(qoi), np.std(qoi), np.mean(qoi_kla), np.std(qoi_kla) os.makedirs("Figures", exist_ok=True) @@ -139,8 +148,8 @@ def get_qoi_uq(kla_co2, cs_co2, kla_h2, cs_h2): 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) +mean_cstar_co2 = np.random.uniform(7.68, 8.22, nuq) +mean_cstar_h2 = np.random.uniform(0.516, 0.548, nuq) tmp_cs_h2 = [] @@ -176,8 +185,12 @@ def get_qoi_uq(kla_co2, cs_co2, kla_h2, cs_h2): 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) +qoi_m, qoi_s, qoi_kla_m, qoi_kla_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") +with open("qoi_kla.txt", "w+") as f: + f.write(f"{qoi_kla_m},{qoi_kla_s}\n") diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index a73720a8..57cbb5f3 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -601,6 +601,66 @@ def overwrite_scale(case_folder, scale): f.write(line) +# setFields liquid-init box upper corner in UNSCALED (per-block) units: y is the +# 4-block liquid fill height; x/z are made wide to cover the whole domain. +_SETFIELDS_BOX_UPPER = (200.0, 4.0, 200.0) + + +def overwrite_setfields_box(case_folder, scale): + """Rewrite the setFields liquid-init box to `scale` * ``_SETFIELDS_BOX_UPPER``. + + setFields runs after transformPoints, so the box lives in scaled + coordinates; the lower corner stays ``(-1 -1 -1)``. + """ + bx, by, bz = (v * scale for v in _SETFIELDS_BOX_UPPER) + filename = os.path.join(case_folder, "system", "setFieldsDict") + with open(filename, "r+") as f: + lines = f.readlines() + with open(filename, "w+") as f: + for line in lines: + if line.strip().startswith("box "): + indent = line[: len(line) - len(line.lstrip())] + f.write(f"{indent}box (-1.0 -1.0 -1.0) ({bx} {by} {bz});\n") + else: + f.write(line) + + +def overwrite_qoi_params(case_folder, rhog, cstar_co2, cstar_h2): + """Rewrite the per-level QoI parameters in get_qoi.py. + + These are hardcoded per dimension in get_qoi.py, so each level needs its + own values (the mixer power is NOT touched here -- get_qoi.py reads it from + mixers.json). + + :param rhog: gas density [kg/m3] used in the injection-power estimate. + :param cstar_co2: (low, high) uniform-prior bounds for the CO2 c*. + :param cstar_h2: (low, high) uniform-prior bounds for the H2 c*. + """ + co2_lo, co2_hi = cstar_co2 + h2_lo, h2_hi = cstar_h2 + filename = os.path.join(case_folder, "get_qoi.py") + with open(filename, "r+") as f: + lines = f.readlines() + with open(filename, "w+") as f: + for line in lines: + stripped = line.strip() + if stripped.startswith("rhog ="): + indent = line[: len(line) - len(line.lstrip())] + f.write(f"{indent}rhog = {rhog} # kg /m3\n") + elif stripped.startswith("mean_cstar_co2 ="): + f.write( + "mean_cstar_co2 = " + f"np.random.uniform({co2_lo}, {co2_hi}, nuq)\n" + ) + elif stripped.startswith("mean_cstar_h2 ="): + f.write( + "mean_cstar_h2 = " + f"np.random.uniform({h2_lo}, {h2_hi}, nuq)\n" + ) + else: + f.write(line) + + def overwrite_ncores(case_folder, n): """Rewrite ``numberOfSubdomains`` in system/decomposeParDict to `n`.""" filename = os.path.join(case_folder, "system", "decomposeParDict") @@ -648,6 +708,11 @@ def write_script_post_single(case_folder, account="gas2fuels"): f.write("bash computeQOI.sh\n") +def write_foam_stub(case_folder: str) -> None: + """Create an empty ``test.foam`` so ParaView can open the case.""" + open(os.path.join(case_folder, "test.foam"), "w").close() + + def write_pack_scripts( study_folder, sim_ids, @@ -710,17 +775,30 @@ def generate_leveled_reactor_cases( vvm=0.4, constantD=True, start_time=3, + rhog=None, + cstar_co2=None, + cstar_h2=None, template_folder="loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", account="gas2fuels", - cores_per_sim=4, - sims_per_node=26, + cores_per_sim=16, + cores_per_node=128, ): """Generate one scale level of the actuator-disk (ball) design sweep. One template drives every level; the level `scale` is applied both to the mixers.json rescale (mixer positions) and to presteps.sh transformPoints. Uses the first `n_sim` designs of `config_dict`, so ``Sim_i`` is the same - design at every level. `mixer_params` holds Np/Vtip/sigma/radius/swirl_sign. + design at every level. `mixer_params` holds Np/Vtip/sigma/radius and the + per-branch `sign` and `swirl_sign` (each a dict keyed by branch_id), which + are written verbatim into each mixer entry of mixers.json. + + `rhog`, `cstar_co2` and `cstar_h2` are the per-level QoI parameters; when + given they are written into each case's get_qoi.py (see + :func:`overwrite_qoi_params`). Left as ``None`` the template get_qoi.py is + used unchanged. + + Each sim runs on `cores_per_sim` cores; the node-packing bundles fit + ``cores_per_node // cores_per_sim`` sims per node. """ if not os.path.isabs(template_folder): template_folder = os.path.join( @@ -786,7 +864,9 @@ def generate_leveled_reactor_cases( mix_list = [] for branch in (0, 1, 2): for iind in np.argwhere(config_dict[sim_id][branch] == 0)[:, 0]: - sign = "+" if branch == 0 else "-" + # sign / swirl_sign are per-branch (keyed by branch_id) so the + # axial push and swirl form one coherent loop circulation; the + # values live in mixer_params and are written to mixers.json. frac = branchcom_spots[branch][iind] # derive this mixer's power from Np/Vtip and its (scaled) radius probe = ActuatorMixer() @@ -806,8 +886,8 @@ def generate_leveled_reactor_cases( "branch_id": branch, "frac_space": float(frac), "start_time": start_time, - "sign": sign, - "swirl_sign": mixer_params["swirl_sign"], + "sign": mixer_params["sign"][branch], + "swirl_sign": mixer_params["swirl_sign"][branch], "radius": mixer_params["radius"], "Vtip": mixer_params["Vtip"], "Np": mixer_params["Np"], @@ -823,11 +903,22 @@ def generate_leveled_reactor_cases( ) overwrite_vvm(case_folder=case, vvm=vvm) overwrite_scale(case_folder=case, scale=scale) + overwrite_setfields_box(case_folder=case, scale=scale) + if rhog is not None and cstar_co2 is not None and cstar_h2 is not None: + overwrite_qoi_params( + case_folder=case, + rhog=rhog, + cstar_co2=cstar_co2, + cstar_h2=cstar_h2, + ) overwrite_ncores(case_folder=case, n=cores_per_sim) overwrite_bubble_size_model(case_folder=case, constantD=constantD) write_script_single(case, account=account, cores=cores_per_sim) write_script_post_single(case, account=account) + write_foam_stub(case) + # pack as many sims per node as the requested cores allow + sims_per_node = max(1, cores_per_node // cores_per_sim) write_pack_scripts( study_folder, sim_ids, From a15f682353f8831c0d79fd704069656ac0e44ce5 Mon Sep 17 00:00:00 2001 From: Malik Date: Mon, 3 Aug 2026 15:29:29 -0600 Subject: [PATCH 14/37] add static mixer class --- bird/preprocess/dynamic_mixer/mixer.py | 103 +++++++++++++++++++++++++ tests/preprocess/test_static_mixer.py | 63 +++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 tests/preprocess/test_static_mixer.py diff --git a/bird/preprocess/dynamic_mixer/mixer.py b/bird/preprocess/dynamic_mixer/mixer.py index fb4cec3a..92c99dd5 100644 --- a/bird/preprocess/dynamic_mixer/mixer.py +++ b/bird/preprocess/dynamic_mixer/mixer.py @@ -217,3 +217,106 @@ def check_status(self, blocks=None) -> None: if blocks is not None: logger.info(f"\tbranch = {blocks}") self.ready = True + + +class StaticMixer: + """Passive (unpowered) momentum-source mixer used by the ``ball`` source. + + Unlike :class:`ActuatorMixer`, no power is injected: the swirl is imposed as + an energy-neutral axial-to-azimuthal redirection and the axial drag is a pure + loss. The drive is a swirl number ``S`` and a loss coefficient ``K`` rather + than ``Np``/``Vtip``; ``sign`` is the mixer orientation (the source is + inactive when the inflow opposes it) and ``swirl_sign`` the rotation sense. + """ + + def __init__(self): + self.x = None + self.y = None + self.z = None + self.normal_dir = None + self.R = None # physical mixer radius [m] + self.S = 0.35 # swirl number [-] + self.K = 0.5 # loss coefficient [-] + self.sign = None # mixer orientation, "+" / "-" + self.swirl_sign = "+" # rotation sense, "+" / "-" + self.start_time = 1.0 + self.ready = False + + def _read_common(self, mixer_dict: dict) -> None: + """Read the per-mixer keys.""" + if "S" in mixer_dict: + self.S = mixer_dict["S"] + if "K" in mixer_dict: + self.K = mixer_dict["K"] + if "sign" in mixer_dict: + self.sign = mixer_dict["sign"] + if "swirl_sign" in mixer_dict: + self.swirl_sign = mixer_dict["swirl_sign"] + if "start_time" in mixer_dict: + self.start_time = mixer_dict["start_time"] + + def update_from_expl_dict(self, mixer_dict: dict) -> None: + """Populate from an explicit mixer dict (absolute position and radius).""" + if "x" in mixer_dict: + self.x = mixer_dict["x"] + if "y" in mixer_dict: + self.y = mixer_dict["y"] + if "z" in mixer_dict: + self.z = mixer_dict["z"] + if "normal_dir" in mixer_dict: + self.normal_dir = mixer_dict["normal_dir"] + if "radius" in mixer_dict: + # explicit mode: radius is absolute [m] + self.R = mixer_dict["radius"] + self._read_common(mixer_dict) + self.check_status() + + def update_from_loop_dict(self, mixer_dict: dict, geom_dict: dict) -> None: + """Populate from a loop mixer dict. + + :param mixer_dict: mixer entry with ``branch_id``, ``frac_space`` and, + optionally, ``radius`` as a fraction of the branch cross-section + (``0.5`` spans the whole tube). + :param geom_dict: output of ``from_block_rect_to_seg`` (``segments`` and + ``blocksize``). + """ + segment = geom_dict["segments"][mixer_dict["branch_id"]] + pos = segment["start"] + mixer_dict["frac_space"] * segment["conn"] + self.x = float(pos[0]) + self.y = float(pos[1]) + self.z = float(pos[2]) + self.normal_dir = segment["normal_dir"] + # radius is a fraction of the branch cross-section (as for the actuator + # mixer): R = frac * mean of the two block sizes transverse to the axis. + bx, by, bz = geom_dict["blocksize"] + transverse = {0: (by, bz), 1: (bx, bz), 2: (bx, by)}[self.normal_dir] + frac = mixer_dict.get("radius", 0.4) + self.R = frac * 0.5 * (transverse[0] + transverse[1]) + self._read_common(mixer_dict) + self.check_status(blocks=segment["blocks"]) + + def check_status(self, blocks=None) -> None: + """Log the resolved mixer and set ``ready`` if all fields are present.""" + if ( + self.x is None + or self.y is None + or self.z is None + or self.normal_dir is None + or self.R is None + or self.sign not in ("+", "-") + or self.swirl_sign not in ("+", "-") + ): + self.ready = False + else: + logger.info( + f"\n\tpos({self.x:.2g}, {self.y:.2g}, {self.z:.2g})" + + f"\n\tnormal_dir {self.normal_dir}" + + f"\n\tR {self.R:.2g}" + + f"\n\tS {self.S:.2g}" + + f"\n\tK {self.K:.2g}" + + f"\n\tsign {self.sign} swirl_sign {self.swirl_sign}" + + f"\n\tstart_time {self.start_time:.2g}" + ) + if blocks is not None: + logger.info(f"\tbranch = {blocks}") + self.ready = True diff --git a/tests/preprocess/test_static_mixer.py b/tests/preprocess/test_static_mixer.py new file mode 100644 index 00000000..18fb37ab --- /dev/null +++ b/tests/preprocess/test_static_mixer.py @@ -0,0 +1,63 @@ +from bird.meshing.block_rect_mesh import from_block_rect_to_seg +from bird.preprocess.dynamic_mixer.mixer import StaticMixer + + +def test_StaticMixer(): + geom = { + "OverallDomain": { + a: {"size_per_block": 1.0, "rescale": 2.76} + for a in ("x", "y", "z") + }, + "Fluids": [[[0, 0, 0], [9, 0, 0]]], + } + g = from_block_rect_to_seg(geom) + + # loop mode: radius is a fraction of the branch cross-section + # (0.5 spans the whole tube) + m = StaticMixer() + m.update_from_loop_dict( + { + "branch_id": 0, + "frac_space": 0.4, + "radius": 0.5, + "sign": "+", + "swirl_sign": "+", + "S": 0.35, + "K": 0.5, + "start_time": 3, + }, + g, + ) + assert m.ready + assert m.normal_dir == 0 + assert abs(m.R - 0.5 * 2.76) < 1e-9 # frac * mean transverse block size + assert (m.S, m.K) == (0.35, 0.5) + assert m.sign == "+" and m.swirl_sign == "+" + assert m.start_time == 3 + # position = segment start + frac*conn, block size = 2.76 + assert abs(m.x - (0.5 * 2.76 + 0.4 * 9 * 2.76)) < 1e-6 + assert abs(m.y - 0.5 * 2.76) < 1e-6 + + # explicit mode: radius is absolute, defaults for S/K + m2 = StaticMixer() + m2.update_from_expl_dict( + { + "x": 0.1, + "y": 0.2, + "z": 0.3, + "normal_dir": 1, + "radius": 0.05, + "sign": "-", + "swirl_sign": "-", + } + ) + assert m2.ready and abs(m2.R - 0.05) < 1e-12 and m2.normal_dir == 1 + assert (m2.S, m2.K) == (0.35, 0.5) # defaults + assert m2.sign == "-" and m2.swirl_sign == "-" + + # missing sign leaves the mixer not ready + m3 = StaticMixer() + m3.update_from_expl_dict( + {"x": 0.1, "y": 0.2, "z": 0.3, "normal_dir": 1, "radius": 0.05} + ) + assert not m3.ready From fbdd061546c7145297fb24742a7ad01a9261dfca Mon Sep 17 00:00:00 2001 From: Malik Date: Mon, 3 Aug 2026 15:36:30 -0600 Subject: [PATCH 15/37] add write_static_mixer_ball to the static mixer class --- bird/preprocess/dynamic_mixer/io_fvModels.py | 139 +++++++++++++++++++ tests/preprocess/test_static_mixer.py | 55 ++++++++ 2 files changed, 194 insertions(+) diff --git a/bird/preprocess/dynamic_mixer/io_fvModels.py b/bird/preprocess/dynamic_mixer/io_fvModels.py index e2077c17..29f56be3 100644 --- a/bird/preprocess/dynamic_mixer/io_fvModels.py +++ b/bird/preprocess/dynamic_mixer/io_fvModels.py @@ -534,6 +534,145 @@ def write_mixer_ball( f.write("\t\t}\n") +def write_static_mixer_ball(mixer, output_folder): + """Append one passive ``ball`` static-mixer block to ``fvModels``. + + The swirl is imposed as an azimuthal body force proportional to the local + axial dynamic pressure (Kiesewetter), balanced cell-by-cell by an + energy-neutral axial reaction; a lumped axial drag adds the viscous loss. + The source is inactive when the inflow opposes the mixer orientation. + + :param mixer: a ready + :class:`~bird.preprocess.dynamic_mixer.mixer.StaticMixer`. + """ + nd = int(mixer.normal_dir) + dn = ["dx", "dy", "dz"][nd] + # theta_hat = n_hat x r_hat, per axis: (component index, numerator expr) + tan = { + 0: [(1, "-dz"), (2, "dy")], + 1: [(0, "dz"), (2, "-dx")], + 2: [(0, "-dy"), (1, "dx")], + }[nd] + push_ax = "1.0" if mixer.sign == "+" else "-1.0" + push_th = "1.0" if mixer.swirl_sign == "+" else "-1.0" + + with open(os.path.join(output_folder, "fvModels"), "a+") as f: + f.write("\t\t// ===== static mixer =====\n") + f.write("\t\t{\n") + f.write(f"\t\t\tconst double Rmix = {mixer.R};\n") + f.write("\t\t\tconst double area = pi*Rmix*Rmix;\n") + f.write(f"\t\t\tconst double Snum = {mixer.S};\n") + f.write(f"\t\t\tconst double Kloss = {mixer.K};\n") + f.write(f"\t\t\tconst double startT = {mixer.start_time};\n") + f.write( + f"\t\t\tconst double px = {mixer.x}, py = {mixer.y}, pz = {mixer.z};\n" + ) + f.write("\t\t\tif (time.value() > startT)\n") + f.write("\t\t\t{\n") + # --- sense V1 and rho over the upstream half-ball --- + # (duplicated from write_mixer_ball; the dynamic path is kept untouched) + f.write("\t\t\t\tscalar sV = 0.0, sVU = 0.0, sVrho = 0.0;\n") + f.write("\t\t\t\tforAll(C, i)\n") + f.write("\t\t\t\t{\n") + f.write( + "\t\t\t\t\tconst double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz;\n" + ) + f.write("\t\t\t\t\tconst double d2 = dx*dx + dy*dy + dz*dz;\n") + f.write(f"\t\t\t\t\tif (d2 <= Rmix*Rmix && {push_ax}*{dn} < 0.0)\n") + f.write("\t\t\t\t\t{\n") + f.write("\t\t\t\t\t\tconst double w = V[i]*alphaL[i];\n") + f.write( + f"\t\t\t\t\t\tsV += w; sVU += w*UL[i][{nd}]; sVrho += w*rhoL[i];\n" + ) + f.write("\t\t\t\t\t}\n") + f.write("\t\t\t\t}\n") + f.write("\t\t\t\treduce(sV, sumOp());\n") + f.write("\t\t\t\treduce(sVU, sumOp());\n") + f.write("\t\t\t\treduce(sVrho, sumOp());\n") + f.write( + f"\t\t\t\tdouble V1 = (sV>1e-30) ? {push_ax}*(sVU/sV) : 0.0;\n" + ) + f.write("\t\t\t\tif (V1 < 0.0) V1 = 0.0;\n") + f.write( + "\t\t\t\tconst double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0;\n" + ) + # --- passive loads (no Newton solve) --- + f.write("\t\t\t\tconst double Qsw = Snum*Rmix*rhoM*area*V1*V1;\n") + f.write("\t\t\t\tconst double Tls = 0.5*Kloss*rhoM*area*V1*V1;\n") + # --- pass 1: normalisation sums over the ball --- + f.write("\t\t\t\tscalar Sax = 0.0, Ssw = 0.0;\n") + f.write("\t\t\t\tforAll(C, i)\n") + f.write("\t\t\t\t{\n") + f.write( + "\t\t\t\t\tconst double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz;\n" + ) + f.write("\t\t\t\t\tconst double d2 = dx*dx + dy*dy + dz*dz;\n") + f.write("\t\t\t\t\tif (d2 <= Rmix*Rmix)\n") + f.write("\t\t\t\t\t{\n") + f.write( + "\t\t\t\t\t\tconst double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i]));\n" + ) + f.write("\t\t\t\t\t\tconst double g = std::exp(-d2/(epsi*epsi));\n") + f.write( + f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n" + ) + f.write(f"\t\t\t\t\t\tconst double ux = UL[i][{nd}];\n") + f.write("\t\t\t\t\t\tSax += alphaL[i]*g*V[i];\n") + f.write("\t\t\t\t\t\tSsw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i];\n") + f.write("\t\t\t\t\t}\n") + f.write("\t\t\t\t}\n") + f.write("\t\t\t\treduce(Sax, sumOp());\n") + f.write("\t\t\t\treduce(Ssw, sumOp());\n") + # --- pass 2: apply --- + f.write("\t\t\t\tforAll(C, i)\n") + f.write("\t\t\t\t{\n") + f.write( + "\t\t\t\t\tconst double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz;\n" + ) + f.write("\t\t\t\t\tconst double d2 = dx*dx + dy*dy + dz*dz;\n") + f.write("\t\t\t\t\tif (d2 <= Rmix*Rmix)\n") + f.write("\t\t\t\t\t{\n") + f.write( + "\t\t\t\t\t\tconst double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i]));\n" + ) + f.write("\t\t\t\t\t\tconst double g = std::exp(-d2/(epsi*epsi));\n") + # viscous drag (lumped): opposes the oriented inflow + f.write("\t\t\t\t\t\tif (Sax > 1e-30)\n") + f.write("\t\t\t\t\t\t{\n") + f.write("\t\t\t\t\t\t\tconst double fvisc = Tls/Sax*alphaL[i]*g;\n") + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += -{push_ax}*fvisc*V[i];\n") + f.write("\t\t\t\t\t\t}\n") + # swirl + energy-neutral axial reaction (local, velocity-weighted) + f.write( + f"\t\t\t\t\t\tconst double rr = std::sqrt(d2-({dn})*({dn}));\n" + ) + f.write("\t\t\t\t\t\tif (rr > 1e-3*Rmix && Ssw > 1e-30)\n") + f.write("\t\t\t\t\t\t{\n") + f.write(f"\t\t\t\t\t\t\tconst double ux = UL[i][{nd}];\n") + f.write( + f"\t\t\t\t\t\t\tconst double uth = UL[i][{tan[0][0]}]*(({tan[0][1]})/rr) + UL[i][{tan[1][0]}]*(({tan[1][1]})/rr);\n" + ) + f.write("\t\t\t\t\t\t\tconst double A0 = Qsw/Ssw;\n") + f.write( + "\t\t\t\t\t\t\tconst double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g;\n" + ) + f.write( + f"\t\t\t\t\t\t\tUsource[i][{tan[0][0]}] += {push_th}*fsw*V[i]*(({tan[0][1]})/rr);\n" + ) + f.write( + f"\t\t\t\t\t\t\tUsource[i][{tan[1][0]}] += {push_th}*fsw*V[i]*(({tan[1][1]})/rr);\n" + ) + f.write( + "\t\t\t\t\t\t\tconst double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g;\n" + ) + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += -{push_th}*fcp*V[i];\n") + f.write("\t\t\t\t\t\t}\n") + f.write("\t\t\t\t\t}\n") + f.write("\t\t\t\t}\n") + f.write("\t\t\t}\n") + f.write("\t\t}\n") + + def write_end(output_folder): with open(os.path.join(output_folder, "fvModels"), "a+") as f: f.write("\t#};\n") diff --git a/tests/preprocess/test_static_mixer.py b/tests/preprocess/test_static_mixer.py index 18fb37ab..cd63dbbb 100644 --- a/tests/preprocess/test_static_mixer.py +++ b/tests/preprocess/test_static_mixer.py @@ -1,4 +1,12 @@ +import tempfile +from pathlib import Path + from bird.meshing.block_rect_mesh import from_block_rect_to_seg +from bird.preprocess.dynamic_mixer.io_fvModels import ( + write_end, + write_preamble_ball, + write_static_mixer_ball, +) from bird.preprocess.dynamic_mixer.mixer import StaticMixer @@ -61,3 +69,50 @@ def test_StaticMixer(): {"x": 0.1, "y": 0.2, "z": 0.3, "normal_dir": 1, "radius": 0.05} ) assert not m3.ready + + +def test_write_static_mixer_ball(): + m = StaticMixer() + m.update_from_expl_dict( + { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "normal_dir": 1, + "radius": 0.05, + "sign": "+", + "swirl_sign": "+", + "S": 0.35, + "K": 0.5, + "start_time": 1, + } + ) + assert m.ready + + with tempfile.TemporaryDirectory() as tmpdirname: + write_preamble_ball(tmpdirname) + write_static_mixer_ball(m, tmpdirname) + write_end(tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + + assert "// ===== static mixer =====" in txt + assert "dynamicMix_util" not in txt # no external header + assert "V2" not in txt # passive: no Newton solve + # passive loads (no tip speed / power) + assert "const double Qsw = Snum*Rmix*rhoM*area*V1*V1;" in txt + assert "const double Tls = 0.5*Kloss*rhoM*area*V1*V1;" in txt + # activation gate inherited from the sensing block + assert "if (V1 < 0.0) V1 = 0.0;" in txt + # exact conservation: runtime-summed normalisers (velocity-weighted swirl) + assert "reduce(Sax, sumOp());" in txt + assert "reduce(Ssw, sumOp());" in txt + assert "Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i];" in txt + assert "const double A0 = Qsw/Ssw;" in txt + # energy-neutral axial reaction f_cp ~ rho*ux*uth + assert "const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g;" in txt + # normal_dir=1 -> theta_hat = (dz/rr, 0, -dx/rr); swirl on components 0 and 2 + assert "Usource[i][0] += 1.0*fsw*V[i]*((dz)/rr);" in txt + assert "Usource[i][2] += 1.0*fsw*V[i]*((-dx)/rr);" in txt + # axial reaction and viscous drag on the normal component (index 1) + assert "Usource[i][1] += -1.0*fcp*V[i];" in txt + assert "Usource[i][1] += -1.0*fvisc*V[i];" in txt From 37c330e7da7696ef581408112cfe050eca5096da Mon Sep 17 00:00:00 2001 From: Malik Date: Mon, 3 Aug 2026 15:40:44 -0600 Subject: [PATCH 16/37] make sure we can mix static and dynamic mixers --- .../dynamic_mixer/mixing_fvModels.py | 44 +++++++++-- tests/preprocess/test_static_mixer.py | 73 +++++++++++++++++++ 2 files changed, 111 insertions(+), 6 deletions(-) diff --git a/bird/preprocess/dynamic_mixer/mixing_fvModels.py b/bird/preprocess/dynamic_mixer/mixing_fvModels.py index 1c23ca07..630f8ca7 100644 --- a/bird/preprocess/dynamic_mixer/mixing_fvModels.py +++ b/bird/preprocess/dynamic_mixer/mixing_fvModels.py @@ -1,12 +1,16 @@ from bird.meshing.block_rect_mesh import from_block_rect_to_seg from bird.preprocess.dynamic_mixer.io_fvModels import * -from bird.preprocess.dynamic_mixer.mixer import ActuatorMixer, Mixer +from bird.preprocess.dynamic_mixer.mixer import ( + ActuatorMixer, + Mixer, + StaticMixer, +) def check_input(input_dict): assert isinstance(input_dict, dict) mix_type = [] - for mix in input_dict["mixers"]: + for mix in input_dict.get("mixers", []): if "x" in mix: mix_type.append("expl") else: @@ -25,11 +29,25 @@ def check_input(input_dict): return mix_type +def check_static_input(input_dict): + """Return the expl/loop type of each entry in the ``static_mixers`` list.""" + static_mix_type = [] + for mix in input_dict.get("static_mixers", []): + static_mix_type.append("expl" if "x" in mix else "loop") + if "loop" in static_mix_type: + assert "Geometry" in input_dict + assert "OverallDomain" in input_dict["Geometry"] + assert "Fluids" in input_dict["Geometry"] + return static_mix_type + + def write_fvModel(input_dict, output_folder=".", force_sign=False): # Switch on the volumetric source: "ball" (new, exact-conservation # actuator-disk) vs "pancake" (legacy, default). The legacy path below is # left byte-for-byte unchanged. - if input_dict.get("volumetric_source", "pancake") == "ball": + if input_dict.get( + "volumetric_source", "pancake" + ) == "ball" or input_dict.get("static_mixers"): write_fvModel_ball(input_dict, output_folder=output_folder) return mix_type = check_input(input_dict) @@ -64,14 +82,18 @@ def write_fvModel_ball(input_dict, output_folder="."): Reads the top-level ``power`` (``from_P`` / ``from_Np_Vtip``) and ``momentum_source`` (``axial`` / ``axial_and_swirl``) modes; both default to - the new full model. Each mixer is an - :class:`~bird.preprocess.dynamic_mixer.mixer.ActuatorMixer`. + the new full model. Each dynamic mixer is an + :class:`~bird.preprocess.dynamic_mixer.mixer.ActuatorMixer`; each entry of the + optional ``static_mixers`` list is a + :class:`~bird.preprocess.dynamic_mixer.mixer.StaticMixer` appended to the same + codedSource. """ mix_type = check_input(input_dict) + static_mix_type = check_static_input(input_dict) power_mode = input_dict.get("power", "from_Np_Vtip") momentum_mode = input_dict.get("momentum_source", "axial_and_swirl") write_preamble_ball(output_folder) - if "loop" in mix_type: + if "loop" in mix_type or "loop" in static_mix_type: geom_dict = from_block_rect_to_seg(input_dict["Geometry"]) for imix, mtype in enumerate(mix_type): mixer = ActuatorMixer() @@ -81,4 +103,14 @@ def write_fvModel_ball(input_dict, output_folder="."): mixer.update_from_loop_dict(input_dict["mixers"][imix], geom_dict) if mixer.ready: write_mixer_ball(mixer, output_folder, power_mode, momentum_mode) + for imix, mtype in enumerate(static_mix_type): + mixer = StaticMixer() + if mtype == "expl": + mixer.update_from_expl_dict(input_dict["static_mixers"][imix]) + elif mtype == "loop": + mixer.update_from_loop_dict( + input_dict["static_mixers"][imix], geom_dict + ) + if mixer.ready: + write_static_mixer_ball(mixer, output_folder) write_end(output_folder) diff --git a/tests/preprocess/test_static_mixer.py b/tests/preprocess/test_static_mixer.py index cd63dbbb..7d257c01 100644 --- a/tests/preprocess/test_static_mixer.py +++ b/tests/preprocess/test_static_mixer.py @@ -8,6 +8,7 @@ write_static_mixer_ball, ) from bird.preprocess.dynamic_mixer.mixer import StaticMixer +from bird.preprocess.dynamic_mixer.mixing_fvModels import write_fvModel def test_StaticMixer(): @@ -116,3 +117,75 @@ def test_write_static_mixer_ball(): # axial reaction and viscous drag on the normal component (index 1) assert "Usource[i][1] += -1.0*fcp*V[i];" in txt assert "Usource[i][1] += -1.0*fvisc*V[i];" in txt + + +def test_write_fvModel_static_mixers(): + # static-only, explicit placement: the ball path is auto-triggered by the + # presence of the static_mixers list (no volumetric_source needed) + d = { + "static_mixers": [ + { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "normal_dir": 1, + "radius": 0.05, + "sign": "+", + "swirl_sign": "+", + "S": 0.35, + "K": 0.5, + "start_time": 1, + } + ] + } + with tempfile.TemporaryDirectory() as tmpdirname: + write_fvModel(d, output_folder=tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + assert txt.count("codedSource") == 1 # single preamble + assert "// ===== static mixer =====" in txt + assert "// ===== ball mixer =====" not in txt # no dynamic block + assert txt.rstrip().endswith("};") # write_end closed the block + + # mixed dynamic + static, loop placement: both share one codedSource + base = { + "Meshing": {"Blockwise": {"x": 10, "y": 10, "z": 10}}, + "Geometry": { + "OverallDomain": { + a: {"nblocks": 10, "size_per_block": 1.0, "rescale": 2.76} + for a in ("x", "y", "z") + }, + "Fluids": [[[0, 0, 0], [9, 0, 0]]], + }, + "volumetric_source": "ball", + "mixers": [ + { + "branch_id": 0, + "frac_space": 0.5, + "radius": 0.4, + "sign": "+", + "swirl_sign": "+", + "Vtip": 1.5, + "Np": 6, + "sigma": 0.35, + "start_time": 1, + } + ], + "static_mixers": [ + { + "branch_id": 0, + "frac_space": 0.6, + "radius": 0.5, + "sign": "+", + "swirl_sign": "+", + "S": 0.35, + "K": 0.5, + "start_time": 1, + } + ], + } + with tempfile.TemporaryDirectory() as tmpdirname: + write_fvModel(base, output_folder=tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + assert txt.count("codedSource") == 1 # one shared block + assert "// ===== ball mixer =====" in txt # dynamic present + assert "// ===== static mixer =====" in txt # static present From 77be001800b976daf653624786f7ad5d36492a0d Mon Sep 17 00:00:00 2001 From: Malik Date: Mon, 3 Aug 2026 15:46:12 -0600 Subject: [PATCH 17/37] Add template for mixer placement --- .../static_expl_list/mixers.json | 5 +++ .../static_loop_list/mixers.json | 24 +++++++++++ tests/preprocess/test_static_mixer.py | 42 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 bird/preprocess/dynamic_mixer/mixing_template/static_expl_list/mixers.json create mode 100644 bird/preprocess/dynamic_mixer/mixing_template/static_loop_list/mixers.json diff --git a/bird/preprocess/dynamic_mixer/mixing_template/static_expl_list/mixers.json b/bird/preprocess/dynamic_mixer/mixing_template/static_expl_list/mixers.json new file mode 100644 index 00000000..af3955cf --- /dev/null +++ b/bird/preprocess/dynamic_mixer/mixing_template/static_expl_list/mixers.json @@ -0,0 +1,5 @@ +{ + "static_mixers": [ + {"x": 0.025, "y": 0.07, "z": 0.025, "normal_dir": 1, "start_time": 1, "S": 0.35, "K": 0.5, "radius": 0.01, "sign": "+", "swirl_sign": "+"} + ] +} diff --git a/bird/preprocess/dynamic_mixer/mixing_template/static_loop_list/mixers.json b/bird/preprocess/dynamic_mixer/mixing_template/static_loop_list/mixers.json new file mode 100644 index 00000000..e3bffcd7 --- /dev/null +++ b/bird/preprocess/dynamic_mixer/mixing_template/static_loop_list/mixers.json @@ -0,0 +1,24 @@ +{ + "Meshing": { + "Blockwise": { + "x" : 10, + "y" : 10, + "z" : 10 + } + }, + "Geometry": { + "OverallDomain": { + "x" : {"nblocks": 10, "size_per_block": 1.0, "rescale": 0.05}, + "y" : {"nblocks": 5, "size_per_block": 1.0, "rescale": 0.05}, + "z" : {"nblocks": 5, "size_per_block": 1.0, "rescale": 0.05} + }, + "Fluids": [ + [ [0,0,0], [9,0,0], [9,0,4], [0,0,4] ], + [ [0,1,4], [0,4,4], [0,4,0], [0,1,0] ] + ] + + }, + "static_mixers": [ + {"branch_id": 0, "frac_space": 0.5, "start_time": 1, "S": 0.35, "K": 0.5, "radius": 0.5, "sign": "+", "swirl_sign": "+"} + ] +} diff --git a/tests/preprocess/test_static_mixer.py b/tests/preprocess/test_static_mixer.py index 7d257c01..bdfe7492 100644 --- a/tests/preprocess/test_static_mixer.py +++ b/tests/preprocess/test_static_mixer.py @@ -1,3 +1,4 @@ +import os import tempfile from pathlib import Path @@ -9,6 +10,7 @@ ) from bird.preprocess.dynamic_mixer.mixer import StaticMixer from bird.preprocess.dynamic_mixer.mixing_fvModels import write_fvModel +from bird.utilities.parser import parse_json def test_StaticMixer(): @@ -189,3 +191,43 @@ def test_write_fvModel_static_mixers(): assert txt.count("codedSource") == 1 # one shared block assert "// ===== ball mixer =====" in txt # dynamic present assert "// ===== static mixer =====" in txt # static present + + +def test_static_expl_list(): + template_dir = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "dynamic_mixer", + "mixing_template", + ) + d = parse_json( + os.path.join(template_dir, "static_expl_list", "mixers.json") + ) + with tempfile.TemporaryDirectory() as tmpdirname: + write_fvModel(d, output_folder=tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + assert "// ===== static mixer =====" in txt + assert txt.rstrip().endswith("};") + + +def test_static_loop_list(): + template_dir = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "dynamic_mixer", + "mixing_template", + ) + d = parse_json( + os.path.join(template_dir, "static_loop_list", "mixers.json") + ) + with tempfile.TemporaryDirectory() as tmpdirname: + write_fvModel(d, output_folder=tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + assert "// ===== static mixer =====" in txt + assert txt.rstrip().endswith("};") From 8b3e5b11f53e64c7ccba2e4d56688e2f295f99cd Mon Sep 17 00:00:00 2001 From: Malik Date: Mon, 3 Aug 2026 15:52:08 -0600 Subject: [PATCH 18/37] doc updates for static/dynamic mixer distinction --- docs/source/bird.preprocess.dynamic_mixer.rst | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/source/bird.preprocess.dynamic_mixer.rst b/docs/source/bird.preprocess.dynamic_mixer.rst index 2849beab..41a3d4b1 100644 --- a/docs/source/bird.preprocess.dynamic_mixer.rst +++ b/docs/source/bird.preprocess.dynamic_mixer.rst @@ -1,6 +1,39 @@ bird.preprocess.dynamic\_mixer package ====================================== +The ``dynamic_mixer`` package generates mixer momentum sources as OpenFOAM coded +``fvModels`` (see +:func:`~bird.preprocess.dynamic_mixer.mixing_fvModels.write_fvModel`). Two mixer +families share the same ``ball`` deposition: + +* **Dynamic** (:class:`~bird.preprocess.dynamic_mixer.mixer.ActuatorMixer`, the + ``mixers`` list) -- an *active* actuator disk driven by a power number ``Np`` + and tip speed ``Vtip``; it adds axial thrust and swirl. +* **Static** (:class:`~bird.preprocess.dynamic_mixer.mixer.StaticMixer`, the + ``static_mixers`` list) -- a *passive* obstacle with no power input: the swirl + is an energy-neutral axial-to-azimuthal redirection (swirl number ``S``) and + the axial drag is a pure viscous loss (loss coefficient ``K``). It is inactive + when the inflow opposes the mixer orientation. The momentum source follows the + energy-neutral swirler model of Kiesewetter (2005). + +Static mixer JSON schema +------------------------ + +Each entry of the ``static_mixers`` list accepts: + +* ``S`` -- swirl number (default ``0.35``). +* ``K`` -- loss coefficient / velocity heads (default ``0.5``). +* ``radius`` -- mixer radius; a fraction of the tube in loop mode (``0.5`` spans + the whole tube) or an absolute radius in metres in explicit mode. +* ``normal_dir`` -- mixer axis (``0``/``1``/``2`` for x/y/z). +* ``sign`` -- mixer orientation along that axis (``"+"``/``"-"``). +* ``swirl_sign`` -- rotation orientation (``"+"``/``"-"``). +* ``start_time`` -- time after which the source is active. + +Placement is either explicit (``x``, ``y``, ``z``) or on a loop branch +(``branch_id`` plus ``frac_space``, the fraction along the branch), exactly as +for the dynamic mixer. + bird.preprocess.dynamic\_mixer.io\_fvModels module -------------------------------------------------- From 704ff343ead5ac2e96adb6d05a232656d9506e79 Mon Sep 17 00:00:00 2001 From: Malik Date: Mon, 3 Aug 2026 16:02:01 -0600 Subject: [PATCH 19/37] sign printing was messed up --- bird/preprocess/dynamic_mixer/io_fvModels.py | 9 ++++-- tests/preprocess/test_static_mixer.py | 32 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/bird/preprocess/dynamic_mixer/io_fvModels.py b/bird/preprocess/dynamic_mixer/io_fvModels.py index 29f56be3..845424ad 100644 --- a/bird/preprocess/dynamic_mixer/io_fvModels.py +++ b/bird/preprocess/dynamic_mixer/io_fvModels.py @@ -555,6 +555,11 @@ def write_static_mixer_ball(mixer, output_folder): }[nd] push_ax = "1.0" if mixer.sign == "+" else "-1.0" push_th = "1.0" if mixer.swirl_sign == "+" else "-1.0" + # pre-combined signs (= -push_ax / -push_th) as single literals, so the + # emitted C++ never contains "--1.0" (a decrement on a literal, a compile + # error). The drag opposes the oriented inflow; the reaction is -push_th. + drag_ax = "-1.0" if mixer.sign == "+" else "1.0" + cp_th = "-1.0" if mixer.swirl_sign == "+" else "1.0" with open(os.path.join(output_folder, "fvModels"), "a+") as f: f.write("\t\t// ===== static mixer =====\n") @@ -640,7 +645,7 @@ def write_static_mixer_ball(mixer, output_folder): f.write("\t\t\t\t\t\tif (Sax > 1e-30)\n") f.write("\t\t\t\t\t\t{\n") f.write("\t\t\t\t\t\t\tconst double fvisc = Tls/Sax*alphaL[i]*g;\n") - f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += -{push_ax}*fvisc*V[i];\n") + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += {drag_ax}*fvisc*V[i];\n") f.write("\t\t\t\t\t\t}\n") # swirl + energy-neutral axial reaction (local, velocity-weighted) f.write( @@ -665,7 +670,7 @@ def write_static_mixer_ball(mixer, output_folder): f.write( "\t\t\t\t\t\t\tconst double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g;\n" ) - f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += -{push_th}*fcp*V[i];\n") + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += {cp_th}*fcp*V[i];\n") f.write("\t\t\t\t\t\t}\n") f.write("\t\t\t\t\t}\n") f.write("\t\t\t\t}\n") diff --git a/tests/preprocess/test_static_mixer.py b/tests/preprocess/test_static_mixer.py index bdfe7492..20b11da3 100644 --- a/tests/preprocess/test_static_mixer.py +++ b/tests/preprocess/test_static_mixer.py @@ -121,6 +121,38 @@ def test_write_static_mixer_ball(): assert "Usource[i][1] += -1.0*fvisc*V[i];" in txt +def test_write_static_mixer_ball_negative_orientation(): + # sign="-" and swirl_sign="-" must not emit "--1.0" (a decrement on a + # literal, which is a C++ compile error) + m = StaticMixer() + m.update_from_expl_dict( + { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "normal_dir": 1, + "radius": 0.05, + "sign": "-", + "swirl_sign": "-", + "S": 0.35, + "K": 0.5, + "start_time": 1, + } + ) + assert m.ready + + with tempfile.TemporaryDirectory() as tmpdirname: + write_preamble_ball(tmpdirname) + write_static_mixer_ball(m, tmpdirname) + write_end(tmpdirname) + txt = Path(tmpdirname, "fvModels").read_text() + + assert "--1.0" not in txt # no decrement-on-literal + # -push_ax and -push_th collapse to +1.0 for the negative orientation + assert "Usource[i][1] += 1.0*fvisc*V[i];" in txt + assert "Usource[i][1] += 1.0*fcp*V[i];" in txt + + def test_write_fvModel_static_mixers(): # static-only, explicit placement: the ball path is auto-triggered by the # presence of the static_mixers list (no volumetric_source needed) From 7b1d084f96d2d22a7fe274330acdec829a49eb35 Mon Sep 17 00:00:00 2001 From: Malik Date: Mon, 3 Aug 2026 16:09:24 -0600 Subject: [PATCH 20/37] add regression test for later --- .github/workflows/ci.yml | 5 + .../loop_reactor_mixing_static/0.orig/CO2.gas | 47 + .../0.orig/CO2.liquid | 42 + .../loop_reactor_mixing_static/0.orig/H2.gas | 47 + .../0.orig/H2.liquid | 42 + .../loop_reactor_mixing_static/0.orig/N2.gas | 47 + .../loop_reactor_mixing_static/0.orig/T.gas | 46 + .../0.orig/T.liquid | 45 + .../loop_reactor_mixing_static/0.orig/U.gas | 47 + .../0.orig/U.liquid | 46 + .../0.orig/Ydefault.gas | 42 + .../0.orig/Ydefault.liquid | 42 + .../0.orig/alpha.gas | 43 + .../0.orig/alpha.liquid | 40 + .../0.orig/alphat.gas | 46 + .../0.orig/alphat.liquid | 44 + .../0.orig/epsilon.gas | 48 + .../0.orig/epsilon.liquid | 43 + .../loop_reactor_mixing_static/0.orig/f.gas | 41 + .../loop_reactor_mixing_static/0.orig/k.gas | 43 + .../0.orig/k.liquid | 44 + .../loop_reactor_mixing_static/0.orig/nut.gas | 48 + .../0.orig/nut.liquid | 43 + .../loop_reactor_mixing_static/0.orig/p | 39 + .../loop_reactor_mixing_static/0.orig/p_rgh | 43 + .../loop_reactor_mixing_static/Allclean | 24 + .../loop_reactor_mixing_static/README.md | 28 + .../loop_reactor_mixing_static/computeQOI.sh | 13 + .../constant/fvModels | 187 +++ .../loop_reactor_mixing_static/constant/g | 21 + .../constant/globalVars | 83 ++ .../constant/globalVars_temp | 83 ++ .../constant/momentumTransport.gas | 26 + .../constant/momentumTransport.liquid | 27 + .../constant/phaseProperties | 261 ++++ .../constant/phaseProperties_constantd | 261 ++++ .../constant/phaseProperties_pbe | 295 +++++ .../constant/thermophysicalProperties.gas | 142 +++ .../constant/thermophysicalProperties.liquid | 108 ++ .../loop_reactor_mixing_static/get_qoi.py | 199 ++++ .../loop_reactor_mixing_static/presteps.sh | 81 ++ .../read_history.py | 104 ++ .../loop_reactor_mixing_static/run.sh | 72 ++ .../loop_reactor_mixing_static/script | 14 + .../loop_reactor_mixing_static/script_post | 10 + .../system/blockMeshDict | 1050 +++++++++++++++++ .../system/controlDict | 66 ++ .../system/decomposeParDict | 30 + .../system/fvConstraints | 56 + .../system/fvSchemes | 70 ++ .../system/fvSolution | 120 ++ .../system/inlets_outlets.json | 177 +++ .../system/mesh.json | 26 + .../system/mixers.json | 148 +++ .../system/setFieldsDict | 37 + .../writeGlobalVars.py | 42 + tutorial_cases/runall.sh | 4 + 57 files changed, 4928 insertions(+) create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/CO2.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/CO2.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/H2.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/H2.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/N2.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/T.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/T.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/U.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/U.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/Ydefault.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/Ydefault.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/alpha.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/alpha.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/alphat.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/alphat.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/epsilon.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/epsilon.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/f.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/k.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/k.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/nut.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/nut.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/p create mode 100644 tutorial_cases/loop_reactor_mixing_static/0.orig/p_rgh create mode 100755 tutorial_cases/loop_reactor_mixing_static/Allclean create mode 100644 tutorial_cases/loop_reactor_mixing_static/README.md create mode 100644 tutorial_cases/loop_reactor_mixing_static/computeQOI.sh create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/fvModels create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/g create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/globalVars create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/globalVars_temp create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/momentumTransport.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/momentumTransport.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties_constantd create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties_pbe create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/thermophysicalProperties.gas create mode 100644 tutorial_cases/loop_reactor_mixing_static/constant/thermophysicalProperties.liquid create mode 100644 tutorial_cases/loop_reactor_mixing_static/get_qoi.py create mode 100644 tutorial_cases/loop_reactor_mixing_static/presteps.sh create mode 100644 tutorial_cases/loop_reactor_mixing_static/read_history.py create mode 100644 tutorial_cases/loop_reactor_mixing_static/run.sh create mode 100755 tutorial_cases/loop_reactor_mixing_static/script create mode 100755 tutorial_cases/loop_reactor_mixing_static/script_post create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/blockMeshDict create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/controlDict create mode 100755 tutorial_cases/loop_reactor_mixing_static/system/decomposeParDict create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/fvConstraints create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/fvSchemes create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/fvSolution create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/inlets_outlets.json create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/mesh.json create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/mixers.json create mode 100644 tutorial_cases/loop_reactor_mixing_static/system/setFieldsDict create mode 100644 tutorial_cases/loop_reactor_mixing_static/writeGlobalVars.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8efcf29..9b396279 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -252,6 +252,11 @@ jobs: cd tutorial_cases/loop_reactor_mixing_swirl bash run.sh cd ../../ + - name: Run mixing loop reactor with static mixer tutorial + run: | + cd tutorial_cases/loop_reactor_mixing_static + bash run.sh + cd ../../ - name: Run airlift reactor tutorial run: | cd tutorial_cases/airlift_40m diff --git a/tutorial_cases/loop_reactor_mixing_static/0.orig/CO2.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/CO2.gas new file mode 100644 index 00000000..e4165b1a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/CO2.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/CO2.liquid new file mode 100644 index 00000000..4b8ea6a0 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/H2.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/H2.gas new file mode 100644 index 00000000..9f66b2d2 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/H2.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/H2.liquid new file mode 100644 index 00000000..65ae8d34 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/N2.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/N2.gas new file mode 100644 index 00000000..c1d7225f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/T.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/T.gas new file mode 100644 index 00000000..1202c340 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/0.orig/T.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 T.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +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/tutorial_cases/loop_reactor_mixing_static/0.orig/T.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/T.liquid new file mode 100644 index 00000000..d6c1836a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/0.orig/T.liquid @@ -0,0 +1,45 @@ +/*--------------------------------*- 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]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +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/tutorial_cases/loop_reactor_mixing_static/0.orig/U.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/U.gas new file mode 100644 index 00000000..e696566f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/U.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/U.liquid new file mode 100644 index 00000000..1879e020 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/Ydefault.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/Ydefault.gas new file mode 100644 index 00000000..fba2945d --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/Ydefault.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/Ydefault.liquid new file mode 100644 index 00000000..a5108564 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/alpha.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/alpha.gas new file mode 100644 index 00000000..1e303fbe --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/alpha.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/alpha.liquid new file mode 100644 index 00000000..5c92070b --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/alphat.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/alphat.gas new file mode 100644 index 00000000..b867958f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/alphat.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/alphat.liquid new file mode 100644 index 00000000..2569c3ee --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/epsilon.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/epsilon.gas new file mode 100644 index 00000000..707a1cda --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/epsilon.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/epsilon.liquid new file mode 100644 index 00000000..0a4236fd --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/f.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/f.gas new file mode 100644 index 00000000..76ee77a9 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/k.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/k.gas new file mode 100644 index 00000000..4a3d44ca --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/k.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/k.liquid new file mode 100644 index 00000000..cde8f6c1 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/nut.gas b/tutorial_cases/loop_reactor_mixing_static/0.orig/nut.gas new file mode 100644 index 00000000..ba16dd4c --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/nut.liquid b/tutorial_cases/loop_reactor_mixing_static/0.orig/nut.liquid new file mode 100644 index 00000000..1442e07f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/p b/tutorial_cases/loop_reactor_mixing_static/0.orig/p new file mode 100644 index 00000000..b3a295fb --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/0.orig/p_rgh b/tutorial_cases/loop_reactor_mixing_static/0.orig/p_rgh new file mode 100644 index 00000000..88ee7d80 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/Allclean b/tutorial_cases/loop_reactor_mixing_static/Allclean new file mode 100755 index 00000000..dc2f77db --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/loop_reactor_mixing_static/README.md b/tutorial_cases/loop_reactor_mixing_static/README.md new file mode 100644 index 00000000..f8a070ec --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/README.md @@ -0,0 +1,28 @@ +### Loop reactor with passive static mixers + +Same 608 m3 loop reactor as `loop_reactor_mixing`, but the mixers are **passive +static mixers** instead of the powered actuator disks of +`loop_reactor_mixing_swirl`. + +The static mixer injects no power: the swirl is imposed as an energy-neutral +axial-to-azimuthal redirection (Kiesewetter's swirler model) and the axial drag +is a pure viscous loss. It is selected simply by providing a `static_mixers` +list in `system/mixers.json` (the `ball` deposition is triggered automatically), +with per-mixer inputs: + +- `S` — swirl number (target ratio of azimuthal to axial momentum flux). +- `K` — loss coefficient (velocity heads) for the axial pressure drop. +- `radius` — mixer radius as a fraction of the branch cross-section (`0.5` + spans the whole tube), as for the spargers. +- `sign` — mixer orientation; the source is inactive when the inflow + opposes it. +- `swirl_sign` — rotation orientation. +- `start_time` — time after which the source is active. + +Placement uses `branch_id` + `frac_space`, as for the dynamic mixer. This case +places one `+`-oriented and one `-`-oriented mixer so both sign paths are +exercised. + +Single core exec + +1. `bash run.sh` diff --git a/tutorial_cases/loop_reactor_mixing_static/computeQOI.sh b/tutorial_cases/loop_reactor_mixing_static/computeQOI.sh new file mode 100644 index 00000000..3756ed7f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/constant/fvModels b/tutorial_cases/loop_reactor_mixing_static/constant/fvModels new file mode 100644 index 00000000..268773e7 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/constant/fvModels @@ -0,0 +1,187 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object fvModels; +} + +codedSource +{ + type coded; + selectionMode all; + field U.liquid; + name sourceTime; + + codeInclude + #{ + #include + #include + #}; + + codeAddAlphaRhoSup + #{ + const Time& time = mesh().time(); + const scalarField& V = mesh().V(); + vectorField& Usource = eqn.source(); + const vectorField& C = mesh().C(); + const volScalarField& rhoL = + mesh().lookupObject("thermo:rho.liquid"); + const volScalarField& alphaL = + mesh().lookupObject("alpha.liquid"); + const volVectorField& UL = + mesh().lookupObject("U.liquid"); + const double pi = 3.14159265358979; + // ===== static mixer ===== + { + const double Rmix = 1.1046110154250839; + const double area = pi*Rmix*Rmix; + const double Snum = 0.35; + const double Kloss = 0.5; + const double startT = 1; + const double px = 11.32226290810711, py = 1.3807637692813548, pz = 1.3807637692813548; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && 1.0*dx < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][0]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? 1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dx)*(dx)); + const double ux = UL[i][0]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][0] += -1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dx)*(dx)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][0]; + const double uth = UL[i][1]*((-dz)/rr) + UL[i][2]*((dy)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][1] += 1.0*fsw*V[i]*((-dz)/rr); + Usource[i][2] += 1.0*fsw*V[i]*((dy)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][0] += -1.0*fcp*V[i]; + } + } + } + } + } + // ===== static mixer ===== + { + const double Rmix = 1.1046110154250839; + const double area = pi*Rmix*Rmix; + const double Snum = 0.35; + const double Kloss = 0.5; + const double startT = 1; + const double px = 16.293012477519987, py = 1.3807637692813548, pz = 12.426873923532193; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dx < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][0]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dx)*(dx)); + const double ux = UL[i][0]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][0] += 1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dx)*(dx)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][0]; + const double uth = UL[i][1]*((-dz)/rr) + UL[i][2]*((dy)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][1] += -1.0*fsw*V[i]*((-dz)/rr); + Usource[i][2] += -1.0*fsw*V[i]*((dy)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][0] += 1.0*fcp*V[i]; + } + } + } + } + } + #}; +}; diff --git a/tutorial_cases/loop_reactor_mixing_static/constant/g b/tutorial_cases/loop_reactor_mixing_static/constant/g new file mode 100644 index 00000000..770a5619 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/constant/globalVars b/tutorial_cases/loop_reactor_mixing_static/constant/globalVars new file mode 100644 index 00000000..c0dce472 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/constant/globalVars @@ -0,0 +1,83 @@ +T0 300; //initial T(K) which stays constant +VVM 0.4; +//****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 15.8621; +liqVol 608.198; +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/tutorial_cases/loop_reactor_mixing_static/constant/globalVars_temp b/tutorial_cases/loop_reactor_mixing_static/constant/globalVars_temp new file mode 100644 index 00000000..dfddd649 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/constant/globalVars_temp @@ -0,0 +1,83 @@ +T0 300; //initial T(K) which stays constant +VVM 0.4; +//****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/tutorial_cases/loop_reactor_mixing_static/constant/momentumTransport.gas b/tutorial_cases/loop_reactor_mixing_static/constant/momentumTransport.gas new file mode 100644 index 00000000..ca916714 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/constant/momentumTransport.liquid b/tutorial_cases/loop_reactor_mixing_static/constant/momentumTransport.liquid new file mode 100644 index 00000000..2063de0d --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties b/tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties new file mode 100644 index 00000000..e029df99 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties @@ -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/tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties_constantd b/tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties_constantd new file mode 100644 index 00000000..e029df99 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties_pbe b/tutorial_cases/loop_reactor_mixing_static/constant/phaseProperties_pbe new file mode 100644 index 00000000..a3c90f5a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/constant/thermophysicalProperties.gas b/tutorial_cases/loop_reactor_mixing_static/constant/thermophysicalProperties.gas new file mode 100644 index 00000000..11b1c4b9 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/constant/thermophysicalProperties.liquid b/tutorial_cases/loop_reactor_mixing_static/constant/thermophysicalProperties.liquid new file mode 100644 index 00000000..d324ec51 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/get_qoi.py b/tutorial_cases/loop_reactor_mixing_static/get_qoi.py new file mode 100644 index 00000000..7f3897ed --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/get_qoi.py @@ -0,0 +1,199 @@ +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_kla_co2 = kla_co2 * cs_co2 * V_l * 0.04401 + qoi_kla_h2 = kla_h2 * cs_h2 * V_l * 0.002016 + + qoi_co2 = qoi_kla_co2 / (P_mix / 3600 + P_inj / 3600) + qoi_h2 = qoi_kla_h2 / (P_mix / 3600 + P_inj / 3600) + return qoi_co2 * qoi_h2, qoi_kla_co2 * qoi_kla_h2 + + +def get_qoi_uq(kla_co2, cs_co2, kla_h2, cs_h2): + qoi = [] + qoi_kla = [] + for i in range(len(kla_co2)): + if i == 0: + verb = True + else: + verb = False + qoi_tmp, qoi_kla_tmp = get_qoi( + kla_co2[i], cs_co2[i], kla_h2[i], cs_h2[i], verb + ) + qoi.append(qoi_tmp) + qoi_kla.append(qoi_kla_tmp) + qoi = np.array(qoi) + qoi_kla = np.array(qoi_kla) + return np.mean(qoi), np.std(qoi), np.mean(qoi_kla), np.std(qoi_kla) + + +os.makedirs("Figures", exist_ok=True) + +dataFolder = "data" +fold = "local" + +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(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, qoi_kla_m, qoi_kla_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") + +with open("qoi_kla.txt", "w+") as f: + f.write(f"{qoi_kla_m},{qoi_kla_s}\n") diff --git a/tutorial_cases/loop_reactor_mixing_static/presteps.sh b/tutorial_cases/loop_reactor_mixing_static/presteps.sh new file mode 100644 index 00000000..bfcff75f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/presteps.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +# Clean case +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 +#python ../../../applications/write_block_rect_mesh.py -i system/mesh.json -o system + +# Generate boundary stl +python /projects/gas2fuels/BioReactorDesign/applications/write_stl_patch.py -i system/inlets_outlets.json +#python ../../../applications/write_stl_patch.py -i system/inlets_outlets.json + +# Generate mixers +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 + +# Generate species thermo properties +python /projects/gas2fuels/BioReactorDesign//applications/write_species_thermo_prop.py -cf . + +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_pbe 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/tutorial_cases/loop_reactor_mixing_static/read_history.py b/tutorial_cases/loop_reactor_mixing_static/read_history.py new file mode 100644 index 00000000..c27eae94 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/read_history.py @@ -0,0 +1,104 @@ +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 = get_case_times( + case_path, remove_zero=True +) +cell_centers, _ = read_cell_centers(".") +nCells = len(cell_centers) + + +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], + field_dict=field_dict, + ) + co2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + species_name="CO2", + field_dict=field_dict, + ) + h2_history[itime], field_dict = compute_ave_y_liq( + case_path, + time_str_sorted[itime], + species_name="H2", + field_dict=field_dict, + ) + c_co2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + species_name="CO2", + field_dict=field_dict, + ) + c_h2_history[itime], field_dict = compute_ave_conc_liq( + case_path, + time_str_sorted[itime], + species_name="H2", + 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/tutorial_cases/loop_reactor_mixing_static/run.sh b/tutorial_cases/loop_reactor_mixing_static/run.sh new file mode 100644 index 00000000..25251599 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/run.sh @@ -0,0 +1,72 @@ +#!/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 + +# Generate boundary stl +python ../../applications/write_stl_patch.py -i system/inlets_outlets.json + +# Generate mixers +python ../../applications/write_dynMix_fvModels.py -fs -i system/mixers.json -o constant + +# Generate species thermo properties +python ../../applications/write_species_thermo_prop.py -cf . + +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_pbe constant/phaseProperties + +#conda deactivate + +echo RUN +birdmultiphaseEulerFoam diff --git a/tutorial_cases/loop_reactor_mixing_static/script b/tutorial_cases/loop_reactor_mixing_static/script new file mode 100755 index 00000000..efe675ff --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/script_post b/tutorial_cases/loop_reactor_mixing_static/script_post new file mode 100755 index 00000000..aabbc33e --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/system/blockMeshDict b/tutorial_cases/loop_reactor_mixing_static/system/blockMeshDict new file mode 100644 index 00000000..0bb60950 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/system/blockMeshDict @@ -0,0 +1,1050 @@ +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 8.0 0.0) +( 1.0 8.0 0.0) +( 2.0 8.0 0.0) +( 3.0 8.0 0.0) +( 4.0 8.0 0.0) +( 5.0 8.0 0.0) +( 6.0 8.0 0.0) +( 7.0 8.0 0.0) +( 8.0 8.0 0.0) +( 9.0 8.0 0.0) +( 10.0 8.0 0.0) +( 0.0 9.0 0.0) +( 1.0 9.0 0.0) +( 2.0 9.0 0.0) +( 3.0 9.0 0.0) +( 4.0 9.0 0.0) +( 5.0 9.0 0.0) +( 6.0 9.0 0.0) +( 7.0 9.0 0.0) +( 8.0 9.0 0.0) +( 9.0 9.0 0.0) +( 10.0 9.0 0.0) +( 0.0 10.0 0.0) +( 1.0 10.0 0.0) +( 2.0 10.0 0.0) +( 3.0 10.0 0.0) +( 4.0 10.0 0.0) +( 5.0 10.0 0.0) +( 6.0 10.0 0.0) +( 7.0 10.0 0.0) +( 8.0 10.0 0.0) +( 9.0 10.0 0.0) +( 10.0 10.0 0.0) +( 0.0 11.0 0.0) +( 1.0 11.0 0.0) +( 2.0 11.0 0.0) +( 3.0 11.0 0.0) +( 4.0 11.0 0.0) +( 5.0 11.0 0.0) +( 6.0 11.0 0.0) +( 7.0 11.0 0.0) +( 8.0 11.0 0.0) +( 9.0 11.0 0.0) +( 10.0 11.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 8.0 1.0) +( 1.0 8.0 1.0) +( 2.0 8.0 1.0) +( 3.0 8.0 1.0) +( 4.0 8.0 1.0) +( 5.0 8.0 1.0) +( 6.0 8.0 1.0) +( 7.0 8.0 1.0) +( 8.0 8.0 1.0) +( 9.0 8.0 1.0) +( 10.0 8.0 1.0) +( 0.0 9.0 1.0) +( 1.0 9.0 1.0) +( 2.0 9.0 1.0) +( 3.0 9.0 1.0) +( 4.0 9.0 1.0) +( 5.0 9.0 1.0) +( 6.0 9.0 1.0) +( 7.0 9.0 1.0) +( 8.0 9.0 1.0) +( 9.0 9.0 1.0) +( 10.0 9.0 1.0) +( 0.0 10.0 1.0) +( 1.0 10.0 1.0) +( 2.0 10.0 1.0) +( 3.0 10.0 1.0) +( 4.0 10.0 1.0) +( 5.0 10.0 1.0) +( 6.0 10.0 1.0) +( 7.0 10.0 1.0) +( 8.0 10.0 1.0) +( 9.0 10.0 1.0) +( 10.0 10.0 1.0) +( 0.0 11.0 1.0) +( 1.0 11.0 1.0) +( 2.0 11.0 1.0) +( 3.0 11.0 1.0) +( 4.0 11.0 1.0) +( 5.0 11.0 1.0) +( 6.0 11.0 1.0) +( 7.0 11.0 1.0) +( 8.0 11.0 1.0) +( 9.0 11.0 1.0) +( 10.0 11.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 8.0 2.0) +( 1.0 8.0 2.0) +( 2.0 8.0 2.0) +( 3.0 8.0 2.0) +( 4.0 8.0 2.0) +( 5.0 8.0 2.0) +( 6.0 8.0 2.0) +( 7.0 8.0 2.0) +( 8.0 8.0 2.0) +( 9.0 8.0 2.0) +( 10.0 8.0 2.0) +( 0.0 9.0 2.0) +( 1.0 9.0 2.0) +( 2.0 9.0 2.0) +( 3.0 9.0 2.0) +( 4.0 9.0 2.0) +( 5.0 9.0 2.0) +( 6.0 9.0 2.0) +( 7.0 9.0 2.0) +( 8.0 9.0 2.0) +( 9.0 9.0 2.0) +( 10.0 9.0 2.0) +( 0.0 10.0 2.0) +( 1.0 10.0 2.0) +( 2.0 10.0 2.0) +( 3.0 10.0 2.0) +( 4.0 10.0 2.0) +( 5.0 10.0 2.0) +( 6.0 10.0 2.0) +( 7.0 10.0 2.0) +( 8.0 10.0 2.0) +( 9.0 10.0 2.0) +( 10.0 10.0 2.0) +( 0.0 11.0 2.0) +( 1.0 11.0 2.0) +( 2.0 11.0 2.0) +( 3.0 11.0 2.0) +( 4.0 11.0 2.0) +( 5.0 11.0 2.0) +( 6.0 11.0 2.0) +( 7.0 11.0 2.0) +( 8.0 11.0 2.0) +( 9.0 11.0 2.0) +( 10.0 11.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 8.0 3.0) +( 1.0 8.0 3.0) +( 2.0 8.0 3.0) +( 3.0 8.0 3.0) +( 4.0 8.0 3.0) +( 5.0 8.0 3.0) +( 6.0 8.0 3.0) +( 7.0 8.0 3.0) +( 8.0 8.0 3.0) +( 9.0 8.0 3.0) +( 10.0 8.0 3.0) +( 0.0 9.0 3.0) +( 1.0 9.0 3.0) +( 2.0 9.0 3.0) +( 3.0 9.0 3.0) +( 4.0 9.0 3.0) +( 5.0 9.0 3.0) +( 6.0 9.0 3.0) +( 7.0 9.0 3.0) +( 8.0 9.0 3.0) +( 9.0 9.0 3.0) +( 10.0 9.0 3.0) +( 0.0 10.0 3.0) +( 1.0 10.0 3.0) +( 2.0 10.0 3.0) +( 3.0 10.0 3.0) +( 4.0 10.0 3.0) +( 5.0 10.0 3.0) +( 6.0 10.0 3.0) +( 7.0 10.0 3.0) +( 8.0 10.0 3.0) +( 9.0 10.0 3.0) +( 10.0 10.0 3.0) +( 0.0 11.0 3.0) +( 1.0 11.0 3.0) +( 2.0 11.0 3.0) +( 3.0 11.0 3.0) +( 4.0 11.0 3.0) +( 5.0 11.0 3.0) +( 6.0 11.0 3.0) +( 7.0 11.0 3.0) +( 8.0 11.0 3.0) +( 9.0 11.0 3.0) +( 10.0 11.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 8.0 4.0) +( 1.0 8.0 4.0) +( 2.0 8.0 4.0) +( 3.0 8.0 4.0) +( 4.0 8.0 4.0) +( 5.0 8.0 4.0) +( 6.0 8.0 4.0) +( 7.0 8.0 4.0) +( 8.0 8.0 4.0) +( 9.0 8.0 4.0) +( 10.0 8.0 4.0) +( 0.0 9.0 4.0) +( 1.0 9.0 4.0) +( 2.0 9.0 4.0) +( 3.0 9.0 4.0) +( 4.0 9.0 4.0) +( 5.0 9.0 4.0) +( 6.0 9.0 4.0) +( 7.0 9.0 4.0) +( 8.0 9.0 4.0) +( 9.0 9.0 4.0) +( 10.0 9.0 4.0) +( 0.0 10.0 4.0) +( 1.0 10.0 4.0) +( 2.0 10.0 4.0) +( 3.0 10.0 4.0) +( 4.0 10.0 4.0) +( 5.0 10.0 4.0) +( 6.0 10.0 4.0) +( 7.0 10.0 4.0) +( 8.0 10.0 4.0) +( 9.0 10.0 4.0) +( 10.0 10.0 4.0) +( 0.0 11.0 4.0) +( 1.0 11.0 4.0) +( 2.0 11.0 4.0) +( 3.0 11.0 4.0) +( 4.0 11.0 4.0) +( 5.0 11.0 4.0) +( 6.0 11.0 4.0) +( 7.0 11.0 4.0) +( 8.0 11.0 4.0) +( 9.0 11.0 4.0) +( 10.0 11.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) +( 0.0 8.0 5.0) +( 1.0 8.0 5.0) +( 2.0 8.0 5.0) +( 3.0 8.0 5.0) +( 4.0 8.0 5.0) +( 5.0 8.0 5.0) +( 6.0 8.0 5.0) +( 7.0 8.0 5.0) +( 8.0 8.0 5.0) +( 9.0 8.0 5.0) +( 10.0 8.0 5.0) +( 0.0 9.0 5.0) +( 1.0 9.0 5.0) +( 2.0 9.0 5.0) +( 3.0 9.0 5.0) +( 4.0 9.0 5.0) +( 5.0 9.0 5.0) +( 6.0 9.0 5.0) +( 7.0 9.0 5.0) +( 8.0 9.0 5.0) +( 9.0 9.0 5.0) +( 10.0 9.0 5.0) +( 0.0 10.0 5.0) +( 1.0 10.0 5.0) +( 2.0 10.0 5.0) +( 3.0 10.0 5.0) +( 4.0 10.0 5.0) +( 5.0 10.0 5.0) +( 6.0 10.0 5.0) +( 7.0 10.0 5.0) +( 8.0 10.0 5.0) +( 9.0 10.0 5.0) +( 10.0 10.0 5.0) +( 0.0 11.0 5.0) +( 1.0 11.0 5.0) +( 2.0 11.0 5.0) +( 3.0 11.0 5.0) +( 4.0 11.0 5.0) +( 5.0 11.0 5.0) +( 6.0 11.0 5.0) +( 7.0 11.0 5.0) +( 8.0 11.0 5.0) +( 9.0 11.0 5.0) +( 10.0 11.0 5.0) +); + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +blocks +( + + //block 0 +hex (0 1 12 11 132 133 144 143 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 1 +hex (1 2 13 12 133 134 145 144 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 2 +hex (2 3 14 13 134 135 146 145 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 3 +hex (3 4 15 14 135 136 147 146 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 4 +hex (4 5 16 15 136 137 148 147 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 5 +hex (5 6 17 16 137 138 149 148 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 6 +hex (6 7 18 17 138 139 150 149 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 7 +hex (7 8 19 18 139 140 151 150 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 8 +hex (8 9 20 19 140 141 152 151 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 9 +hex (9 10 21 20 141 142 153 152 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 10 +hex (141 142 153 152 273 274 285 284 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 11 +hex (273 274 285 284 405 406 417 416 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 12 +hex (405 406 417 416 537 538 549 548 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 13 +hex (537 538 549 548 669 670 681 680 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 14 +hex (536 537 548 547 668 669 680 679 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 15 +hex (535 536 547 546 667 668 679 678 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 16 +hex (534 535 546 545 666 667 678 677 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 17 +hex (533 534 545 544 665 666 677 676 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 18 +hex (532 533 544 543 664 665 676 675 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 19 +hex (531 532 543 542 663 664 675 674 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 20 +hex (530 531 542 541 662 663 674 673 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 21 +hex (529 530 541 540 661 662 673 672 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 22 +hex (528 529 540 539 660 661 672 671 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 23 +hex (539 540 551 550 671 672 683 682 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 24 +hex (550 551 562 561 682 683 694 693 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 25 +hex (561 562 573 572 693 694 705 704 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 26 +hex (572 573 584 583 704 705 716 715 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 27 +hex (583 584 595 594 715 716 727 726 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 28 +hex (594 595 606 605 726 727 738 737 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 29 +hex (605 606 617 616 737 738 749 748 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 30 +hex (616 617 628 627 748 749 760 759 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 31 +hex (627 628 639 638 759 760 771 770 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 32 +hex (638 639 650 649 770 771 782 781 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 33 +hex (440 441 452 451 572 573 584 583 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 34 +hex (308 309 320 319 440 441 452 451 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 35 +hex (176 177 188 187 308 309 320 319 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 36 +hex (44 45 56 55 176 177 188 187 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 37 +hex (55 56 67 66 187 188 199 198 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 38 +hex (66 67 78 77 198 199 210 209 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 39 +hex (77 78 89 88 209 210 221 220 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 40 +hex (88 89 100 99 220 221 232 231 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 41 +hex (99 100 111 110 231 232 243 242 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 42 +hex (110 111 122 121 242 243 254 253 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 43 +hex (33 34 45 44 165 166 177 176 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 44 +hex (22 23 34 33 154 155 166 165 ) +( 10 10 10 ) +SimpleGrading (1 1 1) + + //block 45 +hex (11 12 23 22 143 144 155 154 ) +( 10 10 10 ) +SimpleGrading (1 1 1) +); + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +defaultPatch +{ type wall;} + +patches +( +); diff --git a/tutorial_cases/loop_reactor_mixing_static/system/controlDict b/tutorial_cases/loop_reactor_mixing_static/system/controlDict new file mode 100644 index 00000000..f4665ed8 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/system/controlDict @@ -0,0 +1,66 @@ +/*--------------------------------*- 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 writeNow;//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) + #includeFunc writeObjects(thermo:mu.liquid) + #includeFunc writeObjects(thermo:mu.gas) + #includeFunc fieldAverage(U.air, U.water, alpha.air, p) +} + + +// ************************************************************************* // diff --git a/tutorial_cases/loop_reactor_mixing_static/system/decomposeParDict b/tutorial_cases/loop_reactor_mixing_static/system/decomposeParDict new file mode 100755 index 00000000..f8397e73 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/system/fvConstraints b/tutorial_cases/loop_reactor_mixing_static/system/fvConstraints new file mode 100644 index 00000000..334f1c8f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/system/fvSchemes b/tutorial_cases/loop_reactor_mixing_static/system/fvSchemes new file mode 100644 index 00000000..52e6e13a --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/system/fvSolution b/tutorial_cases/loop_reactor_mixing_static/system/fvSolution new file mode 100644 index 00000000..2e69fdfa --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/system/inlets_outlets.json b/tutorial_cases/loop_reactor_mixing_static/system/inlets_outlets.json new file mode 100644 index 00000000..a083d47f --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/system/inlets_outlets.json @@ -0,0 +1,177 @@ +{ + "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 + ] + ] + ] + }, + "inlets": [ + { + "branch_id": 0, + "type": "circle", + "frac_space": 0.2, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom" + }, + { + "branch_id": 1, + "type": "circle", + "frac_space": 0.2, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom" + }, + { + "branch_id": 1, + "type": "circle", + "frac_space": 0.8, + "normal_dir": 1, + "radius": 0.4, + "nelements": 50, + "block_pos": "bottom" + }, + { + "branch_id": 2, + "type": "circle", + "frac_space": 0.8, + "normal_dir": 1, + "radius": 0.4, + "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" + } + ] +} \ No newline at end of file diff --git a/tutorial_cases/loop_reactor_mixing_static/system/mesh.json b/tutorial_cases/loop_reactor_mixing_static/system/mesh.json new file mode 100644 index 00000000..29841d7e --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/system/mixers.json b/tutorial_cases/loop_reactor_mixing_static/system/mixers.json new file mode 100644 index 00000000..87557d6b --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/system/mixers.json @@ -0,0 +1,148 @@ +{ + "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 + ] + ] + ] + }, + "static_mixers": [ + { + "branch_id": 0, + "frac_space": 0.4, + "start_time": 1, + "sign": "+", + "swirl_sign": "+", + "radius": 0.4, + "S": 0.35, + "K": 0.5 + }, + { + "branch_id": 2, + "frac_space": 0.4, + "start_time": 1, + "sign": "-", + "swirl_sign": "-", + "radius": 0.4, + "S": 0.35, + "K": 0.5 + } + ] +} \ No newline at end of file diff --git a/tutorial_cases/loop_reactor_mixing_static/system/setFieldsDict b/tutorial_cases/loop_reactor_mixing_static/system/setFieldsDict new file mode 100644 index 00000000..89a797b9 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/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/tutorial_cases/loop_reactor_mixing_static/writeGlobalVars.py b/tutorial_cases/loop_reactor_mixing_static/writeGlobalVars.py new file mode 100644 index 00000000..e64d69e3 --- /dev/null +++ b/tutorial_cases/loop_reactor_mixing_static/writeGlobalVars.py @@ -0,0 +1,42 @@ +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", + ) + return read_surface_field_value(filename) + + +def getLiqVol(): + volume_field, _ = read_cell_volumes(".") + alpha_field, _ = read_field(".", "0", field_name="alpha.liquid") + return np.sum(volume_field * alpha_field) + + +if __name__ == "__main__": + A = readInletArea() + V = getLiqVol() + writeGvars(A, V) diff --git a/tutorial_cases/runall.sh b/tutorial_cases/runall.sh index 894373a2..31e971fb 100644 --- a/tutorial_cases/runall.sh +++ b/tutorial_cases/runall.sh @@ -46,6 +46,10 @@ cd ../../ cd tutorial_cases/loop_reactor_mixing_swirl bash run.sh cd ../../ +## Run mixing loop reactor with static mixer tutorial +cd tutorial_cases/loop_reactor_mixing_static +bash run.sh +cd ../../ ## Run airlift reactor tutorial cd tutorial_cases/airlift_40m bash run.sh From e5f1debeb125c0dedabaddc28297db856f2212bf Mon Sep 17 00:00:00 2001 From: Malik Date: Fri, 7 Aug 2026 11:42:18 -0600 Subject: [PATCH 21/37] sign issue --- bird/preprocess/dynamic_mixer/io_fvModels.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bird/preprocess/dynamic_mixer/io_fvModels.py b/bird/preprocess/dynamic_mixer/io_fvModels.py index 845424ad..f8b02d38 100644 --- a/bird/preprocess/dynamic_mixer/io_fvModels.py +++ b/bird/preprocess/dynamic_mixer/io_fvModels.py @@ -512,7 +512,7 @@ def write_mixer_ball( f.write("\t\t\t\t\t\tif (Sax > 1e-30)\n") f.write("\t\t\t\t\t\t{\n") f.write("\t\t\t\t\t\t\tconst double fax = Tax/Sax*alphaL[i]*g;\n") - f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += {push_ax}*fax*V[i];\n") + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] -= {push_ax}*fax*V[i];\n") f.write("\t\t\t\t\t\t}\n") if swirl: f.write( @@ -522,10 +522,10 @@ def write_mixer_ball( f.write("\t\t\t\t\t\t{\n") f.write("\t\t\t\t\t\t\tconst double fth = Qsw/Sth*alphaL[i]*g;\n") f.write( - f"\t\t\t\t\t\t\tUsource[i][{tan[0][0]}] += {push_th}*fth*V[i]*(({tan[0][1]})/rr);\n" + f"\t\t\t\t\t\t\tUsource[i][{tan[0][0]}] -= {push_th}*fth*V[i]*(({tan[0][1]})/rr);\n" ) f.write( - f"\t\t\t\t\t\t\tUsource[i][{tan[1][0]}] += {push_th}*fth*V[i]*(({tan[1][1]})/rr);\n" + f"\t\t\t\t\t\t\tUsource[i][{tan[1][0]}] -= {push_th}*fth*V[i]*(({tan[1][1]})/rr);\n" ) f.write("\t\t\t\t\t\t}\n") f.write("\t\t\t\t\t}\n") @@ -645,7 +645,7 @@ def write_static_mixer_ball(mixer, output_folder): f.write("\t\t\t\t\t\tif (Sax > 1e-30)\n") f.write("\t\t\t\t\t\t{\n") f.write("\t\t\t\t\t\t\tconst double fvisc = Tls/Sax*alphaL[i]*g;\n") - f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += {drag_ax}*fvisc*V[i];\n") + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] -= {drag_ax}*fvisc*V[i];\n") f.write("\t\t\t\t\t\t}\n") # swirl + energy-neutral axial reaction (local, velocity-weighted) f.write( @@ -662,15 +662,15 @@ def write_static_mixer_ball(mixer, output_folder): "\t\t\t\t\t\t\tconst double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g;\n" ) f.write( - f"\t\t\t\t\t\t\tUsource[i][{tan[0][0]}] += {push_th}*fsw*V[i]*(({tan[0][1]})/rr);\n" + f"\t\t\t\t\t\t\tUsource[i][{tan[0][0]}] -= {push_th}*fsw*V[i]*(({tan[0][1]})/rr);\n" ) f.write( - f"\t\t\t\t\t\t\tUsource[i][{tan[1][0]}] += {push_th}*fsw*V[i]*(({tan[1][1]})/rr);\n" + f"\t\t\t\t\t\t\tUsource[i][{tan[1][0]}] -= {push_th}*fsw*V[i]*(({tan[1][1]})/rr);\n" ) f.write( "\t\t\t\t\t\t\tconst double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g;\n" ) - f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] += {cp_th}*fcp*V[i];\n") + f.write(f"\t\t\t\t\t\t\tUsource[i][{nd}] -= {cp_th}*fcp*V[i];\n") f.write("\t\t\t\t\t\t}\n") f.write("\t\t\t\t\t}\n") f.write("\t\t\t\t}\n") From 8d596d7bed351104df09d90b79f8575a457b0b34 Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 18 Aug 2026 10:36:11 -0600 Subject: [PATCH 22/37] source is -= --- tests/preprocess/test_static_mixer.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/preprocess/test_static_mixer.py b/tests/preprocess/test_static_mixer.py index 20b11da3..cbfa52ae 100644 --- a/tests/preprocess/test_static_mixer.py +++ b/tests/preprocess/test_static_mixer.py @@ -114,11 +114,13 @@ def test_write_static_mixer_ball(): # energy-neutral axial reaction f_cp ~ rho*ux*uth assert "const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g;" in txt # normal_dir=1 -> theta_hat = (dz/rr, 0, -dx/rr); swirl on components 0 and 2 - assert "Usource[i][0] += 1.0*fsw*V[i]*((dz)/rr);" in txt - assert "Usource[i][2] += 1.0*fsw*V[i]*((-dx)/rr);" in txt + # Usource -= B applies body force +B (adds B to the RHS), so the deposition + # is emitted with -= and the pre-combined sign literals. + assert "Usource[i][0] -= 1.0*fsw*V[i]*((dz)/rr);" in txt + assert "Usource[i][2] -= 1.0*fsw*V[i]*((-dx)/rr);" in txt # axial reaction and viscous drag on the normal component (index 1) - assert "Usource[i][1] += -1.0*fcp*V[i];" in txt - assert "Usource[i][1] += -1.0*fvisc*V[i];" in txt + assert "Usource[i][1] -= -1.0*fcp*V[i];" in txt + assert "Usource[i][1] -= -1.0*fvisc*V[i];" in txt def test_write_static_mixer_ball_negative_orientation(): @@ -148,9 +150,9 @@ def test_write_static_mixer_ball_negative_orientation(): txt = Path(tmpdirname, "fvModels").read_text() assert "--1.0" not in txt # no decrement-on-literal - # -push_ax and -push_th collapse to +1.0 for the negative orientation - assert "Usource[i][1] += 1.0*fvisc*V[i];" in txt - assert "Usource[i][1] += 1.0*fcp*V[i];" in txt + # drag_ax and cp_th are +1.0 for the negative orientation + assert "Usource[i][1] -= 1.0*fvisc*V[i];" in txt + assert "Usource[i][1] -= 1.0*fcp*V[i];" in txt def test_write_fvModel_static_mixers(): From 4024b107cd8b6a96174410e992e8a037f508c68c Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 18 Aug 2026 10:50:14 -0600 Subject: [PATCH 23/37] regen fvModels --- .../constant/fvModels | 16 ++++++------- .../constant/fvModels | 24 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tutorial_cases/loop_reactor_mixing_static/constant/fvModels b/tutorial_cases/loop_reactor_mixing_static/constant/fvModels index 268773e7..6b37fb16 100644 --- a/tutorial_cases/loop_reactor_mixing_static/constant/fvModels +++ b/tutorial_cases/loop_reactor_mixing_static/constant/fvModels @@ -90,7 +90,7 @@ codedSource if (Sax > 1e-30) { const double fvisc = Tls/Sax*alphaL[i]*g; - Usource[i][0] += -1.0*fvisc*V[i]; + Usource[i][0] -= -1.0*fvisc*V[i]; } const double rr = std::sqrt(d2-(dx)*(dx)); if (rr > 1e-3*Rmix && Ssw > 1e-30) @@ -99,10 +99,10 @@ codedSource const double uth = UL[i][1]*((-dz)/rr) + UL[i][2]*((dy)/rr); const double A0 = Qsw/Ssw; const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; - Usource[i][1] += 1.0*fsw*V[i]*((-dz)/rr); - Usource[i][2] += 1.0*fsw*V[i]*((dy)/rr); + Usource[i][1] -= 1.0*fsw*V[i]*((-dz)/rr); + Usource[i][2] -= 1.0*fsw*V[i]*((dy)/rr); const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; - Usource[i][0] += -1.0*fcp*V[i]; + Usource[i][0] -= -1.0*fcp*V[i]; } } } @@ -165,7 +165,7 @@ codedSource if (Sax > 1e-30) { const double fvisc = Tls/Sax*alphaL[i]*g; - Usource[i][0] += 1.0*fvisc*V[i]; + Usource[i][0] -= 1.0*fvisc*V[i]; } const double rr = std::sqrt(d2-(dx)*(dx)); if (rr > 1e-3*Rmix && Ssw > 1e-30) @@ -174,10 +174,10 @@ codedSource const double uth = UL[i][1]*((-dz)/rr) + UL[i][2]*((dy)/rr); const double A0 = Qsw/Ssw; const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; - Usource[i][1] += -1.0*fsw*V[i]*((-dz)/rr); - Usource[i][2] += -1.0*fsw*V[i]*((dy)/rr); + Usource[i][1] -= -1.0*fsw*V[i]*((-dz)/rr); + Usource[i][2] -= -1.0*fsw*V[i]*((dy)/rr); const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; - Usource[i][0] += 1.0*fcp*V[i]; + Usource[i][0] -= 1.0*fcp*V[i]; } } } diff --git a/tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels b/tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels index 8bad277c..51497b7c 100644 --- a/tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels +++ b/tutorial_cases/loop_reactor_mixing_swirl/constant/fvModels @@ -99,14 +99,14 @@ codedSource if (Sax > 1e-30) { const double fax = Tax/Sax*alphaL[i]*g; - Usource[i][0] += 1.0*fax*V[i]; + Usource[i][0] -= 1.0*fax*V[i]; } const double rr = std::sqrt(d2-(dx)*(dx)); if (rr > 1e-3*Rmix && Sth > 1e-30) { const double fth = Qsw/Sth*alphaL[i]*g; - Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); - Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + Usource[i][1] -= 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] -= 1.0*fth*V[i]*((dy)/rr); } } } @@ -178,14 +178,14 @@ codedSource if (Sax > 1e-30) { const double fax = Tax/Sax*alphaL[i]*g; - Usource[i][0] += 1.0*fax*V[i]; + Usource[i][0] -= 1.0*fax*V[i]; } const double rr = std::sqrt(d2-(dx)*(dx)); if (rr > 1e-3*Rmix && Sth > 1e-30) { const double fth = Qsw/Sth*alphaL[i]*g; - Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); - Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + Usource[i][1] -= 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] -= 1.0*fth*V[i]*((dy)/rr); } } } @@ -257,14 +257,14 @@ codedSource if (Sax > 1e-30) { const double fax = Tax/Sax*alphaL[i]*g; - Usource[i][0] += 1.0*fax*V[i]; + Usource[i][0] -= 1.0*fax*V[i]; } const double rr = std::sqrt(d2-(dx)*(dx)); if (rr > 1e-3*Rmix && Sth > 1e-30) { const double fth = Qsw/Sth*alphaL[i]*g; - Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); - Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + Usource[i][1] -= 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] -= 1.0*fth*V[i]*((dy)/rr); } } } @@ -336,14 +336,14 @@ codedSource if (Sax > 1e-30) { const double fax = Tax/Sax*alphaL[i]*g; - Usource[i][0] += -1.0*fax*V[i]; + Usource[i][0] -= -1.0*fax*V[i]; } const double rr = std::sqrt(d2-(dx)*(dx)); if (rr > 1e-3*Rmix && Sth > 1e-30) { const double fth = Qsw/Sth*alphaL[i]*g; - Usource[i][1] += 1.0*fth*V[i]*((-dz)/rr); - Usource[i][2] += 1.0*fth*V[i]*((dy)/rr); + Usource[i][1] -= 1.0*fth*V[i]*((-dz)/rr); + Usource[i][2] -= 1.0*fth*V[i]*((dy)/rr); } } } From 6525f17dbbd4936311aceed3e8861df896992f5c Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 19 Aug 2026 15:36:21 -0600 Subject: [PATCH 24/37] add per level timestepping option --- bird/preprocess/json_gen/generate_designs.py | 26 ++++++++ tests/preprocess/test_case_gen.py | 62 ++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index 57cbb5f3..a48a6858 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -674,6 +674,24 @@ def overwrite_ncores(case_folder, n): f.write(line) +def overwrite_controldict(case_folder, params): + """Rewrite time-stepping entries in system/controlDict. + + :param params: dict with any of ``deltaT``, ``endTime``, ``maxCo``, + ``maxDeltaT``; each present key overwrites its scalar entry. + """ + filename = os.path.join(case_folder, "system", "controlDict") + with open(filename, "r+") as f: + lines = f.readlines() + with open(filename, "w+") as f: + for line in lines: + key = line.strip().split(None, 1)[0] if line.strip() else "" + if key in params: + f.write(f"{key:<16}{params[key]};\n") + else: + f.write(line) + + def write_script_single( case_folder, account="gas2fuels", @@ -782,6 +800,7 @@ def generate_leveled_reactor_cases( account="gas2fuels", cores_per_sim=16, cores_per_node=128, + controldict_params=None, ): """Generate one scale level of the actuator-disk (ball) design sweep. @@ -799,6 +818,11 @@ def generate_leveled_reactor_cases( Each sim runs on `cores_per_sim` cores; the node-packing bundles fit ``cores_per_node // cores_per_sim`` sims per node. + + `controldict_params`, when given, is a dict of ``system/controlDict`` + scalar entries (any of ``deltaT``, ``endTime``, ``maxCo``, ``maxDeltaT``) + written into every case of this level via :func:`overwrite_controldict`; + left ``None`` the template controlDict is used unchanged. """ if not os.path.isabs(template_folder): template_folder = os.path.join( @@ -912,6 +936,8 @@ def generate_leveled_reactor_cases( cstar_h2=cstar_h2, ) overwrite_ncores(case_folder=case, n=cores_per_sim) + if controldict_params is not None: + overwrite_controldict(case_folder=case, params=controldict_params) overwrite_bubble_size_model(case_folder=case, constantD=constantD) write_script_single(case, account=account, cores=cores_per_sim) write_script_post_single(case, account=account) diff --git a/tests/preprocess/test_case_gen.py b/tests/preprocess/test_case_gen.py index fbb15b26..a47c86da 100644 --- a/tests/preprocess/test_case_gen.py +++ b/tests/preprocess/test_case_gen.py @@ -129,3 +129,65 @@ def random_sample(branches_com, branchcom_spots, config_dict={}): save_config_dict( f"{study_folder}/branchcom_spots.pkl", branchcom_spots ) + + +def test_overwrite_controldict(): + + template_control_dict = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "data_case_gen", + "loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", + "system", + "controlDict", + ) + + def read_scalars(control_dict_path): + scalars = {} + with open(control_dict_path, "r") as f: + for line in f: + tokens = line.strip().rstrip(";").split() + if len(tokens) == 2 and tokens[0] in ( + "deltaT", + "endTime", + "maxCo", + "maxDeltaT", + ): + scalars[tokens[0]] = tokens[1] + return scalars + + # a full params dict is applied verbatim to a fresh case copy + params = { + "maxCo": "0.25", + "maxDeltaT": "0.00025", + "deltaT": "1e-5", + "endTime": "100", + } + with tempfile.TemporaryDirectory() as tmpdirname: + case = os.path.join(tmpdirname, "case") + os.makedirs(os.path.join(case, "system")) + shutil.copy( + template_control_dict, + os.path.join(case, "system", "controlDict"), + ) + overwrite_controldict(case, params) + written = read_scalars(os.path.join(case, "system", "controlDict")) + assert written == params + + # a partial params dict overwrites only the keys it names + with tempfile.TemporaryDirectory() as tmpdirname: + case = os.path.join(tmpdirname, "case") + os.makedirs(os.path.join(case, "system")) + shutil.copy( + template_control_dict, + os.path.join(case, "system", "controlDict"), + ) + before = read_scalars(os.path.join(case, "system", "controlDict")) + overwrite_controldict(case, {"endTime": "50"}) + after = read_scalars(os.path.join(case, "system", "controlDict")) + assert after["endTime"] == "50" + assert after["deltaT"] == before["deltaT"] + assert after["maxCo"] == before["maxCo"] From 225384232aa89ac36350f2ef342e60945669c85d Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 19 Aug 2026 15:41:53 -0600 Subject: [PATCH 25/37] robust template folder resolution --- bird/preprocess/json_gen/generate_designs.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index a48a6858..77a8ab36 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -824,9 +824,20 @@ def generate_leveled_reactor_cases( written into every case of this level via :func:`overwrite_controldict`; left ``None`` the template controlDict is used unchanged. """ - if not os.path.isabs(template_folder): - template_folder = os.path.join( - BIRD_DIR, "preprocess", "data_case_gen", template_folder + # Resolve template_folder: use it if it points at a real directory (an + # absolute path or one relative to the cwd, e.g. "./template_kom"); + # otherwise fall back to a template bundled under data_case_gen. + bundled = os.path.join( + BIRD_DIR, "preprocess", "data_case_gen", template_folder + ) + if os.path.isdir(template_folder): + template_folder = os.path.abspath(template_folder) + elif os.path.isdir(bundled): + template_folder = bundled + else: + raise FileNotFoundError( + f"template_folder not found: {template_folder!r} is not a " + f"directory, nor is {bundled!r}" ) geom_dict = make_default_geom_dict_from_file( os.path.join(template_folder, "system", "mesh.json") From d14d7890c1b79c216a0cc0fba4a5a71fec4fa067 Mon Sep 17 00:00:00 2001 From: Malik Date: Thu, 20 Aug 2026 12:01:05 -0600 Subject: [PATCH 26/37] make sure outlets are read from template json --- bird/preprocess/json_gen/generate_designs.py | 30 +++---- tests/preprocess/test_case_gen.py | 84 ++++++++++++++++++++ 2 files changed, 99 insertions(+), 15 deletions(-) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index 77a8ab36..71002048 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -1,3 +1,4 @@ +import json import os import pickle import shutil @@ -705,7 +706,7 @@ def write_script_single( f.write("#SBATCH --job-name=lev_single\n") f.write("#SBATCH --nodes=1\n") f.write(f"#SBATCH --ntasks-per-node={cores}\n") - f.write("#SBATCH --time=07:59:00\n") + f.write("#SBATCH --time=47:59:00\n") f.write(f"#SBATCH --account={account}\n\n") f.write("bash presteps.sh\n") f.write(f"source {ofbashrc}\n") @@ -759,7 +760,7 @@ def write_pack_scripts( f.write(f"#SBATCH --job-name=lev_{pack_name}\n") f.write("#SBATCH --nodes=1\n") f.write("#SBATCH --exclusive\n") - f.write("#SBATCH --time=07:59:00\n") + f.write("#SBATCH --time=47:59:00\n") f.write(f"#SBATCH --account={account}\n\n") f.write(f"source {ofbashrc}\n\n") f.write("run_sim () {\n") @@ -864,19 +865,18 @@ def generate_leveled_reactor_cases( case = os.path.join(study_folder, sim_folder) shutil.copytree(template_folder, case) - bc_dict = {"inlets": [], "outlets": []} - for br in (6, 4): - bc_dict["outlets"].append( - { - "branch_id": br, - "type": "circle", - "frac_space": 1, - "normal_dir": 1, - "radius": 0.4, - "nelements": 50, - "block_pos": "top", - } - ) + # Outlets are template-driven: read from the template's + # inlets_outlets.json so each head shape carries its own outlet -- the + # square-head templates define a full-face rectangle covering the whole + # top patch, while the baseline templates define the two disk outlets on + # branches 6 and 4. Inlets stay config-driven, built below. + with open( + os.path.join(template_folder, "system", "inlets_outlets.json") + ) as f: + bc_dict = { + "inlets": [], + "outlets": json.load(f).get("outlets", []), + } for branch in (0, 1, 2): for iind in np.argwhere(config_dict[sim_id][branch] == 1)[:, 0]: bc_dict["inlets"].append( diff --git a/tests/preprocess/test_case_gen.py b/tests/preprocess/test_case_gen.py index a47c86da..f060c61e 100644 --- a/tests/preprocess/test_case_gen.py +++ b/tests/preprocess/test_case_gen.py @@ -1,3 +1,4 @@ +import json import os import pickle import shutil @@ -131,6 +132,89 @@ def random_sample(branches_com, branchcom_spots, config_dict={}): ) +def test_generate_leveled_reactor_cases(): + # Outlets must be template-driven (read from the template's + # inlets_outlets.json), not hardcoded: a template carrying a distinctive + # full-face rectangle outlet must reproduce that rectangle in every + # generated case. Under the old hardcoded (branch 6 & 4 disks) path this + # assertion fails. + BIRD_CASE_GEN_DATA_DIR = os.path.join( + Path(__file__).parent, + "..", + "..", + "bird", + "preprocess", + "data_case_gen", + ) + bundled = os.path.join( + BIRD_CASE_GEN_DATA_DIR, + "loop_reactor_pbe_dynmix_nonstat_headbranch_scaleup", + ) + rectangle = { + "type": "rectangle", + "normal_dir": 1, + "centx": 0.5, + "centy": 11.0, + "centz": 2.5, + "width": 3.0, + "height": 7.0, + } + + branchcom_spots = { + 0: np.linspace(0.2, 0.8, 4), + 1: np.linspace(0.2, 0.8, 3), + 2: np.linspace(0.2, 0.8, 4), + } + branches_com = [0, 1, 2] + config_dict = {} + for _ in range(10): + config = { + b: np.random.choice([0, 1, 2], size=len(branchcom_spots[b])) + for b in branches_com + } + if check_config(config): + config_dict[len(config_dict)] = config + if len(config_dict) >= 1: + break + mixer_params = { + "Np": 6, + "Vtip": 1.5, + "sigma": 0.35, + "radius": 0.4, + "sign": {0: "+", 1: "+", 2: "-"}, + "swirl_sign": {0: "+", 1: "+", 2: "-"}, + } + + with tempfile.TemporaryDirectory() as tmpdirname: + # a template whose outlet differs from the old hardcoded disks + template = os.path.join(tmpdirname, "template") + shutil.copytree(bundled, template) + io_path = os.path.join(template, "system", "inlets_outlets.json") + with open(io_path) as f: + io = json.load(f) + io["outlets"] = [rectangle] + with open(io_path, "w") as f: + json.dump(io, f) + + study = os.path.join(tmpdirname, "study") + generate_leveled_reactor_cases( + config_dict, + branchcom_spots, + scale=0.05, + n_sim=1, + study_folder=study, + mixer_params=mixer_params, + template_folder=template, + constantD=True, + start_time=1, + ) + with open( + os.path.join(study, "Sim_0000", "system", "inlets_outlets.json") + ) as f: + generated = json.load(f) + assert generated["outlets"] == [rectangle] + + def test_overwrite_controldict(): template_control_dict = os.path.join( From da321fc2b80dd4979b4e8fc2b6ed39e259f37520 Mon Sep 17 00:00:00 2001 From: Malik Date: Fri, 21 Aug 2026 17:39:06 -0600 Subject: [PATCH 27/37] use random sampling or shared pool depending on if we need to compute correlations --- bird/preprocess/json_gen/generate_designs.py | 88 +++++++++++++++++++- tests/preprocess/test_case_gen.py | 69 +++++++++++++++ 2 files changed, 153 insertions(+), 4 deletions(-) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index 71002048..0ff5d5aa 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -43,6 +43,12 @@ def compare_config(config1, config2): def check_config(config): + """Accept a design only if it has at least one sparger. + + Choice value ``1`` marks a sparger (a bottom gas inlet); ``0`` a mixer, + ``2`` nothing. A design with no sparger is rejected, so + :func:`sample_placement_designs` never keeps one. + """ success = False inlet_exist = False for key in config: @@ -67,6 +73,73 @@ def load_config_dict(filename): return config_dict +def sample_placement_designs( + branches_com, + branchcom_spots, + n_designs, + choices=(0, 1, 2), + max_attempts=1_000_000, +): + """Randomly sample `n_designs` distinct, valid placement designs. + + Draws are non-deterministic (the caller must NOT seed for reproducibility). + Each design maps ``branch_id -> array`` of per-spot choices; + :func:`check_config` keeps only designs with at least one inlet and + :func:`compare_config` rejects duplicates. Keys are contiguous ``0..n-1``. + + :param branches_com: branch ids on which choices are placed. + :param branchcom_spots: ``branch_id -> array`` of candidate spot fractions. + :param n_designs: number of distinct valid designs to return. + :param choices: per-spot categorical choices (e.g. mixer/sparger/none). + :param max_attempts: give up after this many draws. + """ + config_dict = {} + attempts = 0 + while len(config_dict) < n_designs and attempts < max_attempts: + config = { + b: np.random.choice(choices, size=len(branchcom_spots[b])) + for b in branches_com + } + attempts += 1 + if any(compare_config(config_dict[k], config) for k in config_dict): + continue + if check_config(config): + config_dict[len(config_dict)] = config + if len(config_dict) < n_designs: + raise RuntimeError( + f"only found {len(config_dict)} designs in {attempts} attempts" + ) + return config_dict + + +def load_or_sample_designs( + design_file, + branches_com, + branchcom_spots, + n_designs, + choices=(0, 1, 2), +): + """Borrow designs from `design_file` if it exists, else sample and save. + + The first sweep run samples a fresh random design set (see + :func:`sample_placement_designs`) and pickles it to `design_file`; every + later sweep pointed at the same file loads it instead of re-sampling, so + ``Sim_i`` is the same design across all sweeps without relying on a fixed + seed. `design_file` should hold enough designs for the largest sweep -- + downstream slicing (``sorted(config_dict)[:n_sim]``) selects the first + `n_sim`. + """ + if os.path.exists(design_file): + logger.info(f"Borrowing designs from existing {design_file}") + return load_config_dict(design_file) + logger.info(f"Sampling {n_designs} designs and saving to {design_file}") + config_dict = sample_placement_designs( + branches_com, branchcom_spots, n_designs, choices=choices + ) + save_config_dict(design_file, config_dict) + return config_dict + + def write_script_start(filename, n): with open(filename, "w+") as f: for i in range(n): @@ -698,6 +771,7 @@ def write_script_single( account="gas2fuels", cores=4, solver="birdmultiphaseEulerFoam", + walltime="47:59:00", ): """Write a per-case SLURM script (``script_single``) running one case.""" ofbashrc = "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" @@ -706,7 +780,7 @@ def write_script_single( f.write("#SBATCH --job-name=lev_single\n") f.write("#SBATCH --nodes=1\n") f.write(f"#SBATCH --ntasks-per-node={cores}\n") - f.write("#SBATCH --time=47:59:00\n") + f.write(f"#SBATCH --time={walltime}\n") f.write(f"#SBATCH --account={account}\n\n") f.write("bash presteps.sh\n") f.write(f"source {ofbashrc}\n") @@ -739,6 +813,7 @@ def write_pack_scripts( cores_per_sim=4, account="gas2fuels", solver="birdmultiphaseEulerFoam", + walltime="47:59:00", ): """Write node-packing scripts (Option A): pack_XXX bundles + submit_all.sh. @@ -760,7 +835,7 @@ def write_pack_scripts( f.write(f"#SBATCH --job-name=lev_{pack_name}\n") f.write("#SBATCH --nodes=1\n") f.write("#SBATCH --exclusive\n") - f.write("#SBATCH --time=47:59:00\n") + f.write(f"#SBATCH --time={walltime}\n") f.write(f"#SBATCH --account={account}\n\n") f.write(f"source {ofbashrc}\n\n") f.write("run_sim () {\n") @@ -802,6 +877,7 @@ def generate_leveled_reactor_cases( cores_per_sim=16, cores_per_node=128, controldict_params=None, + walltime="47:59:00", ): """Generate one scale level of the actuator-disk (ball) design sweep. @@ -818,7 +894,8 @@ def generate_leveled_reactor_cases( used unchanged. Each sim runs on `cores_per_sim` cores; the node-packing bundles fit - ``cores_per_node // cores_per_sim`` sims per node. + ``cores_per_node // cores_per_sim`` sims per node. `walltime` is the SLURM + ``--time`` written into both the per-case and node-packing scripts. `controldict_params`, when given, is a dict of ``system/controlDict`` scalar entries (any of ``deltaT``, ``endTime``, ``maxCo``, ``maxDeltaT``) @@ -950,7 +1027,9 @@ def generate_leveled_reactor_cases( if controldict_params is not None: overwrite_controldict(case_folder=case, params=controldict_params) overwrite_bubble_size_model(case_folder=case, constantD=constantD) - write_script_single(case, account=account, cores=cores_per_sim) + write_script_single( + case, account=account, cores=cores_per_sim, walltime=walltime + ) write_script_post_single(case, account=account) write_foam_stub(case) @@ -962,6 +1041,7 @@ def generate_leveled_reactor_cases( sims_per_node=sims_per_node, cores_per_sim=cores_per_sim, account=account, + walltime=walltime, ) write_prep(os.path.join(study_folder, "prep.sh"), n_sim) save_config_dict(os.path.join(study_folder, "configs.pkl"), config_dict) diff --git a/tests/preprocess/test_case_gen.py b/tests/preprocess/test_case_gen.py index f060c61e..025ba510 100644 --- a/tests/preprocess/test_case_gen.py +++ b/tests/preprocess/test_case_gen.py @@ -275,3 +275,72 @@ def read_scalars(control_dict_path): assert after["endTime"] == "50" assert after["deltaT"] == before["deltaT"] assert after["maxCo"] == before["maxCo"] + + +def test_sample_placement_designs(): + branchcom_spots = { + 0: np.linspace(0.2, 0.8, 4), + 1: np.linspace(0.2, 0.8, 3), + 2: np.linspace(0.2, 0.8, 4), + } + branches_com = [0, 1, 2] + n_designs = 30 + config_dict = sample_placement_designs( + branches_com, branchcom_spots, n_designs + ) + # exactly n_designs, contiguous keys, one array per branch of the right size + assert len(config_dict) == n_designs + assert sorted(config_dict) == list(range(n_designs)) + for design in config_dict.values(): + for b in branches_com: + assert design[b].shape == (len(branchcom_spots[b]),) + # every design is valid (>=1 inlet) and all are distinct + assert all(check_config(d) for d in config_dict.values()) + for i in range(n_designs): + for j in range(i + 1, n_designs): + assert not compare_config(config_dict[i], config_dict[j]) + # sampling is non-deterministic: an independent draw differs + other = sample_placement_designs(branches_com, branchcom_spots, n_designs) + assert any( + not np.array_equal(config_dict[k][b], other[k][b]) + for k in range(n_designs) + for b in branches_com + ) + # too many designs for the space raises rather than looping forever + try: + sample_placement_designs( + branches_com, branchcom_spots, n_designs, max_attempts=5 + ) + raised = False + except RuntimeError: + raised = True + assert raised + + +def test_load_or_sample_designs(): + branchcom_spots = { + 0: np.linspace(0.2, 0.8, 4), + 1: np.linspace(0.2, 0.8, 3), + 2: np.linspace(0.2, 0.8, 4), + } + branches_com = [0, 1, 2] + with tempfile.TemporaryDirectory() as tmpdirname: + design_file = os.path.join(tmpdirname, "designs.pkl") + # first call: no file yet -> samples and saves + first = load_or_sample_designs( + design_file, branches_com, branchcom_spots, n_designs=20 + ) + assert os.path.exists(design_file) + assert len(first) == 20 + # second call: file exists -> borrows the identical set (n_designs + # is ignored once a file is present, so a later sweep asking for + # fewer still reuses the saved pool) + borrowed = load_or_sample_designs( + design_file, branches_com, branchcom_spots, n_designs=5 + ) + assert len(borrowed) == 20 + assert all( + np.array_equal(first[k][b], borrowed[k][b]) + for k in first + for b in branches_com + ) From 2a5ca8f42435b8be963da48689162f866a022e29 Mon Sep 17 00:00:00 2001 From: Malik Date: Fri, 21 Aug 2026 17:50:26 -0600 Subject: [PATCH 28/37] also pack post proc --- tests/preprocess/test_case_gen.py | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/preprocess/test_case_gen.py b/tests/preprocess/test_case_gen.py index 025ba510..2abe5791 100644 --- a/tests/preprocess/test_case_gen.py +++ b/tests/preprocess/test_case_gen.py @@ -344,3 +344,43 @@ def test_load_or_sample_designs(): for k in first for b in branches_com ) + + +def test_write_pack_post_scripts(): + sim_ids = list(range(5)) + with tempfile.TemporaryDirectory() as tmpdirname: + write_pack_post_scripts( + tmpdirname, + sim_ids, + sims_per_node=2, + account="catmod", + walltime="1:00:00", + ) + # 5 sims, 2 per node -> 3 bundles (2, 2, 1) + packs = sorted( + p for p in os.listdir(tmpdirname) if p.startswith("pack_post_") + ) + assert packs == ["pack_post_000", "pack_post_001", "pack_post_002"] + + first = Path(tmpdirname, "pack_post_000").read_text() + # header: 1 core per sim in the bundle, catmod, 1h + assert "#SBATCH --ntasks-per-node=2" in first + assert "#SBATCH --account=catmod" in first + assert "#SBATCH --time=1:00:00" in first + # the post pipeline, once per sim in the bundle + assert "reconstructPar -newTimes" in first + assert "python read_history.py -cr .. -cn local -df data" in first + assert "python get_qoi.py" in first + assert first.count("run_post Sim_") == 2 + assert first.rstrip().endswith("wait") + # last bundle has the single trailing sim + last = Path(tmpdirname, "pack_post_002").read_text() + assert "#SBATCH --ntasks-per-node=1" in last + assert last.count("run_post Sim_") == 1 + + submit = Path(tmpdirname, "submit_all_post.sh").read_text() + assert submit.splitlines() == [ + "sbatch pack_post_000", + "sbatch pack_post_001", + "sbatch pack_post_002", + ] From 0e70925895c2224e53b2d0c9bf4407370783b2d8 Mon Sep 17 00:00:00 2001 From: Malik Date: Fri, 21 Aug 2026 17:55:24 -0600 Subject: [PATCH 29/37] pack post proc together --- bird/preprocess/json_gen/generate_designs.py | 65 ++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/bird/preprocess/json_gen/generate_designs.py b/bird/preprocess/json_gen/generate_designs.py index 0ff5d5aa..254b722f 100644 --- a/bird/preprocess/json_gen/generate_designs.py +++ b/bird/preprocess/json_gen/generate_designs.py @@ -859,6 +859,64 @@ def write_pack_scripts( f.write(f"sbatch {pack_name}\n") +def write_pack_post_scripts( + study_folder, + sim_ids, + sims_per_node=26, + account="gas2fuels", + walltime="1:00:00", +): + """Write post-processing packing scripts: pack_post_XXX + submit_all_post.sh. + + Mirrors :func:`write_pack_scripts` one-to-one (same `sims_per_node` + bundling, so ``pack_post_b`` post-processes exactly the sims in + ``pack_b``), but each sim runs the QoI pipeline on a single core: + ``reconstructPar`` then ``read_history.py`` + ``get_qoi.py`` under the + bird_mixer conda env. The bundle requests one core per sim + (``ntasks-per-node = len(bundle)``) and its sims run concurrently, each in + its own subshell so their conda state stays isolated. ``submit_all_post.sh`` + sbatches every bundle. + """ + ofbashrc = "/projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc" + conda_env = "/projects/gas2fuels/conda_env/bird_mixer/" + bundles = [ + sim_ids[i : i + sims_per_node] + for i in range(0, len(sim_ids), sims_per_node) + ] + pack_names = [] + for b, bundle in enumerate(bundles): + pack_name = f"pack_post_{b:03}" + pack_names.append(pack_name) + with open(os.path.join(study_folder, pack_name), "w+") as f: + f.write("#!/bin/bash\n") + f.write(f"#SBATCH --job-name=lev_{pack_name}\n") + f.write("#SBATCH --nodes=1\n") + f.write(f"#SBATCH --ntasks-per-node={len(bundle)}\n") + f.write(f"#SBATCH --time={walltime}\n") + f.write(f"#SBATCH --account={account}\n\n") + f.write("run_post () {\n") + f.write("(\n") + f.write('\tcd "$1"\n') + f.write(f"\tsource {ofbashrc}\n") + f.write("\treconstructPar -newTimes > log.reconstruct 2>&1\n") + f.write("\tmodule load conda\n") + f.write(f"\tconda activate {conda_env}\n") + f.write( + "\tpython read_history.py -cr .. -cn local -df data" + " > log.readhist 2>&1\n" + ) + f.write("\tpython get_qoi.py > log.getqoi 2>&1\n") + f.write("\tconda deactivate\n") + f.write(") &\n") + f.write("}\n\n") + for sim_id in bundle: + f.write(f"run_post {id2simfolder(sim_id)}\n") + f.write("wait\n") + with open(os.path.join(study_folder, "submit_all_post.sh"), "w+") as f: + for pack_name in pack_names: + f.write(f"sbatch {pack_name}\n") + + def generate_leveled_reactor_cases( config_dict, branchcom_spots, @@ -1043,6 +1101,13 @@ def generate_leveled_reactor_cases( account=account, walltime=walltime, ) + # post-processing packs mirror the run packs one-to-one (1 core per sim) + write_pack_post_scripts( + study_folder, + sim_ids, + sims_per_node=cores_per_node, + account=account, + ) write_prep(os.path.join(study_folder, "prep.sh"), n_sim) save_config_dict(os.path.join(study_folder, "configs.pkl"), config_dict) save_config_dict( From bdf264fbd7714ef92119310f5409864b8370b6a4 Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 1 Sep 2026 15:56:25 -0600 Subject: [PATCH 30/37] add uloop validation case --- .github/workflows/ci.yml | 15 + .../uloop_valadbeigy_exp1/0.orig/N2.gas | 45 ++ .../uloop_valadbeigy_exp1/0.orig/N2.liquid | 61 +++ .../uloop_valadbeigy_exp1/0.orig/O2.gas | 45 ++ .../uloop_valadbeigy_exp1/0.orig/O2.liquid | 62 +++ .../uloop_valadbeigy_exp1/0.orig/T.gas | 48 ++ .../uloop_valadbeigy_exp1/0.orig/T.liquid | 67 +++ .../uloop_valadbeigy_exp1/0.orig/U.gas | 65 +++ .../uloop_valadbeigy_exp1/0.orig/U.liquid | 70 +++ .../uloop_valadbeigy_exp1/0.orig/Ydefault.gas | 44 ++ .../0.orig/Ydefault.liquid | 64 +++ .../uloop_valadbeigy_exp1/0.orig/Z.liquid | 62 +++ .../uloop_valadbeigy_exp1/0.orig/alpha.gas | 65 +++ .../uloop_valadbeigy_exp1/0.orig/alpha.liquid | 62 +++ .../uloop_valadbeigy_exp1/0.orig/alphat.gas | 46 ++ .../0.orig/alphat.liquid | 47 ++ .../uloop_valadbeigy_exp1/0.orig/k.gas | 43 ++ .../uloop_valadbeigy_exp1/0.orig/k.liquid | 63 +++ .../uloop_valadbeigy_exp1/0.orig/nut.gas | 44 ++ .../uloop_valadbeigy_exp1/0.orig/nut.liquid | 44 ++ .../uloop_valadbeigy_exp1/0.orig/omega.gas | 43 ++ .../uloop_valadbeigy_exp1/0.orig/omega.liquid | 63 +++ .../uloop_valadbeigy_exp1/0.orig/p | 44 ++ .../uloop_valadbeigy_exp1/0.orig/p_rgh | 47 ++ .../uloop_valadbeigy_exp1/Allclean | 24 + .../uloop_valadbeigy_exp1/build_uloop_hex.py | 418 ++++++++++++++++++ .../uloop_valadbeigy_exp1/constant/fvModels | 266 +++++++++++ .../uloop_valadbeigy_exp1/constant/g | 21 + .../uloop_valadbeigy_exp1/constant/globalVars | 70 +++ .../constant/globalVars_temp | 70 +++ .../constant/momentumTransport.gas | 26 ++ .../constant/momentumTransport.liquid | 27 ++ .../constant/phaseProperties | 261 +++++++++++ .../constant/phaseProperties_constantd | 261 +++++++++++ .../constant/thermophysicalProperties.gas | 89 ++++ .../constant/thermophysicalProperties.liquid | 132 ++++++ .../uloop_valadbeigy_exp1/get_mixing_time.py | 209 +++++++++ .../uloop_valadbeigy_exp1/presteps.sh | 77 ++++ .../uloop_valadbeigy_exp1/run.sh | 75 ++++ .../uloop_valadbeigy_exp1/script | 16 + .../uloop_valadbeigy_exp1/script_post | 14 + .../uloop_valadbeigy_exp1/stitch_and_check.sh | 87 ++++ .../uloop_valadbeigy_exp1/system/controlDict | 95 ++++ .../system/createPatchDict | 35 ++ .../system/decomposeParDict | 30 ++ .../system/fvConstraints | 56 +++ .../uloop_valadbeigy_exp1/system/fvSchemes | 76 ++++ .../uloop_valadbeigy_exp1/system/fvSolution | 121 +++++ .../system/inlets_outlets.json | 24 + .../uloop_valadbeigy_exp1/system/mixers.json | 45 ++ .../system/setFieldsDict | 43 ++ .../uloop_valadbeigy_exp1/writeGlobalVars.py | 75 ++++ .../uloop_valadbeigy_exp2/0.orig/N2.gas | 45 ++ .../uloop_valadbeigy_exp2/0.orig/N2.liquid | 61 +++ .../uloop_valadbeigy_exp2/0.orig/O2.gas | 45 ++ .../uloop_valadbeigy_exp2/0.orig/O2.liquid | 62 +++ .../uloop_valadbeigy_exp2/0.orig/T.gas | 48 ++ .../uloop_valadbeigy_exp2/0.orig/T.liquid | 67 +++ .../uloop_valadbeigy_exp2/0.orig/U.gas | 65 +++ .../uloop_valadbeigy_exp2/0.orig/U.liquid | 70 +++ .../uloop_valadbeigy_exp2/0.orig/Ydefault.gas | 44 ++ .../0.orig/Ydefault.liquid | 64 +++ .../uloop_valadbeigy_exp2/0.orig/Z.liquid | 62 +++ .../uloop_valadbeigy_exp2/0.orig/alpha.gas | 65 +++ .../uloop_valadbeigy_exp2/0.orig/alpha.liquid | 62 +++ .../uloop_valadbeigy_exp2/0.orig/alphat.gas | 46 ++ .../0.orig/alphat.liquid | 47 ++ .../uloop_valadbeigy_exp2/0.orig/k.gas | 43 ++ .../uloop_valadbeigy_exp2/0.orig/k.liquid | 63 +++ .../uloop_valadbeigy_exp2/0.orig/nut.gas | 44 ++ .../uloop_valadbeigy_exp2/0.orig/nut.liquid | 44 ++ .../uloop_valadbeigy_exp2/0.orig/omega.gas | 43 ++ .../uloop_valadbeigy_exp2/0.orig/omega.liquid | 63 +++ .../uloop_valadbeigy_exp2/0.orig/p | 44 ++ .../uloop_valadbeigy_exp2/0.orig/p_rgh | 47 ++ .../uloop_valadbeigy_exp2/Allclean | 24 + .../uloop_valadbeigy_exp2/build_uloop_hex.py | 418 ++++++++++++++++++ .../uloop_valadbeigy_exp2/constant/fvModels | 266 +++++++++++ .../uloop_valadbeigy_exp2/constant/g | 21 + .../uloop_valadbeigy_exp2/constant/globalVars | 70 +++ .../constant/globalVars_temp | 70 +++ .../constant/momentumTransport.gas | 26 ++ .../constant/momentumTransport.liquid | 27 ++ .../constant/phaseProperties | 261 +++++++++++ .../constant/phaseProperties_constantd | 261 +++++++++++ .../constant/thermophysicalProperties.gas | 89 ++++ .../constant/thermophysicalProperties.liquid | 132 ++++++ .../uloop_valadbeigy_exp2/get_mixing_time.py | 209 +++++++++ .../uloop_valadbeigy_exp2/presteps.sh | 77 ++++ .../uloop_valadbeigy_exp2/run.sh | 75 ++++ .../uloop_valadbeigy_exp2/script | 16 + .../uloop_valadbeigy_exp2/script_post | 14 + .../uloop_valadbeigy_exp2/stitch_and_check.sh | 87 ++++ .../uloop_valadbeigy_exp2/system/controlDict | 95 ++++ .../system/createPatchDict | 35 ++ .../system/decomposeParDict | 30 ++ .../system/fvConstraints | 56 +++ .../uloop_valadbeigy_exp2/system/fvSchemes | 76 ++++ .../uloop_valadbeigy_exp2/system/fvSolution | 121 +++++ .../system/inlets_outlets.json | 24 + .../uloop_valadbeigy_exp2/system/mixers.json | 45 ++ .../system/setFieldsDict | 43 ++ .../uloop_valadbeigy_exp2/writeGlobalVars.py | 75 ++++ .../uloop_valadbeigy_exp3/0.orig/N2.gas | 45 ++ .../uloop_valadbeigy_exp3/0.orig/N2.liquid | 61 +++ .../uloop_valadbeigy_exp3/0.orig/O2.gas | 45 ++ .../uloop_valadbeigy_exp3/0.orig/O2.liquid | 62 +++ .../uloop_valadbeigy_exp3/0.orig/T.gas | 48 ++ .../uloop_valadbeigy_exp3/0.orig/T.liquid | 67 +++ .../uloop_valadbeigy_exp3/0.orig/U.gas | 65 +++ .../uloop_valadbeigy_exp3/0.orig/U.liquid | 70 +++ .../uloop_valadbeigy_exp3/0.orig/Ydefault.gas | 44 ++ .../0.orig/Ydefault.liquid | 64 +++ .../uloop_valadbeigy_exp3/0.orig/Z.liquid | 62 +++ .../uloop_valadbeigy_exp3/0.orig/alpha.gas | 65 +++ .../uloop_valadbeigy_exp3/0.orig/alpha.liquid | 62 +++ .../uloop_valadbeigy_exp3/0.orig/alphat.gas | 46 ++ .../0.orig/alphat.liquid | 47 ++ .../uloop_valadbeigy_exp3/0.orig/k.gas | 43 ++ .../uloop_valadbeigy_exp3/0.orig/k.liquid | 63 +++ .../uloop_valadbeigy_exp3/0.orig/nut.gas | 44 ++ .../uloop_valadbeigy_exp3/0.orig/nut.liquid | 44 ++ .../uloop_valadbeigy_exp3/0.orig/omega.gas | 43 ++ .../uloop_valadbeigy_exp3/0.orig/omega.liquid | 63 +++ .../uloop_valadbeigy_exp3/0.orig/p | 44 ++ .../uloop_valadbeigy_exp3/0.orig/p_rgh | 47 ++ .../uloop_valadbeigy_exp3/Allclean | 24 + .../uloop_valadbeigy_exp3/build_uloop_hex.py | 418 ++++++++++++++++++ .../uloop_valadbeigy_exp3/constant/fvModels | 266 +++++++++++ .../uloop_valadbeigy_exp3/constant/g | 21 + .../uloop_valadbeigy_exp3/constant/globalVars | 70 +++ .../constant/globalVars_temp | 70 +++ .../constant/momentumTransport.gas | 26 ++ .../constant/momentumTransport.liquid | 27 ++ .../constant/phaseProperties | 261 +++++++++++ .../constant/phaseProperties_constantd | 261 +++++++++++ .../constant/thermophysicalProperties.gas | 89 ++++ .../constant/thermophysicalProperties.liquid | 132 ++++++ .../uloop_valadbeigy_exp3/get_mixing_time.py | 209 +++++++++ .../uloop_valadbeigy_exp3/presteps.sh | 77 ++++ .../uloop_valadbeigy_exp3/run.sh | 75 ++++ .../uloop_valadbeigy_exp3/script | 16 + .../uloop_valadbeigy_exp3/script_post | 14 + .../uloop_valadbeigy_exp3/stitch_and_check.sh | 87 ++++ .../uloop_valadbeigy_exp3/system/controlDict | 95 ++++ .../system/createPatchDict | 35 ++ .../system/decomposeParDict | 30 ++ .../system/fvConstraints | 56 +++ .../uloop_valadbeigy_exp3/system/fvSchemes | 76 ++++ .../uloop_valadbeigy_exp3/system/fvSolution | 121 +++++ .../system/inlets_outlets.json | 24 + .../uloop_valadbeigy_exp3/system/mixers.json | 45 ++ .../system/setFieldsDict | 43 ++ .../uloop_valadbeigy_exp3/writeGlobalVars.py | 75 ++++ pyproject.toml | 1 + 155 files changed, 11977 insertions(+) create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/T.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/T.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/U.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/U.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/Z.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/alphat.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/alphat.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/k.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/k.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/p create mode 100644 experimental_cases/uloop_valadbeigy_exp1/0.orig/p_rgh create mode 100755 experimental_cases/uloop_valadbeigy_exp1/Allclean create mode 100644 experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/fvModels create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/g create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/globalVars create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/globalVars_temp create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/momentumTransport.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/momentumTransport.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/phaseProperties create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/phaseProperties_constantd create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py create mode 100755 experimental_cases/uloop_valadbeigy_exp1/presteps.sh create mode 100755 experimental_cases/uloop_valadbeigy_exp1/run.sh create mode 100644 experimental_cases/uloop_valadbeigy_exp1/script create mode 100755 experimental_cases/uloop_valadbeigy_exp1/script_post create mode 100755 experimental_cases/uloop_valadbeigy_exp1/stitch_and_check.sh create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/controlDict create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/createPatchDict create mode 100755 experimental_cases/uloop_valadbeigy_exp1/system/decomposeParDict create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/fvConstraints create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/fvSchemes create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/fvSolution create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/inlets_outlets.json create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/mixers.json create mode 100644 experimental_cases/uloop_valadbeigy_exp1/system/setFieldsDict create mode 100644 experimental_cases/uloop_valadbeigy_exp1/writeGlobalVars.py create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/T.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/T.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/U.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/U.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/Z.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/alphat.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/alphat.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/k.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/k.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/p create mode 100644 experimental_cases/uloop_valadbeigy_exp2/0.orig/p_rgh create mode 100755 experimental_cases/uloop_valadbeigy_exp2/Allclean create mode 100644 experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/fvModels create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/g create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/globalVars create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/globalVars_temp create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/momentumTransport.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/momentumTransport.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/phaseProperties create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/phaseProperties_constantd create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py create mode 100755 experimental_cases/uloop_valadbeigy_exp2/presteps.sh create mode 100755 experimental_cases/uloop_valadbeigy_exp2/run.sh create mode 100644 experimental_cases/uloop_valadbeigy_exp2/script create mode 100755 experimental_cases/uloop_valadbeigy_exp2/script_post create mode 100755 experimental_cases/uloop_valadbeigy_exp2/stitch_and_check.sh create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/controlDict create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/createPatchDict create mode 100755 experimental_cases/uloop_valadbeigy_exp2/system/decomposeParDict create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/fvConstraints create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/fvSchemes create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/fvSolution create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/inlets_outlets.json create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/mixers.json create mode 100644 experimental_cases/uloop_valadbeigy_exp2/system/setFieldsDict create mode 100644 experimental_cases/uloop_valadbeigy_exp2/writeGlobalVars.py create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/T.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/T.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/U.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/U.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/Z.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/alphat.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/alphat.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/k.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/k.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/p create mode 100644 experimental_cases/uloop_valadbeigy_exp3/0.orig/p_rgh create mode 100755 experimental_cases/uloop_valadbeigy_exp3/Allclean create mode 100644 experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/fvModels create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/g create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/globalVars create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/globalVars_temp create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/momentumTransport.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/momentumTransport.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/phaseProperties create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/phaseProperties_constantd create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.gas create mode 100644 experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.liquid create mode 100644 experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py create mode 100755 experimental_cases/uloop_valadbeigy_exp3/presteps.sh create mode 100755 experimental_cases/uloop_valadbeigy_exp3/run.sh create mode 100644 experimental_cases/uloop_valadbeigy_exp3/script create mode 100755 experimental_cases/uloop_valadbeigy_exp3/script_post create mode 100755 experimental_cases/uloop_valadbeigy_exp3/stitch_and_check.sh create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/controlDict create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/createPatchDict create mode 100755 experimental_cases/uloop_valadbeigy_exp3/system/decomposeParDict create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/fvConstraints create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/fvSchemes create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/fvSolution create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/inlets_outlets.json create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/mixers.json create mode 100644 experimental_cases/uloop_valadbeigy_exp3/system/setFieldsDict create mode 100644 experimental_cases/uloop_valadbeigy_exp3/writeGlobalVars.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b396279..da95571b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -222,6 +222,21 @@ jobs: cd experimental_cases/deckwer19 bash run.sh cd ../../ + - name: Run uloop_valadbeigy_exp1 + run: | + cd experimental_cases/ + bash run.sh + cd ../../ + - name: Run uloop_valadbeigy_exp2 + run: | + cd experimental_cases/ + bash run.sh + cd ../../ + - name: Run uloop_valadbeigy_exp3 + run: | + cd experimental_cases/ + bash run.sh + cd ../../ - name: Run side sparger tutorial run: | cd tutorial_cases/side_sparger diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.gas new file mode 100644 index 00000000..d52e4d25 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.gas @@ -0,0 +1,45 @@ +/*--------------------------------*- 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 $f_N2; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $f_N2; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.liquid new file mode 100644 index 00000000..9cea536f --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/N2.liquid @@ -0,0 +1,61 @@ +/*--------------------------------*- 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.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeN2liq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.gas new file mode 100644 index 00000000..8225d524 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.gas @@ -0,0 +1,45 @@ +/*--------------------------------*- 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 O2.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $f_O2; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $f_O2; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.liquid new file mode 100644 index 00000000..2cfefccb --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/O2.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 O2.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeO2liq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/T.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/T.gas new file mode 100644 index 00000000..8388cb6d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/T.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 T.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $T0; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue $internalField; + value $internalField; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/T.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/T.liquid new file mode 100644 index 00000000..89c826e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/T.liquid @@ -0,0 +1,67 @@ +/*--------------------------------*- 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]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform $T0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $T0; + name dyeTliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue $internalField; + value $internalField; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/U.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/U.gas new file mode 100644 index 00000000..27a4d894 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/U.gas @@ -0,0 +1,65 @@ +/*--------------------------------*- 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 0.0); + +#include "${FOAM_CASE}/constant/globalVars" + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type codedFixedValue; + value uniform (0.0 0.0 0.0); + name spargerInjection; + codeInclude + #{ + #include "volFields.H" + #}; + code + #{ + const scalar Q = 4.0*1e-3/60; // 4 L/min + + vectorField Up(this->size(), Foam::vector::zero); + const scalar area = gSum(this->patch().magSf()); + if (area > SMALL) + { + Up = -(Q/area)*this->patch().nf(); + } + this->operator==(Up); + #}; + } + + dye_inlet + { + type slip; + } + outlet + { + type pressureInletOutletVelocity; + phi phi.gas; + value $internalField; + } + walls + { + type slip; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/U.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/U.liquid new file mode 100644 index 00000000..ceebde58 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/U.liquid @@ -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 volVectorField; + object U.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform (0.0 0.0 0.0); + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type noSlip; + } + dye_inlet + { + type codedFixedValue; + value uniform (0.0 0.0 0.0); + name dyeInjection; + codeInclude + #{ + #include "volFields.H" + #}; + code + #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + vectorField Up(this->size(), Foam::vector::zero); + if (t >= tStart && t < tStop) + { + const scalar dyeVol = 50.0e-6; // m3 (50 mL) + const scalar Q = dyeVol/(tStop - tStart); // m3/s + const scalar area = gSum(this->patch().magSf()); + if (area > SMALL) + { + Up = -(Q/area)*this->patch().nf(); + } + } + this->operator==(Up); + #}; + + } + + outlet + { + type noSlip; + } + walls + { + type noSlip; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.gas new file mode 100644 index 00000000..03b3da41 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.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 Ydefault.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform 0.0; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.liquid new file mode 100644 index 00000000..b7c305e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/Ydefault.liquid @@ -0,0 +1,64 @@ +/*--------------------------------*- 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; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Ydefault = the inert specie (water). At the dye inlet the injected fluid is +// pure tracer Z, so water = 0 there. + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 1.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeYdliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/Z.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/Z.liquid new file mode 100644 index 00000000..9529319c --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/Z.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 Z.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform 1.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeZliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.gas new file mode 100644 index 00000000..2258d0a6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.gas @@ -0,0 +1,65 @@ +/*--------------------------------*- 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 1.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform 1.0; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeAlphaGas; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue uniform 1.0; + value uniform 1.0; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.liquid new file mode 100644 index 00000000..d6470775 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/alpha.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 0.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform 0.0; + } + dye_inlet + { + type codedMixed; + refValue uniform 1.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeAlphaliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type fixedValue; + value uniform 0.0; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/alphat.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/alphat.gas new file mode 100644 index 00000000..928026d5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/alphat.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/alphat.liquid new file mode 100644 index 00000000..1bbd1cca --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/k.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/k.gas new file mode 100644 index 00000000..461ac6e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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 0.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform $k_inlet_gas; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/k.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/k.liquid new file mode 100644 index 00000000..17a7ca05 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/k.liquid @@ -0,0 +1,63 @@ +/*--------------------------------*- 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 0.0; + +boundaryField +{ + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform $k_inlet_liq; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $k_inlet_liq; + name dyekinlet; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type kqRWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.gas new file mode 100644 index 00000000..b3dea556 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.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 nut.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-4; + +boundaryField +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.liquid new file mode 100644 index 00000000..b8303c6a --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/nut.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 nut.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-2; + +boundaryField +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type nutkWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.gas b/experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.gas new file mode 100644 index 00000000..ee1c4607 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.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 omega.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $omega_inlet_gas; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform $omega_inlet_gas; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.liquid b/experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.liquid new file mode 100644 index 00000000..55f48dcc --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/omega.liquid @@ -0,0 +1,63 @@ +/*--------------------------------*- 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 omega.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $omega_inlet_liq; + +boundaryField +{ + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform $omega_inlet_liq; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $omega_inlet_liq; + name dyeepsinlet; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = 1.0; + const scalar tStop = 2.0; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + + outlet + { + type zeroGradient; + } + walls + { + type omegaWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/p b/experimental_cases/uloop_valadbeigy_exp1/0.orig/p new file mode 100644 index 00000000..2c787dc3 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/p @@ -0,0 +1,44 @@ +/*--------------------------------*- 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 +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/0.orig/p_rgh b/experimental_cases/uloop_valadbeigy_exp1/0.orig/p_rgh new file mode 100644 index 00000000..2cc1f127 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/0.orig/p_rgh @@ -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 volScalarField; + object p_rgh; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 101325; + +boundaryField +{ + sparger + { + type fixedFluxPressure; + value $internalField; + } + dye_inlet + { + type fixedFluxPressure; + value $internalField; + } + outlet + { + type prghTotalPressure; + p0 $internalField; + U U.gas; + phi phi.gas; + value $internalField; + } + walls + { + type fixedFluxPressure; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/Allclean b/experimental_cases/uloop_valadbeigy_exp1/Allclean new file mode 100755 index 00000000..dc2f77db --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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/experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py b/experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py new file mode 100644 index 00000000..bef692ab --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py @@ -0,0 +1,418 @@ +""" +Reproduce the case in "Hydrodynamic optimization of a newly designed and fabricated U-Loop bioreactor using Taguchi–ANOVA analysis", Valadbeigy et al., Biochemical Engineering Journal, July 2026 + +Open-top U-loop reactor as 3 stitchable gmsh blocks + +Structured hex mesh everywhere except the U-loop<->tank junction + +Blocks (all interfaces are perimeter-matched -> OpenFOAM integral `stitchMesh`): + A hex : the U pipe + B tet : U-loop <-> tank junction + Two down-stubs (filleted where they meet the tank floor) + C hex : structured-hex tank, extruded up to the open top (Z_TOP). + Flat top face is the `outlet` boundary; sides = wall. + +This output block{A,B,C}.{msh,vtk} +""" + +import math +import gmsh +import numpy as np + +# Geometrical parameters +R = 0.020 # DN40 pipe [m] +R_BEND = 0.045 # elbow centerline bend radius [m] +X_LEG = 0.063 # leg half spacing [m] +Z_HORIZ = 0.000 # bottom height [m] +Z_TANK = 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] +R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] +TANK_LEN = 2 * R_TANK +Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] +FILLET_R = 0.01 # junction fillet radius [m] + + +def loop_pipe_length(include_tank=False): + ''' Compute pipe length which is reported in the paper''' + r_bend = R_BEND + z_bend_top = Z_HORIZ + r_bend + leg_top = Z_TANK if include_tank else (Z_TANK - R_TANK) + leg = leg_top - z_bend_top + arc = 0.5 * math.pi * r_bend + horiz = 2.0 * (X_LEG - r_bend) + return 2.0 * leg + 2.0 * arc + horiz + + +def reactor_volume(tank_fraction=1.0): + v_pipe = math.pi * R**2 * loop_pipe_length(include_tank=False) + v_tank = math.pi * R_TANK**2 * TANK_LEN + return v_pipe + tank_fraction * v_tank + +# --- derived helper dimensions (I need that later) +Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows +Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 +Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 +HX = TANK_LEN / 2.0 # tank box half-width along x +HY = R_TANK # tank box half-width along y (cross-section) + +STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] +B_SLAB = 0.03 # how far do we extend the filletted junction into the hex tank [m] +Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] +Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] + +# --- resolution +RI_FRAC = 0.5 +N_SIDE = 6 # even -> circle/Pillow rims share nodes +N_RAD = max(1, round(N_SIDE * (1 - RI_FRAC) / (RI_FRAC * math.sqrt(2)))) +H_AX = 0.004 # target axial cell size for the pipe sweeps +N_TANK = 30 # structured cells per tank-square edge +# FINER mesh at the junction is obtained with SMALLER JUNCTION_RES +JUNCTION_RES = 1.8 + + +# ---- iterative mesh cleanup +N_LEG = max(1, round((Z_AB - Z_BEND_TOP) / H_AX)) +N_ARC = max(1, round((R_BEND * math.pi / 2) / H_AX)) +N_HOR = max(1, round(2 * (X_LEG - R_BEND) / H_AX)) +N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells + + +def _pillow(geo, cx, cy, cz, r, n_side, n_rad): + '''Pillow shape cylindrical mesh cross-section + Normal direction is z (consistently with the block cylindrical meshing)''' + ang = [math.pi / 4 + k * math.pi / 2 for k in range(4)] + ri = RI_FRAC * r + c = geo.addPoint(cx, cy, cz) + Q = [geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) for a in ang] + A = [geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) for a in ang] + Qe = [geo.addLine(Q[i], Q[(i + 1) % 4]) for i in range(4)] + Rad = [geo.addLine(Q[i], A[i]) for i in range(4)] + Arc = [geo.addCircleArc(A[i], c, A[(i + 1) % 4]) for i in range(4)] + surfs = [geo.addPlaneSurface([geo.addCurveLoop(Qe)])] + for i in range(4): + surfs.append(geo.addSurfaceFilling( + [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])])) + for e in Qe + Arc: + geo.mesh.setTransfiniteCurve(e, n_side + 1) + for e in Rad: + geo.mesh.setTransfiniteCurve(e, n_rad + 1) + for s in surfs: + geo.mesh.setTransfiniteSurface(s) + geo.mesh.setRecombine(2, s) + return surfs + + +def _isflat(s, idx, val, tol=1e-6): + ''' + True if surface *s* lies entirely on the plane coord[idx] == val. + I.e. is a constant coordinate plane + useful to check if what we extruded gives us a flat surface + ''' + bb = gmsh.model.getBoundingBox(2, s) + return abs(bb[idx] - val) < tol and abs(bb[idx + 3] - val) < tol + + +def _tip(idx, val): + ''' + Find flat boundary surface after gmesh extrusion + ''' + vols = [t for _, t in gmsh.model.getEntities(3)] + bnd = {t for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False)} + return [(2, s) for s in bnd if _isflat(s, idx, val)] + + +def _by(surfs, idx, val): + '''Filter surface to those lying on the plane coord[idx] == val.''' + return [s for s in surfs if _isflat(s, idx, val)] + + +def _cx(s): + '''Bounding box used to distinguish the left and right leg''' + bb = gmsh.model.getBoundingBox(2, s) + return 0.5 * (bb[0] + bb[3]) + + +def _rims(surfs): + ''' find the rims at the junction between the legs and the filleted tets + and for the junction between tet block and hex tank block''' + rim = set() + for s in surfs: + for _, cc in gmsh.model.getBoundary([(2, s)], oriented=False): + rim.add(cc) + return rim + + +def _boundary_surfs(): + """Returns volume IDs and their boundary surface IDs.""" + vols = [t for _, t in gmsh.model.getEntities(3)] + return vols, [t for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False)] + + +def _cell_volume(): + """Sum of all 3-D cell volumes""" + tags, coords, _ = gmsh.model.mesh.getNodes() + coords = coords.reshape(-1, 3) + idx = {int(t): i for i, t in enumerate(tags)} + npe = {4: 4, 5: 8, 6: 6, 7: 5} + fans = { + 4: [(0, 1, 2, 3)], + 5: [(0, 1, 2, 6), (0, 2, 3, 6), (0, 3, 7, 6), + (0, 7, 4, 6), (0, 4, 5, 6), (0, 5, 1, 6)], + 6: [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)], + 7: [(0, 1, 2, 4), (0, 2, 3, 4)], + } + total = 0.0 + ets, _, enodes = gmsh.model.mesh.getElements(3) + for et, en in zip(ets, enodes): + conn = np.array([idx[int(t)] for t in en]).reshape(-1, npe[et]) + P = coords[conn] + for a, b, c, d in fans[et]: + v = P[:, a], P[:, b], P[:, c], P[:, d] + total += np.abs(np.einsum( + "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0])).sum() + return total / 6.0 + + +def _write(path, tag): + ''' Write Gmesh object to .msh and print summary''' + TYPE = {4: "tet", 5: "hex", 6: "prism", 7: "pyramid"} + ets, etags, _ = gmsh.model.mesh.getElements(3) + counts = {TYPE.get(e, e): len(t) for e, t in zip(ets, etags)} + vol = _cell_volume() + print(f"[block {tag}] cells={counts} volume={vol * 1e3:.3f} L") + gmsh.option.setNumber("Mesh.MshFileVersion", 2.2) + gmsh.write(path) + gmsh.write(path.rsplit(".", 1)[0] + ".vtk") + return vol + + +# --- U pipe (structured hex) +def build_block_A(path): + """Extrude the pillow cross-section down in sequence + 1) left leg + 2) 90 deg elbow + 3) bottom leg + 4) second 90 degree elbow + 5) up the right leg + + the two leg-top meet the filleted mesh as z=Z_AB""" + gmsh.initialize() + gmsh.model.add("A") + gmsh.option.setNumber("General.Terminal", 0) + geo = gmsh.model.geo + + disk = _pillow(geo, -X_LEG, 0, Z_AB, R, N_SIDE, N_RAD) + geo.extrude([(2, s) for s in disk], 0, 0, -(Z_AB - Z_BEND_TOP), + numElements=[N_LEG], recombine=True) + geo.synchronize() + + # left elbow: revolve the leg-bottom disk about y through the bend centre + geo.revolve(_tip(2, Z_BEND_TOP), -X_LEG + R_BEND, 0, Z_BEND_TOP, 0, -1, 0, + math.pi / 2, numElements=[N_ARC], recombine=True) + geo.synchronize() + + # bottom horizontal run: extrude +x + geo.extrude(_tip(0, -X_LEG + R_BEND), 2 * (X_LEG - R_BEND), 0, 0, + numElements=[N_HOR], recombine=True) + geo.synchronize() + + # right elbow + geo.revolve(_tip(0, X_LEG - R_BEND), X_LEG - R_BEND, 0, Z_BEND_TOP, 0, -1, 0, + math.pi / 2, numElements=[N_ARC], recombine=True) + geo.synchronize() + + # right leg: extrude +z up to Z_AB + geo.extrude(_tip(2, Z_BEND_TOP), 0, 0, Z_AB - Z_BEND_TOP, + numElements=[N_LEG], recombine=True) + geo.synchronize() + + vols, bnd = _boundary_surfs() + iface = _by(bnd, 2, Z_AB) + legL = [s for s in iface if _cx(s) < 0] + legR = [s for s in iface if _cx(s) > 0] + walls = [s for s in bnd if s not in iface] + gmsh.model.addPhysicalGroup(3, vols, name="pipeU") + gmsh.model.addPhysicalGroup(2, legL, name="int_A_legL") + gmsh.model.addPhysicalGroup(2, legR, name="int_A_legR") + gmsh.model.addPhysicalGroup(2, walls, name="wall_A") + + gmsh.model.mesh.generate(3) + vol = _write(path, "A") + gmsh.finalize() + return vol + + +# ---- block B: U-loop <-> tank junction +def build_block_B(path): + '''Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). + 1. Rectangular from Z_BOT to Z_BC (the tank-floor transition layer). + 2. Two cylindrical partial leds + 3. Fillet + + Interface matching (that was the hard part!) + - Bottom circles (int_B_legL/R at Z_AB): rim nodes match A's pillow perimeter. + - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. + ''' + + gmsh.initialize() + gmsh.model.add("B") + gmsh.option.setNumber("General.Terminal", 0) + occ = gmsh.model.occ + + pen = 0.4 * (Z_BC - Z_BOT) + slab = occ.addBox(-HX, -HY, Z_BOT, 2 * HX, 2 * HY, Z_BC - Z_BOT) + stubs = [occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) + for sx in (-X_LEG, X_LEG)] + S, _ = occ.fuse([(3, slab)], [(3, s) for s in stubs]) + occ.synchronize() + vol = S[0][1] + + ring = [] + for _, e in gmsh.model.getEntities(1): + ex, _ey, ez = occ.getCenterOfMass(1, e) + x0, _, _, x1, _, _ = gmsh.model.getBoundingBox(1, e) + if abs(ez - Z_BOT) < 1e-3 and abs(abs(ex) - X_LEG) < 0.02 \ + and (x1 - x0) < 3 * R: + ring.append(e) + occ.fillet([vol], ring, [FILLET_R]) + occ.synchronize() + + vols, bnd = _boundary_surfs() + bot = _by(bnd, 2, Z_AB) # two pipe circles -> A + top = _by(bnd, 2, Z_BC) # tank square -> C + walls = [s for s in bnd if s not in bot and s not in top] + legL = [s for s in bot if occ.getCenterOfMass(2, s)[0] < 0] + legR = [s for s in bot if occ.getCenterOfMass(2, s)[0] > 0] + + for s in bot: # match each stub rim to A (4*N_SIDE) + rc = _rims([s]) + per = max(1, round(4 * N_SIDE / len(rc))) + for cc in rc: + gmsh.model.mesh.setTransfiniteCurve(cc, per + 1) + for cc in _rims(top): # match tank square rim to C (N_TANK/edge) + gmsh.model.mesh.setTransfiniteCurve(cc, N_TANK + 1) + + gmsh.model.addPhysicalGroup(3, vols, name="juncB") + gmsh.model.addPhysicalGroup(2, legL, name="int_B_legL") + gmsh.model.addPhysicalGroup(2, legR, name="int_B_legR") + gmsh.model.addPhysicalGroup(2, top, name="int_B_top") + gmsh.model.addPhysicalGroup(2, walls, name="wall_B") + gmsh.option.setNumber("Mesh.MeshSizeMax", R / N_SIDE * JUNCTION_RES) + gmsh.option.setNumber("Mesh.Optimize", 1) + gmsh.option.setNumber("Mesh.OptimizeNetgen", 1) + gmsh.model.mesh.generate(3) + gmsh.model.mesh.optimize("Netgen") + vol = _write(path, "B") + gmsh.finalize() + return vol + + +# --- block C: hex tank +def build_block_C(path): + """Structured-hex tank extruded from Z_BC to Z_TOP. + + 1) N_TANK nodes per edge, matching block B's top + 2) Extrude +z to Z_TOP with N_HC uniform layers. + + Top face is the open outlet boundary; + Sides are wall_C; + Bottom is for stitching to block B. + """ + gmsh.initialize() + gmsh.model.add("C") + gmsh.option.setNumber("General.Terminal", 0) + geo = gmsh.model.geo + + p = [geo.addPoint(-HX, -HY, Z_BC), geo.addPoint(HX, -HY, Z_BC), + geo.addPoint(HX, HY, Z_BC), geo.addPoint(-HX, HY, Z_BC)] + l = [geo.addLine(p[i], p[(i + 1) % 4]) for i in range(4)] + sq = geo.addPlaneSurface([geo.addCurveLoop(l)]) + for e in l: + geo.mesh.setTransfiniteCurve(e, N_TANK + 1) + geo.mesh.setTransfiniteSurface(sq) + geo.mesh.setRecombine(2, sq) + geo.extrude([(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True) + geo.synchronize() + + vols, bnd = _boundary_surfs() + bot = _by(bnd, 2, Z_BC) + top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) + walls = [s for s in bnd if s not in bot and s not in top] + gmsh.model.addPhysicalGroup(3, vols, name="tank") + gmsh.model.addPhysicalGroup(2, bot, name="int_C_bot") + gmsh.model.addPhysicalGroup(2, top, name="outlet") + gmsh.model.addPhysicalGroup(2, walls, name="wall_C") + gmsh.model.mesh.generate(3) + vol = _write(path, "C") + gmsh.finalize() + return vol + + +# ---- verify that the junction that stitch mesh will operate on has consistent +# face perimeter +def _plane_nodes(path, zval): + """All mesh nodes at z == zval from a .msh file, returned as (x, y) pairs.""" + gmsh.initialize() + gmsh.open(path) + _, coords, _ = gmsh.model.mesh.getNodes() + coords = coords.reshape(-1, 3) + pts = [(x, y) for x, y, z in coords if abs(z - zval) < 1e-6] + gmsh.finalize() + return pts + + +def _circle_rim(pts, cx, r): + """Subset of (x, y) points lying on a circle centred at (cx, 0) with radius r.""" + return sorted((round(x, 9), round(y, 9)) for x, y in pts + if abs(math.hypot(x - cx, y) - r) < 1e-4) + + +def _square_rim(pts, hx, hy): + """Subset of (x, y) points lying on the perimeter of a [-hx,hx] x [-hy,hy] rectangle.""" + return sorted((round(x, 9), round(y, 9)) for x, y in pts + if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4) + + +def _verify(name, a, b, tol=1e-9): + """Assert two rim point sets have the same count and are coincident within tol.""" + assert len(a) == len(b), \ + f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." + worst = max(min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a) + ok = worst < tol + print(f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}") + assert ok, f"{name}: rims not coincident (gap {worst:.1e} > {tol})." + + +def verify_interfaces(): + """Check that A-B circle rims and B-C square rim match node-for-node across blocks.""" + A_ab = _plane_nodes("blockA.msh", Z_AB) + B_ab = _plane_nodes("blockB.msh", Z_AB) + _verify("A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R)) + _verify("A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R)) + B_bc = _plane_nodes("blockB.msh", Z_BC) + C_bc = _plane_nodes("blockC.msh", Z_BC) + _verify("B-C square", _square_rim(B_bc, HX, HY), _square_rim(C_bc, HX, HY)) + + +# ---- main +if __name__ == "__main__": + print(f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " + f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}") + vols = { + "A": build_block_A("blockA.msh"), + "B": build_block_B("blockB.msh"), + "C": build_block_C("blockC.msh"), + } + verify_interfaces() + + box_tank = (2 * HX) * (2 * HY) * (Z_TOP - Z_BOT) + print("=" * 70) + print(f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " + f"excl. tank = {loop_pipe_length(False):.4f} m") + print(f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " + f"= {sum(vols.values()) * 1e3:.3f} L") + print(f" of which the box tank alone = {box_tank * 1e3:.3f} L") + print(f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " + f"({2 * HX:.3f} x {2 * HY:.3f} m)") + print("[write] block{A,B,C}.{msh,vtk} -> will stitch next") diff --git a/experimental_cases/uloop_valadbeigy_exp1/constant/fvModels b/experimental_cases/uloop_valadbeigy_exp1/constant/fvModels new file mode 100644 index 00000000..b8553170 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/constant/fvModels @@ -0,0 +1,266 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object fvModels; +} + +codedSource +{ + type coded; + selectionMode all; + field U.liquid; + name sourceTime; + + codeInclude + #{ + #include + #include + #}; + + codeAddAlphaRhoSup + #{ + const Time& time = mesh().time(); + const scalarField& V = mesh().V(); + vectorField& Usource = eqn.source(); + const vectorField& C = mesh().C(); + const volScalarField& rhoL = + mesh().lookupObject("thermo:rho.liquid"); + const volScalarField& alphaL = + mesh().lookupObject("alpha.liquid"); + const volVectorField& UL = + mesh().lookupObject("U.liquid"); + const double pi = 3.14159265358979; + // ===== ball mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Vtip = 5; + const double sigma = 0.35; + const double startT = 0.1; + const double px = 0.063, py = 0.47, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double rhs = 4.0*9.75/(rhoM*area); + double V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs)); + for (int it = 0; it < 100; ++it) + { + const double F = (V2-V1)*(V2+V1)*(V2+V1) + 0.35*(V1+V2)*Vtip*Vtip - rhs; + const double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1 + 0.35*Vtip*Vtip; + const double dV = F/dF; + V2 -= dV; + if (std::abs(dV) < 1e-10) break; + } + const double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1); + const double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip; + scalar Sax = 0.0, Sth = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + Sax += alphaL[i]*g*V[i]; + const double rr = std::sqrt(d2-(dy)*(dy)); + Sth += alphaL[i]*g*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Sth, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fax = Tax/Sax*alphaL[i]*g; + Usource[i][1] -= -1.0*fax*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Sth > 1e-30) + { + const double fth = Qsw/Sth*alphaL[i]*g; + Usource[i][0] -= -1.0*fth*V[i]*((dz)/rr); + Usource[i][2] -= -1.0*fth*V[i]*((-dx)/rr); + } + } + } + } + } + // ===== static mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Snum = 0.6; + const double Kloss = 0.5; + const double startT = 0.1; + const double px = 0.063, py = 0.05, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dy)*(dy)); + const double ux = UL[i][1]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][1] -= 1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][1]; + const double uth = UL[i][0]*((dz)/rr) + UL[i][2]*((-dx)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][0] -= -1.0*fsw*V[i]*((dz)/rr); + Usource[i][2] -= -1.0*fsw*V[i]*((-dx)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][1] -= 1.0*fcp*V[i]; + } + } + } + } + } + // ===== static mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Snum = 0.6; + const double Kloss = 0.5; + const double startT = 0.1; + const double px = -0.063, py = 0.05, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && 1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? 1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dy)*(dy)); + const double ux = UL[i][1]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][1] -= -1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][1]; + const double uth = UL[i][0]*((dz)/rr) + UL[i][2]*((-dx)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][0] -= 1.0*fsw*V[i]*((dz)/rr); + Usource[i][2] -= 1.0*fsw*V[i]*((-dx)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][1] -= -1.0*fcp*V[i]; + } + } + } + } + } + #}; +}; diff --git a/experimental_cases/uloop_valadbeigy_exp1/constant/g b/experimental_cases/uloop_valadbeigy_exp1/constant/g new file mode 100644 index 00000000..770a5619 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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/experimental_cases/uloop_valadbeigy_exp1/constant/globalVars b/experimental_cases/uloop_valadbeigy_exp1/constant/globalVars new file mode 100644 index 00000000..ef6762ad --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/constant/globalVars @@ -0,0 +1,70 @@ +T0 300; //initial T(K) which stays constant +//****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_N2 31.2e-3; +//****** diffusion coeff *********** +D_O2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_O2,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_N2_298 0.015; +DH_N2 1300; +He_O2 #calc "$H_O2_298 * exp($DH_O2 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas mass frac (air)************* +f_O2 0.233; +f_N2 0.767; +//*******aeration / dye injection************** +gasFlowRate 6.6667e-5; // 4 L/min air sparged = 4e-3/60 m3/s +// Dye = 50 mL over [dyeStart, dyeStop]; injected by the codedFixedValue BC on +// U.liquid at dye_inlet (which recomputes the patch area at runtime). presteps.sh +// substitutes these values into the __DYE_START__/__DYE_STOP__ tokens of the +// coded dye BCs in 0/ ($vars do not expand inside the #{ #} code blocks). +// dyeStart is also read by get_mixing_time.py. +dyeStart 1.0; // s, dye injection start (after steady circulation) +dyeStop 2.0; // s, dye injection stop (1 s window, 50 mL total) +dyeVol 50.0e-6; // m3 (50 mL); MUST match the literal in 0.orig/U.liquid +//********************************* +inletA 0.000251007; +inletA_dye 0.000251007; +liqVol 0.00588087; +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$gasFlowRate / ($inletA * $alphaGas)"; // nominal sparger gas velocity (turbulence BCs) +//********************************* +LeLiqO2 #calc "$kThermLiq / $rho0MixLiq / $D_O2 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_O2*$LeLiqO2+$f_N2*$LeLiqN2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kO2 #calc "$D_O2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrO2 #calc "$muMixLiq*$CpMixLiq / $kO2"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.04; // mixing length = DN40 pipe diameter (was 0.5 m, too large -> eps too small) +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)"; +//********************************* +// Dye-inlet (liquid) turbulence: the dye is injected at its OWN velocity through +// its OWN (smaller) pipe, so k/eps there must not reuse the sparger-gas values. +// uDye = injected volumetric flow / dye_inlet area; l_scale_dye = dye pipe diameter. +l_scale_dye 0.02; // dye pipe diameter [m] +uDye #calc "$dyeVol / (($dyeStop - $dyeStart) * $inletA_dye)"; // nominal dye injection velocity +k_inlet_dye #calc "1.5 * Foam::pow(($uDye), 2) * Foam::pow($intensity, 2)"; +eps_inlet_dye #calc "pow(0.09,0.75) * Foam::pow($k_inlet_dye, 1.5) / ($l_scale_dye * 0.07)"; diff --git a/experimental_cases/uloop_valadbeigy_exp1/constant/globalVars_temp b/experimental_cases/uloop_valadbeigy_exp1/constant/globalVars_temp new file mode 100644 index 00000000..178935a4 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/constant/globalVars_temp @@ -0,0 +1,70 @@ +T0 300; //initial T(K) which stays constant +//****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_N2 31.2e-3; +//****** diffusion coeff *********** +D_O2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_O2,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_N2_298 0.015; +DH_N2 1300; +He_O2 #calc "$H_O2_298 * exp($DH_O2 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas mass frac (air)************* +f_O2 0.233; +f_N2 0.767; +//*******aeration / dye injection************** +gasFlowRate 6.6667e-5; // 4 L/min air sparged = 4e-3/60 m3/s +// Dye = 50 mL over [dyeStart, dyeStop]; injected by the codedFixedValue BC on +// U.liquid at dye_inlet (which recomputes the patch area at runtime). presteps.sh +// substitutes these values into the __DYE_START__/__DYE_STOP__ tokens of the +// coded dye BCs in 0/ ($vars do not expand inside the #{ #} code blocks). +// dyeStart is also read by get_mixing_time.py. +dyeStart 1.0; // s, dye injection start (after steady circulation) +dyeStop 2.0; // s, dye injection stop (1 s window, 50 mL total) +dyeVol 50.0e-6; // m3 (50 mL); MUST match the literal in 0.orig/U.liquid +//********************************* +inletA ; // sparger patch area [m2], filled by writeGlobalVars.py +inletA_dye ; // dye_inlet patch area [m2], filled by writeGlobalVars.py +liqVol ; // liquid volume [m3], filled by writeGlobalVars.py +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$gasFlowRate / ($inletA * $alphaGas)"; // nominal sparger gas velocity (turbulence BCs) +//********************************* +LeLiqO2 #calc "$kThermLiq / $rho0MixLiq / $D_O2 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_O2*$LeLiqO2+$f_N2*$LeLiqN2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kO2 #calc "$D_O2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrO2 #calc "$muMixLiq*$CpMixLiq / $kO2"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.04; // mixing length = DN40 pipe diameter (was 0.5 m, too large -> eps too small) +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)"; +//********************************* +// Dye-inlet (liquid) turbulence: the dye is injected at its OWN velocity through +// its OWN (smaller) pipe, so k/eps there must not reuse the sparger-gas values. +// uDye = injected volumetric flow / dye_inlet area; l_scale_dye = dye pipe diameter. +l_scale_dye 0.02; // dye pipe diameter [m] +uDye #calc "$dyeVol / (($dyeStop - $dyeStart) * $inletA_dye)"; // nominal dye injection velocity +k_inlet_dye #calc "1.5 * Foam::pow(($uDye), 2) * Foam::pow($intensity, 2)"; +eps_inlet_dye #calc "pow(0.09,0.75) * Foam::pow($k_inlet_dye, 1.5) / ($l_scale_dye * 0.07)"; diff --git a/experimental_cases/uloop_valadbeigy_exp1/constant/momentumTransport.gas b/experimental_cases/uloop_valadbeigy_exp1/constant/momentumTransport.gas new file mode 100644 index 00000000..cca64eef --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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 kOmegaSSTSato; + turbulence on; + printCoeff on; +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/constant/momentumTransport.liquid b/experimental_cases/uloop_valadbeigy_exp1/constant/momentumTransport.liquid new file mode 100644 index 00000000..df3e15b5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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 kOmegaSSTSato; + turbulence on; + printCoeffs on; +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/constant/phaseProperties b/experimental_cases/uloop_valadbeigy_exp1/constant/phaseProperties new file mode 100644 index 00000000..d8d0e1c5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/constant/phaseProperties @@ -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 ( O2 N2 ); + k ( $He_O2 $He_N2 ); + 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/experimental_cases/uloop_valadbeigy_exp1/constant/phaseProperties_constantd b/experimental_cases/uloop_valadbeigy_exp1/constant/phaseProperties_constantd new file mode 100644 index 00000000..d8d0e1c5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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 ( O2 N2 ); + k ( $He_O2 $He_N2 ); + 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/experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.gas b/experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.gas new file mode 100644 index 00000000..bbec9049 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.gas @@ -0,0 +1,89 @@ +/*--------------------------------*- 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 +( + O2 + N2 +); + +defaultSpecie N2; + +O2 +{ + specie + { + molWeight 31.9988; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.28253784 0.00148308754 -7.57966669e-07 2.09470555e-10 -2.16717794e-14 -1088.45772 5.45323129 ); + lowCpCoeffs ( 3.78245636 -0.00299673416 9.84730201e-06 -9.68129509e-09 3.24372837e-12 -1063.94356 3.65767573 ); + } + transport + { + As 1.693411300e-06; + Ts 127; + } + elements + { + O 2; + } +} + +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; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.liquid b/experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.liquid new file mode 100644 index 00000000..b0c78662 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/constant/thermophysicalProperties.liquid @@ -0,0 +1,132 @@ +/*--------------------------------*- 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 +( + O2 + N2 + water + Z +); + +inertSpecie water; + +water +{ + specie + { + molWeight 18.0153; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrMixLiq; + } +} + +// Passive dye tracer: identical to water, no interphase mass transfer (Z is +// deliberately absent from every Henry / diffusiveMassTransfer list). +Z +{ + specie + { + molWeight 18.0153; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrMixLiq; + } +} + +O2 +{ + specie + { + molWeight 31.9988; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrO2; + } +} + +N2 +{ + specie + { + molWeight 28.0134; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrN2; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py b/experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py new file mode 100644 index 00000000..2f86cf69 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py @@ -0,0 +1,209 @@ +"""Volume-averaged dye tracer (Z.liquid) in the bottom U-bend box vs time. + +For every time folder this reads the ``Z.liquid`` with BiRD +then computes the cell-volume-weighted average over the +cells whose centres lie inside the box + + x in [-0.01, 0.01], y in [-0.1, 0.1], z in [-0.1, 0.1] [m] + +and plots that average versus time. +""" + +import os + +import numpy as np +from prettyPlot.plotting import plt, pretty_labels + +from bird.utilities.ofio import ( + get_case_times, + read_cell_centers, + read_cell_volumes, + read_field, +) + +CASE = os.path.dirname(os.path.abspath(__file__)) + +# Averaging box [m]. The mesh is y-up after the presteps transformPoints. +X_BOUNDS = (-0.01, 0.01) +Y_BOUNDS = (-0.1, 0.1) +Z_BOUNDS = (-0.1, 0.1) + +# Mixing-time criterion. +BAND = 0.05 # within +/-5% of the final well-mixed value +TAIL_WINDOW = 0.5 # s, tail used to define the final value + + +def read_dye_start(): + """Dye injection start time [s], read from constant/globalVars.""" + with open(os.path.join(CASE, "constant", "globalVars")) as f: + for line in f: + if line.startswith("dyeStart"): + return float(line.split()[1].rstrip(";")) + return 1.0 + + +def box_mask(cell_centers): + """Boolean mask of cells whose centres lie inside the averaging box.""" + x, y, z = cell_centers[:, 0], cell_centers[:, 1], cell_centers[:, 2] + return ( + (x >= X_BOUNDS[0]) + & (x <= X_BOUNDS[1]) + & (y >= Y_BOUNDS[0]) + & (y <= Y_BOUNDS[1]) + & (z >= Z_BOUNDS[0]) + & (z <= Z_BOUNDS[1]) + ) + + +def box_volume_average(z_field, cell_volumes, mask): + """Cell-volume-weighted average of z_field over the masked cells. + + read_field returns a bare float for a uniform OpenFOAM field (e.g. the dye + tracer before injection starts); the volume average is then exactly that + value. Otherwise it is the volume-weighted mean over the box cells. + """ + if np.ndim(z_field) == 0: + return float(z_field) + else: + vol = cell_volumes[mask] + return float(np.sum(z_field[mask] * vol) / np.sum(vol)) + + +def mixing_time(t_arr, z_arr, t_start, continuous=True): + """Mixing time from the box-averaged dye signal. + + The final well-mixed value is the mean of the signal over the last + TAIL_WINDOW seconds. The mixing time is the interval from dye injection + (t_start) to the last instant the signal leaves the +/-BAND envelope of + that final value (after which it stays inside for good). + + Parameters + ---------- + t_arr : array-like + Time array. + z_arr : array-like + Box-averaged dye signal. + t_start : float + Injection time. + continuous : bool, optional + If True, linearly interpolates between the last timestep outside the + band and the first timestep inside to find the exact crossing time. + + Returns + ------- + t_mix : float + Mixing time measured from injection [s]. + t_settle : float + Absolute simulation time at which the signal settles [s]. + z_final : float + Final well-mixed box-averaged value. + """ + # Calculate final value and allowable band + z_final = float(np.mean(z_arr[t_arr >= t_arr[-1] - TAIL_WINDOW])) + band = BAND * abs(z_final) + + post = t_arr >= t_start + outside = post & (np.abs(z_arr - z_final) > band) + + if not np.any(outside): + # already within the band from injection onward + t_settle = t_start + else: + last_out = np.nonzero(outside)[0][-1] + + # Check if we have a subsequent point to interpolate with + if last_out + 1 < len(t_arr): + if continuous: + # Extract time and Z values for the crossing interval + t0, t1 = t_arr[last_out], t_arr[last_out + 1] + z0, z1 = z_arr[last_out], z_arr[last_out + 1] + + # Determine which boundary of the band was crossed + if z0 > z_final: + z_target = z_final + band # Crossed the top boundary + else: + z_target = z_final - band # Crossed the bottom boundary + + # Linearly interpolate to find the exact time t_settle at z_target + if z1 != z0: # Safety check to prevent division by zero + t_settle = t0 + (t1 - t0) * (z_target - z0) / (z1 - z0) + else: + t_settle = t1 + else: + # Original discrete behavior + t_settle = t_arr[last_out + 1] + else: + # The signal was outside the band up to the very last recorded timestep + t_settle = t_arr[last_out] + + return t_settle - t_start, t_settle, z_final + +if __name__ == "__main__": + os.makedirs(os.path.join(CASE, "Figures"), exist_ok=True) + + # Geometry is time-independent: read the cell centres and cell volumes once + # and keep them in the shared field_dict cache. + cell_centers, geom = read_cell_centers(CASE) + n_cells = cell_centers.shape[0] + cell_volumes, geom = read_cell_volumes(CASE, field_dict=geom) + + mask = box_mask(cell_centers) + if mask.sum() == 0: + raise RuntimeError("averaging box contains no cell centres") + + times_float, times_str = get_case_times(CASE) + + t_list, z_list = [], [] + for t_val, t_str in zip(times_float, times_str): + try: + # Fresh field_dict per time so Z.liquid is never served stale. + z_field, _ = read_field(CASE, t_str, "Z.liquid", n_cells=n_cells) + except FileNotFoundError: + continue + t_list.append(t_val) + z_list.append(box_volume_average(z_field, cell_volumes, mask)) + + order = np.argsort(t_list) + t_arr = np.asarray(t_list)[order] + z_arr = np.asarray(z_list)[order] + + np.savetxt( + os.path.join(CASE, "Z_box_average.dat"), + np.column_stack([t_arr, z_arr]), + header="time[s] volAvg(Z.liquid)_box", + ) + + dye_start = read_dye_start() + t_mix, t_settle, z_final = mixing_time(t_arr, z_arr, dye_start) + t_mix_disc, t_settle_disc, z_final_disc = mixing_time(t_arr, z_arr, dye_start, continuous=False) + + with open(os.path.join(CASE, "mix_time.txt"), "w") as f: + f.write(f"Continuous: {t_mix:.4f}\n") + f.write(f"Discrete: {t_mix_disc:.4f}\n") + + fig, ax = plt.subplots(figsize=(6, 4)) + ax.plot(t_arr, z_arr, color="k") + ax.axhline(z_final, color="b", ls="--", label=r"$Z_{final}$") + ax.axhspan( + (1 - BAND) * z_final, (1 + BAND) * z_final, color="b", alpha=0.15 + ) + ax.axvline( + t_settle, color="r", ls=":", label=f"$t_{{mix}}$={t_mix_disc:.2f} s" + ) + ax.set_xlim(left=dye_start) + pretty_labels("time [s]", r"box-averaged $Z_{liquid}$ [-]", 14, ax=ax) + ax.legend() + fig.savefig( + os.path.join(CASE, "Figures", "Z_box_average.png"), + dpi=150, + bbox_inches="tight", + ) + + print(f"box cells : {int(mask.sum())}") + print(f"time folders averaged : {len(t_arr)}") + print(f"final well-mixed Z discrete : {z_final_disc:.6g}") + print(f"final well-mixed Z continous : {z_final:.6g}") + print(f"dye injection start : {dye_start:.3f} s") + print(f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)") + print(f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)") + print("wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png") diff --git a/experimental_cases/uloop_valadbeigy_exp1/presteps.sh b/experimental_cases/uloop_valadbeigy_exp1/presteps.sh new file mode 100755 index 00000000..d32003ce --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/presteps.sh @@ -0,0 +1,77 @@ +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +./Allclean + +set -e # Exit on any error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + +echo PRESTEP 1 +BIRD_DIR=$(python -c "import bird; print(bird.BIRD_DIR)") +APPLICATIONS=$(dirname "$BIRD_DIR")/applications + +python "$APPLICATIONS/write_stl_patch.py" -i system/inlets_outlets.json +python "$APPLICATIONS/write_dynMix_fvModels.py" -i system/mixers.json -o constant + +echo PRESTEP 2 +python build_uloop_hex.py +bash stitch_and_check.sh \ + --mesh blockC.msh \ + --mesh blockB.msh --mesh blockA.msh \ + --stitch int_B_top:int_C_bot \ + --stitch int_A_legL:int_B_legL \ + --stitch int_A_legR:int_B_legR \ + --case stitched_case_uloop +touch stitched_case_uloop/test.foam +cp -r stitched_case_uloop/constant/polyMesh constant/polyMesh +createPatch -overwrite +transformPoints "rotate=((0 0 1) (0 1 0))" + +# Make a local tmp folder to preprocess the mesh +mkdir tmp + +# --- sparger --- +surfaceToPatch -tol 1e-3 sparger.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/sparger\.stl/sparger/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +# --- dye_inlet --- +surfaceToPatch -tol 1e-3 dye_inlet.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/dye_inlet\.stl/dye_inlet/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +foamDictionary constant/polyMesh/boundary -entry entry0/walls/type -set wall +foamDictionary constant/polyMesh/boundary -entry entry0/dye_inlet/type -set wall + +# setup IC +cp -r 0.orig 0 + +DYE_START=$(grep -E '^[[:space:]]*dyeStart[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStart[[:space:]]+([0-9.eE+-]+).*/\1/') +DYE_STOP=$(grep -E '^[[:space:]]*dyeStop[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStop[[:space:]]+([0-9.eE+-]+).*/\1/') +echo "Dye injection window: dyeStart=$DYE_START dyeStop=$DYE_STOP" +grep -rl '__DYE_START__\|__DYE_STOP__' 0 | xargs -r sed -i "s/__DYE_START__/${DYE_START}/g; s/__DYE_STOP__/${DYE_STOP}/g" + +setFields + +postProcess -func 'patchIntegrate(patch="sparger", field="alpha.gas")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.liquid")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.gas")' +postProcess -func writeCellVolumes +writeMeshObj + +echo PRESTEP 3 +python writeGlobalVars.py +cp constant/phaseProperties_constantd constant/phaseProperties + +conda deactivate diff --git a/experimental_cases/uloop_valadbeigy_exp1/run.sh b/experimental_cases/uloop_valadbeigy_exp1/run.sh new file mode 100755 index 00000000..c9d5f3cd --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/run.sh @@ -0,0 +1,75 @@ +#!/bin/bash +### OpenFOAM command +./Allclean +set -e # Exit on any error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + +### BiRD command +echo PRESTEP 1 +BIRD_DIR=$(python -c "import bird; print(bird.BIRD_DIR)") +APPLICATIONS=$(dirname "$BIRD_DIR")/applications + +python "$APPLICATIONS/write_stl_patch.py" -i system/inlets_outlets.json +python "$APPLICATIONS/write_dynMix_fvModels.py" -i system/mixers.json -o constant + +echo PRESTEP 2 +python build_uloop_hex.py +bash stitch_and_check.sh \ + --mesh blockC.msh \ + --mesh blockB.msh --mesh blockA.msh \ + --stitch int_B_top:int_C_bot \ + --stitch int_A_legL:int_B_legL \ + --stitch int_A_legR:int_B_legR \ + --case stitched_case_uloop +touch stitched_case_uloop/test.foam +cp -r stitched_case_uloop/constant/polyMesh constant/polyMesh +createPatch -overwrite +transformPoints "rotate=((0 0 1) (0 1 0))" + +# Make a local tmp folder to preprocess the mesh +mkdir tmp + +# --- sparger --- +surfaceToPatch -tol 1e-3 sparger.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/sparger\.stl/sparger/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +# --- dye_inlet --- +surfaceToPatch -tol 1e-3 dye_inlet.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/dye_inlet\.stl/dye_inlet/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +foamDictionary constant/polyMesh/boundary -entry entry0/walls/type -set wall +foamDictionary constant/polyMesh/boundary -entry entry0/dye_inlet/type -set wall + +cp -r 0.orig 0 + +DYE_START=$(grep -E '^[[:space:]]*dyeStart[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStart[[:space:]]+([0-9.eE+-]+).*/\1/') +DYE_STOP=$(grep -E '^[[:space:]]*dyeStop[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStop[[:space:]]+([0-9.eE+-]+).*/\1/') +echo "Dye injection window: dyeStart=$DYE_START dyeStop=$DYE_STOP" +grep -rl '__DYE_START__\|__DYE_STOP__' 0 | xargs -r sed -i "s/__DYE_START__/${DYE_START}/g; s/__DYE_STOP__/${DYE_STOP}/g" + +setFields + +postProcess -func 'patchIntegrate(patch="sparger", field="alpha.gas")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.liquid")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.gas")' +postProcess -func writeCellVolumes +writeMeshObj + +echo PRESTEP 3 +python writeGlobalVars.py +cp constant/phaseProperties_constantd constant/phaseProperties + + +birdmultiphaseEulerFoam diff --git a/experimental_cases/uloop_valadbeigy_exp1/script b/experimental_cases/uloop_valadbeigy_exp1/script new file mode 100644 index 00000000..06eaf486 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/script @@ -0,0 +1,16 @@ +#!/bin/bash +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=14:59:00 +#SBATCH --account=gas2fuels + +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 -fields "(U.liquid alpha.gas Z.liquid)" +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +python get_mixing_time.py diff --git a/experimental_cases/uloop_valadbeigy_exp1/script_post b/experimental_cases/uloop_valadbeigy_exp1/script_post new file mode 100755 index 00000000..dcbf59d8 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/script_post @@ -0,0 +1,14 @@ +#!/bin/bash +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=01:59:00 +#SBATCH --account=gas2fuels +#SBATCH --dependency=afterany:15800966 + +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +reconstructPar -newTimes -fields "(Z.liquid U.liquid alpha.gas)" +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +python get_mixing_time.py diff --git a/experimental_cases/uloop_valadbeigy_exp1/stitch_and_check.sh b/experimental_cases/uloop_valadbeigy_exp1/stitch_and_check.sh new file mode 100755 index 00000000..d83110d9 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/stitch_and_check.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# +# Stitch a list of gmsh block meshes into ONE OpenFOAM mesh and run checkMesh. +# +# Each mesh is converted with gmshToFoam; mesh #0 becomes the master case that +# the rest are merged into; then each --stitch pair is coupled with integral +# (non -perfect) stitchMesh +# +# Options: +# --mesh (repeatable; order = stack order, #0 is master) +# --stitch (repeatable; integral, for non-matching faces) +# --stitch-perfect (repeatable; -perfect, for conformal faces) +# --case (output case dir, default: stitched_case) + +set -euo pipefail + +CASE="stitched_case" +MESHES=() +STITCHES=() # entries: "integral m:s" or "perfect m:s" (order preserved) +while [[ $# -gt 0 ]]; do + case "$1" in + --mesh) MESHES+=("$2"); shift 2;; + --stitch) STITCHES+=("integral $2"); shift 2;; + --stitch-perfect) STITCHES+=("perfect $2"); shift 2;; + --case) CASE="$2"; shift 2;; + *) echo "unknown argument: $1" >&2; exit 1;; + esac +done + +# >=1 mesh: with a single --mesh and no --stitch this just gmshToFoam+checkMesh +# one block (useful to isolate which block owns a checkMesh failure). +[[ ${#MESHES[@]} -ge 1 ]] || { echo "need at least one --mesh file" >&2; exit 1; } +command -v gmshToFoam >/dev/null 2>&1 || { + echo "OpenFOAM not found on PATH — source OpenFOAM-9 first." >&2; exit 1; } + +# minimal case skeleton (mesh utilities need controlDict/fvSchemes/fvSolution) +write_system() { + local d="$1"; mkdir -p "$d/system" "$d/constant" + cat > "$d/system/controlDict" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object controlDict; } +application checkMesh; +startFrom startTime; startTime 0; +stopAt endTime; endTime 1; +deltaT 1; writeControl timeStep; writeInterval 1; +EOF + cat > "$d/system/fvSchemes" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object fvSchemes; } +ddtSchemes { default steadyState; } +gradSchemes { default Gauss linear; } +divSchemes { default none; } +laplacianSchemes { default Gauss linear corrected; } +interpolationSchemes { default linear; } +snGradSchemes { default corrected; } +EOF + cat > "$d/system/fvSolution" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object fvSolution; } +solvers {} +EOF +} + +echo "==> master case: $CASE (from ${MESHES[0]})" +rm -rf "$CASE"; write_system "$CASE" +gmshToFoam "${MESHES[0]}" -case "$CASE" + +# convert + merge the remaining blocks into the master +for ((i=1; i<${#MESHES[@]}; i++)); do + sub="${CASE}_add${i}" + echo "==> add block $i: ${MESHES[$i]}" + rm -rf "$sub"; write_system "$sub" + gmshToFoam "${MESHES[$i]}" -case "$sub" + # merge into master (OF-9 foundation syntax) + mergeMeshes "$CASE" "$sub" -overwrite + rm -rf "$sub" +done + +# couple each interface with its chosen mode +for spec in "${STITCHES[@]}"; do + mode="${spec%% *}"; pair="${spec#* }" + master="${pair%%:*}"; slave="${pair##*:}" + flags="-overwrite"; [[ "$mode" == perfect ]] && flags="$flags -perfect" + echo "==> stitchMesh ($mode) $master $slave" + stitchMesh $flags "$master" "$slave" -case "$CASE" +done + +echo "==> checkMesh" +checkMesh -allGeometry -allTopology -case "$CASE" +echo "==> done. Mesh in $CASE/constant/polyMesh" diff --git a/experimental_cases/uloop_valadbeigy_exp1/system/controlDict b/experimental_cases/uloop_valadbeigy_exp1/system/controlDict new file mode 100644 index 00000000..cbee600d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/system/controlDict @@ -0,0 +1,95 @@ +/*--------------------------------*- 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 writeNow;//endTime; + +// ~20 s spin-up for steady circulation, dye pulse at t=20-21 s, then ~24 s to +// capture a ~10 s mixing time. +endTime 20; + +deltaT 1e-6; + +writeControl adjustableRunTime; +//writeControl timeStep; + +writeInterval 0.1; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable yes; + +adjustTimeStep yes; + +maxCo 1.0; + +maxDeltaT 0.001; + + +functions +{ + + limitNut + { + type coded; + libs ("libutilityFunctionObjects.so"); + name limitNut; + codeExecute + #{ + const scalar nutMaxLiq = 1e-3; // [m2/s] liquid nut ceiling (tune) + //const scalar nutMaxGas = 1e-3; // [m2/s] gas nut ceiling (tune) + + volScalarField& nutLiq = + mesh().lookupObjectRef("nut.liquid"); + //volScalarField& nutGas = + // mesh().lookupObjectRef("nut.gas"); + + nutLiq = min(nutLiq, dimensionedScalar(nutLiq.dimensions(), nutMaxLiq)); + //nutGas = min(nutGas, dimensionedScalar(nutGas.dimensions(), nutMaxGas)); + nutLiq.correctBoundaryConditions(); + //nutGas.correctBoundaryConditions(); + + //Info<< "limitNut: max nut.liq=" << max(nutLiq).value() + // << " nut.gas=" << max(nutGas).value() << endl; + Info<< "limitNut: max nut.liq=" << max(nutLiq).value() << endl; + #}; + } + + #includeFunc writeObjects(thermo:rho.gas) + #includeFunc writeObjects(thermo:rho.liquid) + + // Mixing time is post-processed offline from the written time folders + // (Z.liquid + alpha.liquid), so no sensor/dyeMean function objects here. + // Set writeInterval to the temporal resolution the offline analysis needs. +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/system/createPatchDict b/experimental_cases/uloop_valadbeigy_exp1/system/createPatchDict new file mode 100644 index 00000000..ceca5e01 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/system/createPatchDict @@ -0,0 +1,35 @@ +/*--------------------------------*- 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 createPatchDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Consolidate the per-block wall patches (wall_A/B/C) into a single "walls" patch. +// createPatch also drops the now-empty stitch interface patches (int_*, 0 faces). +// The open tank roof is already its own "outlet" mesh patch (block C physical +// group), so it is NOT merged here and needs no surfaceToPatch. sparger and +// dye_inlet are still carved from "walls" with surfaceToPatch in presteps.sh. + +pointSync false; + +patches +( + { + name walls; + patchInfo { type wall; } + constructFrom patches; + patches (wall_A wall_B wall_C); + } +); + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/system/decomposeParDict b/experimental_cases/uloop_valadbeigy_exp1/system/decomposeParDict new file mode 100755 index 00000000..f8397e73 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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/experimental_cases/uloop_valadbeigy_exp1/system/fvConstraints b/experimental_cases/uloop_valadbeigy_exp1/system/fvConstraints new file mode 100644 index 00000000..334f1c8f --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/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/experimental_cases/uloop_valadbeigy_exp1/system/fvSchemes b/experimental_cases/uloop_valadbeigy_exp1/system/fvSchemes new file mode 100644 index 00000000..4052644d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/system/fvSchemes @@ -0,0 +1,76 @@ +/*--------------------------------*- 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; + default cellLimited 1.5 leastSquares 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.*,U.*\)" Gauss Minmod; + "div\(phi.*,U.*\)" Gauss Minmod; + "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 limitedLinear 1; + "div\(\(\(\(alpha.*\*thermo:rho.*\)*nuEff.*\)*dev2\(T\(grad\(U.*\)\)\)\)\)" Gauss linear; +} + +laplacianSchemes +{ + //default Gauss linear corrected; + default Gauss linear corrected 0.33; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + //default uncorrected; + default limited corrected 0.33; +} + +wallDist +{ + //method Poisson; + //nRequired true; + method meshWave; +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/system/fvSolution b/experimental_cases/uloop_valadbeigy_exp1/system/fvSolution new file mode 100644 index 00000000..64b22685 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/system/fvSolution @@ -0,0 +1,121 @@ +/*--------------------------------*- 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|epsilon|omega).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-7; + relTol 1e-3; + minIter 0; + maxIter 5; + } + + "(e|h).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 1e-3; + minIter 0; + maxIter 0; + } + + "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 2; + +} + +relaxationFactors +{ + equations + { + ".*" 1; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/system/inlets_outlets.json b/experimental_cases/uloop_valadbeigy_exp1/system/inlets_outlets.json new file mode 100644 index 00000000..5233240a --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/system/inlets_outlets.json @@ -0,0 +1,24 @@ +{ + "sparger": [ + { + "type": "circle", + "centx": 0.083, + "centy": 0.23, + "centz": 0.0, + "normal_dir": 0, + "radius": 0.01, + "nelements": 50 + } + ], + "dye_inlet": [ + { + "type": "circle", + "centx": 0.083, + "centy": 0.65, + "centz": 0.0, + "normal_dir": 0, + "radius": 0.01, + "nelements": 50 + } + ] +} diff --git a/experimental_cases/uloop_valadbeigy_exp1/system/mixers.json b/experimental_cases/uloop_valadbeigy_exp1/system/mixers.json new file mode 100644 index 00000000..1f51bc16 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/system/mixers.json @@ -0,0 +1,45 @@ +{ + "mixers": [ + { + "x": 0.063, + "y": 0.47, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "power": 9.75, + "Vtip": 5, + "sign": "-", + "swirl_sign": "-" + } + ], + "static_mixers": [ + { + "x": 0.063, + "y": 0.05, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "K": 0.5, + "S": 0.6, + "sign": "-", + "swirl_sign": "-" + }, + { + "x": -0.063, + "y": 0.05, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "K": 0.5, + "S": 0.6, + "sign": "+", + "swirl_sign": "+" + } + ], + "volumetric_source": "ball", + "power": "from_P", + "momentum_source": "axial_and_swirl" +} diff --git a/experimental_cases/uloop_valadbeigy_exp1/system/setFieldsDict b/experimental_cases/uloop_valadbeigy_exp1/system/setFieldsDict new file mode 100644 index 00000000..354c50b6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/system/setFieldsDict @@ -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 dictionary; + location "system"; + object setFieldsDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Applied AFTER the z->y rotation, so the vertical coordinate is y. +// Liquid fills everything below y = 0.8 m (loop + lower half of the degassing +// tank, whose axis is at y = 0.8); gas headspace above. + +// 0.99/0.01 (not 1/0) for numerical stability. +defaultFieldValues +( + volScalarFieldValue alpha.gas 0.99 + volScalarFieldValue alpha.liquid 0.01 + volScalarFieldValue Z.liquid 0 +); + +regions +( + boxToCell + { + box (-1.0 -1.0 -1.0) (1.0 0.8 1.0); + fieldValues + ( + volScalarFieldValue alpha.gas 0.01 + volScalarFieldValue alpha.liquid 0.99 + ); + } +); + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp1/writeGlobalVars.py b/experimental_cases/uloop_valadbeigy_exp1/writeGlobalVars.py new file mode 100644 index 00000000..f20defef --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp1/writeGlobalVars.py @@ -0,0 +1,75 @@ +import os + +import numpy as np + +from bird.utilities.ofio import * + + +def writeGvars(inletA, inletA_dye, 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: + # match on the first whitespace-delimited token so "inletA" does not + # also capture "inletA_dye" + token = line.split()[0] if line.split() else "" + if token == "inletA": + f.write(f"inletA\t{inletA:g};\n") + elif token == "inletA_dye": + f.write(f"inletA_dye\t{inletA_dye:g};\n") + elif token == "liqVol": + f.write(f"liqVol\t{liqVol:g};\n") + else: + f.write(line) + + +def readInletArea(): + # sparger patch area (alpha.gas = 1 there, so the integral is the area); + # used only for the nominal gas velocity in the turbulence inlet BCs. + filename = os.path.join( + "postProcessing", + "patchIntegrate(patch=sparger,field=alpha.gas)", + "0", + "surfaceFieldValue.dat", + ) + return read_surface_field_value(filename) + + +def readDyeInletArea(): + # dye_inlet patch area. Unlike the sparger (alpha.gas = 1 there, so a single + # integral gives the area), no field is uniformly 1 at the dye port, so use + # area = integral(alpha.liquid) + integral(alpha.gas): the phase fractions sum + # to 1 pointwise, hence their integrals sum to the exact patch area. + base = os.path.join("postProcessing") + a_liq = read_surface_field_value( + os.path.join( + base, + "patchIntegrate(patch=dye_inlet,field=alpha.liquid)", + "0", + "surfaceFieldValue.dat", + ) + ) + a_gas = read_surface_field_value( + os.path.join( + base, + "patchIntegrate(patch=dye_inlet,field=alpha.gas)", + "0", + "surfaceFieldValue.dat", + ) + ) + return a_liq + a_gas + + +def getLiqVol(): + volume_field, _ = read_cell_volumes(".") + alpha_field, _ = read_field(".", "0", field_name="alpha.liquid") + return np.sum(volume_field * alpha_field) + + +if __name__ == "__main__": + A = readInletArea() + A_dye = readDyeInletArea() + V = getLiqVol() + writeGvars(A, A_dye, V) diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.gas new file mode 100644 index 00000000..d52e4d25 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.gas @@ -0,0 +1,45 @@ +/*--------------------------------*- 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 $f_N2; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $f_N2; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.liquid new file mode 100644 index 00000000..9cea536f --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/N2.liquid @@ -0,0 +1,61 @@ +/*--------------------------------*- 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.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeN2liq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.gas new file mode 100644 index 00000000..8225d524 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.gas @@ -0,0 +1,45 @@ +/*--------------------------------*- 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 O2.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $f_O2; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $f_O2; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.liquid new file mode 100644 index 00000000..2cfefccb --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/O2.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 O2.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeO2liq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/T.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/T.gas new file mode 100644 index 00000000..8388cb6d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/T.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 T.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $T0; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue $internalField; + value $internalField; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/T.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/T.liquid new file mode 100644 index 00000000..89c826e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/T.liquid @@ -0,0 +1,67 @@ +/*--------------------------------*- 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]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform $T0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $T0; + name dyeTliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue $internalField; + value $internalField; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/U.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/U.gas new file mode 100644 index 00000000..27a4d894 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/U.gas @@ -0,0 +1,65 @@ +/*--------------------------------*- 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 0.0); + +#include "${FOAM_CASE}/constant/globalVars" + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type codedFixedValue; + value uniform (0.0 0.0 0.0); + name spargerInjection; + codeInclude + #{ + #include "volFields.H" + #}; + code + #{ + const scalar Q = 4.0*1e-3/60; // 4 L/min + + vectorField Up(this->size(), Foam::vector::zero); + const scalar area = gSum(this->patch().magSf()); + if (area > SMALL) + { + Up = -(Q/area)*this->patch().nf(); + } + this->operator==(Up); + #}; + } + + dye_inlet + { + type slip; + } + outlet + { + type pressureInletOutletVelocity; + phi phi.gas; + value $internalField; + } + walls + { + type slip; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/U.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/U.liquid new file mode 100644 index 00000000..ceebde58 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/U.liquid @@ -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 volVectorField; + object U.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform (0.0 0.0 0.0); + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type noSlip; + } + dye_inlet + { + type codedFixedValue; + value uniform (0.0 0.0 0.0); + name dyeInjection; + codeInclude + #{ + #include "volFields.H" + #}; + code + #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + vectorField Up(this->size(), Foam::vector::zero); + if (t >= tStart && t < tStop) + { + const scalar dyeVol = 50.0e-6; // m3 (50 mL) + const scalar Q = dyeVol/(tStop - tStart); // m3/s + const scalar area = gSum(this->patch().magSf()); + if (area > SMALL) + { + Up = -(Q/area)*this->patch().nf(); + } + } + this->operator==(Up); + #}; + + } + + outlet + { + type noSlip; + } + walls + { + type noSlip; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.gas new file mode 100644 index 00000000..03b3da41 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.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 Ydefault.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform 0.0; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.liquid new file mode 100644 index 00000000..b7c305e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/Ydefault.liquid @@ -0,0 +1,64 @@ +/*--------------------------------*- 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; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Ydefault = the inert specie (water). At the dye inlet the injected fluid is +// pure tracer Z, so water = 0 there. + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 1.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeYdliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/Z.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/Z.liquid new file mode 100644 index 00000000..9529319c --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/Z.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 Z.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform 1.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeZliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.gas new file mode 100644 index 00000000..2258d0a6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.gas @@ -0,0 +1,65 @@ +/*--------------------------------*- 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 1.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform 1.0; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeAlphaGas; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue uniform 1.0; + value uniform 1.0; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.liquid new file mode 100644 index 00000000..d6470775 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/alpha.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 0.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform 0.0; + } + dye_inlet + { + type codedMixed; + refValue uniform 1.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeAlphaliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type fixedValue; + value uniform 0.0; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/alphat.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/alphat.gas new file mode 100644 index 00000000..928026d5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/alphat.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/alphat.liquid new file mode 100644 index 00000000..1bbd1cca --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/k.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/k.gas new file mode 100644 index 00000000..461ac6e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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 0.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform $k_inlet_gas; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/k.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/k.liquid new file mode 100644 index 00000000..17a7ca05 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/k.liquid @@ -0,0 +1,63 @@ +/*--------------------------------*- 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 0.0; + +boundaryField +{ + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform $k_inlet_liq; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $k_inlet_liq; + name dyekinlet; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type kqRWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.gas new file mode 100644 index 00000000..b3dea556 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.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 nut.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-4; + +boundaryField +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.liquid new file mode 100644 index 00000000..b8303c6a --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/nut.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 nut.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-2; + +boundaryField +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type nutkWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.gas b/experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.gas new file mode 100644 index 00000000..ee1c4607 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.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 omega.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $omega_inlet_gas; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform $omega_inlet_gas; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.liquid b/experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.liquid new file mode 100644 index 00000000..55f48dcc --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/omega.liquid @@ -0,0 +1,63 @@ +/*--------------------------------*- 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 omega.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $omega_inlet_liq; + +boundaryField +{ + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform $omega_inlet_liq; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $omega_inlet_liq; + name dyeepsinlet; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = 1.0; + const scalar tStop = 2.0; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + + outlet + { + type zeroGradient; + } + walls + { + type omegaWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/p b/experimental_cases/uloop_valadbeigy_exp2/0.orig/p new file mode 100644 index 00000000..2c787dc3 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/p @@ -0,0 +1,44 @@ +/*--------------------------------*- 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 +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/0.orig/p_rgh b/experimental_cases/uloop_valadbeigy_exp2/0.orig/p_rgh new file mode 100644 index 00000000..2cc1f127 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/0.orig/p_rgh @@ -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 volScalarField; + object p_rgh; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 101325; + +boundaryField +{ + sparger + { + type fixedFluxPressure; + value $internalField; + } + dye_inlet + { + type fixedFluxPressure; + value $internalField; + } + outlet + { + type prghTotalPressure; + p0 $internalField; + U U.gas; + phi phi.gas; + value $internalField; + } + walls + { + type fixedFluxPressure; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/Allclean b/experimental_cases/uloop_valadbeigy_exp2/Allclean new file mode 100755 index 00000000..dc2f77db --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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/experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py b/experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py new file mode 100644 index 00000000..bef692ab --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py @@ -0,0 +1,418 @@ +""" +Reproduce the case in "Hydrodynamic optimization of a newly designed and fabricated U-Loop bioreactor using Taguchi–ANOVA analysis", Valadbeigy et al., Biochemical Engineering Journal, July 2026 + +Open-top U-loop reactor as 3 stitchable gmsh blocks + +Structured hex mesh everywhere except the U-loop<->tank junction + +Blocks (all interfaces are perimeter-matched -> OpenFOAM integral `stitchMesh`): + A hex : the U pipe + B tet : U-loop <-> tank junction + Two down-stubs (filleted where they meet the tank floor) + C hex : structured-hex tank, extruded up to the open top (Z_TOP). + Flat top face is the `outlet` boundary; sides = wall. + +This output block{A,B,C}.{msh,vtk} +""" + +import math +import gmsh +import numpy as np + +# Geometrical parameters +R = 0.020 # DN40 pipe [m] +R_BEND = 0.045 # elbow centerline bend radius [m] +X_LEG = 0.063 # leg half spacing [m] +Z_HORIZ = 0.000 # bottom height [m] +Z_TANK = 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] +R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] +TANK_LEN = 2 * R_TANK +Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] +FILLET_R = 0.01 # junction fillet radius [m] + + +def loop_pipe_length(include_tank=False): + ''' Compute pipe length which is reported in the paper''' + r_bend = R_BEND + z_bend_top = Z_HORIZ + r_bend + leg_top = Z_TANK if include_tank else (Z_TANK - R_TANK) + leg = leg_top - z_bend_top + arc = 0.5 * math.pi * r_bend + horiz = 2.0 * (X_LEG - r_bend) + return 2.0 * leg + 2.0 * arc + horiz + + +def reactor_volume(tank_fraction=1.0): + v_pipe = math.pi * R**2 * loop_pipe_length(include_tank=False) + v_tank = math.pi * R_TANK**2 * TANK_LEN + return v_pipe + tank_fraction * v_tank + +# --- derived helper dimensions (I need that later) +Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows +Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 +Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 +HX = TANK_LEN / 2.0 # tank box half-width along x +HY = R_TANK # tank box half-width along y (cross-section) + +STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] +B_SLAB = 0.03 # how far do we extend the filletted junction into the hex tank [m] +Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] +Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] + +# --- resolution +RI_FRAC = 0.5 +N_SIDE = 6 # even -> circle/Pillow rims share nodes +N_RAD = max(1, round(N_SIDE * (1 - RI_FRAC) / (RI_FRAC * math.sqrt(2)))) +H_AX = 0.004 # target axial cell size for the pipe sweeps +N_TANK = 30 # structured cells per tank-square edge +# FINER mesh at the junction is obtained with SMALLER JUNCTION_RES +JUNCTION_RES = 1.8 + + +# ---- iterative mesh cleanup +N_LEG = max(1, round((Z_AB - Z_BEND_TOP) / H_AX)) +N_ARC = max(1, round((R_BEND * math.pi / 2) / H_AX)) +N_HOR = max(1, round(2 * (X_LEG - R_BEND) / H_AX)) +N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells + + +def _pillow(geo, cx, cy, cz, r, n_side, n_rad): + '''Pillow shape cylindrical mesh cross-section + Normal direction is z (consistently with the block cylindrical meshing)''' + ang = [math.pi / 4 + k * math.pi / 2 for k in range(4)] + ri = RI_FRAC * r + c = geo.addPoint(cx, cy, cz) + Q = [geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) for a in ang] + A = [geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) for a in ang] + Qe = [geo.addLine(Q[i], Q[(i + 1) % 4]) for i in range(4)] + Rad = [geo.addLine(Q[i], A[i]) for i in range(4)] + Arc = [geo.addCircleArc(A[i], c, A[(i + 1) % 4]) for i in range(4)] + surfs = [geo.addPlaneSurface([geo.addCurveLoop(Qe)])] + for i in range(4): + surfs.append(geo.addSurfaceFilling( + [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])])) + for e in Qe + Arc: + geo.mesh.setTransfiniteCurve(e, n_side + 1) + for e in Rad: + geo.mesh.setTransfiniteCurve(e, n_rad + 1) + for s in surfs: + geo.mesh.setTransfiniteSurface(s) + geo.mesh.setRecombine(2, s) + return surfs + + +def _isflat(s, idx, val, tol=1e-6): + ''' + True if surface *s* lies entirely on the plane coord[idx] == val. + I.e. is a constant coordinate plane + useful to check if what we extruded gives us a flat surface + ''' + bb = gmsh.model.getBoundingBox(2, s) + return abs(bb[idx] - val) < tol and abs(bb[idx + 3] - val) < tol + + +def _tip(idx, val): + ''' + Find flat boundary surface after gmesh extrusion + ''' + vols = [t for _, t in gmsh.model.getEntities(3)] + bnd = {t for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False)} + return [(2, s) for s in bnd if _isflat(s, idx, val)] + + +def _by(surfs, idx, val): + '''Filter surface to those lying on the plane coord[idx] == val.''' + return [s for s in surfs if _isflat(s, idx, val)] + + +def _cx(s): + '''Bounding box used to distinguish the left and right leg''' + bb = gmsh.model.getBoundingBox(2, s) + return 0.5 * (bb[0] + bb[3]) + + +def _rims(surfs): + ''' find the rims at the junction between the legs and the filleted tets + and for the junction between tet block and hex tank block''' + rim = set() + for s in surfs: + for _, cc in gmsh.model.getBoundary([(2, s)], oriented=False): + rim.add(cc) + return rim + + +def _boundary_surfs(): + """Returns volume IDs and their boundary surface IDs.""" + vols = [t for _, t in gmsh.model.getEntities(3)] + return vols, [t for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False)] + + +def _cell_volume(): + """Sum of all 3-D cell volumes""" + tags, coords, _ = gmsh.model.mesh.getNodes() + coords = coords.reshape(-1, 3) + idx = {int(t): i for i, t in enumerate(tags)} + npe = {4: 4, 5: 8, 6: 6, 7: 5} + fans = { + 4: [(0, 1, 2, 3)], + 5: [(0, 1, 2, 6), (0, 2, 3, 6), (0, 3, 7, 6), + (0, 7, 4, 6), (0, 4, 5, 6), (0, 5, 1, 6)], + 6: [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)], + 7: [(0, 1, 2, 4), (0, 2, 3, 4)], + } + total = 0.0 + ets, _, enodes = gmsh.model.mesh.getElements(3) + for et, en in zip(ets, enodes): + conn = np.array([idx[int(t)] for t in en]).reshape(-1, npe[et]) + P = coords[conn] + for a, b, c, d in fans[et]: + v = P[:, a], P[:, b], P[:, c], P[:, d] + total += np.abs(np.einsum( + "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0])).sum() + return total / 6.0 + + +def _write(path, tag): + ''' Write Gmesh object to .msh and print summary''' + TYPE = {4: "tet", 5: "hex", 6: "prism", 7: "pyramid"} + ets, etags, _ = gmsh.model.mesh.getElements(3) + counts = {TYPE.get(e, e): len(t) for e, t in zip(ets, etags)} + vol = _cell_volume() + print(f"[block {tag}] cells={counts} volume={vol * 1e3:.3f} L") + gmsh.option.setNumber("Mesh.MshFileVersion", 2.2) + gmsh.write(path) + gmsh.write(path.rsplit(".", 1)[0] + ".vtk") + return vol + + +# --- U pipe (structured hex) +def build_block_A(path): + """Extrude the pillow cross-section down in sequence + 1) left leg + 2) 90 deg elbow + 3) bottom leg + 4) second 90 degree elbow + 5) up the right leg + + the two leg-top meet the filleted mesh as z=Z_AB""" + gmsh.initialize() + gmsh.model.add("A") + gmsh.option.setNumber("General.Terminal", 0) + geo = gmsh.model.geo + + disk = _pillow(geo, -X_LEG, 0, Z_AB, R, N_SIDE, N_RAD) + geo.extrude([(2, s) for s in disk], 0, 0, -(Z_AB - Z_BEND_TOP), + numElements=[N_LEG], recombine=True) + geo.synchronize() + + # left elbow: revolve the leg-bottom disk about y through the bend centre + geo.revolve(_tip(2, Z_BEND_TOP), -X_LEG + R_BEND, 0, Z_BEND_TOP, 0, -1, 0, + math.pi / 2, numElements=[N_ARC], recombine=True) + geo.synchronize() + + # bottom horizontal run: extrude +x + geo.extrude(_tip(0, -X_LEG + R_BEND), 2 * (X_LEG - R_BEND), 0, 0, + numElements=[N_HOR], recombine=True) + geo.synchronize() + + # right elbow + geo.revolve(_tip(0, X_LEG - R_BEND), X_LEG - R_BEND, 0, Z_BEND_TOP, 0, -1, 0, + math.pi / 2, numElements=[N_ARC], recombine=True) + geo.synchronize() + + # right leg: extrude +z up to Z_AB + geo.extrude(_tip(2, Z_BEND_TOP), 0, 0, Z_AB - Z_BEND_TOP, + numElements=[N_LEG], recombine=True) + geo.synchronize() + + vols, bnd = _boundary_surfs() + iface = _by(bnd, 2, Z_AB) + legL = [s for s in iface if _cx(s) < 0] + legR = [s for s in iface if _cx(s) > 0] + walls = [s for s in bnd if s not in iface] + gmsh.model.addPhysicalGroup(3, vols, name="pipeU") + gmsh.model.addPhysicalGroup(2, legL, name="int_A_legL") + gmsh.model.addPhysicalGroup(2, legR, name="int_A_legR") + gmsh.model.addPhysicalGroup(2, walls, name="wall_A") + + gmsh.model.mesh.generate(3) + vol = _write(path, "A") + gmsh.finalize() + return vol + + +# ---- block B: U-loop <-> tank junction +def build_block_B(path): + '''Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). + 1. Rectangular from Z_BOT to Z_BC (the tank-floor transition layer). + 2. Two cylindrical partial leds + 3. Fillet + + Interface matching (that was the hard part!) + - Bottom circles (int_B_legL/R at Z_AB): rim nodes match A's pillow perimeter. + - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. + ''' + + gmsh.initialize() + gmsh.model.add("B") + gmsh.option.setNumber("General.Terminal", 0) + occ = gmsh.model.occ + + pen = 0.4 * (Z_BC - Z_BOT) + slab = occ.addBox(-HX, -HY, Z_BOT, 2 * HX, 2 * HY, Z_BC - Z_BOT) + stubs = [occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) + for sx in (-X_LEG, X_LEG)] + S, _ = occ.fuse([(3, slab)], [(3, s) for s in stubs]) + occ.synchronize() + vol = S[0][1] + + ring = [] + for _, e in gmsh.model.getEntities(1): + ex, _ey, ez = occ.getCenterOfMass(1, e) + x0, _, _, x1, _, _ = gmsh.model.getBoundingBox(1, e) + if abs(ez - Z_BOT) < 1e-3 and abs(abs(ex) - X_LEG) < 0.02 \ + and (x1 - x0) < 3 * R: + ring.append(e) + occ.fillet([vol], ring, [FILLET_R]) + occ.synchronize() + + vols, bnd = _boundary_surfs() + bot = _by(bnd, 2, Z_AB) # two pipe circles -> A + top = _by(bnd, 2, Z_BC) # tank square -> C + walls = [s for s in bnd if s not in bot and s not in top] + legL = [s for s in bot if occ.getCenterOfMass(2, s)[0] < 0] + legR = [s for s in bot if occ.getCenterOfMass(2, s)[0] > 0] + + for s in bot: # match each stub rim to A (4*N_SIDE) + rc = _rims([s]) + per = max(1, round(4 * N_SIDE / len(rc))) + for cc in rc: + gmsh.model.mesh.setTransfiniteCurve(cc, per + 1) + for cc in _rims(top): # match tank square rim to C (N_TANK/edge) + gmsh.model.mesh.setTransfiniteCurve(cc, N_TANK + 1) + + gmsh.model.addPhysicalGroup(3, vols, name="juncB") + gmsh.model.addPhysicalGroup(2, legL, name="int_B_legL") + gmsh.model.addPhysicalGroup(2, legR, name="int_B_legR") + gmsh.model.addPhysicalGroup(2, top, name="int_B_top") + gmsh.model.addPhysicalGroup(2, walls, name="wall_B") + gmsh.option.setNumber("Mesh.MeshSizeMax", R / N_SIDE * JUNCTION_RES) + gmsh.option.setNumber("Mesh.Optimize", 1) + gmsh.option.setNumber("Mesh.OptimizeNetgen", 1) + gmsh.model.mesh.generate(3) + gmsh.model.mesh.optimize("Netgen") + vol = _write(path, "B") + gmsh.finalize() + return vol + + +# --- block C: hex tank +def build_block_C(path): + """Structured-hex tank extruded from Z_BC to Z_TOP. + + 1) N_TANK nodes per edge, matching block B's top + 2) Extrude +z to Z_TOP with N_HC uniform layers. + + Top face is the open outlet boundary; + Sides are wall_C; + Bottom is for stitching to block B. + """ + gmsh.initialize() + gmsh.model.add("C") + gmsh.option.setNumber("General.Terminal", 0) + geo = gmsh.model.geo + + p = [geo.addPoint(-HX, -HY, Z_BC), geo.addPoint(HX, -HY, Z_BC), + geo.addPoint(HX, HY, Z_BC), geo.addPoint(-HX, HY, Z_BC)] + l = [geo.addLine(p[i], p[(i + 1) % 4]) for i in range(4)] + sq = geo.addPlaneSurface([geo.addCurveLoop(l)]) + for e in l: + geo.mesh.setTransfiniteCurve(e, N_TANK + 1) + geo.mesh.setTransfiniteSurface(sq) + geo.mesh.setRecombine(2, sq) + geo.extrude([(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True) + geo.synchronize() + + vols, bnd = _boundary_surfs() + bot = _by(bnd, 2, Z_BC) + top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) + walls = [s for s in bnd if s not in bot and s not in top] + gmsh.model.addPhysicalGroup(3, vols, name="tank") + gmsh.model.addPhysicalGroup(2, bot, name="int_C_bot") + gmsh.model.addPhysicalGroup(2, top, name="outlet") + gmsh.model.addPhysicalGroup(2, walls, name="wall_C") + gmsh.model.mesh.generate(3) + vol = _write(path, "C") + gmsh.finalize() + return vol + + +# ---- verify that the junction that stitch mesh will operate on has consistent +# face perimeter +def _plane_nodes(path, zval): + """All mesh nodes at z == zval from a .msh file, returned as (x, y) pairs.""" + gmsh.initialize() + gmsh.open(path) + _, coords, _ = gmsh.model.mesh.getNodes() + coords = coords.reshape(-1, 3) + pts = [(x, y) for x, y, z in coords if abs(z - zval) < 1e-6] + gmsh.finalize() + return pts + + +def _circle_rim(pts, cx, r): + """Subset of (x, y) points lying on a circle centred at (cx, 0) with radius r.""" + return sorted((round(x, 9), round(y, 9)) for x, y in pts + if abs(math.hypot(x - cx, y) - r) < 1e-4) + + +def _square_rim(pts, hx, hy): + """Subset of (x, y) points lying on the perimeter of a [-hx,hx] x [-hy,hy] rectangle.""" + return sorted((round(x, 9), round(y, 9)) for x, y in pts + if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4) + + +def _verify(name, a, b, tol=1e-9): + """Assert two rim point sets have the same count and are coincident within tol.""" + assert len(a) == len(b), \ + f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." + worst = max(min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a) + ok = worst < tol + print(f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}") + assert ok, f"{name}: rims not coincident (gap {worst:.1e} > {tol})." + + +def verify_interfaces(): + """Check that A-B circle rims and B-C square rim match node-for-node across blocks.""" + A_ab = _plane_nodes("blockA.msh", Z_AB) + B_ab = _plane_nodes("blockB.msh", Z_AB) + _verify("A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R)) + _verify("A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R)) + B_bc = _plane_nodes("blockB.msh", Z_BC) + C_bc = _plane_nodes("blockC.msh", Z_BC) + _verify("B-C square", _square_rim(B_bc, HX, HY), _square_rim(C_bc, HX, HY)) + + +# ---- main +if __name__ == "__main__": + print(f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " + f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}") + vols = { + "A": build_block_A("blockA.msh"), + "B": build_block_B("blockB.msh"), + "C": build_block_C("blockC.msh"), + } + verify_interfaces() + + box_tank = (2 * HX) * (2 * HY) * (Z_TOP - Z_BOT) + print("=" * 70) + print(f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " + f"excl. tank = {loop_pipe_length(False):.4f} m") + print(f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " + f"= {sum(vols.values()) * 1e3:.3f} L") + print(f" of which the box tank alone = {box_tank * 1e3:.3f} L") + print(f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " + f"({2 * HX:.3f} x {2 * HY:.3f} m)") + print("[write] block{A,B,C}.{msh,vtk} -> will stitch next") diff --git a/experimental_cases/uloop_valadbeigy_exp2/constant/fvModels b/experimental_cases/uloop_valadbeigy_exp2/constant/fvModels new file mode 100644 index 00000000..b8553170 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/constant/fvModels @@ -0,0 +1,266 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object fvModels; +} + +codedSource +{ + type coded; + selectionMode all; + field U.liquid; + name sourceTime; + + codeInclude + #{ + #include + #include + #}; + + codeAddAlphaRhoSup + #{ + const Time& time = mesh().time(); + const scalarField& V = mesh().V(); + vectorField& Usource = eqn.source(); + const vectorField& C = mesh().C(); + const volScalarField& rhoL = + mesh().lookupObject("thermo:rho.liquid"); + const volScalarField& alphaL = + mesh().lookupObject("alpha.liquid"); + const volVectorField& UL = + mesh().lookupObject("U.liquid"); + const double pi = 3.14159265358979; + // ===== ball mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Vtip = 5; + const double sigma = 0.35; + const double startT = 0.1; + const double px = 0.063, py = 0.47, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double rhs = 4.0*9.75/(rhoM*area); + double V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs)); + for (int it = 0; it < 100; ++it) + { + const double F = (V2-V1)*(V2+V1)*(V2+V1) + 0.35*(V1+V2)*Vtip*Vtip - rhs; + const double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1 + 0.35*Vtip*Vtip; + const double dV = F/dF; + V2 -= dV; + if (std::abs(dV) < 1e-10) break; + } + const double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1); + const double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip; + scalar Sax = 0.0, Sth = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + Sax += alphaL[i]*g*V[i]; + const double rr = std::sqrt(d2-(dy)*(dy)); + Sth += alphaL[i]*g*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Sth, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fax = Tax/Sax*alphaL[i]*g; + Usource[i][1] -= -1.0*fax*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Sth > 1e-30) + { + const double fth = Qsw/Sth*alphaL[i]*g; + Usource[i][0] -= -1.0*fth*V[i]*((dz)/rr); + Usource[i][2] -= -1.0*fth*V[i]*((-dx)/rr); + } + } + } + } + } + // ===== static mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Snum = 0.6; + const double Kloss = 0.5; + const double startT = 0.1; + const double px = 0.063, py = 0.05, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dy)*(dy)); + const double ux = UL[i][1]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][1] -= 1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][1]; + const double uth = UL[i][0]*((dz)/rr) + UL[i][2]*((-dx)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][0] -= -1.0*fsw*V[i]*((dz)/rr); + Usource[i][2] -= -1.0*fsw*V[i]*((-dx)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][1] -= 1.0*fcp*V[i]; + } + } + } + } + } + // ===== static mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Snum = 0.6; + const double Kloss = 0.5; + const double startT = 0.1; + const double px = -0.063, py = 0.05, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && 1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? 1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dy)*(dy)); + const double ux = UL[i][1]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][1] -= -1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][1]; + const double uth = UL[i][0]*((dz)/rr) + UL[i][2]*((-dx)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][0] -= 1.0*fsw*V[i]*((dz)/rr); + Usource[i][2] -= 1.0*fsw*V[i]*((-dx)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][1] -= -1.0*fcp*V[i]; + } + } + } + } + } + #}; +}; diff --git a/experimental_cases/uloop_valadbeigy_exp2/constant/g b/experimental_cases/uloop_valadbeigy_exp2/constant/g new file mode 100644 index 00000000..770a5619 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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/experimental_cases/uloop_valadbeigy_exp2/constant/globalVars b/experimental_cases/uloop_valadbeigy_exp2/constant/globalVars new file mode 100644 index 00000000..ef6762ad --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/constant/globalVars @@ -0,0 +1,70 @@ +T0 300; //initial T(K) which stays constant +//****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_N2 31.2e-3; +//****** diffusion coeff *********** +D_O2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_O2,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_N2_298 0.015; +DH_N2 1300; +He_O2 #calc "$H_O2_298 * exp($DH_O2 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas mass frac (air)************* +f_O2 0.233; +f_N2 0.767; +//*******aeration / dye injection************** +gasFlowRate 6.6667e-5; // 4 L/min air sparged = 4e-3/60 m3/s +// Dye = 50 mL over [dyeStart, dyeStop]; injected by the codedFixedValue BC on +// U.liquid at dye_inlet (which recomputes the patch area at runtime). presteps.sh +// substitutes these values into the __DYE_START__/__DYE_STOP__ tokens of the +// coded dye BCs in 0/ ($vars do not expand inside the #{ #} code blocks). +// dyeStart is also read by get_mixing_time.py. +dyeStart 1.0; // s, dye injection start (after steady circulation) +dyeStop 2.0; // s, dye injection stop (1 s window, 50 mL total) +dyeVol 50.0e-6; // m3 (50 mL); MUST match the literal in 0.orig/U.liquid +//********************************* +inletA 0.000251007; +inletA_dye 0.000251007; +liqVol 0.00588087; +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$gasFlowRate / ($inletA * $alphaGas)"; // nominal sparger gas velocity (turbulence BCs) +//********************************* +LeLiqO2 #calc "$kThermLiq / $rho0MixLiq / $D_O2 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_O2*$LeLiqO2+$f_N2*$LeLiqN2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kO2 #calc "$D_O2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrO2 #calc "$muMixLiq*$CpMixLiq / $kO2"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.04; // mixing length = DN40 pipe diameter (was 0.5 m, too large -> eps too small) +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)"; +//********************************* +// Dye-inlet (liquid) turbulence: the dye is injected at its OWN velocity through +// its OWN (smaller) pipe, so k/eps there must not reuse the sparger-gas values. +// uDye = injected volumetric flow / dye_inlet area; l_scale_dye = dye pipe diameter. +l_scale_dye 0.02; // dye pipe diameter [m] +uDye #calc "$dyeVol / (($dyeStop - $dyeStart) * $inletA_dye)"; // nominal dye injection velocity +k_inlet_dye #calc "1.5 * Foam::pow(($uDye), 2) * Foam::pow($intensity, 2)"; +eps_inlet_dye #calc "pow(0.09,0.75) * Foam::pow($k_inlet_dye, 1.5) / ($l_scale_dye * 0.07)"; diff --git a/experimental_cases/uloop_valadbeigy_exp2/constant/globalVars_temp b/experimental_cases/uloop_valadbeigy_exp2/constant/globalVars_temp new file mode 100644 index 00000000..178935a4 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/constant/globalVars_temp @@ -0,0 +1,70 @@ +T0 300; //initial T(K) which stays constant +//****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_N2 31.2e-3; +//****** diffusion coeff *********** +D_O2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_O2,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_N2_298 0.015; +DH_N2 1300; +He_O2 #calc "$H_O2_298 * exp($DH_O2 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas mass frac (air)************* +f_O2 0.233; +f_N2 0.767; +//*******aeration / dye injection************** +gasFlowRate 6.6667e-5; // 4 L/min air sparged = 4e-3/60 m3/s +// Dye = 50 mL over [dyeStart, dyeStop]; injected by the codedFixedValue BC on +// U.liquid at dye_inlet (which recomputes the patch area at runtime). presteps.sh +// substitutes these values into the __DYE_START__/__DYE_STOP__ tokens of the +// coded dye BCs in 0/ ($vars do not expand inside the #{ #} code blocks). +// dyeStart is also read by get_mixing_time.py. +dyeStart 1.0; // s, dye injection start (after steady circulation) +dyeStop 2.0; // s, dye injection stop (1 s window, 50 mL total) +dyeVol 50.0e-6; // m3 (50 mL); MUST match the literal in 0.orig/U.liquid +//********************************* +inletA ; // sparger patch area [m2], filled by writeGlobalVars.py +inletA_dye ; // dye_inlet patch area [m2], filled by writeGlobalVars.py +liqVol ; // liquid volume [m3], filled by writeGlobalVars.py +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$gasFlowRate / ($inletA * $alphaGas)"; // nominal sparger gas velocity (turbulence BCs) +//********************************* +LeLiqO2 #calc "$kThermLiq / $rho0MixLiq / $D_O2 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_O2*$LeLiqO2+$f_N2*$LeLiqN2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kO2 #calc "$D_O2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrO2 #calc "$muMixLiq*$CpMixLiq / $kO2"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.04; // mixing length = DN40 pipe diameter (was 0.5 m, too large -> eps too small) +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)"; +//********************************* +// Dye-inlet (liquid) turbulence: the dye is injected at its OWN velocity through +// its OWN (smaller) pipe, so k/eps there must not reuse the sparger-gas values. +// uDye = injected volumetric flow / dye_inlet area; l_scale_dye = dye pipe diameter. +l_scale_dye 0.02; // dye pipe diameter [m] +uDye #calc "$dyeVol / (($dyeStop - $dyeStart) * $inletA_dye)"; // nominal dye injection velocity +k_inlet_dye #calc "1.5 * Foam::pow(($uDye), 2) * Foam::pow($intensity, 2)"; +eps_inlet_dye #calc "pow(0.09,0.75) * Foam::pow($k_inlet_dye, 1.5) / ($l_scale_dye * 0.07)"; diff --git a/experimental_cases/uloop_valadbeigy_exp2/constant/momentumTransport.gas b/experimental_cases/uloop_valadbeigy_exp2/constant/momentumTransport.gas new file mode 100644 index 00000000..cca64eef --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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 kOmegaSSTSato; + turbulence on; + printCoeff on; +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/constant/momentumTransport.liquid b/experimental_cases/uloop_valadbeigy_exp2/constant/momentumTransport.liquid new file mode 100644 index 00000000..df3e15b5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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 kOmegaSSTSato; + turbulence on; + printCoeffs on; +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/constant/phaseProperties b/experimental_cases/uloop_valadbeigy_exp2/constant/phaseProperties new file mode 100644 index 00000000..d8d0e1c5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/constant/phaseProperties @@ -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 ( O2 N2 ); + k ( $He_O2 $He_N2 ); + 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/experimental_cases/uloop_valadbeigy_exp2/constant/phaseProperties_constantd b/experimental_cases/uloop_valadbeigy_exp2/constant/phaseProperties_constantd new file mode 100644 index 00000000..d8d0e1c5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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 ( O2 N2 ); + k ( $He_O2 $He_N2 ); + 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/experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.gas b/experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.gas new file mode 100644 index 00000000..bbec9049 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.gas @@ -0,0 +1,89 @@ +/*--------------------------------*- 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 +( + O2 + N2 +); + +defaultSpecie N2; + +O2 +{ + specie + { + molWeight 31.9988; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.28253784 0.00148308754 -7.57966669e-07 2.09470555e-10 -2.16717794e-14 -1088.45772 5.45323129 ); + lowCpCoeffs ( 3.78245636 -0.00299673416 9.84730201e-06 -9.68129509e-09 3.24372837e-12 -1063.94356 3.65767573 ); + } + transport + { + As 1.693411300e-06; + Ts 127; + } + elements + { + O 2; + } +} + +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; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.liquid b/experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.liquid new file mode 100644 index 00000000..b0c78662 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/constant/thermophysicalProperties.liquid @@ -0,0 +1,132 @@ +/*--------------------------------*- 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 +( + O2 + N2 + water + Z +); + +inertSpecie water; + +water +{ + specie + { + molWeight 18.0153; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrMixLiq; + } +} + +// Passive dye tracer: identical to water, no interphase mass transfer (Z is +// deliberately absent from every Henry / diffusiveMassTransfer list). +Z +{ + specie + { + molWeight 18.0153; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrMixLiq; + } +} + +O2 +{ + specie + { + molWeight 31.9988; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrO2; + } +} + +N2 +{ + specie + { + molWeight 28.0134; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrN2; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py b/experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py new file mode 100644 index 00000000..2f86cf69 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py @@ -0,0 +1,209 @@ +"""Volume-averaged dye tracer (Z.liquid) in the bottom U-bend box vs time. + +For every time folder this reads the ``Z.liquid`` with BiRD +then computes the cell-volume-weighted average over the +cells whose centres lie inside the box + + x in [-0.01, 0.01], y in [-0.1, 0.1], z in [-0.1, 0.1] [m] + +and plots that average versus time. +""" + +import os + +import numpy as np +from prettyPlot.plotting import plt, pretty_labels + +from bird.utilities.ofio import ( + get_case_times, + read_cell_centers, + read_cell_volumes, + read_field, +) + +CASE = os.path.dirname(os.path.abspath(__file__)) + +# Averaging box [m]. The mesh is y-up after the presteps transformPoints. +X_BOUNDS = (-0.01, 0.01) +Y_BOUNDS = (-0.1, 0.1) +Z_BOUNDS = (-0.1, 0.1) + +# Mixing-time criterion. +BAND = 0.05 # within +/-5% of the final well-mixed value +TAIL_WINDOW = 0.5 # s, tail used to define the final value + + +def read_dye_start(): + """Dye injection start time [s], read from constant/globalVars.""" + with open(os.path.join(CASE, "constant", "globalVars")) as f: + for line in f: + if line.startswith("dyeStart"): + return float(line.split()[1].rstrip(";")) + return 1.0 + + +def box_mask(cell_centers): + """Boolean mask of cells whose centres lie inside the averaging box.""" + x, y, z = cell_centers[:, 0], cell_centers[:, 1], cell_centers[:, 2] + return ( + (x >= X_BOUNDS[0]) + & (x <= X_BOUNDS[1]) + & (y >= Y_BOUNDS[0]) + & (y <= Y_BOUNDS[1]) + & (z >= Z_BOUNDS[0]) + & (z <= Z_BOUNDS[1]) + ) + + +def box_volume_average(z_field, cell_volumes, mask): + """Cell-volume-weighted average of z_field over the masked cells. + + read_field returns a bare float for a uniform OpenFOAM field (e.g. the dye + tracer before injection starts); the volume average is then exactly that + value. Otherwise it is the volume-weighted mean over the box cells. + """ + if np.ndim(z_field) == 0: + return float(z_field) + else: + vol = cell_volumes[mask] + return float(np.sum(z_field[mask] * vol) / np.sum(vol)) + + +def mixing_time(t_arr, z_arr, t_start, continuous=True): + """Mixing time from the box-averaged dye signal. + + The final well-mixed value is the mean of the signal over the last + TAIL_WINDOW seconds. The mixing time is the interval from dye injection + (t_start) to the last instant the signal leaves the +/-BAND envelope of + that final value (after which it stays inside for good). + + Parameters + ---------- + t_arr : array-like + Time array. + z_arr : array-like + Box-averaged dye signal. + t_start : float + Injection time. + continuous : bool, optional + If True, linearly interpolates between the last timestep outside the + band and the first timestep inside to find the exact crossing time. + + Returns + ------- + t_mix : float + Mixing time measured from injection [s]. + t_settle : float + Absolute simulation time at which the signal settles [s]. + z_final : float + Final well-mixed box-averaged value. + """ + # Calculate final value and allowable band + z_final = float(np.mean(z_arr[t_arr >= t_arr[-1] - TAIL_WINDOW])) + band = BAND * abs(z_final) + + post = t_arr >= t_start + outside = post & (np.abs(z_arr - z_final) > band) + + if not np.any(outside): + # already within the band from injection onward + t_settle = t_start + else: + last_out = np.nonzero(outside)[0][-1] + + # Check if we have a subsequent point to interpolate with + if last_out + 1 < len(t_arr): + if continuous: + # Extract time and Z values for the crossing interval + t0, t1 = t_arr[last_out], t_arr[last_out + 1] + z0, z1 = z_arr[last_out], z_arr[last_out + 1] + + # Determine which boundary of the band was crossed + if z0 > z_final: + z_target = z_final + band # Crossed the top boundary + else: + z_target = z_final - band # Crossed the bottom boundary + + # Linearly interpolate to find the exact time t_settle at z_target + if z1 != z0: # Safety check to prevent division by zero + t_settle = t0 + (t1 - t0) * (z_target - z0) / (z1 - z0) + else: + t_settle = t1 + else: + # Original discrete behavior + t_settle = t_arr[last_out + 1] + else: + # The signal was outside the band up to the very last recorded timestep + t_settle = t_arr[last_out] + + return t_settle - t_start, t_settle, z_final + +if __name__ == "__main__": + os.makedirs(os.path.join(CASE, "Figures"), exist_ok=True) + + # Geometry is time-independent: read the cell centres and cell volumes once + # and keep them in the shared field_dict cache. + cell_centers, geom = read_cell_centers(CASE) + n_cells = cell_centers.shape[0] + cell_volumes, geom = read_cell_volumes(CASE, field_dict=geom) + + mask = box_mask(cell_centers) + if mask.sum() == 0: + raise RuntimeError("averaging box contains no cell centres") + + times_float, times_str = get_case_times(CASE) + + t_list, z_list = [], [] + for t_val, t_str in zip(times_float, times_str): + try: + # Fresh field_dict per time so Z.liquid is never served stale. + z_field, _ = read_field(CASE, t_str, "Z.liquid", n_cells=n_cells) + except FileNotFoundError: + continue + t_list.append(t_val) + z_list.append(box_volume_average(z_field, cell_volumes, mask)) + + order = np.argsort(t_list) + t_arr = np.asarray(t_list)[order] + z_arr = np.asarray(z_list)[order] + + np.savetxt( + os.path.join(CASE, "Z_box_average.dat"), + np.column_stack([t_arr, z_arr]), + header="time[s] volAvg(Z.liquid)_box", + ) + + dye_start = read_dye_start() + t_mix, t_settle, z_final = mixing_time(t_arr, z_arr, dye_start) + t_mix_disc, t_settle_disc, z_final_disc = mixing_time(t_arr, z_arr, dye_start, continuous=False) + + with open(os.path.join(CASE, "mix_time.txt"), "w") as f: + f.write(f"Continuous: {t_mix:.4f}\n") + f.write(f"Discrete: {t_mix_disc:.4f}\n") + + fig, ax = plt.subplots(figsize=(6, 4)) + ax.plot(t_arr, z_arr, color="k") + ax.axhline(z_final, color="b", ls="--", label=r"$Z_{final}$") + ax.axhspan( + (1 - BAND) * z_final, (1 + BAND) * z_final, color="b", alpha=0.15 + ) + ax.axvline( + t_settle, color="r", ls=":", label=f"$t_{{mix}}$={t_mix_disc:.2f} s" + ) + ax.set_xlim(left=dye_start) + pretty_labels("time [s]", r"box-averaged $Z_{liquid}$ [-]", 14, ax=ax) + ax.legend() + fig.savefig( + os.path.join(CASE, "Figures", "Z_box_average.png"), + dpi=150, + bbox_inches="tight", + ) + + print(f"box cells : {int(mask.sum())}") + print(f"time folders averaged : {len(t_arr)}") + print(f"final well-mixed Z discrete : {z_final_disc:.6g}") + print(f"final well-mixed Z continous : {z_final:.6g}") + print(f"dye injection start : {dye_start:.3f} s") + print(f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)") + print(f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)") + print("wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png") diff --git a/experimental_cases/uloop_valadbeigy_exp2/presteps.sh b/experimental_cases/uloop_valadbeigy_exp2/presteps.sh new file mode 100755 index 00000000..d32003ce --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/presteps.sh @@ -0,0 +1,77 @@ +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +./Allclean + +set -e # Exit on any error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + +echo PRESTEP 1 +BIRD_DIR=$(python -c "import bird; print(bird.BIRD_DIR)") +APPLICATIONS=$(dirname "$BIRD_DIR")/applications + +python "$APPLICATIONS/write_stl_patch.py" -i system/inlets_outlets.json +python "$APPLICATIONS/write_dynMix_fvModels.py" -i system/mixers.json -o constant + +echo PRESTEP 2 +python build_uloop_hex.py +bash stitch_and_check.sh \ + --mesh blockC.msh \ + --mesh blockB.msh --mesh blockA.msh \ + --stitch int_B_top:int_C_bot \ + --stitch int_A_legL:int_B_legL \ + --stitch int_A_legR:int_B_legR \ + --case stitched_case_uloop +touch stitched_case_uloop/test.foam +cp -r stitched_case_uloop/constant/polyMesh constant/polyMesh +createPatch -overwrite +transformPoints "rotate=((0 0 1) (0 1 0))" + +# Make a local tmp folder to preprocess the mesh +mkdir tmp + +# --- sparger --- +surfaceToPatch -tol 1e-3 sparger.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/sparger\.stl/sparger/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +# --- dye_inlet --- +surfaceToPatch -tol 1e-3 dye_inlet.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/dye_inlet\.stl/dye_inlet/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +foamDictionary constant/polyMesh/boundary -entry entry0/walls/type -set wall +foamDictionary constant/polyMesh/boundary -entry entry0/dye_inlet/type -set wall + +# setup IC +cp -r 0.orig 0 + +DYE_START=$(grep -E '^[[:space:]]*dyeStart[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStart[[:space:]]+([0-9.eE+-]+).*/\1/') +DYE_STOP=$(grep -E '^[[:space:]]*dyeStop[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStop[[:space:]]+([0-9.eE+-]+).*/\1/') +echo "Dye injection window: dyeStart=$DYE_START dyeStop=$DYE_STOP" +grep -rl '__DYE_START__\|__DYE_STOP__' 0 | xargs -r sed -i "s/__DYE_START__/${DYE_START}/g; s/__DYE_STOP__/${DYE_STOP}/g" + +setFields + +postProcess -func 'patchIntegrate(patch="sparger", field="alpha.gas")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.liquid")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.gas")' +postProcess -func writeCellVolumes +writeMeshObj + +echo PRESTEP 3 +python writeGlobalVars.py +cp constant/phaseProperties_constantd constant/phaseProperties + +conda deactivate diff --git a/experimental_cases/uloop_valadbeigy_exp2/run.sh b/experimental_cases/uloop_valadbeigy_exp2/run.sh new file mode 100755 index 00000000..c9d5f3cd --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/run.sh @@ -0,0 +1,75 @@ +#!/bin/bash +### OpenFOAM command +./Allclean +set -e # Exit on any error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + +### BiRD command +echo PRESTEP 1 +BIRD_DIR=$(python -c "import bird; print(bird.BIRD_DIR)") +APPLICATIONS=$(dirname "$BIRD_DIR")/applications + +python "$APPLICATIONS/write_stl_patch.py" -i system/inlets_outlets.json +python "$APPLICATIONS/write_dynMix_fvModels.py" -i system/mixers.json -o constant + +echo PRESTEP 2 +python build_uloop_hex.py +bash stitch_and_check.sh \ + --mesh blockC.msh \ + --mesh blockB.msh --mesh blockA.msh \ + --stitch int_B_top:int_C_bot \ + --stitch int_A_legL:int_B_legL \ + --stitch int_A_legR:int_B_legR \ + --case stitched_case_uloop +touch stitched_case_uloop/test.foam +cp -r stitched_case_uloop/constant/polyMesh constant/polyMesh +createPatch -overwrite +transformPoints "rotate=((0 0 1) (0 1 0))" + +# Make a local tmp folder to preprocess the mesh +mkdir tmp + +# --- sparger --- +surfaceToPatch -tol 1e-3 sparger.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/sparger\.stl/sparger/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +# --- dye_inlet --- +surfaceToPatch -tol 1e-3 dye_inlet.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/dye_inlet\.stl/dye_inlet/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +foamDictionary constant/polyMesh/boundary -entry entry0/walls/type -set wall +foamDictionary constant/polyMesh/boundary -entry entry0/dye_inlet/type -set wall + +cp -r 0.orig 0 + +DYE_START=$(grep -E '^[[:space:]]*dyeStart[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStart[[:space:]]+([0-9.eE+-]+).*/\1/') +DYE_STOP=$(grep -E '^[[:space:]]*dyeStop[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStop[[:space:]]+([0-9.eE+-]+).*/\1/') +echo "Dye injection window: dyeStart=$DYE_START dyeStop=$DYE_STOP" +grep -rl '__DYE_START__\|__DYE_STOP__' 0 | xargs -r sed -i "s/__DYE_START__/${DYE_START}/g; s/__DYE_STOP__/${DYE_STOP}/g" + +setFields + +postProcess -func 'patchIntegrate(patch="sparger", field="alpha.gas")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.liquid")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.gas")' +postProcess -func writeCellVolumes +writeMeshObj + +echo PRESTEP 3 +python writeGlobalVars.py +cp constant/phaseProperties_constantd constant/phaseProperties + + +birdmultiphaseEulerFoam diff --git a/experimental_cases/uloop_valadbeigy_exp2/script b/experimental_cases/uloop_valadbeigy_exp2/script new file mode 100644 index 00000000..06eaf486 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/script @@ -0,0 +1,16 @@ +#!/bin/bash +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=14:59:00 +#SBATCH --account=gas2fuels + +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 -fields "(U.liquid alpha.gas Z.liquid)" +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +python get_mixing_time.py diff --git a/experimental_cases/uloop_valadbeigy_exp2/script_post b/experimental_cases/uloop_valadbeigy_exp2/script_post new file mode 100755 index 00000000..dcbf59d8 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/script_post @@ -0,0 +1,14 @@ +#!/bin/bash +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=01:59:00 +#SBATCH --account=gas2fuels +#SBATCH --dependency=afterany:15800966 + +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +reconstructPar -newTimes -fields "(Z.liquid U.liquid alpha.gas)" +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +python get_mixing_time.py diff --git a/experimental_cases/uloop_valadbeigy_exp2/stitch_and_check.sh b/experimental_cases/uloop_valadbeigy_exp2/stitch_and_check.sh new file mode 100755 index 00000000..d83110d9 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/stitch_and_check.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# +# Stitch a list of gmsh block meshes into ONE OpenFOAM mesh and run checkMesh. +# +# Each mesh is converted with gmshToFoam; mesh #0 becomes the master case that +# the rest are merged into; then each --stitch pair is coupled with integral +# (non -perfect) stitchMesh +# +# Options: +# --mesh (repeatable; order = stack order, #0 is master) +# --stitch (repeatable; integral, for non-matching faces) +# --stitch-perfect (repeatable; -perfect, for conformal faces) +# --case (output case dir, default: stitched_case) + +set -euo pipefail + +CASE="stitched_case" +MESHES=() +STITCHES=() # entries: "integral m:s" or "perfect m:s" (order preserved) +while [[ $# -gt 0 ]]; do + case "$1" in + --mesh) MESHES+=("$2"); shift 2;; + --stitch) STITCHES+=("integral $2"); shift 2;; + --stitch-perfect) STITCHES+=("perfect $2"); shift 2;; + --case) CASE="$2"; shift 2;; + *) echo "unknown argument: $1" >&2; exit 1;; + esac +done + +# >=1 mesh: with a single --mesh and no --stitch this just gmshToFoam+checkMesh +# one block (useful to isolate which block owns a checkMesh failure). +[[ ${#MESHES[@]} -ge 1 ]] || { echo "need at least one --mesh file" >&2; exit 1; } +command -v gmshToFoam >/dev/null 2>&1 || { + echo "OpenFOAM not found on PATH — source OpenFOAM-9 first." >&2; exit 1; } + +# minimal case skeleton (mesh utilities need controlDict/fvSchemes/fvSolution) +write_system() { + local d="$1"; mkdir -p "$d/system" "$d/constant" + cat > "$d/system/controlDict" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object controlDict; } +application checkMesh; +startFrom startTime; startTime 0; +stopAt endTime; endTime 1; +deltaT 1; writeControl timeStep; writeInterval 1; +EOF + cat > "$d/system/fvSchemes" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object fvSchemes; } +ddtSchemes { default steadyState; } +gradSchemes { default Gauss linear; } +divSchemes { default none; } +laplacianSchemes { default Gauss linear corrected; } +interpolationSchemes { default linear; } +snGradSchemes { default corrected; } +EOF + cat > "$d/system/fvSolution" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object fvSolution; } +solvers {} +EOF +} + +echo "==> master case: $CASE (from ${MESHES[0]})" +rm -rf "$CASE"; write_system "$CASE" +gmshToFoam "${MESHES[0]}" -case "$CASE" + +# convert + merge the remaining blocks into the master +for ((i=1; i<${#MESHES[@]}; i++)); do + sub="${CASE}_add${i}" + echo "==> add block $i: ${MESHES[$i]}" + rm -rf "$sub"; write_system "$sub" + gmshToFoam "${MESHES[$i]}" -case "$sub" + # merge into master (OF-9 foundation syntax) + mergeMeshes "$CASE" "$sub" -overwrite + rm -rf "$sub" +done + +# couple each interface with its chosen mode +for spec in "${STITCHES[@]}"; do + mode="${spec%% *}"; pair="${spec#* }" + master="${pair%%:*}"; slave="${pair##*:}" + flags="-overwrite"; [[ "$mode" == perfect ]] && flags="$flags -perfect" + echo "==> stitchMesh ($mode) $master $slave" + stitchMesh $flags "$master" "$slave" -case "$CASE" +done + +echo "==> checkMesh" +checkMesh -allGeometry -allTopology -case "$CASE" +echo "==> done. Mesh in $CASE/constant/polyMesh" diff --git a/experimental_cases/uloop_valadbeigy_exp2/system/controlDict b/experimental_cases/uloop_valadbeigy_exp2/system/controlDict new file mode 100644 index 00000000..cbee600d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/system/controlDict @@ -0,0 +1,95 @@ +/*--------------------------------*- 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 writeNow;//endTime; + +// ~20 s spin-up for steady circulation, dye pulse at t=20-21 s, then ~24 s to +// capture a ~10 s mixing time. +endTime 20; + +deltaT 1e-6; + +writeControl adjustableRunTime; +//writeControl timeStep; + +writeInterval 0.1; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable yes; + +adjustTimeStep yes; + +maxCo 1.0; + +maxDeltaT 0.001; + + +functions +{ + + limitNut + { + type coded; + libs ("libutilityFunctionObjects.so"); + name limitNut; + codeExecute + #{ + const scalar nutMaxLiq = 1e-3; // [m2/s] liquid nut ceiling (tune) + //const scalar nutMaxGas = 1e-3; // [m2/s] gas nut ceiling (tune) + + volScalarField& nutLiq = + mesh().lookupObjectRef("nut.liquid"); + //volScalarField& nutGas = + // mesh().lookupObjectRef("nut.gas"); + + nutLiq = min(nutLiq, dimensionedScalar(nutLiq.dimensions(), nutMaxLiq)); + //nutGas = min(nutGas, dimensionedScalar(nutGas.dimensions(), nutMaxGas)); + nutLiq.correctBoundaryConditions(); + //nutGas.correctBoundaryConditions(); + + //Info<< "limitNut: max nut.liq=" << max(nutLiq).value() + // << " nut.gas=" << max(nutGas).value() << endl; + Info<< "limitNut: max nut.liq=" << max(nutLiq).value() << endl; + #}; + } + + #includeFunc writeObjects(thermo:rho.gas) + #includeFunc writeObjects(thermo:rho.liquid) + + // Mixing time is post-processed offline from the written time folders + // (Z.liquid + alpha.liquid), so no sensor/dyeMean function objects here. + // Set writeInterval to the temporal resolution the offline analysis needs. +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/system/createPatchDict b/experimental_cases/uloop_valadbeigy_exp2/system/createPatchDict new file mode 100644 index 00000000..ceca5e01 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/system/createPatchDict @@ -0,0 +1,35 @@ +/*--------------------------------*- 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 createPatchDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Consolidate the per-block wall patches (wall_A/B/C) into a single "walls" patch. +// createPatch also drops the now-empty stitch interface patches (int_*, 0 faces). +// The open tank roof is already its own "outlet" mesh patch (block C physical +// group), so it is NOT merged here and needs no surfaceToPatch. sparger and +// dye_inlet are still carved from "walls" with surfaceToPatch in presteps.sh. + +pointSync false; + +patches +( + { + name walls; + patchInfo { type wall; } + constructFrom patches; + patches (wall_A wall_B wall_C); + } +); + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/system/decomposeParDict b/experimental_cases/uloop_valadbeigy_exp2/system/decomposeParDict new file mode 100755 index 00000000..f8397e73 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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/experimental_cases/uloop_valadbeigy_exp2/system/fvConstraints b/experimental_cases/uloop_valadbeigy_exp2/system/fvConstraints new file mode 100644 index 00000000..334f1c8f --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/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/experimental_cases/uloop_valadbeigy_exp2/system/fvSchemes b/experimental_cases/uloop_valadbeigy_exp2/system/fvSchemes new file mode 100644 index 00000000..4052644d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/system/fvSchemes @@ -0,0 +1,76 @@ +/*--------------------------------*- 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; + default cellLimited 1.5 leastSquares 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.*,U.*\)" Gauss Minmod; + "div\(phi.*,U.*\)" Gauss Minmod; + "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 limitedLinear 1; + "div\(\(\(\(alpha.*\*thermo:rho.*\)*nuEff.*\)*dev2\(T\(grad\(U.*\)\)\)\)\)" Gauss linear; +} + +laplacianSchemes +{ + //default Gauss linear corrected; + default Gauss linear corrected 0.33; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + //default uncorrected; + default limited corrected 0.33; +} + +wallDist +{ + //method Poisson; + //nRequired true; + method meshWave; +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/system/fvSolution b/experimental_cases/uloop_valadbeigy_exp2/system/fvSolution new file mode 100644 index 00000000..64b22685 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/system/fvSolution @@ -0,0 +1,121 @@ +/*--------------------------------*- 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|epsilon|omega).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-7; + relTol 1e-3; + minIter 0; + maxIter 5; + } + + "(e|h).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 1e-3; + minIter 0; + maxIter 0; + } + + "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 2; + +} + +relaxationFactors +{ + equations + { + ".*" 1; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/system/inlets_outlets.json b/experimental_cases/uloop_valadbeigy_exp2/system/inlets_outlets.json new file mode 100644 index 00000000..5233240a --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/system/inlets_outlets.json @@ -0,0 +1,24 @@ +{ + "sparger": [ + { + "type": "circle", + "centx": 0.083, + "centy": 0.23, + "centz": 0.0, + "normal_dir": 0, + "radius": 0.01, + "nelements": 50 + } + ], + "dye_inlet": [ + { + "type": "circle", + "centx": 0.083, + "centy": 0.65, + "centz": 0.0, + "normal_dir": 0, + "radius": 0.01, + "nelements": 50 + } + ] +} diff --git a/experimental_cases/uloop_valadbeigy_exp2/system/mixers.json b/experimental_cases/uloop_valadbeigy_exp2/system/mixers.json new file mode 100644 index 00000000..eccd2af9 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/system/mixers.json @@ -0,0 +1,45 @@ +{ + "mixers": [ + { + "x": 0.063, + "y": 0.47, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "power": 14.95, + "Vtip": 5, + "sign": "-", + "swirl_sign": "-" + } + ], + "static_mixers": [ + { + "x": 0.063, + "y": 0.05, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "K": 0.5, + "S": 0.6, + "sign": "-", + "swirl_sign": "-" + }, + { + "x": -0.063, + "y": 0.275, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "K": 0.5, + "S": 0.6, + "sign": "+", + "swirl_sign": "+" + } + ], + "volumetric_source": "ball", + "power": "from_P", + "momentum_source": "axial_and_swirl" +} diff --git a/experimental_cases/uloop_valadbeigy_exp2/system/setFieldsDict b/experimental_cases/uloop_valadbeigy_exp2/system/setFieldsDict new file mode 100644 index 00000000..354c50b6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/system/setFieldsDict @@ -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 dictionary; + location "system"; + object setFieldsDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Applied AFTER the z->y rotation, so the vertical coordinate is y. +// Liquid fills everything below y = 0.8 m (loop + lower half of the degassing +// tank, whose axis is at y = 0.8); gas headspace above. + +// 0.99/0.01 (not 1/0) for numerical stability. +defaultFieldValues +( + volScalarFieldValue alpha.gas 0.99 + volScalarFieldValue alpha.liquid 0.01 + volScalarFieldValue Z.liquid 0 +); + +regions +( + boxToCell + { + box (-1.0 -1.0 -1.0) (1.0 0.8 1.0); + fieldValues + ( + volScalarFieldValue alpha.gas 0.01 + volScalarFieldValue alpha.liquid 0.99 + ); + } +); + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp2/writeGlobalVars.py b/experimental_cases/uloop_valadbeigy_exp2/writeGlobalVars.py new file mode 100644 index 00000000..f20defef --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp2/writeGlobalVars.py @@ -0,0 +1,75 @@ +import os + +import numpy as np + +from bird.utilities.ofio import * + + +def writeGvars(inletA, inletA_dye, 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: + # match on the first whitespace-delimited token so "inletA" does not + # also capture "inletA_dye" + token = line.split()[0] if line.split() else "" + if token == "inletA": + f.write(f"inletA\t{inletA:g};\n") + elif token == "inletA_dye": + f.write(f"inletA_dye\t{inletA_dye:g};\n") + elif token == "liqVol": + f.write(f"liqVol\t{liqVol:g};\n") + else: + f.write(line) + + +def readInletArea(): + # sparger patch area (alpha.gas = 1 there, so the integral is the area); + # used only for the nominal gas velocity in the turbulence inlet BCs. + filename = os.path.join( + "postProcessing", + "patchIntegrate(patch=sparger,field=alpha.gas)", + "0", + "surfaceFieldValue.dat", + ) + return read_surface_field_value(filename) + + +def readDyeInletArea(): + # dye_inlet patch area. Unlike the sparger (alpha.gas = 1 there, so a single + # integral gives the area), no field is uniformly 1 at the dye port, so use + # area = integral(alpha.liquid) + integral(alpha.gas): the phase fractions sum + # to 1 pointwise, hence their integrals sum to the exact patch area. + base = os.path.join("postProcessing") + a_liq = read_surface_field_value( + os.path.join( + base, + "patchIntegrate(patch=dye_inlet,field=alpha.liquid)", + "0", + "surfaceFieldValue.dat", + ) + ) + a_gas = read_surface_field_value( + os.path.join( + base, + "patchIntegrate(patch=dye_inlet,field=alpha.gas)", + "0", + "surfaceFieldValue.dat", + ) + ) + return a_liq + a_gas + + +def getLiqVol(): + volume_field, _ = read_cell_volumes(".") + alpha_field, _ = read_field(".", "0", field_name="alpha.liquid") + return np.sum(volume_field * alpha_field) + + +if __name__ == "__main__": + A = readInletArea() + A_dye = readDyeInletArea() + V = getLiqVol() + writeGvars(A, A_dye, V) diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.gas new file mode 100644 index 00000000..d52e4d25 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.gas @@ -0,0 +1,45 @@ +/*--------------------------------*- 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 $f_N2; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $f_N2; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.liquid new file mode 100644 index 00000000..9cea536f --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/N2.liquid @@ -0,0 +1,61 @@ +/*--------------------------------*- 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.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeN2liq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.gas new file mode 100644 index 00000000..8225d524 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.gas @@ -0,0 +1,45 @@ +/*--------------------------------*- 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 O2.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +dimensions [0 0 0 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $f_O2; + + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $f_O2; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.liquid new file mode 100644 index 00000000..2cfefccb --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/O2.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 O2.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeO2liq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/T.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/T.gas new file mode 100644 index 00000000..8388cb6d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/T.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 T.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform $T0; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue $internalField; + value $internalField; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/T.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/T.liquid new file mode 100644 index 00000000..89c826e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/T.liquid @@ -0,0 +1,67 @@ +/*--------------------------------*- 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]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $T0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform $T0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $T0; + name dyeTliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type inletOutlet; + phi phi.liquid; + inletValue $internalField; + value $internalField; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/U.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/U.gas new file mode 100644 index 00000000..27a4d894 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/U.gas @@ -0,0 +1,65 @@ +/*--------------------------------*- 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 0.0); + +#include "${FOAM_CASE}/constant/globalVars" + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type codedFixedValue; + value uniform (0.0 0.0 0.0); + name spargerInjection; + codeInclude + #{ + #include "volFields.H" + #}; + code + #{ + const scalar Q = 4.0*1e-3/60; // 4 L/min + + vectorField Up(this->size(), Foam::vector::zero); + const scalar area = gSum(this->patch().magSf()); + if (area > SMALL) + { + Up = -(Q/area)*this->patch().nf(); + } + this->operator==(Up); + #}; + } + + dye_inlet + { + type slip; + } + outlet + { + type pressureInletOutletVelocity; + phi phi.gas; + value $internalField; + } + walls + { + type slip; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/U.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/U.liquid new file mode 100644 index 00000000..ceebde58 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/U.liquid @@ -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 volVectorField; + object U.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform (0.0 0.0 0.0); + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type noSlip; + } + dye_inlet + { + type codedFixedValue; + value uniform (0.0 0.0 0.0); + name dyeInjection; + codeInclude + #{ + #include "volFields.H" + #}; + code + #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + vectorField Up(this->size(), Foam::vector::zero); + if (t >= tStart && t < tStop) + { + const scalar dyeVol = 50.0e-6; // m3 (50 mL) + const scalar Q = dyeVol/(tStop - tStart); // m3/s + const scalar area = gSum(this->patch().magSf()); + if (area > SMALL) + { + Up = -(Q/area)*this->patch().nf(); + } + } + this->operator==(Up); + #}; + + } + + outlet + { + type noSlip; + } + walls + { + type noSlip; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.gas new file mode 100644 index 00000000..03b3da41 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.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 Ydefault.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type fixedValue; + value uniform 0.0; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.liquid new file mode 100644 index 00000000..b7c305e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/Ydefault.liquid @@ -0,0 +1,64 @@ +/*--------------------------------*- 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; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Ydefault = the inert specie (water). At the dye inlet the injected fluid is +// pure tracer Z, so water = 0 there. + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 1.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeYdliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/Z.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/Z.liquid new file mode 100644 index 00000000..9529319c --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/Z.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 Z.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 0 0 0 0]; + +internalField uniform 0.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform 1.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeZliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.gas new file mode 100644 index 00000000..2258d0a6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.gas @@ -0,0 +1,65 @@ +/*--------------------------------*- 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 1.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform 1.0; + } + dye_inlet + { + type codedMixed; + refValue uniform 0.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeAlphaGas; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type inletOutlet; + phi phi.gas; + inletValue uniform 1.0; + value uniform 1.0; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.liquid new file mode 100644 index 00000000..d6470775 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/alpha.liquid @@ -0,0 +1,62 @@ +/*--------------------------------*- 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 0.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform 0.0; + } + dye_inlet + { + type codedMixed; + refValue uniform 1.0; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform 0.0; + name dyeAlphaliq; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + outlet + { + type fixedValue; + value uniform 0.0; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/alphat.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/alphat.gas new file mode 100644 index 00000000..928026d5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/alphat.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/alphat.liquid new file mode 100644 index 00000000..1bbd1cca --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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.0; + +boundaryField +{ + #includeEtc "caseDicts/setConstraintTypes" + + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/k.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/k.gas new file mode 100644 index 00000000..461ac6e6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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 0.0; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform $k_inlet_gas; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/k.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/k.liquid new file mode 100644 index 00000000..17a7ca05 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/k.liquid @@ -0,0 +1,63 @@ +/*--------------------------------*- 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 0.0; + +boundaryField +{ + sparger + { + type zeroGradient; + } + + dye_inlet + { + type codedMixed; + refValue uniform $k_inlet_liq; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $k_inlet_liq; + name dyekinlet; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = __DYE_START__; + const scalar tStop = __DYE_STOP__; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + outlet + { + type zeroGradient; + } + walls + { + type kqRWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.gas new file mode 100644 index 00000000..b3dea556 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.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 nut.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-4; + +boundaryField +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.liquid new file mode 100644 index 00000000..b8303c6a --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/nut.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 nut.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 1e-2; + +boundaryField +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type nutkWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.gas b/experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.gas new file mode 100644 index 00000000..ee1c4607 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.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 omega.gas; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $omega_inlet_gas; + +boundaryField +{ + sparger + { + type fixedValue; + value uniform $omega_inlet_gas; + } + dye_inlet + { + type zeroGradient; + } + outlet + { + type zeroGradient; + } + walls + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.liquid b/experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.liquid new file mode 100644 index 00000000..55f48dcc --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/omega.liquid @@ -0,0 +1,63 @@ +/*--------------------------------*- 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 omega.liquid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +#include "${FOAM_CASE}/constant/globalVars" + +internalField uniform $omega_inlet_liq; + +boundaryField +{ + sparger + { + type zeroGradient; + } + dye_inlet + { + type codedMixed; + refValue uniform $omega_inlet_liq; + refGradient uniform 0.0; + valueFraction uniform 0.0; + value uniform $omega_inlet_liq; + name dyeepsinlet; + code #{ + const scalar t = this->db().time().value(); + const scalar tStart = 1.0; + const scalar tStop = 2.0; + if (t >= tStart && t < tStop) + { + this->valueFraction() = 1.0; + } + else + { + this->valueFraction() = 0.0; + } + #}; + } + + + outlet + { + type zeroGradient; + } + walls + { + type omegaWallFunction; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/p b/experimental_cases/uloop_valadbeigy_exp3/0.orig/p new file mode 100644 index 00000000..2c787dc3 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/p @@ -0,0 +1,44 @@ +/*--------------------------------*- 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 +{ + sparger + { + type calculated; + value $internalField; + } + dye_inlet + { + type calculated; + value $internalField; + } + outlet + { + type calculated; + value $internalField; + } + walls + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/0.orig/p_rgh b/experimental_cases/uloop_valadbeigy_exp3/0.orig/p_rgh new file mode 100644 index 00000000..2cc1f127 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/0.orig/p_rgh @@ -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 volScalarField; + object p_rgh; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 101325; + +boundaryField +{ + sparger + { + type fixedFluxPressure; + value $internalField; + } + dye_inlet + { + type fixedFluxPressure; + value $internalField; + } + outlet + { + type prghTotalPressure; + p0 $internalField; + U U.gas; + phi phi.gas; + value $internalField; + } + walls + { + type fixedFluxPressure; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/Allclean b/experimental_cases/uloop_valadbeigy_exp3/Allclean new file mode 100755 index 00000000..dc2f77db --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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/experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py b/experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py new file mode 100644 index 00000000..bef692ab --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py @@ -0,0 +1,418 @@ +""" +Reproduce the case in "Hydrodynamic optimization of a newly designed and fabricated U-Loop bioreactor using Taguchi–ANOVA analysis", Valadbeigy et al., Biochemical Engineering Journal, July 2026 + +Open-top U-loop reactor as 3 stitchable gmsh blocks + +Structured hex mesh everywhere except the U-loop<->tank junction + +Blocks (all interfaces are perimeter-matched -> OpenFOAM integral `stitchMesh`): + A hex : the U pipe + B tet : U-loop <-> tank junction + Two down-stubs (filleted where they meet the tank floor) + C hex : structured-hex tank, extruded up to the open top (Z_TOP). + Flat top face is the `outlet` boundary; sides = wall. + +This output block{A,B,C}.{msh,vtk} +""" + +import math +import gmsh +import numpy as np + +# Geometrical parameters +R = 0.020 # DN40 pipe [m] +R_BEND = 0.045 # elbow centerline bend radius [m] +X_LEG = 0.063 # leg half spacing [m] +Z_HORIZ = 0.000 # bottom height [m] +Z_TANK = 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] +R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] +TANK_LEN = 2 * R_TANK +Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] +FILLET_R = 0.01 # junction fillet radius [m] + + +def loop_pipe_length(include_tank=False): + ''' Compute pipe length which is reported in the paper''' + r_bend = R_BEND + z_bend_top = Z_HORIZ + r_bend + leg_top = Z_TANK if include_tank else (Z_TANK - R_TANK) + leg = leg_top - z_bend_top + arc = 0.5 * math.pi * r_bend + horiz = 2.0 * (X_LEG - r_bend) + return 2.0 * leg + 2.0 * arc + horiz + + +def reactor_volume(tank_fraction=1.0): + v_pipe = math.pi * R**2 * loop_pipe_length(include_tank=False) + v_tank = math.pi * R_TANK**2 * TANK_LEN + return v_pipe + tank_fraction * v_tank + +# --- derived helper dimensions (I need that later) +Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows +Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 +Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 +HX = TANK_LEN / 2.0 # tank box half-width along x +HY = R_TANK # tank box half-width along y (cross-section) + +STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] +B_SLAB = 0.03 # how far do we extend the filletted junction into the hex tank [m] +Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] +Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] + +# --- resolution +RI_FRAC = 0.5 +N_SIDE = 6 # even -> circle/Pillow rims share nodes +N_RAD = max(1, round(N_SIDE * (1 - RI_FRAC) / (RI_FRAC * math.sqrt(2)))) +H_AX = 0.004 # target axial cell size for the pipe sweeps +N_TANK = 30 # structured cells per tank-square edge +# FINER mesh at the junction is obtained with SMALLER JUNCTION_RES +JUNCTION_RES = 1.8 + + +# ---- iterative mesh cleanup +N_LEG = max(1, round((Z_AB - Z_BEND_TOP) / H_AX)) +N_ARC = max(1, round((R_BEND * math.pi / 2) / H_AX)) +N_HOR = max(1, round(2 * (X_LEG - R_BEND) / H_AX)) +N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells + + +def _pillow(geo, cx, cy, cz, r, n_side, n_rad): + '''Pillow shape cylindrical mesh cross-section + Normal direction is z (consistently with the block cylindrical meshing)''' + ang = [math.pi / 4 + k * math.pi / 2 for k in range(4)] + ri = RI_FRAC * r + c = geo.addPoint(cx, cy, cz) + Q = [geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) for a in ang] + A = [geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) for a in ang] + Qe = [geo.addLine(Q[i], Q[(i + 1) % 4]) for i in range(4)] + Rad = [geo.addLine(Q[i], A[i]) for i in range(4)] + Arc = [geo.addCircleArc(A[i], c, A[(i + 1) % 4]) for i in range(4)] + surfs = [geo.addPlaneSurface([geo.addCurveLoop(Qe)])] + for i in range(4): + surfs.append(geo.addSurfaceFilling( + [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])])) + for e in Qe + Arc: + geo.mesh.setTransfiniteCurve(e, n_side + 1) + for e in Rad: + geo.mesh.setTransfiniteCurve(e, n_rad + 1) + for s in surfs: + geo.mesh.setTransfiniteSurface(s) + geo.mesh.setRecombine(2, s) + return surfs + + +def _isflat(s, idx, val, tol=1e-6): + ''' + True if surface *s* lies entirely on the plane coord[idx] == val. + I.e. is a constant coordinate plane + useful to check if what we extruded gives us a flat surface + ''' + bb = gmsh.model.getBoundingBox(2, s) + return abs(bb[idx] - val) < tol and abs(bb[idx + 3] - val) < tol + + +def _tip(idx, val): + ''' + Find flat boundary surface after gmesh extrusion + ''' + vols = [t for _, t in gmsh.model.getEntities(3)] + bnd = {t for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False)} + return [(2, s) for s in bnd if _isflat(s, idx, val)] + + +def _by(surfs, idx, val): + '''Filter surface to those lying on the plane coord[idx] == val.''' + return [s for s in surfs if _isflat(s, idx, val)] + + +def _cx(s): + '''Bounding box used to distinguish the left and right leg''' + bb = gmsh.model.getBoundingBox(2, s) + return 0.5 * (bb[0] + bb[3]) + + +def _rims(surfs): + ''' find the rims at the junction between the legs and the filleted tets + and for the junction between tet block and hex tank block''' + rim = set() + for s in surfs: + for _, cc in gmsh.model.getBoundary([(2, s)], oriented=False): + rim.add(cc) + return rim + + +def _boundary_surfs(): + """Returns volume IDs and their boundary surface IDs.""" + vols = [t for _, t in gmsh.model.getEntities(3)] + return vols, [t for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False)] + + +def _cell_volume(): + """Sum of all 3-D cell volumes""" + tags, coords, _ = gmsh.model.mesh.getNodes() + coords = coords.reshape(-1, 3) + idx = {int(t): i for i, t in enumerate(tags)} + npe = {4: 4, 5: 8, 6: 6, 7: 5} + fans = { + 4: [(0, 1, 2, 3)], + 5: [(0, 1, 2, 6), (0, 2, 3, 6), (0, 3, 7, 6), + (0, 7, 4, 6), (0, 4, 5, 6), (0, 5, 1, 6)], + 6: [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)], + 7: [(0, 1, 2, 4), (0, 2, 3, 4)], + } + total = 0.0 + ets, _, enodes = gmsh.model.mesh.getElements(3) + for et, en in zip(ets, enodes): + conn = np.array([idx[int(t)] for t in en]).reshape(-1, npe[et]) + P = coords[conn] + for a, b, c, d in fans[et]: + v = P[:, a], P[:, b], P[:, c], P[:, d] + total += np.abs(np.einsum( + "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0])).sum() + return total / 6.0 + + +def _write(path, tag): + ''' Write Gmesh object to .msh and print summary''' + TYPE = {4: "tet", 5: "hex", 6: "prism", 7: "pyramid"} + ets, etags, _ = gmsh.model.mesh.getElements(3) + counts = {TYPE.get(e, e): len(t) for e, t in zip(ets, etags)} + vol = _cell_volume() + print(f"[block {tag}] cells={counts} volume={vol * 1e3:.3f} L") + gmsh.option.setNumber("Mesh.MshFileVersion", 2.2) + gmsh.write(path) + gmsh.write(path.rsplit(".", 1)[0] + ".vtk") + return vol + + +# --- U pipe (structured hex) +def build_block_A(path): + """Extrude the pillow cross-section down in sequence + 1) left leg + 2) 90 deg elbow + 3) bottom leg + 4) second 90 degree elbow + 5) up the right leg + + the two leg-top meet the filleted mesh as z=Z_AB""" + gmsh.initialize() + gmsh.model.add("A") + gmsh.option.setNumber("General.Terminal", 0) + geo = gmsh.model.geo + + disk = _pillow(geo, -X_LEG, 0, Z_AB, R, N_SIDE, N_RAD) + geo.extrude([(2, s) for s in disk], 0, 0, -(Z_AB - Z_BEND_TOP), + numElements=[N_LEG], recombine=True) + geo.synchronize() + + # left elbow: revolve the leg-bottom disk about y through the bend centre + geo.revolve(_tip(2, Z_BEND_TOP), -X_LEG + R_BEND, 0, Z_BEND_TOP, 0, -1, 0, + math.pi / 2, numElements=[N_ARC], recombine=True) + geo.synchronize() + + # bottom horizontal run: extrude +x + geo.extrude(_tip(0, -X_LEG + R_BEND), 2 * (X_LEG - R_BEND), 0, 0, + numElements=[N_HOR], recombine=True) + geo.synchronize() + + # right elbow + geo.revolve(_tip(0, X_LEG - R_BEND), X_LEG - R_BEND, 0, Z_BEND_TOP, 0, -1, 0, + math.pi / 2, numElements=[N_ARC], recombine=True) + geo.synchronize() + + # right leg: extrude +z up to Z_AB + geo.extrude(_tip(2, Z_BEND_TOP), 0, 0, Z_AB - Z_BEND_TOP, + numElements=[N_LEG], recombine=True) + geo.synchronize() + + vols, bnd = _boundary_surfs() + iface = _by(bnd, 2, Z_AB) + legL = [s for s in iface if _cx(s) < 0] + legR = [s for s in iface if _cx(s) > 0] + walls = [s for s in bnd if s not in iface] + gmsh.model.addPhysicalGroup(3, vols, name="pipeU") + gmsh.model.addPhysicalGroup(2, legL, name="int_A_legL") + gmsh.model.addPhysicalGroup(2, legR, name="int_A_legR") + gmsh.model.addPhysicalGroup(2, walls, name="wall_A") + + gmsh.model.mesh.generate(3) + vol = _write(path, "A") + gmsh.finalize() + return vol + + +# ---- block B: U-loop <-> tank junction +def build_block_B(path): + '''Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). + 1. Rectangular from Z_BOT to Z_BC (the tank-floor transition layer). + 2. Two cylindrical partial leds + 3. Fillet + + Interface matching (that was the hard part!) + - Bottom circles (int_B_legL/R at Z_AB): rim nodes match A's pillow perimeter. + - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. + ''' + + gmsh.initialize() + gmsh.model.add("B") + gmsh.option.setNumber("General.Terminal", 0) + occ = gmsh.model.occ + + pen = 0.4 * (Z_BC - Z_BOT) + slab = occ.addBox(-HX, -HY, Z_BOT, 2 * HX, 2 * HY, Z_BC - Z_BOT) + stubs = [occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) + for sx in (-X_LEG, X_LEG)] + S, _ = occ.fuse([(3, slab)], [(3, s) for s in stubs]) + occ.synchronize() + vol = S[0][1] + + ring = [] + for _, e in gmsh.model.getEntities(1): + ex, _ey, ez = occ.getCenterOfMass(1, e) + x0, _, _, x1, _, _ = gmsh.model.getBoundingBox(1, e) + if abs(ez - Z_BOT) < 1e-3 and abs(abs(ex) - X_LEG) < 0.02 \ + and (x1 - x0) < 3 * R: + ring.append(e) + occ.fillet([vol], ring, [FILLET_R]) + occ.synchronize() + + vols, bnd = _boundary_surfs() + bot = _by(bnd, 2, Z_AB) # two pipe circles -> A + top = _by(bnd, 2, Z_BC) # tank square -> C + walls = [s for s in bnd if s not in bot and s not in top] + legL = [s for s in bot if occ.getCenterOfMass(2, s)[0] < 0] + legR = [s for s in bot if occ.getCenterOfMass(2, s)[0] > 0] + + for s in bot: # match each stub rim to A (4*N_SIDE) + rc = _rims([s]) + per = max(1, round(4 * N_SIDE / len(rc))) + for cc in rc: + gmsh.model.mesh.setTransfiniteCurve(cc, per + 1) + for cc in _rims(top): # match tank square rim to C (N_TANK/edge) + gmsh.model.mesh.setTransfiniteCurve(cc, N_TANK + 1) + + gmsh.model.addPhysicalGroup(3, vols, name="juncB") + gmsh.model.addPhysicalGroup(2, legL, name="int_B_legL") + gmsh.model.addPhysicalGroup(2, legR, name="int_B_legR") + gmsh.model.addPhysicalGroup(2, top, name="int_B_top") + gmsh.model.addPhysicalGroup(2, walls, name="wall_B") + gmsh.option.setNumber("Mesh.MeshSizeMax", R / N_SIDE * JUNCTION_RES) + gmsh.option.setNumber("Mesh.Optimize", 1) + gmsh.option.setNumber("Mesh.OptimizeNetgen", 1) + gmsh.model.mesh.generate(3) + gmsh.model.mesh.optimize("Netgen") + vol = _write(path, "B") + gmsh.finalize() + return vol + + +# --- block C: hex tank +def build_block_C(path): + """Structured-hex tank extruded from Z_BC to Z_TOP. + + 1) N_TANK nodes per edge, matching block B's top + 2) Extrude +z to Z_TOP with N_HC uniform layers. + + Top face is the open outlet boundary; + Sides are wall_C; + Bottom is for stitching to block B. + """ + gmsh.initialize() + gmsh.model.add("C") + gmsh.option.setNumber("General.Terminal", 0) + geo = gmsh.model.geo + + p = [geo.addPoint(-HX, -HY, Z_BC), geo.addPoint(HX, -HY, Z_BC), + geo.addPoint(HX, HY, Z_BC), geo.addPoint(-HX, HY, Z_BC)] + l = [geo.addLine(p[i], p[(i + 1) % 4]) for i in range(4)] + sq = geo.addPlaneSurface([geo.addCurveLoop(l)]) + for e in l: + geo.mesh.setTransfiniteCurve(e, N_TANK + 1) + geo.mesh.setTransfiniteSurface(sq) + geo.mesh.setRecombine(2, sq) + geo.extrude([(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True) + geo.synchronize() + + vols, bnd = _boundary_surfs() + bot = _by(bnd, 2, Z_BC) + top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) + walls = [s for s in bnd if s not in bot and s not in top] + gmsh.model.addPhysicalGroup(3, vols, name="tank") + gmsh.model.addPhysicalGroup(2, bot, name="int_C_bot") + gmsh.model.addPhysicalGroup(2, top, name="outlet") + gmsh.model.addPhysicalGroup(2, walls, name="wall_C") + gmsh.model.mesh.generate(3) + vol = _write(path, "C") + gmsh.finalize() + return vol + + +# ---- verify that the junction that stitch mesh will operate on has consistent +# face perimeter +def _plane_nodes(path, zval): + """All mesh nodes at z == zval from a .msh file, returned as (x, y) pairs.""" + gmsh.initialize() + gmsh.open(path) + _, coords, _ = gmsh.model.mesh.getNodes() + coords = coords.reshape(-1, 3) + pts = [(x, y) for x, y, z in coords if abs(z - zval) < 1e-6] + gmsh.finalize() + return pts + + +def _circle_rim(pts, cx, r): + """Subset of (x, y) points lying on a circle centred at (cx, 0) with radius r.""" + return sorted((round(x, 9), round(y, 9)) for x, y in pts + if abs(math.hypot(x - cx, y) - r) < 1e-4) + + +def _square_rim(pts, hx, hy): + """Subset of (x, y) points lying on the perimeter of a [-hx,hx] x [-hy,hy] rectangle.""" + return sorted((round(x, 9), round(y, 9)) for x, y in pts + if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4) + + +def _verify(name, a, b, tol=1e-9): + """Assert two rim point sets have the same count and are coincident within tol.""" + assert len(a) == len(b), \ + f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." + worst = max(min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a) + ok = worst < tol + print(f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}") + assert ok, f"{name}: rims not coincident (gap {worst:.1e} > {tol})." + + +def verify_interfaces(): + """Check that A-B circle rims and B-C square rim match node-for-node across blocks.""" + A_ab = _plane_nodes("blockA.msh", Z_AB) + B_ab = _plane_nodes("blockB.msh", Z_AB) + _verify("A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R)) + _verify("A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R)) + B_bc = _plane_nodes("blockB.msh", Z_BC) + C_bc = _plane_nodes("blockC.msh", Z_BC) + _verify("B-C square", _square_rim(B_bc, HX, HY), _square_rim(C_bc, HX, HY)) + + +# ---- main +if __name__ == "__main__": + print(f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " + f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}") + vols = { + "A": build_block_A("blockA.msh"), + "B": build_block_B("blockB.msh"), + "C": build_block_C("blockC.msh"), + } + verify_interfaces() + + box_tank = (2 * HX) * (2 * HY) * (Z_TOP - Z_BOT) + print("=" * 70) + print(f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " + f"excl. tank = {loop_pipe_length(False):.4f} m") + print(f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " + f"= {sum(vols.values()) * 1e3:.3f} L") + print(f" of which the box tank alone = {box_tank * 1e3:.3f} L") + print(f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " + f"({2 * HX:.3f} x {2 * HY:.3f} m)") + print("[write] block{A,B,C}.{msh,vtk} -> will stitch next") diff --git a/experimental_cases/uloop_valadbeigy_exp3/constant/fvModels b/experimental_cases/uloop_valadbeigy_exp3/constant/fvModels new file mode 100644 index 00000000..b8553170 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/constant/fvModels @@ -0,0 +1,266 @@ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant"; + object fvModels; +} + +codedSource +{ + type coded; + selectionMode all; + field U.liquid; + name sourceTime; + + codeInclude + #{ + #include + #include + #}; + + codeAddAlphaRhoSup + #{ + const Time& time = mesh().time(); + const scalarField& V = mesh().V(); + vectorField& Usource = eqn.source(); + const vectorField& C = mesh().C(); + const volScalarField& rhoL = + mesh().lookupObject("thermo:rho.liquid"); + const volScalarField& alphaL = + mesh().lookupObject("alpha.liquid"); + const volVectorField& UL = + mesh().lookupObject("U.liquid"); + const double pi = 3.14159265358979; + // ===== ball mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Vtip = 5; + const double sigma = 0.35; + const double startT = 0.1; + const double px = 0.063, py = 0.47, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double rhs = 4.0*9.75/(rhoM*area); + double V2 = (V1>1e-6) ? 2.0*V1 : std::cbrt(std::abs(rhs)); + for (int it = 0; it < 100; ++it) + { + const double F = (V2-V1)*(V2+V1)*(V2+V1) + 0.35*(V1+V2)*Vtip*Vtip - rhs; + const double dF = 3.0*V2*V2 + 2.0*V1*V2 - V1*V1 + 0.35*Vtip*Vtip; + const double dV = F/dF; + V2 -= dV; + if (std::abs(dV) < 1e-10) break; + } + const double Tax = 0.5*rhoM*area*(V2*V2 - V1*V1); + const double Qsw = 0.25*rhoM*(V1+V2)*sigma*Rmix*area*Vtip; + scalar Sax = 0.0, Sth = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + Sax += alphaL[i]*g*V[i]; + const double rr = std::sqrt(d2-(dy)*(dy)); + Sth += alphaL[i]*g*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Sth, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fax = Tax/Sax*alphaL[i]*g; + Usource[i][1] -= -1.0*fax*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Sth > 1e-30) + { + const double fth = Qsw/Sth*alphaL[i]*g; + Usource[i][0] -= -1.0*fth*V[i]*((dz)/rr); + Usource[i][2] -= -1.0*fth*V[i]*((-dx)/rr); + } + } + } + } + } + // ===== static mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Snum = 0.6; + const double Kloss = 0.5; + const double startT = 0.1; + const double px = 0.063, py = 0.05, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && -1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? -1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dy)*(dy)); + const double ux = UL[i][1]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][1] -= 1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][1]; + const double uth = UL[i][0]*((dz)/rr) + UL[i][2]*((-dx)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][0] -= -1.0*fsw*V[i]*((dz)/rr); + Usource[i][2] -= -1.0*fsw*V[i]*((-dx)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][1] -= 1.0*fcp*V[i]; + } + } + } + } + } + // ===== static mixer ===== + { + const double Rmix = 0.018; + const double area = pi*Rmix*Rmix; + const double Snum = 0.6; + const double Kloss = 0.5; + const double startT = 0.1; + const double px = -0.063, py = 0.05, pz = 0.0; + if (time.value() > startT) + { + scalar sV = 0.0, sVU = 0.0, sVrho = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix && 1.0*dy < 0.0) + { + const double w = V[i]*alphaL[i]; + sV += w; sVU += w*UL[i][1]; sVrho += w*rhoL[i]; + } + } + reduce(sV, sumOp()); + reduce(sVU, sumOp()); + reduce(sVrho, sumOp()); + double V1 = (sV>1e-30) ? 1.0*(sVU/sV) : 0.0; + if (V1 < 0.0) V1 = 0.0; + const double rhoM = (sV>1e-30) ? sVrho/sV : 1000.0; + const double Qsw = Snum*Rmix*rhoM*area*V1*V1; + const double Tls = 0.5*Kloss*rhoM*area*V1*V1; + scalar Sax = 0.0, Ssw = 0.0; + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + const double rr = std::sqrt(d2-(dy)*(dy)); + const double ux = UL[i][1]; + Sax += alphaL[i]*g*V[i]; + Ssw += alphaL[i]*g*rhoL[i]*ux*ux*rr*V[i]; + } + } + reduce(Sax, sumOp()); + reduce(Ssw, sumOp()); + forAll(C, i) + { + const double dx=C[i].x()-px, dy=C[i].y()-py, dz=C[i].z()-pz; + const double d2 = dx*dx + dy*dy + dz*dz; + if (d2 <= Rmix*Rmix) + { + const double epsi = std::max(0.6123724356957945*Rmix, 2.0*std::cbrt(V[i])); + const double g = std::exp(-d2/(epsi*epsi)); + if (Sax > 1e-30) + { + const double fvisc = Tls/Sax*alphaL[i]*g; + Usource[i][1] -= -1.0*fvisc*V[i]; + } + const double rr = std::sqrt(d2-(dy)*(dy)); + if (rr > 1e-3*Rmix && Ssw > 1e-30) + { + const double ux = UL[i][1]; + const double uth = UL[i][0]*((dz)/rr) + UL[i][2]*((-dx)/rr); + const double A0 = Qsw/Ssw; + const double fsw = A0*rhoL[i]*ux*ux*alphaL[i]*g; + Usource[i][0] -= 1.0*fsw*V[i]*((dz)/rr); + Usource[i][2] -= 1.0*fsw*V[i]*((-dx)/rr); + const double fcp = A0*rhoL[i]*ux*uth*alphaL[i]*g; + Usource[i][1] -= -1.0*fcp*V[i]; + } + } + } + } + } + #}; +}; diff --git a/experimental_cases/uloop_valadbeigy_exp3/constant/g b/experimental_cases/uloop_valadbeigy_exp3/constant/g new file mode 100644 index 00000000..770a5619 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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/experimental_cases/uloop_valadbeigy_exp3/constant/globalVars b/experimental_cases/uloop_valadbeigy_exp3/constant/globalVars new file mode 100644 index 00000000..ef6762ad --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/constant/globalVars @@ -0,0 +1,70 @@ +T0 300; //initial T(K) which stays constant +//****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_N2 31.2e-3; +//****** diffusion coeff *********** +D_O2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_O2,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_N2_298 0.015; +DH_N2 1300; +He_O2 #calc "$H_O2_298 * exp($DH_O2 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas mass frac (air)************* +f_O2 0.233; +f_N2 0.767; +//*******aeration / dye injection************** +gasFlowRate 6.6667e-5; // 4 L/min air sparged = 4e-3/60 m3/s +// Dye = 50 mL over [dyeStart, dyeStop]; injected by the codedFixedValue BC on +// U.liquid at dye_inlet (which recomputes the patch area at runtime). presteps.sh +// substitutes these values into the __DYE_START__/__DYE_STOP__ tokens of the +// coded dye BCs in 0/ ($vars do not expand inside the #{ #} code blocks). +// dyeStart is also read by get_mixing_time.py. +dyeStart 1.0; // s, dye injection start (after steady circulation) +dyeStop 2.0; // s, dye injection stop (1 s window, 50 mL total) +dyeVol 50.0e-6; // m3 (50 mL); MUST match the literal in 0.orig/U.liquid +//********************************* +inletA 0.000251007; +inletA_dye 0.000251007; +liqVol 0.00588087; +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$gasFlowRate / ($inletA * $alphaGas)"; // nominal sparger gas velocity (turbulence BCs) +//********************************* +LeLiqO2 #calc "$kThermLiq / $rho0MixLiq / $D_O2 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_O2*$LeLiqO2+$f_N2*$LeLiqN2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kO2 #calc "$D_O2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrO2 #calc "$muMixLiq*$CpMixLiq / $kO2"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.04; // mixing length = DN40 pipe diameter (was 0.5 m, too large -> eps too small) +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)"; +//********************************* +// Dye-inlet (liquid) turbulence: the dye is injected at its OWN velocity through +// its OWN (smaller) pipe, so k/eps there must not reuse the sparger-gas values. +// uDye = injected volumetric flow / dye_inlet area; l_scale_dye = dye pipe diameter. +l_scale_dye 0.02; // dye pipe diameter [m] +uDye #calc "$dyeVol / (($dyeStop - $dyeStart) * $inletA_dye)"; // nominal dye injection velocity +k_inlet_dye #calc "1.5 * Foam::pow(($uDye), 2) * Foam::pow($intensity, 2)"; +eps_inlet_dye #calc "pow(0.09,0.75) * Foam::pow($k_inlet_dye, 1.5) / ($l_scale_dye * 0.07)"; diff --git a/experimental_cases/uloop_valadbeigy_exp3/constant/globalVars_temp b/experimental_cases/uloop_valadbeigy_exp3/constant/globalVars_temp new file mode 100644 index 00000000..178935a4 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/constant/globalVars_temp @@ -0,0 +1,70 @@ +T0 300; //initial T(K) which stays constant +//****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_N2 31.2e-3; +//****** diffusion coeff *********** +D_O2 #calc "1.173e-16 * pow($WC_psi * $WC_M,0.5) * $T0 / $muMixLiq / pow($WC_V_O2,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_N2_298 0.015; +DH_N2 1300; +He_O2 #calc "$H_O2_298 * exp($DH_O2 *(1. / $T0 - 1./298.15))"; +He_N2 #calc "$H_N2_298 * exp($DH_N2 *(1. / $T0 - 1./298.15))"; +//*******inlet gas mass frac (air)************* +f_O2 0.233; +f_N2 0.767; +//*******aeration / dye injection************** +gasFlowRate 6.6667e-5; // 4 L/min air sparged = 4e-3/60 m3/s +// Dye = 50 mL over [dyeStart, dyeStop]; injected by the codedFixedValue BC on +// U.liquid at dye_inlet (which recomputes the patch area at runtime). presteps.sh +// substitutes these values into the __DYE_START__/__DYE_STOP__ tokens of the +// coded dye BCs in 0/ ($vars do not expand inside the #{ #} code blocks). +// dyeStart is also read by get_mixing_time.py. +dyeStart 1.0; // s, dye injection start (after steady circulation) +dyeStop 2.0; // s, dye injection stop (1 s window, 50 mL total) +dyeVol 50.0e-6; // m3 (50 mL); MUST match the literal in 0.orig/U.liquid +//********************************* +inletA ; // sparger patch area [m2], filled by writeGlobalVars.py +inletA_dye ; // dye_inlet patch area [m2], filled by writeGlobalVars.py +liqVol ; // liquid volume [m3], filled by writeGlobalVars.py +alphaGas 1; +alphaLiq 0; +uGasPhase #calc "$gasFlowRate / ($inletA * $alphaGas)"; // nominal sparger gas velocity (turbulence BCs) +//********************************* +LeLiqO2 #calc "$kThermLiq / $rho0MixLiq / $D_O2 / $CpMixLiq"; +LeLiqN2 #calc "$kThermLiq / $rho0MixLiq / $D_N2 / $CpMixLiq"; +LeLiqMix #calc "$f_O2*$LeLiqO2+$f_N2*$LeLiqN2"; +PrMixLiq #calc "$CpMixLiq * $muMixLiq / $kThermLiq"; +//********************************* +kO2 #calc "$D_O2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrO2 #calc "$muMixLiq*$CpMixLiq / $kO2"; + +kN2 #calc "$D_N2*$rho0MixLiq*$CpMixLiq*$LeLiqMix"; +PrN2 #calc "$muMixLiq*$CpMixLiq / $kN2"; +//********************************* +l_scale 0.04; // mixing length = DN40 pipe diameter (was 0.5 m, too large -> eps too small) +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)"; +//********************************* +// Dye-inlet (liquid) turbulence: the dye is injected at its OWN velocity through +// its OWN (smaller) pipe, so k/eps there must not reuse the sparger-gas values. +// uDye = injected volumetric flow / dye_inlet area; l_scale_dye = dye pipe diameter. +l_scale_dye 0.02; // dye pipe diameter [m] +uDye #calc "$dyeVol / (($dyeStop - $dyeStart) * $inletA_dye)"; // nominal dye injection velocity +k_inlet_dye #calc "1.5 * Foam::pow(($uDye), 2) * Foam::pow($intensity, 2)"; +eps_inlet_dye #calc "pow(0.09,0.75) * Foam::pow($k_inlet_dye, 1.5) / ($l_scale_dye * 0.07)"; diff --git a/experimental_cases/uloop_valadbeigy_exp3/constant/momentumTransport.gas b/experimental_cases/uloop_valadbeigy_exp3/constant/momentumTransport.gas new file mode 100644 index 00000000..cca64eef --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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 kOmegaSSTSato; + turbulence on; + printCoeff on; +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/constant/momentumTransport.liquid b/experimental_cases/uloop_valadbeigy_exp3/constant/momentumTransport.liquid new file mode 100644 index 00000000..df3e15b5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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 kOmegaSSTSato; + turbulence on; + printCoeffs on; +} + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/constant/phaseProperties b/experimental_cases/uloop_valadbeigy_exp3/constant/phaseProperties new file mode 100644 index 00000000..d8d0e1c5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/constant/phaseProperties @@ -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 ( O2 N2 ); + k ( $He_O2 $He_N2 ); + 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/experimental_cases/uloop_valadbeigy_exp3/constant/phaseProperties_constantd b/experimental_cases/uloop_valadbeigy_exp3/constant/phaseProperties_constantd new file mode 100644 index 00000000..d8d0e1c5 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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 ( O2 N2 ); + k ( $He_O2 $He_N2 ); + 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/experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.gas b/experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.gas new file mode 100644 index 00000000..bbec9049 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.gas @@ -0,0 +1,89 @@ +/*--------------------------------*- 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 +( + O2 + N2 +); + +defaultSpecie N2; + +O2 +{ + specie + { + molWeight 31.9988; + } + thermodynamics + { + Tlow 200; + Thigh 3500; + Tcommon 1000; + highCpCoeffs ( 3.28253784 0.00148308754 -7.57966669e-07 2.09470555e-10 -2.16717794e-14 -1088.45772 5.45323129 ); + lowCpCoeffs ( 3.78245636 -0.00299673416 9.84730201e-06 -9.68129509e-09 3.24372837e-12 -1063.94356 3.65767573 ); + } + transport + { + As 1.693411300e-06; + Ts 127; + } + elements + { + O 2; + } +} + +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; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.liquid b/experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.liquid new file mode 100644 index 00000000..b0c78662 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/constant/thermophysicalProperties.liquid @@ -0,0 +1,132 @@ +/*--------------------------------*- 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 +( + O2 + N2 + water + Z +); + +inertSpecie water; + +water +{ + specie + { + molWeight 18.0153; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrMixLiq; + } +} + +// Passive dye tracer: identical to water, no interphase mass transfer (Z is +// deliberately absent from every Henry / diffusiveMassTransfer list). +Z +{ + specie + { + molWeight 18.0153; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrMixLiq; + } +} + +O2 +{ + specie + { + molWeight 31.9988; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrO2; + } +} + +N2 +{ + specie + { + molWeight 28.0134; + } + equationOfState + { + rho $rho0MixLiq; + } + thermodynamics + { + Cp $CpMixLiq; + Hf -1.5879e+07; + } + transport + { + mu $muMixLiq; + Pr $PrN2; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py b/experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py new file mode 100644 index 00000000..2f86cf69 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py @@ -0,0 +1,209 @@ +"""Volume-averaged dye tracer (Z.liquid) in the bottom U-bend box vs time. + +For every time folder this reads the ``Z.liquid`` with BiRD +then computes the cell-volume-weighted average over the +cells whose centres lie inside the box + + x in [-0.01, 0.01], y in [-0.1, 0.1], z in [-0.1, 0.1] [m] + +and plots that average versus time. +""" + +import os + +import numpy as np +from prettyPlot.plotting import plt, pretty_labels + +from bird.utilities.ofio import ( + get_case_times, + read_cell_centers, + read_cell_volumes, + read_field, +) + +CASE = os.path.dirname(os.path.abspath(__file__)) + +# Averaging box [m]. The mesh is y-up after the presteps transformPoints. +X_BOUNDS = (-0.01, 0.01) +Y_BOUNDS = (-0.1, 0.1) +Z_BOUNDS = (-0.1, 0.1) + +# Mixing-time criterion. +BAND = 0.05 # within +/-5% of the final well-mixed value +TAIL_WINDOW = 0.5 # s, tail used to define the final value + + +def read_dye_start(): + """Dye injection start time [s], read from constant/globalVars.""" + with open(os.path.join(CASE, "constant", "globalVars")) as f: + for line in f: + if line.startswith("dyeStart"): + return float(line.split()[1].rstrip(";")) + return 1.0 + + +def box_mask(cell_centers): + """Boolean mask of cells whose centres lie inside the averaging box.""" + x, y, z = cell_centers[:, 0], cell_centers[:, 1], cell_centers[:, 2] + return ( + (x >= X_BOUNDS[0]) + & (x <= X_BOUNDS[1]) + & (y >= Y_BOUNDS[0]) + & (y <= Y_BOUNDS[1]) + & (z >= Z_BOUNDS[0]) + & (z <= Z_BOUNDS[1]) + ) + + +def box_volume_average(z_field, cell_volumes, mask): + """Cell-volume-weighted average of z_field over the masked cells. + + read_field returns a bare float for a uniform OpenFOAM field (e.g. the dye + tracer before injection starts); the volume average is then exactly that + value. Otherwise it is the volume-weighted mean over the box cells. + """ + if np.ndim(z_field) == 0: + return float(z_field) + else: + vol = cell_volumes[mask] + return float(np.sum(z_field[mask] * vol) / np.sum(vol)) + + +def mixing_time(t_arr, z_arr, t_start, continuous=True): + """Mixing time from the box-averaged dye signal. + + The final well-mixed value is the mean of the signal over the last + TAIL_WINDOW seconds. The mixing time is the interval from dye injection + (t_start) to the last instant the signal leaves the +/-BAND envelope of + that final value (after which it stays inside for good). + + Parameters + ---------- + t_arr : array-like + Time array. + z_arr : array-like + Box-averaged dye signal. + t_start : float + Injection time. + continuous : bool, optional + If True, linearly interpolates between the last timestep outside the + band and the first timestep inside to find the exact crossing time. + + Returns + ------- + t_mix : float + Mixing time measured from injection [s]. + t_settle : float + Absolute simulation time at which the signal settles [s]. + z_final : float + Final well-mixed box-averaged value. + """ + # Calculate final value and allowable band + z_final = float(np.mean(z_arr[t_arr >= t_arr[-1] - TAIL_WINDOW])) + band = BAND * abs(z_final) + + post = t_arr >= t_start + outside = post & (np.abs(z_arr - z_final) > band) + + if not np.any(outside): + # already within the band from injection onward + t_settle = t_start + else: + last_out = np.nonzero(outside)[0][-1] + + # Check if we have a subsequent point to interpolate with + if last_out + 1 < len(t_arr): + if continuous: + # Extract time and Z values for the crossing interval + t0, t1 = t_arr[last_out], t_arr[last_out + 1] + z0, z1 = z_arr[last_out], z_arr[last_out + 1] + + # Determine which boundary of the band was crossed + if z0 > z_final: + z_target = z_final + band # Crossed the top boundary + else: + z_target = z_final - band # Crossed the bottom boundary + + # Linearly interpolate to find the exact time t_settle at z_target + if z1 != z0: # Safety check to prevent division by zero + t_settle = t0 + (t1 - t0) * (z_target - z0) / (z1 - z0) + else: + t_settle = t1 + else: + # Original discrete behavior + t_settle = t_arr[last_out + 1] + else: + # The signal was outside the band up to the very last recorded timestep + t_settle = t_arr[last_out] + + return t_settle - t_start, t_settle, z_final + +if __name__ == "__main__": + os.makedirs(os.path.join(CASE, "Figures"), exist_ok=True) + + # Geometry is time-independent: read the cell centres and cell volumes once + # and keep them in the shared field_dict cache. + cell_centers, geom = read_cell_centers(CASE) + n_cells = cell_centers.shape[0] + cell_volumes, geom = read_cell_volumes(CASE, field_dict=geom) + + mask = box_mask(cell_centers) + if mask.sum() == 0: + raise RuntimeError("averaging box contains no cell centres") + + times_float, times_str = get_case_times(CASE) + + t_list, z_list = [], [] + for t_val, t_str in zip(times_float, times_str): + try: + # Fresh field_dict per time so Z.liquid is never served stale. + z_field, _ = read_field(CASE, t_str, "Z.liquid", n_cells=n_cells) + except FileNotFoundError: + continue + t_list.append(t_val) + z_list.append(box_volume_average(z_field, cell_volumes, mask)) + + order = np.argsort(t_list) + t_arr = np.asarray(t_list)[order] + z_arr = np.asarray(z_list)[order] + + np.savetxt( + os.path.join(CASE, "Z_box_average.dat"), + np.column_stack([t_arr, z_arr]), + header="time[s] volAvg(Z.liquid)_box", + ) + + dye_start = read_dye_start() + t_mix, t_settle, z_final = mixing_time(t_arr, z_arr, dye_start) + t_mix_disc, t_settle_disc, z_final_disc = mixing_time(t_arr, z_arr, dye_start, continuous=False) + + with open(os.path.join(CASE, "mix_time.txt"), "w") as f: + f.write(f"Continuous: {t_mix:.4f}\n") + f.write(f"Discrete: {t_mix_disc:.4f}\n") + + fig, ax = plt.subplots(figsize=(6, 4)) + ax.plot(t_arr, z_arr, color="k") + ax.axhline(z_final, color="b", ls="--", label=r"$Z_{final}$") + ax.axhspan( + (1 - BAND) * z_final, (1 + BAND) * z_final, color="b", alpha=0.15 + ) + ax.axvline( + t_settle, color="r", ls=":", label=f"$t_{{mix}}$={t_mix_disc:.2f} s" + ) + ax.set_xlim(left=dye_start) + pretty_labels("time [s]", r"box-averaged $Z_{liquid}$ [-]", 14, ax=ax) + ax.legend() + fig.savefig( + os.path.join(CASE, "Figures", "Z_box_average.png"), + dpi=150, + bbox_inches="tight", + ) + + print(f"box cells : {int(mask.sum())}") + print(f"time folders averaged : {len(t_arr)}") + print(f"final well-mixed Z discrete : {z_final_disc:.6g}") + print(f"final well-mixed Z continous : {z_final:.6g}") + print(f"dye injection start : {dye_start:.3f} s") + print(f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)") + print(f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)") + print("wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png") diff --git a/experimental_cases/uloop_valadbeigy_exp3/presteps.sh b/experimental_cases/uloop_valadbeigy_exp3/presteps.sh new file mode 100755 index 00000000..d32003ce --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/presteps.sh @@ -0,0 +1,77 @@ +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +./Allclean + +set -e # Exit on any error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + + +echo PRESTEP 1 +BIRD_DIR=$(python -c "import bird; print(bird.BIRD_DIR)") +APPLICATIONS=$(dirname "$BIRD_DIR")/applications + +python "$APPLICATIONS/write_stl_patch.py" -i system/inlets_outlets.json +python "$APPLICATIONS/write_dynMix_fvModels.py" -i system/mixers.json -o constant + +echo PRESTEP 2 +python build_uloop_hex.py +bash stitch_and_check.sh \ + --mesh blockC.msh \ + --mesh blockB.msh --mesh blockA.msh \ + --stitch int_B_top:int_C_bot \ + --stitch int_A_legL:int_B_legL \ + --stitch int_A_legR:int_B_legR \ + --case stitched_case_uloop +touch stitched_case_uloop/test.foam +cp -r stitched_case_uloop/constant/polyMesh constant/polyMesh +createPatch -overwrite +transformPoints "rotate=((0 0 1) (0 1 0))" + +# Make a local tmp folder to preprocess the mesh +mkdir tmp + +# --- sparger --- +surfaceToPatch -tol 1e-3 sparger.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/sparger\.stl/sparger/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +# --- dye_inlet --- +surfaceToPatch -tol 1e-3 dye_inlet.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/dye_inlet\.stl/dye_inlet/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +foamDictionary constant/polyMesh/boundary -entry entry0/walls/type -set wall +foamDictionary constant/polyMesh/boundary -entry entry0/dye_inlet/type -set wall + +# setup IC +cp -r 0.orig 0 + +DYE_START=$(grep -E '^[[:space:]]*dyeStart[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStart[[:space:]]+([0-9.eE+-]+).*/\1/') +DYE_STOP=$(grep -E '^[[:space:]]*dyeStop[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStop[[:space:]]+([0-9.eE+-]+).*/\1/') +echo "Dye injection window: dyeStart=$DYE_START dyeStop=$DYE_STOP" +grep -rl '__DYE_START__\|__DYE_STOP__' 0 | xargs -r sed -i "s/__DYE_START__/${DYE_START}/g; s/__DYE_STOP__/${DYE_STOP}/g" + +setFields + +postProcess -func 'patchIntegrate(patch="sparger", field="alpha.gas")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.liquid")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.gas")' +postProcess -func writeCellVolumes +writeMeshObj + +echo PRESTEP 3 +python writeGlobalVars.py +cp constant/phaseProperties_constantd constant/phaseProperties + +conda deactivate diff --git a/experimental_cases/uloop_valadbeigy_exp3/run.sh b/experimental_cases/uloop_valadbeigy_exp3/run.sh new file mode 100755 index 00000000..c9d5f3cd --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/run.sh @@ -0,0 +1,75 @@ +#!/bin/bash +### OpenFOAM command +./Allclean +set -e # Exit on any error +trap 'echo "ERROR: Something failed! Running cleanup..."; ./Allclean' ERR + +### BiRD command +echo PRESTEP 1 +BIRD_DIR=$(python -c "import bird; print(bird.BIRD_DIR)") +APPLICATIONS=$(dirname "$BIRD_DIR")/applications + +python "$APPLICATIONS/write_stl_patch.py" -i system/inlets_outlets.json +python "$APPLICATIONS/write_dynMix_fvModels.py" -i system/mixers.json -o constant + +echo PRESTEP 2 +python build_uloop_hex.py +bash stitch_and_check.sh \ + --mesh blockC.msh \ + --mesh blockB.msh --mesh blockA.msh \ + --stitch int_B_top:int_C_bot \ + --stitch int_A_legL:int_B_legL \ + --stitch int_A_legR:int_B_legR \ + --case stitched_case_uloop +touch stitched_case_uloop/test.foam +cp -r stitched_case_uloop/constant/polyMesh constant/polyMesh +createPatch -overwrite +transformPoints "rotate=((0 0 1) (0 1 0))" + +# Make a local tmp folder to preprocess the mesh +mkdir tmp + +# --- sparger --- +surfaceToPatch -tol 1e-3 sparger.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/sparger\.stl/sparger/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +# --- dye_inlet --- +surfaceToPatch -tol 1e-3 dye_inlet.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/dye_inlet\.stl/dye_inlet/g' tmp/boundary +cat tmp/boundary > constant/polyMesh/boundary + +foamDictionary constant/polyMesh/boundary -entry entry0/walls/type -set wall +foamDictionary constant/polyMesh/boundary -entry entry0/dye_inlet/type -set wall + +cp -r 0.orig 0 + +DYE_START=$(grep -E '^[[:space:]]*dyeStart[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStart[[:space:]]+([0-9.eE+-]+).*/\1/') +DYE_STOP=$(grep -E '^[[:space:]]*dyeStop[[:space:]]' constant/globalVars_temp | head -1 | sed -E 's/^[[:space:]]*dyeStop[[:space:]]+([0-9.eE+-]+).*/\1/') +echo "Dye injection window: dyeStart=$DYE_START dyeStop=$DYE_STOP" +grep -rl '__DYE_START__\|__DYE_STOP__' 0 | xargs -r sed -i "s/__DYE_START__/${DYE_START}/g; s/__DYE_STOP__/${DYE_STOP}/g" + +setFields + +postProcess -func 'patchIntegrate(patch="sparger", field="alpha.gas")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.liquid")' +postProcess -func 'patchIntegrate(patch="dye_inlet", field="alpha.gas")' +postProcess -func writeCellVolumes +writeMeshObj + +echo PRESTEP 3 +python writeGlobalVars.py +cp constant/phaseProperties_constantd constant/phaseProperties + + +birdmultiphaseEulerFoam diff --git a/experimental_cases/uloop_valadbeigy_exp3/script b/experimental_cases/uloop_valadbeigy_exp3/script new file mode 100644 index 00000000..06eaf486 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/script @@ -0,0 +1,16 @@ +#!/bin/bash +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=14:59:00 +#SBATCH --account=gas2fuels + +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 -fields "(U.liquid alpha.gas Z.liquid)" +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +python get_mixing_time.py diff --git a/experimental_cases/uloop_valadbeigy_exp3/script_post b/experimental_cases/uloop_valadbeigy_exp3/script_post new file mode 100755 index 00000000..dcbf59d8 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/script_post @@ -0,0 +1,14 @@ +#!/bin/bash +#SBATCH --job-name=val2 +##SBATCH --partition=debug +#SBATCH --nodes=1 +#SBATCH --ntasks-per-node=16 +#SBATCH --time=01:59:00 +#SBATCH --account=gas2fuels +#SBATCH --dependency=afterany:15800966 + +source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc +reconstructPar -newTimes -fields "(Z.liquid U.liquid alpha.gas)" +module load conda +conda activate /projects/gas2fuels/conda_env/bird_mixer +python get_mixing_time.py diff --git a/experimental_cases/uloop_valadbeigy_exp3/stitch_and_check.sh b/experimental_cases/uloop_valadbeigy_exp3/stitch_and_check.sh new file mode 100755 index 00000000..d83110d9 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/stitch_and_check.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# +# Stitch a list of gmsh block meshes into ONE OpenFOAM mesh and run checkMesh. +# +# Each mesh is converted with gmshToFoam; mesh #0 becomes the master case that +# the rest are merged into; then each --stitch pair is coupled with integral +# (non -perfect) stitchMesh +# +# Options: +# --mesh (repeatable; order = stack order, #0 is master) +# --stitch (repeatable; integral, for non-matching faces) +# --stitch-perfect (repeatable; -perfect, for conformal faces) +# --case (output case dir, default: stitched_case) + +set -euo pipefail + +CASE="stitched_case" +MESHES=() +STITCHES=() # entries: "integral m:s" or "perfect m:s" (order preserved) +while [[ $# -gt 0 ]]; do + case "$1" in + --mesh) MESHES+=("$2"); shift 2;; + --stitch) STITCHES+=("integral $2"); shift 2;; + --stitch-perfect) STITCHES+=("perfect $2"); shift 2;; + --case) CASE="$2"; shift 2;; + *) echo "unknown argument: $1" >&2; exit 1;; + esac +done + +# >=1 mesh: with a single --mesh and no --stitch this just gmshToFoam+checkMesh +# one block (useful to isolate which block owns a checkMesh failure). +[[ ${#MESHES[@]} -ge 1 ]] || { echo "need at least one --mesh file" >&2; exit 1; } +command -v gmshToFoam >/dev/null 2>&1 || { + echo "OpenFOAM not found on PATH — source OpenFOAM-9 first." >&2; exit 1; } + +# minimal case skeleton (mesh utilities need controlDict/fvSchemes/fvSolution) +write_system() { + local d="$1"; mkdir -p "$d/system" "$d/constant" + cat > "$d/system/controlDict" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object controlDict; } +application checkMesh; +startFrom startTime; startTime 0; +stopAt endTime; endTime 1; +deltaT 1; writeControl timeStep; writeInterval 1; +EOF + cat > "$d/system/fvSchemes" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object fvSchemes; } +ddtSchemes { default steadyState; } +gradSchemes { default Gauss linear; } +divSchemes { default none; } +laplacianSchemes { default Gauss linear corrected; } +interpolationSchemes { default linear; } +snGradSchemes { default corrected; } +EOF + cat > "$d/system/fvSolution" <<'EOF' +FoamFile { version 2.0; format ascii; class dictionary; object fvSolution; } +solvers {} +EOF +} + +echo "==> master case: $CASE (from ${MESHES[0]})" +rm -rf "$CASE"; write_system "$CASE" +gmshToFoam "${MESHES[0]}" -case "$CASE" + +# convert + merge the remaining blocks into the master +for ((i=1; i<${#MESHES[@]}; i++)); do + sub="${CASE}_add${i}" + echo "==> add block $i: ${MESHES[$i]}" + rm -rf "$sub"; write_system "$sub" + gmshToFoam "${MESHES[$i]}" -case "$sub" + # merge into master (OF-9 foundation syntax) + mergeMeshes "$CASE" "$sub" -overwrite + rm -rf "$sub" +done + +# couple each interface with its chosen mode +for spec in "${STITCHES[@]}"; do + mode="${spec%% *}"; pair="${spec#* }" + master="${pair%%:*}"; slave="${pair##*:}" + flags="-overwrite"; [[ "$mode" == perfect ]] && flags="$flags -perfect" + echo "==> stitchMesh ($mode) $master $slave" + stitchMesh $flags "$master" "$slave" -case "$CASE" +done + +echo "==> checkMesh" +checkMesh -allGeometry -allTopology -case "$CASE" +echo "==> done. Mesh in $CASE/constant/polyMesh" diff --git a/experimental_cases/uloop_valadbeigy_exp3/system/controlDict b/experimental_cases/uloop_valadbeigy_exp3/system/controlDict new file mode 100644 index 00000000..cbee600d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/system/controlDict @@ -0,0 +1,95 @@ +/*--------------------------------*- 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 writeNow;//endTime; + +// ~20 s spin-up for steady circulation, dye pulse at t=20-21 s, then ~24 s to +// capture a ~10 s mixing time. +endTime 20; + +deltaT 1e-6; + +writeControl adjustableRunTime; +//writeControl timeStep; + +writeInterval 0.1; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable yes; + +adjustTimeStep yes; + +maxCo 1.0; + +maxDeltaT 0.001; + + +functions +{ + + limitNut + { + type coded; + libs ("libutilityFunctionObjects.so"); + name limitNut; + codeExecute + #{ + const scalar nutMaxLiq = 1e-3; // [m2/s] liquid nut ceiling (tune) + //const scalar nutMaxGas = 1e-3; // [m2/s] gas nut ceiling (tune) + + volScalarField& nutLiq = + mesh().lookupObjectRef("nut.liquid"); + //volScalarField& nutGas = + // mesh().lookupObjectRef("nut.gas"); + + nutLiq = min(nutLiq, dimensionedScalar(nutLiq.dimensions(), nutMaxLiq)); + //nutGas = min(nutGas, dimensionedScalar(nutGas.dimensions(), nutMaxGas)); + nutLiq.correctBoundaryConditions(); + //nutGas.correctBoundaryConditions(); + + //Info<< "limitNut: max nut.liq=" << max(nutLiq).value() + // << " nut.gas=" << max(nutGas).value() << endl; + Info<< "limitNut: max nut.liq=" << max(nutLiq).value() << endl; + #}; + } + + #includeFunc writeObjects(thermo:rho.gas) + #includeFunc writeObjects(thermo:rho.liquid) + + // Mixing time is post-processed offline from the written time folders + // (Z.liquid + alpha.liquid), so no sensor/dyeMean function objects here. + // Set writeInterval to the temporal resolution the offline analysis needs. +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/system/createPatchDict b/experimental_cases/uloop_valadbeigy_exp3/system/createPatchDict new file mode 100644 index 00000000..ceca5e01 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/system/createPatchDict @@ -0,0 +1,35 @@ +/*--------------------------------*- 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 createPatchDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Consolidate the per-block wall patches (wall_A/B/C) into a single "walls" patch. +// createPatch also drops the now-empty stitch interface patches (int_*, 0 faces). +// The open tank roof is already its own "outlet" mesh patch (block C physical +// group), so it is NOT merged here and needs no surfaceToPatch. sparger and +// dye_inlet are still carved from "walls" with surfaceToPatch in presteps.sh. + +pointSync false; + +patches +( + { + name walls; + patchInfo { type wall; } + constructFrom patches; + patches (wall_A wall_B wall_C); + } +); + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/system/decomposeParDict b/experimental_cases/uloop_valadbeigy_exp3/system/decomposeParDict new file mode 100755 index 00000000..f8397e73 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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/experimental_cases/uloop_valadbeigy_exp3/system/fvConstraints b/experimental_cases/uloop_valadbeigy_exp3/system/fvConstraints new file mode 100644 index 00000000..334f1c8f --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/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/experimental_cases/uloop_valadbeigy_exp3/system/fvSchemes b/experimental_cases/uloop_valadbeigy_exp3/system/fvSchemes new file mode 100644 index 00000000..4052644d --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/system/fvSchemes @@ -0,0 +1,76 @@ +/*--------------------------------*- 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; + default cellLimited 1.5 leastSquares 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.*,U.*\)" Gauss Minmod; + "div\(phi.*,U.*\)" Gauss Minmod; + "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 limitedLinear 1; + "div\(\(\(\(alpha.*\*thermo:rho.*\)*nuEff.*\)*dev2\(T\(grad\(U.*\)\)\)\)\)" Gauss linear; +} + +laplacianSchemes +{ + //default Gauss linear corrected; + default Gauss linear corrected 0.33; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + //default uncorrected; + default limited corrected 0.33; +} + +wallDist +{ + //method Poisson; + //nRequired true; + method meshWave; +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/system/fvSolution b/experimental_cases/uloop_valadbeigy_exp3/system/fvSolution new file mode 100644 index 00000000..64b22685 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/system/fvSolution @@ -0,0 +1,121 @@ +/*--------------------------------*- 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|epsilon|omega).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-7; + relTol 1e-3; + minIter 0; + maxIter 5; + } + + "(e|h).*" + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 1e-3; + minIter 0; + maxIter 0; + } + + "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 2; + +} + +relaxationFactors +{ + equations + { + ".*" 1; + } +} + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/system/inlets_outlets.json b/experimental_cases/uloop_valadbeigy_exp3/system/inlets_outlets.json new file mode 100644 index 00000000..5233240a --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/system/inlets_outlets.json @@ -0,0 +1,24 @@ +{ + "sparger": [ + { + "type": "circle", + "centx": 0.083, + "centy": 0.23, + "centz": 0.0, + "normal_dir": 0, + "radius": 0.01, + "nelements": 50 + } + ], + "dye_inlet": [ + { + "type": "circle", + "centx": 0.083, + "centy": 0.65, + "centz": 0.0, + "normal_dir": 0, + "radius": 0.01, + "nelements": 50 + } + ] +} diff --git a/experimental_cases/uloop_valadbeigy_exp3/system/mixers.json b/experimental_cases/uloop_valadbeigy_exp3/system/mixers.json new file mode 100644 index 00000000..41606a06 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/system/mixers.json @@ -0,0 +1,45 @@ +{ + "mixers": [ + { + "x": 0.063, + "y": 0.47, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "power": 20.8, + "Vtip": 5, + "sign": "-", + "swirl_sign": "-" + } + ], + "static_mixers": [ + { + "x": 0.063, + "y": 0.05, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "K": 0.5, + "S": 0.6, + "sign": "-", + "swirl_sign": "-" + }, + { + "x": -0.063, + "y": 0.50, + "z": 0.0, + "normal_dir": 1, + "radius": 0.018, + "start_time": 0.1, + "K": 0.5, + "S": 0.6, + "sign": "+", + "swirl_sign": "+" + } + ], + "volumetric_source": "ball", + "power": "from_P", + "momentum_source": "axial_and_swirl" +} diff --git a/experimental_cases/uloop_valadbeigy_exp3/system/setFieldsDict b/experimental_cases/uloop_valadbeigy_exp3/system/setFieldsDict new file mode 100644 index 00000000..354c50b6 --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/system/setFieldsDict @@ -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 dictionary; + location "system"; + object setFieldsDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Applied AFTER the z->y rotation, so the vertical coordinate is y. +// Liquid fills everything below y = 0.8 m (loop + lower half of the degassing +// tank, whose axis is at y = 0.8); gas headspace above. + +// 0.99/0.01 (not 1/0) for numerical stability. +defaultFieldValues +( + volScalarFieldValue alpha.gas 0.99 + volScalarFieldValue alpha.liquid 0.01 + volScalarFieldValue Z.liquid 0 +); + +regions +( + boxToCell + { + box (-1.0 -1.0 -1.0) (1.0 0.8 1.0); + fieldValues + ( + volScalarFieldValue alpha.gas 0.01 + volScalarFieldValue alpha.liquid 0.99 + ); + } +); + + +// ************************************************************************* // diff --git a/experimental_cases/uloop_valadbeigy_exp3/writeGlobalVars.py b/experimental_cases/uloop_valadbeigy_exp3/writeGlobalVars.py new file mode 100644 index 00000000..f20defef --- /dev/null +++ b/experimental_cases/uloop_valadbeigy_exp3/writeGlobalVars.py @@ -0,0 +1,75 @@ +import os + +import numpy as np + +from bird.utilities.ofio import * + + +def writeGvars(inletA, inletA_dye, 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: + # match on the first whitespace-delimited token so "inletA" does not + # also capture "inletA_dye" + token = line.split()[0] if line.split() else "" + if token == "inletA": + f.write(f"inletA\t{inletA:g};\n") + elif token == "inletA_dye": + f.write(f"inletA_dye\t{inletA_dye:g};\n") + elif token == "liqVol": + f.write(f"liqVol\t{liqVol:g};\n") + else: + f.write(line) + + +def readInletArea(): + # sparger patch area (alpha.gas = 1 there, so the integral is the area); + # used only for the nominal gas velocity in the turbulence inlet BCs. + filename = os.path.join( + "postProcessing", + "patchIntegrate(patch=sparger,field=alpha.gas)", + "0", + "surfaceFieldValue.dat", + ) + return read_surface_field_value(filename) + + +def readDyeInletArea(): + # dye_inlet patch area. Unlike the sparger (alpha.gas = 1 there, so a single + # integral gives the area), no field is uniformly 1 at the dye port, so use + # area = integral(alpha.liquid) + integral(alpha.gas): the phase fractions sum + # to 1 pointwise, hence their integrals sum to the exact patch area. + base = os.path.join("postProcessing") + a_liq = read_surface_field_value( + os.path.join( + base, + "patchIntegrate(patch=dye_inlet,field=alpha.liquid)", + "0", + "surfaceFieldValue.dat", + ) + ) + a_gas = read_surface_field_value( + os.path.join( + base, + "patchIntegrate(patch=dye_inlet,field=alpha.gas)", + "0", + "surfaceFieldValue.dat", + ) + ) + return a_liq + a_gas + + +def getLiqVol(): + volume_field, _ = read_cell_volumes(".") + alpha_field, _ = read_field(".", "0", field_name="alpha.liquid") + return np.sum(volume_field * alpha_field) + + +if __name__ == "__main__": + A = readInletArea() + A_dye = readDyeInletArea() + V = getLiqVol() + writeGvars(A, A_dye, V) diff --git a/pyproject.toml b/pyproject.toml index 28e99d65..ce43724a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,3 +151,4 @@ scikit-learn = ">=1.8.0,<2" [tool.pixi.feature.optim.dependencies] optuna = ">=4.7.0,<5" pandas = ">=3.0.1,<4" +scikit-learn = ">=1.8.0,<2" From f9b1f625792cf5b14055c2ff4393e617ffcb8a5e Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 1 Sep 2026 16:02:42 -0600 Subject: [PATCH 31/37] format --- .../uloop_valadbeigy_exp1/build_uloop_hex.py | 310 ++++++++++++------ .../uloop_valadbeigy_exp1/get_mixing_time.py | 19 +- .../uloop_valadbeigy_exp2/build_uloop_hex.py | 310 ++++++++++++------ .../uloop_valadbeigy_exp2/get_mixing_time.py | 19 +- .../uloop_valadbeigy_exp3/build_uloop_hex.py | 310 ++++++++++++------ .../uloop_valadbeigy_exp3/get_mixing_time.py | 19 +- 6 files changed, 666 insertions(+), 321 deletions(-) diff --git a/experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py b/experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py index bef692ab..43cb6cba 100644 --- a/experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py +++ b/experimental_cases/uloop_valadbeigy_exp1/build_uloop_hex.py @@ -1,14 +1,14 @@ """ Reproduce the case in "Hydrodynamic optimization of a newly designed and fabricated U-Loop bioreactor using Taguchi–ANOVA analysis", Valadbeigy et al., Biochemical Engineering Journal, July 2026 -Open-top U-loop reactor as 3 stitchable gmsh blocks +Open-top U-loop reactor as 3 stitchable gmsh blocks Structured hex mesh everywhere except the U-loop<->tank junction Blocks (all interfaces are perimeter-matched -> OpenFOAM integral `stitchMesh`): - A hex : the U pipe + A hex : the U pipe B tet : U-loop <-> tank junction - Two down-stubs (filleted where they meet the tank floor) + Two down-stubs (filleted where they meet the tank floor) C hex : structured-hex tank, extruded up to the open top (Z_TOP). Flat top face is the `outlet` boundary; sides = wall. @@ -16,23 +16,26 @@ """ import math + import gmsh import numpy as np # Geometrical parameters -R = 0.020 # DN40 pipe [m] -R_BEND = 0.045 # elbow centerline bend radius [m] -X_LEG = 0.063 # leg half spacing [m] -Z_HORIZ = 0.000 # bottom height [m] -Z_TANK = 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] -R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] +R = 0.020 # DN40 pipe [m] +R_BEND = 0.045 # elbow centerline bend radius [m] +X_LEG = 0.063 # leg half spacing [m] +Z_HORIZ = 0.000 # bottom height [m] +Z_TANK = ( + 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] +) +R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] TANK_LEN = 2 * R_TANK -Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] -FILLET_R = 0.01 # junction fillet radius [m] +Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] +FILLET_R = 0.01 # junction fillet radius [m] def loop_pipe_length(include_tank=False): - ''' Compute pipe length which is reported in the paper''' + """Compute pipe length which is reported in the paper""" r_bend = R_BEND z_bend_top = Z_HORIZ + r_bend leg_top = Z_TANK if include_tank else (Z_TANK - R_TANK) @@ -47,50 +50,62 @@ def reactor_volume(tank_fraction=1.0): v_tank = math.pi * R_TANK**2 * TANK_LEN return v_pipe + tank_fraction * v_tank -# --- derived helper dimensions (I need that later) -Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows -Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 -Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 -HX = TANK_LEN / 2.0 # tank box half-width along x -HY = R_TANK # tank box half-width along y (cross-section) -STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] -B_SLAB = 0.03 # how far do we extend the filletted junction into the hex tank [m] -Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] -Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] +# --- derived helper dimensions (I need that later) +Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows +Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 +Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 +HX = TANK_LEN / 2.0 # tank box half-width along x +HY = R_TANK # tank box half-width along y (cross-section) + +STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] +B_SLAB = ( + 0.03 # how far do we extend the filletted junction into the hex tank [m] +) +Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] +Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] # --- resolution RI_FRAC = 0.5 -N_SIDE = 6 # even -> circle/Pillow rims share nodes +N_SIDE = 6 # even -> circle/Pillow rims share nodes N_RAD = max(1, round(N_SIDE * (1 - RI_FRAC) / (RI_FRAC * math.sqrt(2)))) -H_AX = 0.004 # target axial cell size for the pipe sweeps -N_TANK = 30 # structured cells per tank-square edge +H_AX = 0.004 # target axial cell size for the pipe sweeps +N_TANK = 30 # structured cells per tank-square edge # FINER mesh at the junction is obtained with SMALLER JUNCTION_RES JUNCTION_RES = 1.8 -# ---- iterative mesh cleanup +# ---- iterative mesh cleanup N_LEG = max(1, round((Z_AB - Z_BEND_TOP) / H_AX)) N_ARC = max(1, round((R_BEND * math.pi / 2) / H_AX)) N_HOR = max(1, round(2 * (X_LEG - R_BEND) / H_AX)) -N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells +N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells def _pillow(geo, cx, cy, cz, r, n_side, n_rad): - '''Pillow shape cylindrical mesh cross-section - Normal direction is z (consistently with the block cylindrical meshing)''' + """Pillow shape cylindrical mesh cross-section + Normal direction is z (consistently with the block cylindrical meshing)""" ang = [math.pi / 4 + k * math.pi / 2 for k in range(4)] ri = RI_FRAC * r c = geo.addPoint(cx, cy, cz) - Q = [geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) for a in ang] - A = [geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) for a in ang] + Q = [ + geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) + for a in ang + ] + A = [ + geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) + for a in ang + ] Qe = [geo.addLine(Q[i], Q[(i + 1) % 4]) for i in range(4)] Rad = [geo.addLine(Q[i], A[i]) for i in range(4)] Arc = [geo.addCircleArc(A[i], c, A[(i + 1) % 4]) for i in range(4)] surfs = [geo.addPlaneSurface([geo.addCurveLoop(Qe)])] for i in range(4): - surfs.append(geo.addSurfaceFilling( - [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])])) + surfs.append( + geo.addSurfaceFilling( + [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])] + ) + ) for e in Qe + Arc: geo.mesh.setTransfiniteCurve(e, n_side + 1) for e in Rad: @@ -102,39 +117,43 @@ def _pillow(geo, cx, cy, cz, r, n_side, n_rad): def _isflat(s, idx, val, tol=1e-6): - ''' + """ True if surface *s* lies entirely on the plane coord[idx] == val. I.e. is a constant coordinate plane useful to check if what we extruded gives us a flat surface - ''' + """ bb = gmsh.model.getBoundingBox(2, s) return abs(bb[idx] - val) < tol and abs(bb[idx + 3] - val) < tol def _tip(idx, val): - ''' + """ Find flat boundary surface after gmesh extrusion - ''' + """ vols = [t for _, t in gmsh.model.getEntities(3)] - bnd = {t for _, t in gmsh.model.getBoundary( - [(3, v) for v in vols], combined=True, oriented=False)} + bnd = { + t + for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False + ) + } return [(2, s) for s in bnd if _isflat(s, idx, val)] def _by(surfs, idx, val): - '''Filter surface to those lying on the plane coord[idx] == val.''' + """Filter surface to those lying on the plane coord[idx] == val.""" return [s for s in surfs if _isflat(s, idx, val)] def _cx(s): - '''Bounding box used to distinguish the left and right leg''' + """Bounding box used to distinguish the left and right leg""" bb = gmsh.model.getBoundingBox(2, s) return 0.5 * (bb[0] + bb[3]) def _rims(surfs): - ''' find the rims at the junction between the legs and the filleted tets - and for the junction between tet block and hex tank block''' + """find the rims at the junction between the legs and the filleted tets + and for the junction between tet block and hex tank block""" rim = set() for s in surfs: for _, cc in gmsh.model.getBoundary([(2, s)], oriented=False): @@ -145,8 +164,12 @@ def _rims(surfs): def _boundary_surfs(): """Returns volume IDs and their boundary surface IDs.""" vols = [t for _, t in gmsh.model.getEntities(3)] - return vols, [t for _, t in gmsh.model.getBoundary( - [(3, v) for v in vols], combined=True, oriented=False)] + return vols, [ + t + for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False + ) + ] def _cell_volume(): @@ -157,8 +180,14 @@ def _cell_volume(): npe = {4: 4, 5: 8, 6: 6, 7: 5} fans = { 4: [(0, 1, 2, 3)], - 5: [(0, 1, 2, 6), (0, 2, 3, 6), (0, 3, 7, 6), - (0, 7, 4, 6), (0, 4, 5, 6), (0, 5, 1, 6)], + 5: [ + (0, 1, 2, 6), + (0, 2, 3, 6), + (0, 3, 7, 6), + (0, 7, 4, 6), + (0, 4, 5, 6), + (0, 5, 1, 6), + ], 6: [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)], 7: [(0, 1, 2, 4), (0, 2, 3, 4)], } @@ -169,13 +198,16 @@ def _cell_volume(): P = coords[conn] for a, b, c, d in fans[et]: v = P[:, a], P[:, b], P[:, c], P[:, d] - total += np.abs(np.einsum( - "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0])).sum() + total += np.abs( + np.einsum( + "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0] + ) + ).sum() return total / 6.0 def _write(path, tag): - ''' Write Gmesh object to .msh and print summary''' + """Write Gmesh object to .msh and print summary""" TYPE = {4: "tet", 5: "hex", 6: "prism", 7: "pyramid"} ets, etags, _ = gmsh.model.mesh.getElements(3) counts = {TYPE.get(e, e): len(t) for e, t in zip(ets, etags)} @@ -203,28 +235,66 @@ def build_block_A(path): geo = gmsh.model.geo disk = _pillow(geo, -X_LEG, 0, Z_AB, R, N_SIDE, N_RAD) - geo.extrude([(2, s) for s in disk], 0, 0, -(Z_AB - Z_BEND_TOP), - numElements=[N_LEG], recombine=True) + geo.extrude( + [(2, s) for s in disk], + 0, + 0, + -(Z_AB - Z_BEND_TOP), + numElements=[N_LEG], + recombine=True, + ) geo.synchronize() # left elbow: revolve the leg-bottom disk about y through the bend centre - geo.revolve(_tip(2, Z_BEND_TOP), -X_LEG + R_BEND, 0, Z_BEND_TOP, 0, -1, 0, - math.pi / 2, numElements=[N_ARC], recombine=True) + geo.revolve( + _tip(2, Z_BEND_TOP), + -X_LEG + R_BEND, + 0, + Z_BEND_TOP, + 0, + -1, + 0, + math.pi / 2, + numElements=[N_ARC], + recombine=True, + ) geo.synchronize() # bottom horizontal run: extrude +x - geo.extrude(_tip(0, -X_LEG + R_BEND), 2 * (X_LEG - R_BEND), 0, 0, - numElements=[N_HOR], recombine=True) + geo.extrude( + _tip(0, -X_LEG + R_BEND), + 2 * (X_LEG - R_BEND), + 0, + 0, + numElements=[N_HOR], + recombine=True, + ) geo.synchronize() # right elbow - geo.revolve(_tip(0, X_LEG - R_BEND), X_LEG - R_BEND, 0, Z_BEND_TOP, 0, -1, 0, - math.pi / 2, numElements=[N_ARC], recombine=True) + geo.revolve( + _tip(0, X_LEG - R_BEND), + X_LEG - R_BEND, + 0, + Z_BEND_TOP, + 0, + -1, + 0, + math.pi / 2, + numElements=[N_ARC], + recombine=True, + ) geo.synchronize() # right leg: extrude +z up to Z_AB - geo.extrude(_tip(2, Z_BEND_TOP), 0, 0, Z_AB - Z_BEND_TOP, - numElements=[N_LEG], recombine=True) + geo.extrude( + _tip(2, Z_BEND_TOP), + 0, + 0, + Z_AB - Z_BEND_TOP, + numElements=[N_LEG], + recombine=True, + ) geo.synchronize() vols, bnd = _boundary_surfs() @@ -243,17 +313,17 @@ def build_block_A(path): return vol -# ---- block B: U-loop <-> tank junction +# ---- block B: U-loop <-> tank junction def build_block_B(path): - '''Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). + """Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). 1. Rectangular from Z_BOT to Z_BC (the tank-floor transition layer). - 2. Two cylindrical partial leds + 2. Two cylindrical partial leds 3. Fillet - + Interface matching (that was the hard part!) - Bottom circles (int_B_legL/R at Z_AB): rim nodes match A's pillow perimeter. - - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. - ''' + - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. + """ gmsh.initialize() gmsh.model.add("B") @@ -262,8 +332,10 @@ def build_block_B(path): pen = 0.4 * (Z_BC - Z_BOT) slab = occ.addBox(-HX, -HY, Z_BOT, 2 * HX, 2 * HY, Z_BC - Z_BOT) - stubs = [occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) - for sx in (-X_LEG, X_LEG)] + stubs = [ + occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) + for sx in (-X_LEG, X_LEG) + ] S, _ = occ.fuse([(3, slab)], [(3, s) for s in stubs]) occ.synchronize() vol = S[0][1] @@ -272,25 +344,28 @@ def build_block_B(path): for _, e in gmsh.model.getEntities(1): ex, _ey, ez = occ.getCenterOfMass(1, e) x0, _, _, x1, _, _ = gmsh.model.getBoundingBox(1, e) - if abs(ez - Z_BOT) < 1e-3 and abs(abs(ex) - X_LEG) < 0.02 \ - and (x1 - x0) < 3 * R: + if ( + abs(ez - Z_BOT) < 1e-3 + and abs(abs(ex) - X_LEG) < 0.02 + and (x1 - x0) < 3 * R + ): ring.append(e) occ.fillet([vol], ring, [FILLET_R]) occ.synchronize() vols, bnd = _boundary_surfs() - bot = _by(bnd, 2, Z_AB) # two pipe circles -> A - top = _by(bnd, 2, Z_BC) # tank square -> C + bot = _by(bnd, 2, Z_AB) # two pipe circles -> A + top = _by(bnd, 2, Z_BC) # tank square -> C walls = [s for s in bnd if s not in bot and s not in top] legL = [s for s in bot if occ.getCenterOfMass(2, s)[0] < 0] legR = [s for s in bot if occ.getCenterOfMass(2, s)[0] > 0] - for s in bot: # match each stub rim to A (4*N_SIDE) + for s in bot: # match each stub rim to A (4*N_SIDE) rc = _rims([s]) per = max(1, round(4 * N_SIDE / len(rc))) for cc in rc: gmsh.model.mesh.setTransfiniteCurve(cc, per + 1) - for cc in _rims(top): # match tank square rim to C (N_TANK/edge) + for cc in _rims(top): # match tank square rim to C (N_TANK/edge) gmsh.model.mesh.setTransfiniteCurve(cc, N_TANK + 1) gmsh.model.addPhysicalGroup(3, vols, name="juncB") @@ -308,15 +383,15 @@ def build_block_B(path): return vol -# --- block C: hex tank +# --- block C: hex tank def build_block_C(path): """Structured-hex tank extruded from Z_BC to Z_TOP. 1) N_TANK nodes per edge, matching block B's top 2) Extrude +z to Z_TOP with N_HC uniform layers. - - Top face is the open outlet boundary; - Sides are wall_C; + + Top face is the open outlet boundary; + Sides are wall_C; Bottom is for stitching to block B. """ gmsh.initialize() @@ -324,20 +399,26 @@ def build_block_C(path): gmsh.option.setNumber("General.Terminal", 0) geo = gmsh.model.geo - p = [geo.addPoint(-HX, -HY, Z_BC), geo.addPoint(HX, -HY, Z_BC), - geo.addPoint(HX, HY, Z_BC), geo.addPoint(-HX, HY, Z_BC)] + p = [ + geo.addPoint(-HX, -HY, Z_BC), + geo.addPoint(HX, -HY, Z_BC), + geo.addPoint(HX, HY, Z_BC), + geo.addPoint(-HX, HY, Z_BC), + ] l = [geo.addLine(p[i], p[(i + 1) % 4]) for i in range(4)] sq = geo.addPlaneSurface([geo.addCurveLoop(l)]) for e in l: geo.mesh.setTransfiniteCurve(e, N_TANK + 1) geo.mesh.setTransfiniteSurface(sq) geo.mesh.setRecombine(2, sq) - geo.extrude([(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True) + geo.extrude( + [(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True + ) geo.synchronize() vols, bnd = _boundary_surfs() bot = _by(bnd, 2, Z_BC) - top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) + top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) walls = [s for s in bnd if s not in bot and s not in top] gmsh.model.addPhysicalGroup(3, vols, name="tank") gmsh.model.addPhysicalGroup(2, bot, name="int_C_bot") @@ -350,7 +431,7 @@ def build_block_C(path): # ---- verify that the junction that stitch mesh will operate on has consistent -# face perimeter +# face perimeter def _plane_nodes(path, zval): """All mesh nodes at z == zval from a .msh file, returned as (x, y) pairs.""" gmsh.initialize() @@ -364,23 +445,34 @@ def _plane_nodes(path, zval): def _circle_rim(pts, cx, r): """Subset of (x, y) points lying on a circle centred at (cx, 0) with radius r.""" - return sorted((round(x, 9), round(y, 9)) for x, y in pts - if abs(math.hypot(x - cx, y) - r) < 1e-4) + return sorted( + (round(x, 9), round(y, 9)) + for x, y in pts + if abs(math.hypot(x - cx, y) - r) < 1e-4 + ) def _square_rim(pts, hx, hy): """Subset of (x, y) points lying on the perimeter of a [-hx,hx] x [-hy,hy] rectangle.""" - return sorted((round(x, 9), round(y, 9)) for x, y in pts - if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4) + return sorted( + (round(x, 9), round(y, 9)) + for x, y in pts + if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4 + ) def _verify(name, a, b, tol=1e-9): """Assert two rim point sets have the same count and are coincident within tol.""" - assert len(a) == len(b), \ - f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." - worst = max(min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a) + assert len(a) == len( + b + ), f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." + worst = max( + min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a + ) ok = worst < tol - print(f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}") + print( + f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}" + ) assert ok, f"{name}: rims not coincident (gap {worst:.1e} > {tol})." @@ -388,8 +480,12 @@ def verify_interfaces(): """Check that A-B circle rims and B-C square rim match node-for-node across blocks.""" A_ab = _plane_nodes("blockA.msh", Z_AB) B_ab = _plane_nodes("blockB.msh", Z_AB) - _verify("A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R)) - _verify("A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R)) + _verify( + "A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R) + ) + _verify( + "A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R) + ) B_bc = _plane_nodes("blockB.msh", Z_BC) C_bc = _plane_nodes("blockC.msh", Z_BC) _verify("B-C square", _square_rim(B_bc, HX, HY), _square_rim(C_bc, HX, HY)) @@ -397,8 +493,10 @@ def verify_interfaces(): # ---- main if __name__ == "__main__": - print(f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " - f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}") + print( + f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " + f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}" + ) vols = { "A": build_block_A("blockA.msh"), "B": build_block_B("blockB.msh"), @@ -408,11 +506,19 @@ def verify_interfaces(): box_tank = (2 * HX) * (2 * HY) * (Z_TOP - Z_BOT) print("=" * 70) - print(f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " - f"excl. tank = {loop_pipe_length(False):.4f} m") - print(f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " - f"= {sum(vols.values()) * 1e3:.3f} L") - print(f" of which the box tank alone = {box_tank * 1e3:.3f} L") - print(f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " - f"({2 * HX:.3f} x {2 * HY:.3f} m)") + print( + f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " + f"excl. tank = {loop_pipe_length(False):.4f} m" + ) + print( + f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " + f"= {sum(vols.values()) * 1e3:.3f} L" + ) + print( + f" of which the box tank alone = {box_tank * 1e3:.3f} L" + ) + print( + f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " + f"({2 * HX:.3f} x {2 * HY:.3f} m)" + ) print("[write] block{A,B,C}.{msh,vtk} -> will stitch next") diff --git a/experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py b/experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py index 2f86cf69..ffe78675 100644 --- a/experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py +++ b/experimental_cases/uloop_valadbeigy_exp1/get_mixing_time.py @@ -125,7 +125,7 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): z_target = z_final - band # Crossed the bottom boundary # Linearly interpolate to find the exact time t_settle at z_target - if z1 != z0: # Safety check to prevent division by zero + if z1 != z0: # Safety check to prevent division by zero t_settle = t0 + (t1 - t0) * (z_target - z0) / (z1 - z0) else: t_settle = t1 @@ -138,6 +138,7 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): return t_settle - t_start, t_settle, z_final + if __name__ == "__main__": os.makedirs(os.path.join(CASE, "Figures"), exist_ok=True) @@ -175,7 +176,9 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): dye_start = read_dye_start() t_mix, t_settle, z_final = mixing_time(t_arr, z_arr, dye_start) - t_mix_disc, t_settle_disc, z_final_disc = mixing_time(t_arr, z_arr, dye_start, continuous=False) + t_mix_disc, t_settle_disc, z_final_disc = mixing_time( + t_arr, z_arr, dye_start, continuous=False + ) with open(os.path.join(CASE, "mix_time.txt"), "w") as f: f.write(f"Continuous: {t_mix:.4f}\n") @@ -204,6 +207,12 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): print(f"final well-mixed Z discrete : {z_final_disc:.6g}") print(f"final well-mixed Z continous : {z_final:.6g}") print(f"dye injection start : {dye_start:.3f} s") - print(f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)") - print(f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)") - print("wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png") + print( + f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)" + ) + print( + f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)" + ) + print( + "wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png" + ) diff --git a/experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py b/experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py index bef692ab..43cb6cba 100644 --- a/experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py +++ b/experimental_cases/uloop_valadbeigy_exp2/build_uloop_hex.py @@ -1,14 +1,14 @@ """ Reproduce the case in "Hydrodynamic optimization of a newly designed and fabricated U-Loop bioreactor using Taguchi–ANOVA analysis", Valadbeigy et al., Biochemical Engineering Journal, July 2026 -Open-top U-loop reactor as 3 stitchable gmsh blocks +Open-top U-loop reactor as 3 stitchable gmsh blocks Structured hex mesh everywhere except the U-loop<->tank junction Blocks (all interfaces are perimeter-matched -> OpenFOAM integral `stitchMesh`): - A hex : the U pipe + A hex : the U pipe B tet : U-loop <-> tank junction - Two down-stubs (filleted where they meet the tank floor) + Two down-stubs (filleted where they meet the tank floor) C hex : structured-hex tank, extruded up to the open top (Z_TOP). Flat top face is the `outlet` boundary; sides = wall. @@ -16,23 +16,26 @@ """ import math + import gmsh import numpy as np # Geometrical parameters -R = 0.020 # DN40 pipe [m] -R_BEND = 0.045 # elbow centerline bend radius [m] -X_LEG = 0.063 # leg half spacing [m] -Z_HORIZ = 0.000 # bottom height [m] -Z_TANK = 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] -R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] +R = 0.020 # DN40 pipe [m] +R_BEND = 0.045 # elbow centerline bend radius [m] +X_LEG = 0.063 # leg half spacing [m] +Z_HORIZ = 0.000 # bottom height [m] +Z_TANK = ( + 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] +) +R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] TANK_LEN = 2 * R_TANK -Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] -FILLET_R = 0.01 # junction fillet radius [m] +Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] +FILLET_R = 0.01 # junction fillet radius [m] def loop_pipe_length(include_tank=False): - ''' Compute pipe length which is reported in the paper''' + """Compute pipe length which is reported in the paper""" r_bend = R_BEND z_bend_top = Z_HORIZ + r_bend leg_top = Z_TANK if include_tank else (Z_TANK - R_TANK) @@ -47,50 +50,62 @@ def reactor_volume(tank_fraction=1.0): v_tank = math.pi * R_TANK**2 * TANK_LEN return v_pipe + tank_fraction * v_tank -# --- derived helper dimensions (I need that later) -Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows -Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 -Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 -HX = TANK_LEN / 2.0 # tank box half-width along x -HY = R_TANK # tank box half-width along y (cross-section) -STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] -B_SLAB = 0.03 # how far do we extend the filletted junction into the hex tank [m] -Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] -Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] +# --- derived helper dimensions (I need that later) +Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows +Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 +Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 +HX = TANK_LEN / 2.0 # tank box half-width along x +HY = R_TANK # tank box half-width along y (cross-section) + +STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] +B_SLAB = ( + 0.03 # how far do we extend the filletted junction into the hex tank [m] +) +Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] +Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] # --- resolution RI_FRAC = 0.5 -N_SIDE = 6 # even -> circle/Pillow rims share nodes +N_SIDE = 6 # even -> circle/Pillow rims share nodes N_RAD = max(1, round(N_SIDE * (1 - RI_FRAC) / (RI_FRAC * math.sqrt(2)))) -H_AX = 0.004 # target axial cell size for the pipe sweeps -N_TANK = 30 # structured cells per tank-square edge +H_AX = 0.004 # target axial cell size for the pipe sweeps +N_TANK = 30 # structured cells per tank-square edge # FINER mesh at the junction is obtained with SMALLER JUNCTION_RES JUNCTION_RES = 1.8 -# ---- iterative mesh cleanup +# ---- iterative mesh cleanup N_LEG = max(1, round((Z_AB - Z_BEND_TOP) / H_AX)) N_ARC = max(1, round((R_BEND * math.pi / 2) / H_AX)) N_HOR = max(1, round(2 * (X_LEG - R_BEND) / H_AX)) -N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells +N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells def _pillow(geo, cx, cy, cz, r, n_side, n_rad): - '''Pillow shape cylindrical mesh cross-section - Normal direction is z (consistently with the block cylindrical meshing)''' + """Pillow shape cylindrical mesh cross-section + Normal direction is z (consistently with the block cylindrical meshing)""" ang = [math.pi / 4 + k * math.pi / 2 for k in range(4)] ri = RI_FRAC * r c = geo.addPoint(cx, cy, cz) - Q = [geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) for a in ang] - A = [geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) for a in ang] + Q = [ + geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) + for a in ang + ] + A = [ + geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) + for a in ang + ] Qe = [geo.addLine(Q[i], Q[(i + 1) % 4]) for i in range(4)] Rad = [geo.addLine(Q[i], A[i]) for i in range(4)] Arc = [geo.addCircleArc(A[i], c, A[(i + 1) % 4]) for i in range(4)] surfs = [geo.addPlaneSurface([geo.addCurveLoop(Qe)])] for i in range(4): - surfs.append(geo.addSurfaceFilling( - [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])])) + surfs.append( + geo.addSurfaceFilling( + [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])] + ) + ) for e in Qe + Arc: geo.mesh.setTransfiniteCurve(e, n_side + 1) for e in Rad: @@ -102,39 +117,43 @@ def _pillow(geo, cx, cy, cz, r, n_side, n_rad): def _isflat(s, idx, val, tol=1e-6): - ''' + """ True if surface *s* lies entirely on the plane coord[idx] == val. I.e. is a constant coordinate plane useful to check if what we extruded gives us a flat surface - ''' + """ bb = gmsh.model.getBoundingBox(2, s) return abs(bb[idx] - val) < tol and abs(bb[idx + 3] - val) < tol def _tip(idx, val): - ''' + """ Find flat boundary surface after gmesh extrusion - ''' + """ vols = [t for _, t in gmsh.model.getEntities(3)] - bnd = {t for _, t in gmsh.model.getBoundary( - [(3, v) for v in vols], combined=True, oriented=False)} + bnd = { + t + for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False + ) + } return [(2, s) for s in bnd if _isflat(s, idx, val)] def _by(surfs, idx, val): - '''Filter surface to those lying on the plane coord[idx] == val.''' + """Filter surface to those lying on the plane coord[idx] == val.""" return [s for s in surfs if _isflat(s, idx, val)] def _cx(s): - '''Bounding box used to distinguish the left and right leg''' + """Bounding box used to distinguish the left and right leg""" bb = gmsh.model.getBoundingBox(2, s) return 0.5 * (bb[0] + bb[3]) def _rims(surfs): - ''' find the rims at the junction between the legs and the filleted tets - and for the junction between tet block and hex tank block''' + """find the rims at the junction between the legs and the filleted tets + and for the junction between tet block and hex tank block""" rim = set() for s in surfs: for _, cc in gmsh.model.getBoundary([(2, s)], oriented=False): @@ -145,8 +164,12 @@ def _rims(surfs): def _boundary_surfs(): """Returns volume IDs and their boundary surface IDs.""" vols = [t for _, t in gmsh.model.getEntities(3)] - return vols, [t for _, t in gmsh.model.getBoundary( - [(3, v) for v in vols], combined=True, oriented=False)] + return vols, [ + t + for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False + ) + ] def _cell_volume(): @@ -157,8 +180,14 @@ def _cell_volume(): npe = {4: 4, 5: 8, 6: 6, 7: 5} fans = { 4: [(0, 1, 2, 3)], - 5: [(0, 1, 2, 6), (0, 2, 3, 6), (0, 3, 7, 6), - (0, 7, 4, 6), (0, 4, 5, 6), (0, 5, 1, 6)], + 5: [ + (0, 1, 2, 6), + (0, 2, 3, 6), + (0, 3, 7, 6), + (0, 7, 4, 6), + (0, 4, 5, 6), + (0, 5, 1, 6), + ], 6: [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)], 7: [(0, 1, 2, 4), (0, 2, 3, 4)], } @@ -169,13 +198,16 @@ def _cell_volume(): P = coords[conn] for a, b, c, d in fans[et]: v = P[:, a], P[:, b], P[:, c], P[:, d] - total += np.abs(np.einsum( - "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0])).sum() + total += np.abs( + np.einsum( + "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0] + ) + ).sum() return total / 6.0 def _write(path, tag): - ''' Write Gmesh object to .msh and print summary''' + """Write Gmesh object to .msh and print summary""" TYPE = {4: "tet", 5: "hex", 6: "prism", 7: "pyramid"} ets, etags, _ = gmsh.model.mesh.getElements(3) counts = {TYPE.get(e, e): len(t) for e, t in zip(ets, etags)} @@ -203,28 +235,66 @@ def build_block_A(path): geo = gmsh.model.geo disk = _pillow(geo, -X_LEG, 0, Z_AB, R, N_SIDE, N_RAD) - geo.extrude([(2, s) for s in disk], 0, 0, -(Z_AB - Z_BEND_TOP), - numElements=[N_LEG], recombine=True) + geo.extrude( + [(2, s) for s in disk], + 0, + 0, + -(Z_AB - Z_BEND_TOP), + numElements=[N_LEG], + recombine=True, + ) geo.synchronize() # left elbow: revolve the leg-bottom disk about y through the bend centre - geo.revolve(_tip(2, Z_BEND_TOP), -X_LEG + R_BEND, 0, Z_BEND_TOP, 0, -1, 0, - math.pi / 2, numElements=[N_ARC], recombine=True) + geo.revolve( + _tip(2, Z_BEND_TOP), + -X_LEG + R_BEND, + 0, + Z_BEND_TOP, + 0, + -1, + 0, + math.pi / 2, + numElements=[N_ARC], + recombine=True, + ) geo.synchronize() # bottom horizontal run: extrude +x - geo.extrude(_tip(0, -X_LEG + R_BEND), 2 * (X_LEG - R_BEND), 0, 0, - numElements=[N_HOR], recombine=True) + geo.extrude( + _tip(0, -X_LEG + R_BEND), + 2 * (X_LEG - R_BEND), + 0, + 0, + numElements=[N_HOR], + recombine=True, + ) geo.synchronize() # right elbow - geo.revolve(_tip(0, X_LEG - R_BEND), X_LEG - R_BEND, 0, Z_BEND_TOP, 0, -1, 0, - math.pi / 2, numElements=[N_ARC], recombine=True) + geo.revolve( + _tip(0, X_LEG - R_BEND), + X_LEG - R_BEND, + 0, + Z_BEND_TOP, + 0, + -1, + 0, + math.pi / 2, + numElements=[N_ARC], + recombine=True, + ) geo.synchronize() # right leg: extrude +z up to Z_AB - geo.extrude(_tip(2, Z_BEND_TOP), 0, 0, Z_AB - Z_BEND_TOP, - numElements=[N_LEG], recombine=True) + geo.extrude( + _tip(2, Z_BEND_TOP), + 0, + 0, + Z_AB - Z_BEND_TOP, + numElements=[N_LEG], + recombine=True, + ) geo.synchronize() vols, bnd = _boundary_surfs() @@ -243,17 +313,17 @@ def build_block_A(path): return vol -# ---- block B: U-loop <-> tank junction +# ---- block B: U-loop <-> tank junction def build_block_B(path): - '''Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). + """Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). 1. Rectangular from Z_BOT to Z_BC (the tank-floor transition layer). - 2. Two cylindrical partial leds + 2. Two cylindrical partial leds 3. Fillet - + Interface matching (that was the hard part!) - Bottom circles (int_B_legL/R at Z_AB): rim nodes match A's pillow perimeter. - - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. - ''' + - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. + """ gmsh.initialize() gmsh.model.add("B") @@ -262,8 +332,10 @@ def build_block_B(path): pen = 0.4 * (Z_BC - Z_BOT) slab = occ.addBox(-HX, -HY, Z_BOT, 2 * HX, 2 * HY, Z_BC - Z_BOT) - stubs = [occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) - for sx in (-X_LEG, X_LEG)] + stubs = [ + occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) + for sx in (-X_LEG, X_LEG) + ] S, _ = occ.fuse([(3, slab)], [(3, s) for s in stubs]) occ.synchronize() vol = S[0][1] @@ -272,25 +344,28 @@ def build_block_B(path): for _, e in gmsh.model.getEntities(1): ex, _ey, ez = occ.getCenterOfMass(1, e) x0, _, _, x1, _, _ = gmsh.model.getBoundingBox(1, e) - if abs(ez - Z_BOT) < 1e-3 and abs(abs(ex) - X_LEG) < 0.02 \ - and (x1 - x0) < 3 * R: + if ( + abs(ez - Z_BOT) < 1e-3 + and abs(abs(ex) - X_LEG) < 0.02 + and (x1 - x0) < 3 * R + ): ring.append(e) occ.fillet([vol], ring, [FILLET_R]) occ.synchronize() vols, bnd = _boundary_surfs() - bot = _by(bnd, 2, Z_AB) # two pipe circles -> A - top = _by(bnd, 2, Z_BC) # tank square -> C + bot = _by(bnd, 2, Z_AB) # two pipe circles -> A + top = _by(bnd, 2, Z_BC) # tank square -> C walls = [s for s in bnd if s not in bot and s not in top] legL = [s for s in bot if occ.getCenterOfMass(2, s)[0] < 0] legR = [s for s in bot if occ.getCenterOfMass(2, s)[0] > 0] - for s in bot: # match each stub rim to A (4*N_SIDE) + for s in bot: # match each stub rim to A (4*N_SIDE) rc = _rims([s]) per = max(1, round(4 * N_SIDE / len(rc))) for cc in rc: gmsh.model.mesh.setTransfiniteCurve(cc, per + 1) - for cc in _rims(top): # match tank square rim to C (N_TANK/edge) + for cc in _rims(top): # match tank square rim to C (N_TANK/edge) gmsh.model.mesh.setTransfiniteCurve(cc, N_TANK + 1) gmsh.model.addPhysicalGroup(3, vols, name="juncB") @@ -308,15 +383,15 @@ def build_block_B(path): return vol -# --- block C: hex tank +# --- block C: hex tank def build_block_C(path): """Structured-hex tank extruded from Z_BC to Z_TOP. 1) N_TANK nodes per edge, matching block B's top 2) Extrude +z to Z_TOP with N_HC uniform layers. - - Top face is the open outlet boundary; - Sides are wall_C; + + Top face is the open outlet boundary; + Sides are wall_C; Bottom is for stitching to block B. """ gmsh.initialize() @@ -324,20 +399,26 @@ def build_block_C(path): gmsh.option.setNumber("General.Terminal", 0) geo = gmsh.model.geo - p = [geo.addPoint(-HX, -HY, Z_BC), geo.addPoint(HX, -HY, Z_BC), - geo.addPoint(HX, HY, Z_BC), geo.addPoint(-HX, HY, Z_BC)] + p = [ + geo.addPoint(-HX, -HY, Z_BC), + geo.addPoint(HX, -HY, Z_BC), + geo.addPoint(HX, HY, Z_BC), + geo.addPoint(-HX, HY, Z_BC), + ] l = [geo.addLine(p[i], p[(i + 1) % 4]) for i in range(4)] sq = geo.addPlaneSurface([geo.addCurveLoop(l)]) for e in l: geo.mesh.setTransfiniteCurve(e, N_TANK + 1) geo.mesh.setTransfiniteSurface(sq) geo.mesh.setRecombine(2, sq) - geo.extrude([(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True) + geo.extrude( + [(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True + ) geo.synchronize() vols, bnd = _boundary_surfs() bot = _by(bnd, 2, Z_BC) - top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) + top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) walls = [s for s in bnd if s not in bot and s not in top] gmsh.model.addPhysicalGroup(3, vols, name="tank") gmsh.model.addPhysicalGroup(2, bot, name="int_C_bot") @@ -350,7 +431,7 @@ def build_block_C(path): # ---- verify that the junction that stitch mesh will operate on has consistent -# face perimeter +# face perimeter def _plane_nodes(path, zval): """All mesh nodes at z == zval from a .msh file, returned as (x, y) pairs.""" gmsh.initialize() @@ -364,23 +445,34 @@ def _plane_nodes(path, zval): def _circle_rim(pts, cx, r): """Subset of (x, y) points lying on a circle centred at (cx, 0) with radius r.""" - return sorted((round(x, 9), round(y, 9)) for x, y in pts - if abs(math.hypot(x - cx, y) - r) < 1e-4) + return sorted( + (round(x, 9), round(y, 9)) + for x, y in pts + if abs(math.hypot(x - cx, y) - r) < 1e-4 + ) def _square_rim(pts, hx, hy): """Subset of (x, y) points lying on the perimeter of a [-hx,hx] x [-hy,hy] rectangle.""" - return sorted((round(x, 9), round(y, 9)) for x, y in pts - if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4) + return sorted( + (round(x, 9), round(y, 9)) + for x, y in pts + if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4 + ) def _verify(name, a, b, tol=1e-9): """Assert two rim point sets have the same count and are coincident within tol.""" - assert len(a) == len(b), \ - f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." - worst = max(min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a) + assert len(a) == len( + b + ), f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." + worst = max( + min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a + ) ok = worst < tol - print(f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}") + print( + f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}" + ) assert ok, f"{name}: rims not coincident (gap {worst:.1e} > {tol})." @@ -388,8 +480,12 @@ def verify_interfaces(): """Check that A-B circle rims and B-C square rim match node-for-node across blocks.""" A_ab = _plane_nodes("blockA.msh", Z_AB) B_ab = _plane_nodes("blockB.msh", Z_AB) - _verify("A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R)) - _verify("A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R)) + _verify( + "A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R) + ) + _verify( + "A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R) + ) B_bc = _plane_nodes("blockB.msh", Z_BC) C_bc = _plane_nodes("blockC.msh", Z_BC) _verify("B-C square", _square_rim(B_bc, HX, HY), _square_rim(C_bc, HX, HY)) @@ -397,8 +493,10 @@ def verify_interfaces(): # ---- main if __name__ == "__main__": - print(f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " - f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}") + print( + f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " + f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}" + ) vols = { "A": build_block_A("blockA.msh"), "B": build_block_B("blockB.msh"), @@ -408,11 +506,19 @@ def verify_interfaces(): box_tank = (2 * HX) * (2 * HY) * (Z_TOP - Z_BOT) print("=" * 70) - print(f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " - f"excl. tank = {loop_pipe_length(False):.4f} m") - print(f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " - f"= {sum(vols.values()) * 1e3:.3f} L") - print(f" of which the box tank alone = {box_tank * 1e3:.3f} L") - print(f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " - f"({2 * HX:.3f} x {2 * HY:.3f} m)") + print( + f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " + f"excl. tank = {loop_pipe_length(False):.4f} m" + ) + print( + f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " + f"= {sum(vols.values()) * 1e3:.3f} L" + ) + print( + f" of which the box tank alone = {box_tank * 1e3:.3f} L" + ) + print( + f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " + f"({2 * HX:.3f} x {2 * HY:.3f} m)" + ) print("[write] block{A,B,C}.{msh,vtk} -> will stitch next") diff --git a/experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py b/experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py index 2f86cf69..ffe78675 100644 --- a/experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py +++ b/experimental_cases/uloop_valadbeigy_exp2/get_mixing_time.py @@ -125,7 +125,7 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): z_target = z_final - band # Crossed the bottom boundary # Linearly interpolate to find the exact time t_settle at z_target - if z1 != z0: # Safety check to prevent division by zero + if z1 != z0: # Safety check to prevent division by zero t_settle = t0 + (t1 - t0) * (z_target - z0) / (z1 - z0) else: t_settle = t1 @@ -138,6 +138,7 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): return t_settle - t_start, t_settle, z_final + if __name__ == "__main__": os.makedirs(os.path.join(CASE, "Figures"), exist_ok=True) @@ -175,7 +176,9 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): dye_start = read_dye_start() t_mix, t_settle, z_final = mixing_time(t_arr, z_arr, dye_start) - t_mix_disc, t_settle_disc, z_final_disc = mixing_time(t_arr, z_arr, dye_start, continuous=False) + t_mix_disc, t_settle_disc, z_final_disc = mixing_time( + t_arr, z_arr, dye_start, continuous=False + ) with open(os.path.join(CASE, "mix_time.txt"), "w") as f: f.write(f"Continuous: {t_mix:.4f}\n") @@ -204,6 +207,12 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): print(f"final well-mixed Z discrete : {z_final_disc:.6g}") print(f"final well-mixed Z continous : {z_final:.6g}") print(f"dye injection start : {dye_start:.3f} s") - print(f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)") - print(f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)") - print("wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png") + print( + f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)" + ) + print( + f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)" + ) + print( + "wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png" + ) diff --git a/experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py b/experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py index bef692ab..43cb6cba 100644 --- a/experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py +++ b/experimental_cases/uloop_valadbeigy_exp3/build_uloop_hex.py @@ -1,14 +1,14 @@ """ Reproduce the case in "Hydrodynamic optimization of a newly designed and fabricated U-Loop bioreactor using Taguchi–ANOVA analysis", Valadbeigy et al., Biochemical Engineering Journal, July 2026 -Open-top U-loop reactor as 3 stitchable gmsh blocks +Open-top U-loop reactor as 3 stitchable gmsh blocks Structured hex mesh everywhere except the U-loop<->tank junction Blocks (all interfaces are perimeter-matched -> OpenFOAM integral `stitchMesh`): - A hex : the U pipe + A hex : the U pipe B tet : U-loop <-> tank junction - Two down-stubs (filleted where they meet the tank floor) + Two down-stubs (filleted where they meet the tank floor) C hex : structured-hex tank, extruded up to the open top (Z_TOP). Flat top face is the `outlet` boundary; sides = wall. @@ -16,23 +16,26 @@ """ import math + import gmsh import numpy as np # Geometrical parameters -R = 0.020 # DN40 pipe [m] -R_BEND = 0.045 # elbow centerline bend radius [m] -X_LEG = 0.063 # leg half spacing [m] -Z_HORIZ = 0.000 # bottom height [m] -Z_TANK = 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] -R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] +R = 0.020 # DN40 pipe [m] +R_BEND = 0.045 # elbow centerline bend radius [m] +X_LEG = 0.063 # leg half spacing [m] +Z_HORIZ = 0.000 # bottom height [m] +Z_TANK = ( + 0.8 # tank axis height (sets the tank floor Z_BOT = Z_TANK-R_TANK) [m] +) +R_TANK = 0.100 # degassing-tank radius (sets the box cross-section) [m] TANK_LEN = 2 * R_TANK -Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] -FILLET_R = 0.01 # junction fillet radius [m] +Z_OUTLET = 1.3 # open-top outlet height (tank roof) [m] +FILLET_R = 0.01 # junction fillet radius [m] def loop_pipe_length(include_tank=False): - ''' Compute pipe length which is reported in the paper''' + """Compute pipe length which is reported in the paper""" r_bend = R_BEND z_bend_top = Z_HORIZ + r_bend leg_top = Z_TANK if include_tank else (Z_TANK - R_TANK) @@ -47,50 +50,62 @@ def reactor_volume(tank_fraction=1.0): v_tank = math.pi * R_TANK**2 * TANK_LEN return v_pipe + tank_fraction * v_tank -# --- derived helper dimensions (I need that later) -Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows -Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 -Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 -HX = TANK_LEN / 2.0 # tank box half-width along x -HY = R_TANK # tank box half-width along y (cross-section) -STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] -B_SLAB = 0.03 # how far do we extend the filletted junction into the hex tank [m] -Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] -Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] +# --- derived helper dimensions (I need that later) +Z_BEND_TOP = Z_HORIZ + R_BEND # where the bottom legs meet the elbows +Z_BOT = Z_TANK - R_TANK # tank floor (box bottom) = 0.7 +Z_TOP = Z_OUTLET # tank roof / open outlet = 1.3 +HX = TANK_LEN / 2.0 # tank box half-width along x +HY = R_TANK # tank box half-width along y (cross-section) + +STUB = 0.03 # how far do we stop before the legs at the filletted junction [m] +B_SLAB = ( + 0.03 # how far do we extend the filletted junction into the hex tank [m] +) +Z_AB = Z_BOT - STUB # A<->B interface (leg tops) [m] +Z_BC = Z_BOT + B_SLAB # B<->C interface (tank square) [m] # --- resolution RI_FRAC = 0.5 -N_SIDE = 6 # even -> circle/Pillow rims share nodes +N_SIDE = 6 # even -> circle/Pillow rims share nodes N_RAD = max(1, round(N_SIDE * (1 - RI_FRAC) / (RI_FRAC * math.sqrt(2)))) -H_AX = 0.004 # target axial cell size for the pipe sweeps -N_TANK = 30 # structured cells per tank-square edge +H_AX = 0.004 # target axial cell size for the pipe sweeps +N_TANK = 30 # structured cells per tank-square edge # FINER mesh at the junction is obtained with SMALLER JUNCTION_RES JUNCTION_RES = 1.8 -# ---- iterative mesh cleanup +# ---- iterative mesh cleanup N_LEG = max(1, round((Z_AB - Z_BEND_TOP) / H_AX)) N_ARC = max(1, round((R_BEND * math.pi / 2) / H_AX)) N_HOR = max(1, round(2 * (X_LEG - R_BEND) / H_AX)) -N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells +N_HC = max(1, round((Z_TOP - Z_BC) / (2 * HX / N_TANK))) # uniform tank cells def _pillow(geo, cx, cy, cz, r, n_side, n_rad): - '''Pillow shape cylindrical mesh cross-section - Normal direction is z (consistently with the block cylindrical meshing)''' + """Pillow shape cylindrical mesh cross-section + Normal direction is z (consistently with the block cylindrical meshing)""" ang = [math.pi / 4 + k * math.pi / 2 for k in range(4)] ri = RI_FRAC * r c = geo.addPoint(cx, cy, cz) - Q = [geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) for a in ang] - A = [geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) for a in ang] + Q = [ + geo.addPoint(cx + ri * math.cos(a), cy + ri * math.sin(a), cz) + for a in ang + ] + A = [ + geo.addPoint(cx + r * math.cos(a), cy + r * math.sin(a), cz) + for a in ang + ] Qe = [geo.addLine(Q[i], Q[(i + 1) % 4]) for i in range(4)] Rad = [geo.addLine(Q[i], A[i]) for i in range(4)] Arc = [geo.addCircleArc(A[i], c, A[(i + 1) % 4]) for i in range(4)] surfs = [geo.addPlaneSurface([geo.addCurveLoop(Qe)])] for i in range(4): - surfs.append(geo.addSurfaceFilling( - [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])])) + surfs.append( + geo.addSurfaceFilling( + [geo.addCurveLoop([Rad[i], Arc[i], -Rad[(i + 1) % 4], -Qe[i]])] + ) + ) for e in Qe + Arc: geo.mesh.setTransfiniteCurve(e, n_side + 1) for e in Rad: @@ -102,39 +117,43 @@ def _pillow(geo, cx, cy, cz, r, n_side, n_rad): def _isflat(s, idx, val, tol=1e-6): - ''' + """ True if surface *s* lies entirely on the plane coord[idx] == val. I.e. is a constant coordinate plane useful to check if what we extruded gives us a flat surface - ''' + """ bb = gmsh.model.getBoundingBox(2, s) return abs(bb[idx] - val) < tol and abs(bb[idx + 3] - val) < tol def _tip(idx, val): - ''' + """ Find flat boundary surface after gmesh extrusion - ''' + """ vols = [t for _, t in gmsh.model.getEntities(3)] - bnd = {t for _, t in gmsh.model.getBoundary( - [(3, v) for v in vols], combined=True, oriented=False)} + bnd = { + t + for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False + ) + } return [(2, s) for s in bnd if _isflat(s, idx, val)] def _by(surfs, idx, val): - '''Filter surface to those lying on the plane coord[idx] == val.''' + """Filter surface to those lying on the plane coord[idx] == val.""" return [s for s in surfs if _isflat(s, idx, val)] def _cx(s): - '''Bounding box used to distinguish the left and right leg''' + """Bounding box used to distinguish the left and right leg""" bb = gmsh.model.getBoundingBox(2, s) return 0.5 * (bb[0] + bb[3]) def _rims(surfs): - ''' find the rims at the junction between the legs and the filleted tets - and for the junction between tet block and hex tank block''' + """find the rims at the junction between the legs and the filleted tets + and for the junction between tet block and hex tank block""" rim = set() for s in surfs: for _, cc in gmsh.model.getBoundary([(2, s)], oriented=False): @@ -145,8 +164,12 @@ def _rims(surfs): def _boundary_surfs(): """Returns volume IDs and their boundary surface IDs.""" vols = [t for _, t in gmsh.model.getEntities(3)] - return vols, [t for _, t in gmsh.model.getBoundary( - [(3, v) for v in vols], combined=True, oriented=False)] + return vols, [ + t + for _, t in gmsh.model.getBoundary( + [(3, v) for v in vols], combined=True, oriented=False + ) + ] def _cell_volume(): @@ -157,8 +180,14 @@ def _cell_volume(): npe = {4: 4, 5: 8, 6: 6, 7: 5} fans = { 4: [(0, 1, 2, 3)], - 5: [(0, 1, 2, 6), (0, 2, 3, 6), (0, 3, 7, 6), - (0, 7, 4, 6), (0, 4, 5, 6), (0, 5, 1, 6)], + 5: [ + (0, 1, 2, 6), + (0, 2, 3, 6), + (0, 3, 7, 6), + (0, 7, 4, 6), + (0, 4, 5, 6), + (0, 5, 1, 6), + ], 6: [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)], 7: [(0, 1, 2, 4), (0, 2, 3, 4)], } @@ -169,13 +198,16 @@ def _cell_volume(): P = coords[conn] for a, b, c, d in fans[et]: v = P[:, a], P[:, b], P[:, c], P[:, d] - total += np.abs(np.einsum( - "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0])).sum() + total += np.abs( + np.einsum( + "ij,ij->i", np.cross(v[1] - v[0], v[2] - v[0]), v[3] - v[0] + ) + ).sum() return total / 6.0 def _write(path, tag): - ''' Write Gmesh object to .msh and print summary''' + """Write Gmesh object to .msh and print summary""" TYPE = {4: "tet", 5: "hex", 6: "prism", 7: "pyramid"} ets, etags, _ = gmsh.model.mesh.getElements(3) counts = {TYPE.get(e, e): len(t) for e, t in zip(ets, etags)} @@ -203,28 +235,66 @@ def build_block_A(path): geo = gmsh.model.geo disk = _pillow(geo, -X_LEG, 0, Z_AB, R, N_SIDE, N_RAD) - geo.extrude([(2, s) for s in disk], 0, 0, -(Z_AB - Z_BEND_TOP), - numElements=[N_LEG], recombine=True) + geo.extrude( + [(2, s) for s in disk], + 0, + 0, + -(Z_AB - Z_BEND_TOP), + numElements=[N_LEG], + recombine=True, + ) geo.synchronize() # left elbow: revolve the leg-bottom disk about y through the bend centre - geo.revolve(_tip(2, Z_BEND_TOP), -X_LEG + R_BEND, 0, Z_BEND_TOP, 0, -1, 0, - math.pi / 2, numElements=[N_ARC], recombine=True) + geo.revolve( + _tip(2, Z_BEND_TOP), + -X_LEG + R_BEND, + 0, + Z_BEND_TOP, + 0, + -1, + 0, + math.pi / 2, + numElements=[N_ARC], + recombine=True, + ) geo.synchronize() # bottom horizontal run: extrude +x - geo.extrude(_tip(0, -X_LEG + R_BEND), 2 * (X_LEG - R_BEND), 0, 0, - numElements=[N_HOR], recombine=True) + geo.extrude( + _tip(0, -X_LEG + R_BEND), + 2 * (X_LEG - R_BEND), + 0, + 0, + numElements=[N_HOR], + recombine=True, + ) geo.synchronize() # right elbow - geo.revolve(_tip(0, X_LEG - R_BEND), X_LEG - R_BEND, 0, Z_BEND_TOP, 0, -1, 0, - math.pi / 2, numElements=[N_ARC], recombine=True) + geo.revolve( + _tip(0, X_LEG - R_BEND), + X_LEG - R_BEND, + 0, + Z_BEND_TOP, + 0, + -1, + 0, + math.pi / 2, + numElements=[N_ARC], + recombine=True, + ) geo.synchronize() # right leg: extrude +z up to Z_AB - geo.extrude(_tip(2, Z_BEND_TOP), 0, 0, Z_AB - Z_BEND_TOP, - numElements=[N_LEG], recombine=True) + geo.extrude( + _tip(2, Z_BEND_TOP), + 0, + 0, + Z_AB - Z_BEND_TOP, + numElements=[N_LEG], + recombine=True, + ) geo.synchronize() vols, bnd = _boundary_surfs() @@ -243,17 +313,17 @@ def build_block_A(path): return vol -# ---- block B: U-loop <-> tank junction +# ---- block B: U-loop <-> tank junction def build_block_B(path): - '''Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). + """Tet-meshed junction connecting the U-pipe (A) to the hex tank (C). 1. Rectangular from Z_BOT to Z_BC (the tank-floor transition layer). - 2. Two cylindrical partial leds + 2. Two cylindrical partial leds 3. Fillet - + Interface matching (that was the hard part!) - Bottom circles (int_B_legL/R at Z_AB): rim nodes match A's pillow perimeter. - - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. - ''' + - Top rectangle (int_B_top at Z_BC): rim nodes match C's structured grid edges. + """ gmsh.initialize() gmsh.model.add("B") @@ -262,8 +332,10 @@ def build_block_B(path): pen = 0.4 * (Z_BC - Z_BOT) slab = occ.addBox(-HX, -HY, Z_BOT, 2 * HX, 2 * HY, Z_BC - Z_BOT) - stubs = [occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) - for sx in (-X_LEG, X_LEG)] + stubs = [ + occ.addCylinder(sx, 0, Z_AB, 0, 0, (Z_BOT - Z_AB) + pen, R) + for sx in (-X_LEG, X_LEG) + ] S, _ = occ.fuse([(3, slab)], [(3, s) for s in stubs]) occ.synchronize() vol = S[0][1] @@ -272,25 +344,28 @@ def build_block_B(path): for _, e in gmsh.model.getEntities(1): ex, _ey, ez = occ.getCenterOfMass(1, e) x0, _, _, x1, _, _ = gmsh.model.getBoundingBox(1, e) - if abs(ez - Z_BOT) < 1e-3 and abs(abs(ex) - X_LEG) < 0.02 \ - and (x1 - x0) < 3 * R: + if ( + abs(ez - Z_BOT) < 1e-3 + and abs(abs(ex) - X_LEG) < 0.02 + and (x1 - x0) < 3 * R + ): ring.append(e) occ.fillet([vol], ring, [FILLET_R]) occ.synchronize() vols, bnd = _boundary_surfs() - bot = _by(bnd, 2, Z_AB) # two pipe circles -> A - top = _by(bnd, 2, Z_BC) # tank square -> C + bot = _by(bnd, 2, Z_AB) # two pipe circles -> A + top = _by(bnd, 2, Z_BC) # tank square -> C walls = [s for s in bnd if s not in bot and s not in top] legL = [s for s in bot if occ.getCenterOfMass(2, s)[0] < 0] legR = [s for s in bot if occ.getCenterOfMass(2, s)[0] > 0] - for s in bot: # match each stub rim to A (4*N_SIDE) + for s in bot: # match each stub rim to A (4*N_SIDE) rc = _rims([s]) per = max(1, round(4 * N_SIDE / len(rc))) for cc in rc: gmsh.model.mesh.setTransfiniteCurve(cc, per + 1) - for cc in _rims(top): # match tank square rim to C (N_TANK/edge) + for cc in _rims(top): # match tank square rim to C (N_TANK/edge) gmsh.model.mesh.setTransfiniteCurve(cc, N_TANK + 1) gmsh.model.addPhysicalGroup(3, vols, name="juncB") @@ -308,15 +383,15 @@ def build_block_B(path): return vol -# --- block C: hex tank +# --- block C: hex tank def build_block_C(path): """Structured-hex tank extruded from Z_BC to Z_TOP. 1) N_TANK nodes per edge, matching block B's top 2) Extrude +z to Z_TOP with N_HC uniform layers. - - Top face is the open outlet boundary; - Sides are wall_C; + + Top face is the open outlet boundary; + Sides are wall_C; Bottom is for stitching to block B. """ gmsh.initialize() @@ -324,20 +399,26 @@ def build_block_C(path): gmsh.option.setNumber("General.Terminal", 0) geo = gmsh.model.geo - p = [geo.addPoint(-HX, -HY, Z_BC), geo.addPoint(HX, -HY, Z_BC), - geo.addPoint(HX, HY, Z_BC), geo.addPoint(-HX, HY, Z_BC)] + p = [ + geo.addPoint(-HX, -HY, Z_BC), + geo.addPoint(HX, -HY, Z_BC), + geo.addPoint(HX, HY, Z_BC), + geo.addPoint(-HX, HY, Z_BC), + ] l = [geo.addLine(p[i], p[(i + 1) % 4]) for i in range(4)] sq = geo.addPlaneSurface([geo.addCurveLoop(l)]) for e in l: geo.mesh.setTransfiniteCurve(e, N_TANK + 1) geo.mesh.setTransfiniteSurface(sq) geo.mesh.setRecombine(2, sq) - geo.extrude([(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True) + geo.extrude( + [(2, sq)], 0, 0, Z_TOP - Z_BC, numElements=[N_HC], recombine=True + ) geo.synchronize() vols, bnd = _boundary_surfs() bot = _by(bnd, 2, Z_BC) - top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) + top = _by(bnd, 2, Z_TOP) # open top -> outlet boundary (no stitch) walls = [s for s in bnd if s not in bot and s not in top] gmsh.model.addPhysicalGroup(3, vols, name="tank") gmsh.model.addPhysicalGroup(2, bot, name="int_C_bot") @@ -350,7 +431,7 @@ def build_block_C(path): # ---- verify that the junction that stitch mesh will operate on has consistent -# face perimeter +# face perimeter def _plane_nodes(path, zval): """All mesh nodes at z == zval from a .msh file, returned as (x, y) pairs.""" gmsh.initialize() @@ -364,23 +445,34 @@ def _plane_nodes(path, zval): def _circle_rim(pts, cx, r): """Subset of (x, y) points lying on a circle centred at (cx, 0) with radius r.""" - return sorted((round(x, 9), round(y, 9)) for x, y in pts - if abs(math.hypot(x - cx, y) - r) < 1e-4) + return sorted( + (round(x, 9), round(y, 9)) + for x, y in pts + if abs(math.hypot(x - cx, y) - r) < 1e-4 + ) def _square_rim(pts, hx, hy): """Subset of (x, y) points lying on the perimeter of a [-hx,hx] x [-hy,hy] rectangle.""" - return sorted((round(x, 9), round(y, 9)) for x, y in pts - if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4) + return sorted( + (round(x, 9), round(y, 9)) + for x, y in pts + if abs(abs(x) - hx) < 1e-4 or abs(abs(y) - hy) < 1e-4 + ) def _verify(name, a, b, tol=1e-9): """Assert two rim point sets have the same count and are coincident within tol.""" - assert len(a) == len(b), \ - f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." - worst = max(min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a) + assert len(a) == len( + b + ), f"{name}: rim node COUNT differs (A={len(a)}, B={len(b)}) -> areas differ." + worst = max( + min(math.hypot(px - qx, py - qy) for qx, qy in b) for px, py in a + ) ok = worst < tol - print(f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}") + print( + f"[verify {name}] n={len(a)} max rim gap={worst:.2e} m {'OK' if ok else 'FAIL'}" + ) assert ok, f"{name}: rims not coincident (gap {worst:.1e} > {tol})." @@ -388,8 +480,12 @@ def verify_interfaces(): """Check that A-B circle rims and B-C square rim match node-for-node across blocks.""" A_ab = _plane_nodes("blockA.msh", Z_AB) B_ab = _plane_nodes("blockB.msh", Z_AB) - _verify("A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R)) - _verify("A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R)) + _verify( + "A-B legL", _circle_rim(A_ab, -X_LEG, R), _circle_rim(B_ab, -X_LEG, R) + ) + _verify( + "A-B legR", _circle_rim(A_ab, X_LEG, R), _circle_rim(B_ab, X_LEG, R) + ) B_bc = _plane_nodes("blockB.msh", Z_BC) C_bc = _plane_nodes("blockC.msh", Z_BC) _verify("B-C square", _square_rim(B_bc, HX, HY), _square_rim(C_bc, HX, HY)) @@ -397,8 +493,10 @@ def verify_interfaces(): # ---- main if __name__ == "__main__": - print(f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " - f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}") + print( + f"[resolution] N_SIDE={N_SIDE} N_RAD={N_RAD} N_LEG={N_LEG} N_ARC={N_ARC} " + f"N_HOR={N_HOR} N_TANK={N_TANK} N_HC={N_HC}" + ) vols = { "A": build_block_A("blockA.msh"), "B": build_block_B("blockB.msh"), @@ -408,11 +506,19 @@ def verify_interfaces(): box_tank = (2 * HX) * (2 * HY) * (Z_TOP - Z_BOT) print("=" * 70) - print(f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " - f"excl. tank = {loop_pipe_length(False):.4f} m") - print(f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " - f"= {sum(vols.values()) * 1e3:.3f} L") - print(f" of which the box tank alone = {box_tank * 1e3:.3f} L") - print(f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " - f"({2 * HX:.3f} x {2 * HY:.3f} m)") + print( + f"[pipe length] incl. tank = {loop_pipe_length(True):.4f} m " + f"excl. tank = {loop_pipe_length(False):.4f} m" + ) + print( + f"[ieactor volume] this mesh (open-top box tank, blocks A-C) " + f"= {sum(vols.values()) * 1e3:.3f} L" + ) + print( + f" of which the box tank alone = {box_tank * 1e3:.3f} L" + ) + print( + f"[open top] outlet = full tank roof at z={Z_TOP:.3f} m " + f"({2 * HX:.3f} x {2 * HY:.3f} m)" + ) print("[write] block{A,B,C}.{msh,vtk} -> will stitch next") diff --git a/experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py b/experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py index 2f86cf69..ffe78675 100644 --- a/experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py +++ b/experimental_cases/uloop_valadbeigy_exp3/get_mixing_time.py @@ -125,7 +125,7 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): z_target = z_final - band # Crossed the bottom boundary # Linearly interpolate to find the exact time t_settle at z_target - if z1 != z0: # Safety check to prevent division by zero + if z1 != z0: # Safety check to prevent division by zero t_settle = t0 + (t1 - t0) * (z_target - z0) / (z1 - z0) else: t_settle = t1 @@ -138,6 +138,7 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): return t_settle - t_start, t_settle, z_final + if __name__ == "__main__": os.makedirs(os.path.join(CASE, "Figures"), exist_ok=True) @@ -175,7 +176,9 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): dye_start = read_dye_start() t_mix, t_settle, z_final = mixing_time(t_arr, z_arr, dye_start) - t_mix_disc, t_settle_disc, z_final_disc = mixing_time(t_arr, z_arr, dye_start, continuous=False) + t_mix_disc, t_settle_disc, z_final_disc = mixing_time( + t_arr, z_arr, dye_start, continuous=False + ) with open(os.path.join(CASE, "mix_time.txt"), "w") as f: f.write(f"Continuous: {t_mix:.4f}\n") @@ -204,6 +207,12 @@ def mixing_time(t_arr, z_arr, t_start, continuous=True): print(f"final well-mixed Z discrete : {z_final_disc:.6g}") print(f"final well-mixed Z continous : {z_final:.6g}") print(f"dye injection start : {dye_start:.3f} s") - print(f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)") - print(f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)") - print("wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png") + print( + f"mixing time discrete (+/-5%) : {t_mix_disc:.3f} s (settles at t={t_settle_disc:.3f} s)" + ) + print( + f"mixing time continuous (+/-5%) : {t_mix:.3f} s (settles at t={t_settle:.3f} s)" + ) + print( + "wrote mix_time.txt, Z_box_average.dat and Figures/Z_box_average.png" + ) From d33897572d69afbf24bd7a74d2ff40daf96daf9d Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 1 Sep 2026 16:52:30 -0600 Subject: [PATCH 32/37] missing gmsh --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index ce43724a..e5cb07ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ dependencies = [ "corner", "jax>=0.9.0,<0.10", "numpyro>=0.20.0,<0.21", + "gmsh", ] [tool.setuptools.dynamic] From b8535db0f36aac0245fe13ee10df077d9b79c7ed Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 1 Sep 2026 16:55:59 -0600 Subject: [PATCH 33/37] update pixi lock --- pixi.lock | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/pixi.lock b/pixi.lock index d7613d65..c1dd6ca9 100644 --- a/pixi.lock +++ b/pixi.lock @@ -253,6 +253,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/ab/d4/336becb1488ac4fbab205043987128839c8c62d54e2036b7e887bd009040/gmsh-4.15.2-py2.py3-none-manylinux_2_24_x86_64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/a0/ddb3a71359c1df61f3edc408936b5bda7ed402e78ae7e9ef6afd438577c6/jaxlib-0.9.2-cp312-cp312-manylinux_2_27_x86_64.whl @@ -472,6 +473,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/3d/b4/0d25938ad62ea4db6bcbdecbc77c72d05d8a6a679abe4addcd86167ffd77/gmsh-4.15.2-py2.py3-none-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/15/ff3d9fde15b5146a0164505085312d8c9c0b0bbd7be5a15218ead2593307/jaxlib-0.9.2-cp312-cp312-macosx_11_0_arm64.whl @@ -732,6 +734,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/ab/d4/336becb1488ac4fbab205043987128839c8c62d54e2036b7e887bd009040/gmsh-4.15.2-py2.py3-none-manylinux_2_24_x86_64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/a0/ddb3a71359c1df61f3edc408936b5bda7ed402e78ae7e9ef6afd438577c6/jaxlib-0.9.2-cp312-cp312-manylinux_2_27_x86_64.whl @@ -948,6 +951,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/3d/b4/0d25938ad62ea4db6bcbdecbc77c72d05d8a6a679abe4addcd86167ffd77/gmsh-4.15.2-py2.py3-none-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/15/ff3d9fde15b5146a0164505085312d8c9c0b0bbd7be5a15218ead2593307/jaxlib-0.9.2-cp312-cp312-macosx_11_0_arm64.whl @@ -1268,6 +1272,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/ab/d4/336becb1488ac4fbab205043987128839c8c62d54e2036b7e887bd009040/gmsh-4.15.2-py2.py3-none-manylinux_2_24_x86_64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/a0/ddb3a71359c1df61f3edc408936b5bda7ed402e78ae7e9ef6afd438577c6/jaxlib-0.9.2-cp312-cp312-manylinux_2_27_x86_64.whl @@ -1541,6 +1546,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/3d/b4/0d25938ad62ea4db6bcbdecbc77c72d05d8a6a679abe4addcd86167ffd77/gmsh-4.15.2-py2.py3-none-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/15/ff3d9fde15b5146a0164505085312d8c9c0b0bbd7be5a15218ead2593307/jaxlib-0.9.2-cp312-cp312-macosx_11_0_arm64.whl @@ -1824,6 +1830,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/ab/d4/336becb1488ac4fbab205043987128839c8c62d54e2036b7e887bd009040/gmsh-4.15.2-py2.py3-none-manylinux_2_24_x86_64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/a0/ddb3a71359c1df61f3edc408936b5bda7ed402e78ae7e9ef6afd438577c6/jaxlib-0.9.2-cp312-cp312-manylinux_2_27_x86_64.whl @@ -2063,6 +2070,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/3d/b4/0d25938ad62ea4db6bcbdecbc77c72d05d8a6a679abe4addcd86167ffd77/gmsh-4.15.2-py2.py3-none-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/15/ff3d9fde15b5146a0164505085312d8c9c0b0bbd7be5a15218ead2593307/jaxlib-0.9.2-cp312-cp312-macosx_11_0_arm64.whl @@ -2153,6 +2161,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/jsoncpp-1.9.6-hf42df4d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda @@ -2273,6 +2282,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.10.2-pl5321h16c4a6b_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruamel_yaml-0.15.80-py312h98912ed_1009.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda @@ -2282,6 +2292,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlalchemy-2.0.49-py312h5253ce2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.53.0-h04a0ce9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tbb-2022.3.0-hb700be7_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda @@ -2330,6 +2341,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/ab/d4/336becb1488ac4fbab205043987128839c8c62d54e2036b7e887bd009040/gmsh-4.15.2-py2.py3-none-manylinux_2_24_x86_64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/a0/ddb3a71359c1df61f3edc408936b5bda7ed402e78ae7e9ef6afd438577c6/jaxlib-0.9.2-cp312-cp312-manylinux_2_27_x86_64.whl @@ -2409,6 +2421,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/jsoncpp-1.9.6-h726d253_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda @@ -2513,6 +2526,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qt6-main-6.10.2-pl5321h01fc3ab_6.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruamel_yaml-0.15.80-py312h02f2b3b_1009.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he5ca3e3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h0f234b1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda @@ -2522,6 +2536,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlalchemy-2.0.49-py312hb3ab3e3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.53.0-h85ec8f2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tbb-2022.3.0-h4ddebb9_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-w-1.2.0-pyhd8ed1ab_0.conda @@ -2553,6 +2568,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/3d/b4/0d25938ad62ea4db6bcbdecbc77c72d05d8a6a679abe4addcd86167ffd77/gmsh-4.15.2-py2.py3-none-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/15/ff3d9fde15b5146a0164505085312d8c9c0b0bbd7be5a15218ead2593307/jaxlib-0.9.2-cp312-cp312-macosx_11_0_arm64.whl @@ -2813,6 +2829,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py312h5253ce2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/ab/d4/336becb1488ac4fbab205043987128839c8c62d54e2036b7e887bd009040/gmsh-4.15.2-py2.py3-none-manylinux_2_24_x86_64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/33/a0/ddb3a71359c1df61f3edc408936b5bda7ed402e78ae7e9ef6afd438577c6/jaxlib-0.9.2-cp312-cp312-manylinux_2_27_x86_64.whl @@ -3029,6 +3046,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py312h37e1c23_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda + - pypi: https://files.pythonhosted.org/packages/3d/b4/0d25938ad62ea4db6bcbdecbc77c72d05d8a6a679abe4addcd86167ffd77/gmsh-4.15.2-py2.py3-none-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/49/fa/391e437a34e55095173dca5f24070d89cbc233ff85bf1c29c93248c6588d/imageio-2.37.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/9c/e897231c880f69e32251d3b1145894d7a04e4342d9bef8d29644c440d11b/jax-0.9.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/51/15/ff3d9fde15b5146a0164505085312d8c9c0b0bbd7be5a15218ead2593307/jaxlib-0.9.2-cp312-cp312-macosx_11_0_arm64.whl @@ -4274,6 +4292,14 @@ packages: purls: [] size: 558669 timestamp: 1760370890246 +- pypi: https://files.pythonhosted.org/packages/3d/b4/0d25938ad62ea4db6bcbdecbc77c72d05d8a6a679abe4addcd86167ffd77/gmsh-4.15.2-py2.py3-none-macosx_12_0_arm64.whl + name: gmsh + version: 4.15.2 + sha256: f6649b3e59f49272e7ee8ab282ecb4d1a6e0d627e86cf3e3b1a83fd07417e4f8 +- pypi: https://files.pythonhosted.org/packages/ab/d4/336becb1488ac4fbab205043987128839c8c62d54e2036b7e887bd009040/gmsh-4.15.2-py2.py3-none-manylinux_2_24_x86_64.whl + name: gmsh + version: 4.15.2 + sha256: 4076a948ce22625330d1413d4982e22b5c69fc2f0f7951f5df64c778cf54108c - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda sha256: 25ba37da5c39697a77fce2c9a15e48cf0a84f1464ad2aafbe53d8357a9f6cc8c md5: 2cd94587f3a401ae05e03a6caf09539d @@ -6924,8 +6950,8 @@ packages: timestamp: 1768670878127 - pypi: ./ name: nlr-bird - version: 0.0.55 - sha256: 29437d15aaad5ae9d209850c35db2ff653a087bc441869fb78696baf1e3d830f + version: 0.0.56 + sha256: fd725f36ff4cff386880348c4c38d5716a281779613033428c6bf8bdaa866776 requires_dist: - numpy - prettyplot>=0.0.36,<0.0.37 @@ -6935,6 +6961,7 @@ packages: - corner - jax>=0.9.0,<0.10 - numpyro>=0.20.0,<0.21 + - gmsh - sphinx ; extra == 'docs' - sphinx-rtd-theme ; extra == 'docs' - sphinx-autodoc-typehints ; extra == 'docs' From cc91f7041f43c798a21a24137197955059a8b18d Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 1 Sep 2026 17:52:34 -0600 Subject: [PATCH 34/37] fix lib hdf5 link --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index edfbcccd..68839231 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,7 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview + micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install --upgrade nox pip install .[tests] @@ -154,7 +154,7 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview + micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install --upgrade nox pip install nlr-bird[tests] @@ -196,7 +196,7 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview + micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install . @@ -301,7 +301,7 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview + micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install . From fe1ee1ce81783ba389de7d3a6cbf0d91839eb86d Mon Sep 17 00:00:00 2001 From: Malik Date: Tue, 1 Sep 2026 18:11:28 -0600 Subject: [PATCH 35/37] fix hdf5 lib --- .github/workflows/ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68839231..8de6c740 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,8 @@ jobs: environment-name: test-env create-args: >- python=${{ matrix.python-version }} + paraview=6.1 + hdf5=1.14 channels: conda-forge channel-priority: strict cache-downloads: true @@ -89,7 +91,6 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install --upgrade nox pip install .[tests] @@ -147,6 +148,8 @@ jobs: environment-name: test-env create-args: >- python=${{ matrix.python-version }} + paraview=6.1 + hdf5=1.14 channels: conda-forge channel-priority: strict cache-downloads: true @@ -154,7 +157,6 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install --upgrade nox pip install nlr-bird[tests] @@ -189,6 +191,8 @@ jobs: environment-name: test-env create-args: >- python=${{ matrix.python-version }} + paraview=6.1 + hdf5=1.14 channels: conda-forge channel-priority: strict cache-downloads: true @@ -196,7 +200,6 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install . @@ -294,6 +297,8 @@ jobs: environment-name: test-env create-args: >- python=${{ matrix.python-version }} + paraview=6.1 + hdf5=1.14 channels: conda-forge channel-priority: strict cache-downloads: true @@ -301,7 +306,6 @@ jobs: - name: Install dependencies run: | - micromamba install --yes -n test-env -c conda-forge paraview hdf5 pip install --upgrade pip pip install . From eb5d46dc85feccea9e0ea0411f136b1c394dca65 Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 2 Sep 2026 06:49:06 -0600 Subject: [PATCH 36/37] fix application paths and deploy to pypi --- bird/version.py | 2 +- tutorial_cases/OF9/loop_reactor_mixing_static/run.sh | 8 ++++---- tutorial_cases/OF9/loop_reactor_mixing_swirl/run.sh | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bird/version.py b/bird/version.py index 4aae0559..cfeacc5a 100644 --- a/bird/version.py +++ b/bird/version.py @@ -1,3 +1,3 @@ """Bio reactor design version""" -__version__ = "0.0.56" +__version__ = "0.0.57" diff --git a/tutorial_cases/OF9/loop_reactor_mixing_static/run.sh b/tutorial_cases/OF9/loop_reactor_mixing_static/run.sh index 25251599..8ec96fb0 100644 --- a/tutorial_cases/OF9/loop_reactor_mixing_static/run.sh +++ b/tutorial_cases/OF9/loop_reactor_mixing_static/run.sh @@ -12,16 +12,16 @@ 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 +python ../../../applications/write_block_rect_mesh.py -i system/mesh.json -o system # Generate boundary stl -python ../../applications/write_stl_patch.py -i system/inlets_outlets.json +python ../../../applications/write_stl_patch.py -i system/inlets_outlets.json # Generate mixers -python ../../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 # Generate species thermo properties -python ../../applications/write_species_thermo_prop.py -cf . +python ../../../applications/write_species_thermo_prop.py -cf . echo PRESTEP 2 # Mesh gen diff --git a/tutorial_cases/OF9/loop_reactor_mixing_swirl/run.sh b/tutorial_cases/OF9/loop_reactor_mixing_swirl/run.sh index 25251599..8ec96fb0 100644 --- a/tutorial_cases/OF9/loop_reactor_mixing_swirl/run.sh +++ b/tutorial_cases/OF9/loop_reactor_mixing_swirl/run.sh @@ -12,16 +12,16 @@ 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 +python ../../../applications/write_block_rect_mesh.py -i system/mesh.json -o system # Generate boundary stl -python ../../applications/write_stl_patch.py -i system/inlets_outlets.json +python ../../../applications/write_stl_patch.py -i system/inlets_outlets.json # Generate mixers -python ../../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 # Generate species thermo properties -python ../../applications/write_species_thermo_prop.py -cf . +python ../../../applications/write_species_thermo_prop.py -cf . echo PRESTEP 2 # Mesh gen From 1064f7bf1daa2dee3f2d03766b14ca52acdc2914 Mon Sep 17 00:00:00 2001 From: Malik Date: Wed, 2 Sep 2026 07:46:12 -0600 Subject: [PATCH 37/37] remove run all and adjust allclean --- experimental_cases/OF9/deckwer17/Allclean | 2 +- experimental_cases/OF9/deckwer19/Allclean | 2 +- .../bubble_column_pbe_20L/Allclean | 2 +- .../OF9/uloop_valadbeigy_exp1/Allclean | 3 +- .../OF9/uloop_valadbeigy_exp1/script_post | 1 - .../OF9/uloop_valadbeigy_exp2/Allclean | 2 +- .../OF9/uloop_valadbeigy_exp2/script_post | 1 - .../OF9/uloop_valadbeigy_exp3/Allclean | 2 +- .../OF9/uloop_valadbeigy_exp3/script_post | 1 - .../OF9/FlatPanel_250L_ASU/Allclean | 2 +- tutorial_cases/OF9/airlift_40m/Allclean | 2 +- .../OF9/bdofoam_cases/nonreact/Allclean | 2 +- .../OF9/bdofoam_cases/react/Allclean | 2 +- tutorial_cases/OF9/bubble_column_20L/Allclean | 2 +- .../OF9/loop_reactor_mixing/Allclean | 2 +- .../OF9/loop_reactor_mixing_static/Allclean | 2 +- .../OF9/loop_reactor_mixing_swirl/Allclean | 2 +- .../OF9/loop_reactor_reacting/Allclean | 2 +- tutorial_cases/OF9/runall.sh | 61 ------------------- tutorial_cases/OF9/side_sparger/Allclean | 2 +- tutorial_cases/OF9/stirred_tank/Allclean | 2 +- 21 files changed, 17 insertions(+), 82 deletions(-) delete mode 100644 tutorial_cases/OF9/runall.sh diff --git a/experimental_cases/OF9/deckwer17/Allclean b/experimental_cases/OF9/deckwer17/Allclean index dc2f77db..174b6310 100755 --- a/experimental_cases/OF9/deckwer17/Allclean +++ b/experimental_cases/OF9/deckwer17/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/experimental_cases/OF9/deckwer19/Allclean b/experimental_cases/OF9/deckwer19/Allclean index dc2f77db..174b6310 100755 --- a/experimental_cases/OF9/deckwer19/Allclean +++ b/experimental_cases/OF9/deckwer19/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/experimental_cases/OF9/disengagement/bubble_column_pbe_20L/Allclean b/experimental_cases/OF9/disengagement/bubble_column_pbe_20L/Allclean index dc2f77db..174b6310 100755 --- a/experimental_cases/OF9/disengagement/bubble_column_pbe_20L/Allclean +++ b/experimental_cases/OF9/disengagement/bubble_column_pbe_20L/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/experimental_cases/OF9/uloop_valadbeigy_exp1/Allclean b/experimental_cases/OF9/uloop_valadbeigy_exp1/Allclean index dc2f77db..bf27685b 100755 --- a/experimental_cases/OF9/uloop_valadbeigy_exp1/Allclean +++ b/experimental_cases/OF9/uloop_valadbeigy_exp1/Allclean @@ -15,8 +15,7 @@ fi # [ -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 -rf "processor*" rm -f *.obj rm -f *.stl rm -f *.txt diff --git a/experimental_cases/OF9/uloop_valadbeigy_exp1/script_post b/experimental_cases/OF9/uloop_valadbeigy_exp1/script_post index dcbf59d8..f1577763 100755 --- a/experimental_cases/OF9/uloop_valadbeigy_exp1/script_post +++ b/experimental_cases/OF9/uloop_valadbeigy_exp1/script_post @@ -5,7 +5,6 @@ #SBATCH --ntasks-per-node=16 #SBATCH --time=01:59:00 #SBATCH --account=gas2fuels -#SBATCH --dependency=afterany:15800966 source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc reconstructPar -newTimes -fields "(Z.liquid U.liquid alpha.gas)" diff --git a/experimental_cases/OF9/uloop_valadbeigy_exp2/Allclean b/experimental_cases/OF9/uloop_valadbeigy_exp2/Allclean index dc2f77db..174b6310 100755 --- a/experimental_cases/OF9/uloop_valadbeigy_exp2/Allclean +++ b/experimental_cases/OF9/uloop_valadbeigy_exp2/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/experimental_cases/OF9/uloop_valadbeigy_exp2/script_post b/experimental_cases/OF9/uloop_valadbeigy_exp2/script_post index dcbf59d8..f1577763 100755 --- a/experimental_cases/OF9/uloop_valadbeigy_exp2/script_post +++ b/experimental_cases/OF9/uloop_valadbeigy_exp2/script_post @@ -5,7 +5,6 @@ #SBATCH --ntasks-per-node=16 #SBATCH --time=01:59:00 #SBATCH --account=gas2fuels -#SBATCH --dependency=afterany:15800966 source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc reconstructPar -newTimes -fields "(Z.liquid U.liquid alpha.gas)" diff --git a/experimental_cases/OF9/uloop_valadbeigy_exp3/Allclean b/experimental_cases/OF9/uloop_valadbeigy_exp3/Allclean index dc2f77db..174b6310 100755 --- a/experimental_cases/OF9/uloop_valadbeigy_exp3/Allclean +++ b/experimental_cases/OF9/uloop_valadbeigy_exp3/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/experimental_cases/OF9/uloop_valadbeigy_exp3/script_post b/experimental_cases/OF9/uloop_valadbeigy_exp3/script_post index dcbf59d8..f1577763 100755 --- a/experimental_cases/OF9/uloop_valadbeigy_exp3/script_post +++ b/experimental_cases/OF9/uloop_valadbeigy_exp3/script_post @@ -5,7 +5,6 @@ #SBATCH --ntasks-per-node=16 #SBATCH --time=01:59:00 #SBATCH --account=gas2fuels -#SBATCH --dependency=afterany:15800966 source /projects/gas2fuels/ofoam_cray_mpich/OpenFOAM-dev/etc/bashrc reconstructPar -newTimes -fields "(Z.liquid U.liquid alpha.gas)" diff --git a/tutorial_cases/OF9/FlatPanel_250L_ASU/Allclean b/tutorial_cases/OF9/FlatPanel_250L_ASU/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/FlatPanel_250L_ASU/Allclean +++ b/tutorial_cases/OF9/FlatPanel_250L_ASU/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/airlift_40m/Allclean b/tutorial_cases/OF9/airlift_40m/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/airlift_40m/Allclean +++ b/tutorial_cases/OF9/airlift_40m/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/bdofoam_cases/nonreact/Allclean b/tutorial_cases/OF9/bdofoam_cases/nonreact/Allclean index e9221067..e3bca612 100755 --- a/tutorial_cases/OF9/bdofoam_cases/nonreact/Allclean +++ b/tutorial_cases/OF9/bdofoam_cases/nonreact/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" [ -d "fluentInterface" ] && rm -rf "rm -f fluentInterface" # rm -f constant/fvModels rm -f *.obj diff --git a/tutorial_cases/OF9/bdofoam_cases/react/Allclean b/tutorial_cases/OF9/bdofoam_cases/react/Allclean index e9221067..e3bca612 100755 --- a/tutorial_cases/OF9/bdofoam_cases/react/Allclean +++ b/tutorial_cases/OF9/bdofoam_cases/react/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" [ -d "fluentInterface" ] && rm -rf "rm -f fluentInterface" # rm -f constant/fvModels rm -f *.obj diff --git a/tutorial_cases/OF9/bubble_column_20L/Allclean b/tutorial_cases/OF9/bubble_column_20L/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/bubble_column_20L/Allclean +++ b/tutorial_cases/OF9/bubble_column_20L/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/loop_reactor_mixing/Allclean b/tutorial_cases/OF9/loop_reactor_mixing/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/loop_reactor_mixing/Allclean +++ b/tutorial_cases/OF9/loop_reactor_mixing/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/loop_reactor_mixing_static/Allclean b/tutorial_cases/OF9/loop_reactor_mixing_static/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/loop_reactor_mixing_static/Allclean +++ b/tutorial_cases/OF9/loop_reactor_mixing_static/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/loop_reactor_mixing_swirl/Allclean b/tutorial_cases/OF9/loop_reactor_mixing_swirl/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/loop_reactor_mixing_swirl/Allclean +++ b/tutorial_cases/OF9/loop_reactor_mixing_swirl/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/loop_reactor_reacting/Allclean b/tutorial_cases/OF9/loop_reactor_reacting/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/loop_reactor_reacting/Allclean +++ b/tutorial_cases/OF9/loop_reactor_reacting/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/runall.sh b/tutorial_cases/OF9/runall.sh deleted file mode 100644 index 31e971fb..00000000 --- a/tutorial_cases/OF9/runall.sh +++ /dev/null @@ -1,61 +0,0 @@ -#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 mixing loop reactor with swirl tutorial -cd tutorial_cases/loop_reactor_mixing_swirl -bash run.sh -cd ../../ -## Run mixing loop reactor with static mixer tutorial -cd tutorial_cases/loop_reactor_mixing_static -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/OF9/side_sparger/Allclean b/tutorial_cases/OF9/side_sparger/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/side_sparger/Allclean +++ b/tutorial_cases/OF9/side_sparger/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl diff --git a/tutorial_cases/OF9/stirred_tank/Allclean b/tutorial_cases/OF9/stirred_tank/Allclean index dc2f77db..174b6310 100755 --- a/tutorial_cases/OF9/stirred_tank/Allclean +++ b/tutorial_cases/OF9/stirred_tank/Allclean @@ -15,7 +15,7 @@ fi # [ -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 -rf "processor*" # rm -f constant/fvModels rm -f *.obj rm -f *.stl