From ac435688c36887a15526e584848705c75d9f277c Mon Sep 17 00:00:00 2001 From: John Date: Wed, 18 Dec 2024 13:51:00 -0500 Subject: [PATCH 1/4] updated additivefoam templates for ExaCA functionObject to address #63 --- .../template/system/ExaCA} | 22 ++++++------------- .../template/system/controlDict | 13 ++++++++--- .../template/system/fvSchemes | 3 ++- .../template/system/ExaCA} | 21 ++++++------------ .../template/system/controlDict | 11 ++++++++-- .../template/system/fvSchemes | 3 ++- 6 files changed, 37 insertions(+), 36 deletions(-) rename src/myna/application/additivefoam/{solidification_region_stl/template/constant/foamToExaCADict => solidification_region_reduced/template/system/ExaCA} (52%) rename src/myna/application/additivefoam/{solidification_region_reduced/template/constant/foamToExaCADict => solidification_region_stl/template/system/ExaCA} (52%) diff --git a/src/myna/application/additivefoam/solidification_region_stl/template/constant/foamToExaCADict b/src/myna/application/additivefoam/solidification_region_reduced/template/system/ExaCA similarity index 52% rename from src/myna/application/additivefoam/solidification_region_stl/template/constant/foamToExaCADict rename to src/myna/application/additivefoam/solidification_region_reduced/template/system/ExaCA index 93c46491..9b7fd047 100644 --- a/src/myna/application/additivefoam/solidification_region_stl/template/constant/foamToExaCADict +++ b/src/myna/application/additivefoam/solidification_region_reduced/template/system/ExaCA @@ -4,22 +4,14 @@ Created for simulation with Myna ---------------------------------------------------------------------------*/ -FoamFile +ExaCA { - version 2; - format ascii; - class dictionary; - object foamToExaCADict; + type ExaCA; + libs ("libExaCAFunctionObject.so"); + + box ( 0.0495 -0.0495 -0.0003 ) ( 0.0505 -0.0485 0 ); + dx 2.5e-6; + isoValue 1730; } -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // - -execute on; - -box ( 0.172 0.072 -0.0003 ) ( 0.173 0.073 0 ); - -dx 2.5e-06; - -isotherm 1730; - // ************************************************************************* // diff --git a/src/myna/application/additivefoam/solidification_region_reduced/template/system/controlDict b/src/myna/application/additivefoam/solidification_region_reduced/template/system/controlDict index 81e4d42d..27edb701 100644 --- a/src/myna/application/additivefoam/solidification_region_reduced/template/system/controlDict +++ b/src/myna/application/additivefoam/solidification_region_reduced/template/system/controlDict @@ -42,11 +42,18 @@ timeFormat general; timePrecision 8; -runTimeModifiable yes; +runTimeModifiable no; adjustTimeStep yes; maxCo 0.5; -maxFo 50; -maxAlphaCo 0.5; + +maxDi 100; + +maxAlphaCo 1; + +functions +{ + #includeFunc ExaCA +} // ************************************************************************* // diff --git a/src/myna/application/additivefoam/solidification_region_reduced/template/system/fvSchemes b/src/myna/application/additivefoam/solidification_region_reduced/template/system/fvSchemes index 3d73700d..5189fec1 100644 --- a/src/myna/application/additivefoam/solidification_region_reduced/template/system/fvSchemes +++ b/src/myna/application/additivefoam/solidification_region_reduced/template/system/fvSchemes @@ -31,7 +31,8 @@ divSchemes laplacianSchemes { - default Gauss linear corrected; + default Gauss linear corrected; + laplacian(kappa,T) Gauss harmonic corrected; } interpolationSchemes diff --git a/src/myna/application/additivefoam/solidification_region_reduced/template/constant/foamToExaCADict b/src/myna/application/additivefoam/solidification_region_stl/template/system/ExaCA similarity index 52% rename from src/myna/application/additivefoam/solidification_region_reduced/template/constant/foamToExaCADict rename to src/myna/application/additivefoam/solidification_region_stl/template/system/ExaCA index cda7d1a5..59f20dca 100644 --- a/src/myna/application/additivefoam/solidification_region_reduced/template/constant/foamToExaCADict +++ b/src/myna/application/additivefoam/solidification_region_stl/template/system/ExaCA @@ -4,22 +4,15 @@ Created for simulation with Myna ---------------------------------------------------------------------------*/ -FoamFile +ExaCA { - version 2; - format ascii; - class dictionary; - object foamToExaCADict; + type ExaCA; + libs ("libExaCAFunctionObject.so"); + + box ( 0.172 0.072 -0.0003 ) ( 0.173 0.073 0 ); + dx 2.5e-6; + isoValue 1730; } -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // - -execute on; - -box ( 0.0495 -0.0495 -0.0003 ) ( 0.0505 -0.0485 0 ); - -dx 2.5e-06; - -isotherm 1730; // ************************************************************************* // diff --git a/src/myna/application/additivefoam/solidification_region_stl/template/system/controlDict b/src/myna/application/additivefoam/solidification_region_stl/template/system/controlDict index 81e4d42d..0978485e 100644 --- a/src/myna/application/additivefoam/solidification_region_stl/template/system/controlDict +++ b/src/myna/application/additivefoam/solidification_region_stl/template/system/controlDict @@ -47,6 +47,13 @@ runTimeModifiable yes; adjustTimeStep yes; maxCo 0.5; -maxFo 50; -maxAlphaCo 0.5; + +maxDi 100; + +maxAlphaCo 1; + +functions +{ + #includeFunc ExaCA +} // ************************************************************************* // diff --git a/src/myna/application/additivefoam/solidification_region_stl/template/system/fvSchemes b/src/myna/application/additivefoam/solidification_region_stl/template/system/fvSchemes index 3d73700d..5189fec1 100644 --- a/src/myna/application/additivefoam/solidification_region_stl/template/system/fvSchemes +++ b/src/myna/application/additivefoam/solidification_region_stl/template/system/fvSchemes @@ -31,7 +31,8 @@ divSchemes laplacianSchemes { - default Gauss linear corrected; + default Gauss linear corrected; + laplacian(kappa,T) Gauss harmonic corrected; } interpolationSchemes From f84a8eef7617e1f70b289ee58f1816da2e89ec48 Mon Sep 17 00:00:00 2001 From: Gerry Knapp Date: Mon, 9 Sep 2024 10:59:09 -0400 Subject: [PATCH 2/4] add core classes associated with final layer temperature simulations --- src/myna/core/components/component_class_lookup.py | 1 + src/myna/core/components/component_temperature.py | 13 ++++++++++++- src/myna/core/files/file_temperature.py | 9 ++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/myna/core/components/component_class_lookup.py b/src/myna/core/components/component_class_lookup.py index e6762478..39b36a73 100644 --- a/src/myna/core/components/component_class_lookup.py +++ b/src/myna/core/components/component_class_lookup.py @@ -40,6 +40,7 @@ def return_step_class(step_name): "solidification_part_stl": ComponentSolidificationPartSTL(), "solidification_region_stl": ComponentSolidificationRegionSTL(), "temperature_part": ComponentTemperaturePart(), + "temperature_final_part_stl": ComponentTemperatureFinalPartSTL(), "cluster_solidification": ComponentClusterSolidification(), "cluster_supervoxel": ComponentClusterSupervoxel(), "microstructure_part": ComponentMicrostructurePart(), diff --git a/src/myna/core/components/component_temperature.py b/src/myna/core/components/component_temperature.py index 887ddccf..098bf1c6 100644 --- a/src/myna/core/components/component_temperature.py +++ b/src/myna/core/components/component_temperature.py @@ -14,7 +14,7 @@ """ from .component import * -from myna.core.files import FileTemperature +from myna.core.files import FileTemperature, FileTemperatureFinal ################## # Base Component # @@ -52,3 +52,14 @@ class ComponentTemperaturePart(ComponentTemperature): def __init__(self): ComponentTemperature.__init__(self) self.types.extend(["part", "layer"]) + + +class ComponentTemperatureFinalPartSTL(ComponentTemperaturePart): + """Layer-wise Component that outputs the domain temperature + at the end of a layer for a part in the format of the class `FileTemperatureFinal` + """ + + def __init__(self): + ComponentTemperaturePart.__init__(self) + self.data_requirements.extend(["stl"]) + self.output_requirement = FileTemperatureFinal diff --git a/src/myna/core/files/file_temperature.py b/src/myna/core/files/file_temperature.py index c4453a23..455b0e2a 100644 --- a/src/myna/core/files/file_temperature.py +++ b/src/myna/core/files/file_temperature.py @@ -6,7 +6,7 @@ # # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause. # -"""Define file format class related to the spatial distribution of temperature (T) +"""Define file format classes related to the spatial distribution of temperature (T) """ import pandas as pd @@ -43,3 +43,10 @@ def file_is_valid(self): expected_cols = ["x (m)", "y (m)", "t (k)"] expected_cols_types = [float, float, float] return self.columns_are_valid(cols, expected_cols, expected_cols_types) + + +class FileTemperatureFinal(FileTemperature): + """File format class for temperature (T) after a layer is deposited""" + + def __init__(self, file): + FileTemperature.__init__(self, file) From 153d57c7a724c39d697ef908c3f586c4c980fe48 Mon Sep 17 00:00:00 2001 From: Gerry Knapp Date: Tue, 10 Sep 2024 08:42:28 -0400 Subject: [PATCH 3/4] add additivefoam temperature_final_part app --- examples/temperature_final_part/input.yaml | 19 ++ src/myna/application/additivefoam/__init__.py | 1 + .../application/additivefoam/additivefoam.py | 38 ++- src/myna/application/additivefoam/path.py | 89 ++++-- .../temperature_final_part/execute.py | 292 ++++++++++++++++++ .../temperature_final_part/template/0/T | 46 +++ .../template/0/alpha.solid | 34 ++ .../temperature_final_part/template/Allclean | 9 + .../template/constant/g | 21 ++ .../template/constant/heatSourceDict | 35 +++ .../template/constant/scanpath.txt | 2 + .../template/constant/thermoPath | 4 + .../template/constant/transportProperties | 31 ++ .../template/system/blockMeshDict | 81 +++++ .../template/system/controlDict | 51 +++ .../template/system/decomposeParDict | 22 ++ .../template/system/extrudeMeshDict | 36 +++ .../template/system/fvSchemes | 48 +++ .../template/system/fvSolution | 48 +++ .../template/system/mapFieldsDict | 20 ++ .../template/system/setFieldsDict | 30 ++ .../template/system/topSurface | 28 ++ .../additivefoam/update_parameters.py | 109 +++++++ src/myna/application/openfoam/__init__.py | 1 + src/myna/application/openfoam/mesh.py | 6 +- src/myna/application/openfoam/update.py | 27 ++ .../core/components/component_class_lookup.py | 3 +- .../core/components/component_temperature.py | 16 +- src/myna/core/utils/nested_dict_tools.py | 17 + 29 files changed, 1126 insertions(+), 38 deletions(-) create mode 100644 examples/temperature_final_part/input.yaml create mode 100644 src/myna/application/additivefoam/temperature_final_part/execute.py create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/0/T create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/0/alpha.solid create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/Allclean create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/constant/g create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/constant/heatSourceDict create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/constant/scanpath.txt create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/constant/thermoPath create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/constant/transportProperties create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/blockMeshDict create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/controlDict create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/decomposeParDict create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/extrudeMeshDict create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/fvSchemes create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/fvSolution create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/mapFieldsDict create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/setFieldsDict create mode 100644 src/myna/application/additivefoam/temperature_final_part/template/system/topSurface create mode 100644 src/myna/application/additivefoam/update_parameters.py create mode 100644 src/myna/application/openfoam/update.py diff --git a/examples/temperature_final_part/input.yaml b/examples/temperature_final_part/input.yaml new file mode 100644 index 00000000..208afc0c --- /dev/null +++ b/examples/temperature_final_part/input.yaml @@ -0,0 +1,19 @@ +steps: +- macroadditivefoam: + class: temperature_final_part + application: additivefoam + execute: + coarse: 0.5e-3 + pad-xy: 5e-3 + pad-sub: 12.7e-3 + np: 24 + n-cells-per-layer: 1 + layer-time: 5 +data: + build: + datatype: Peregrine + name: myna_output + path: .. + parts: + P5: + layers: [51, 52] diff --git a/src/myna/application/additivefoam/__init__.py b/src/myna/application/additivefoam/__init__.py index 73f384f9..3f1741a6 100644 --- a/src/myna/application/additivefoam/__init__.py +++ b/src/myna/application/additivefoam/__init__.py @@ -14,3 +14,4 @@ from . import path from .additivefoam import AdditiveFOAM +from .update_parameters import * diff --git a/src/myna/application/additivefoam/additivefoam.py b/src/myna/application/additivefoam/additivefoam.py index 2dd1bff0..ab536cc9 100644 --- a/src/myna/application/additivefoam/additivefoam.py +++ b/src/myna/application/additivefoam/additivefoam.py @@ -90,7 +90,7 @@ def __init__( help="Multiple by which to scale the STL file dimensions (default = 0.001, mm -> m)", ) - self.args = self.parser.parse_args() + self.args, _ = self.parser.parse_known_args() super().set_procs() super().check_exe( @@ -100,6 +100,7 @@ def __init__( def update_template_path(self): """Updates the template path parameter""" + print(self.args.template) if self.args.template is None: template_path = os.path.join( os.environ["MYNA_APP_PATH"], @@ -108,6 +109,7 @@ def update_template_path(self): "template", ) self.args.template = template_path + print(self.args.template) def copy_template_to_dir(self, target_dir): """Copies the specified template directory to the specified target directory""" @@ -182,6 +184,21 @@ def update_material_properties(self, case_dir): + f' -set "{absorption}" {case_dir}/constant/heatSourceDict' ) + def get_part_resource_template_dir(self, part): + """Provides the path to the template directory in the myna_resources folder + + Args: + part: The name of the part + """ + return os.path.join( + os.path.dirname(self.input_file), + "myna_resources", + part, + "additivefoam", + self.simulation_type, + "template", + ) + def get_region_resource_template_dir(self, part, region): """Provides the path to the template directory in the myna_resources folder @@ -311,7 +328,7 @@ def update_region_start_and_end_times(self, case_dir, bb_dict, scanpath_name): end_time = np.round(end_time, 5) self.update_start_and_end_times(case_dir, start_time, end_time) - def update_start_and_end_times(self, case_dir, start_time, end_time): + def update_start_and_end_times(self, case_dir, start_time, end_time, n_write=2): """Updates the case to adjust the start and end time by adjusting:" - start and end times of the simulation in system/controlDict @@ -322,6 +339,7 @@ def update_start_and_end_times(self, case_dir, start_time, end_time): case_dir: case directory to update start_time: start time of the simulation end_time: end time of the simulation + n_write: number of times to write output (must be > 0) """ os.system( f"foamDictionary -entry startTime -set {start_time} " @@ -332,14 +350,20 @@ def update_start_and_end_times(self, case_dir, start_time, end_time): + f"{case_dir}/system/controlDict" ) os.system( - f"foamDictionary -entry writeInterval -set {np.round(0.5 * (end_time - start_time), 5)} " + f"foamDictionary -entry writeInterval -set {np.round((1 / n_write) * (end_time - start_time), 8)} " + f"{case_dir}/system/controlDict" ) source = os.path.abspath(os.path.join(case_dir, "0")) - target = os.path.abspath(os.path.join(case_dir, f"{start_time}")) - if os.path.exists(target): - shutil.rmtree(target) - shutil.move(source, target) + target = os.path.abspath( + os.path.join( + case_dir, + f"{int(start_time) if float(start_time).is_integer() else start_time}", + ) + ) + if target != source: + if os.path.exists(target): + shutil.rmtree(target) + shutil.move(source, target) def update_heatsource_scanfile(self, case_dir, scanpath_name): """Updates the heatSourceDict to point to the specified scan path file diff --git a/src/myna/application/additivefoam/path.py b/src/myna/application/additivefoam/path.py index 33988e34..c2e05bac 100644 --- a/src/myna/application/additivefoam/path.py +++ b/src/myna/application/additivefoam/path.py @@ -7,34 +7,75 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause. # import pandas as pd +import polars as pl -def convert_peregrine_scanpath(filename, export_path, power=1): - """converts peregrine scan path units to additivefoam scan path units""" - df = pd.read_csv(filename, sep="\s+") +def convert_peregrine_scanpath(input_file, output_file, power=1): + """Convert Myna scan path to an AdditiveFOAM-compatible scan path - # convert X & Y distances to meters - df["X(m)"] = df["X(mm)"] * 1e-3 - df["Y(m)"] = df["Y(mm)"] * 1e-3 + Args: + input_file: Myna scan path + output_file: AdditiveFOAM scan path to write + power: nominal power of the laser (default 1 makes equivalent to Myna "Pmod") + """ - # set Z value to zero - df["Z(m)"] = df["Z(mm)"] * 0 + data = pl.read_csv(input_file, separator="\t") + data = data.rename( + { + "X(mm)": "X(mm)", + "Y(mm)": "Y(mm)", + "Z(mm)": "Z(mm)", + "Pmod": "Pmod", + "tParam": "tParam", + } + ) + data = data.with_columns( + [ + (pl.col("X(mm)") / 1000.0).alias("X(m)"), # Convert to meters + (pl.col("Y(mm)") / 1000.0).alias("Y(m)"), # Convert to meters + pl.lit(0.0).alias("Z(m)"), # Set Z to zero + (pl.col("Pmod") * power).alias("Power"), # Convert to Watts + ] + ).rename( + {"tParam": "Parameter"} + ) # Rename to Parameter + data = data.select(["Mode", "X(m)", "Y(m)", "Z(m)", "Power", "Parameter"]) + data.write_csv(output_file, separator="\t") - # format columns - round_cols = ["X(m)", "Y(m)", "Z(m)"] - df[round_cols] = df[round_cols].round(6) - for col in round_cols: - df[col] = df[col].map( - lambda x: f'{str(x).ljust(7+len(str(x).split(".")[0]),"0")}' - ) - # set the laser power - df["Power(W)"] = df["Pmod"] * power +def get_scanpath_bounding_box(scanpath, file_format="myna"): + """Returns the bounding box for given scanpath file(s) in meters - # write the converted path to a new file - df.to_csv( - export_path, - columns=["Mode", "X(m)", "Y(m)", "Z(m)", "Power(W)", "tParam"], - sep="\t", - index=False, - ) + Args: + scanpath: file or list of files of scanpaths to find the bounding box + format: the format of the scanpath file ("myna" or "additivefoam") + + Returns: + [[minx, miny, minz],[maxx, maxy, maxz]] + """ + if not isinstance(scanpath, list) and isinstance(scanpath, str): + scanpath = [scanpath] + + xmin, ymin, zmin = [1e10] * 3 + xmax, ymax, zmax = [-1e10] * 3 + + if file_format.lower() == "myna": + xcol, ycol, zcol = ["X(mm)", "Y(mm)", "Z(mm)"] + scale = 1e-3 + elif file_format.lower() == "additivefoam": + xcol, ycol, zcol = ["X(m)", "Y(m)", "Z(m)"] + scale = 1 + + else: + assert file_format.lower() in ["myna", "additivefoam"] + + for f in scanpath: + df = pd.read_csv(f, sep="\t") + xmin = min(xmin, df[xcol].min() * scale) + xmax = max(xmax, df[xcol].max() * scale) + ymin = min(ymin, df[ycol].min() * scale) + ymax = max(ymax, df[ycol].max() * scale) + zmin = min(zmin, df[zcol].min() * scale) + zmax = max(zmax, df[zcol].max() * scale) + + return [[xmin, ymin, zmin], [xmax, ymax, zmax]] diff --git a/src/myna/application/additivefoam/temperature_final_part/execute.py b/src/myna/application/additivefoam/temperature_final_part/execute.py new file mode 100644 index 00000000..f66128b3 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/execute.py @@ -0,0 +1,292 @@ +# +# Copyright (c) 2024 Oak Ridge National Laboratory. +# +# This file is part of Myna. For details, see the top-level license +# at https://github.com/ORNL-MDF/Myna/LICENSE.md. +# +# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause. +# +import os +import subprocess +import shutil +import polars as pl +from myna.core.workflow.load_input import load_input +from myna.core.utils.nested_dict_tools import nested_find_all +from myna.application.additivefoam import AdditiveFOAM +from myna.application.additivefoam.path import ( + convert_peregrine_scanpath, + get_scanpath_bounding_box, +) +import myna.application.openfoam as openfoam + + +def get_myna_file_part(myna_file): + """Returns the string of the part component of the Myna file path + + Args: + - myna_file: path to the Myna file + + Returns: String of the part name + """ + return myna_file.split(os.path.sep)[-4] + + +def get_myna_file_layer(myna_file): + """Returns the string of the layer component of the Myna file path + + Args: + - myna_file: path to the Myna file + + Returns: String of the layer name + """ + return myna_file.split(os.path.sep)[-3] + + +def update_parallel_cmd(app, cmd): + """Adds mpirun and parallel arguments for running OpenFOAM applications in parallel + + Args: + app: AdditiveFOAM(MynaApp) + cmd: list of command arguments to update + """ + if app.args.np > 1: + cmd = ["mpirun", "-np", str(app.args.np)] + cmd + cmd.append("-parallel") + return cmd + + +def convert_temperature_output(case_dir, output_file): + """Extract the top surface temperature and write as csv + + Args: + case_dir: path to the case directory to process + output_file: path to the output file to write + """ + + end_time = float( + openfoam.update.foam_dict_get("endTime", f"{case_dir}/system/controlDict") + ) + if end_time.is_integer(): + end_time = int(end_time) + input_file = os.path.join(case_dir, f"postProcessing/topSurface/{end_time}/top.xy") + + # Read and clean data in-memory + with open(input_file, "r", encoding="utf-8") as f: + data = [ + line.split() + for line in f + if not line.strip().startswith("#") and line.strip() + ] + + # Convert the data to a Polars DataFrame with explicit orientation + df = pl.DataFrame( + data, schema=["x (m)", "y (m)", "z (m)", "T (K)", "is_solid"], orient="row" + ).with_columns( + [ + pl.col("x (m)").cast(pl.Float64), + pl.col("y (m)").cast(pl.Float64), + pl.col("z (m)").cast(pl.Float64), + pl.col("T (K)").cast(pl.Float64), + pl.col("is_solid").cast(pl.Float64), + ] + ) + + # Write the DataFrame to a CSV file + df.write_csv(output_file) + + +def main(): + """Assembles and runs coarse heat transfer simulation for all specified layers. + + Due to each layer relying on the output of the previous layer, this app requires + each layer to run sequentially.""" + + # Create app instance + app = AdditiveFOAM("temperature_final_part") + app.parser.add_argument( + "--n-cells-per-layer", type=int, default=1, help="Number of cells per layer" + ) + app.parser.add_argument( + "--layer-time", + type=float, + default=60.0, + help="Simulation time for each layer in seconds", + ) + app.args, _ = app.parser.parse_known_args() + + # recalculate app arguments after new argparse + app.update_template_path() + app.set_procs() + app.check_exe("macroAdditiveFoam") + + # For each part, get list of directories for each layer, configure and launch + # the corresponding simulations + myna_files = app.settings["data"]["output_paths"][app.step_name] + parts = list(app.settings["data"]["build"]["parts"].keys()) + for part in parts: + # Get list of files associated with the part and extract layer numbers + part_files = [f for f in myna_files if get_myna_file_part(f) == part] + layers = [int(get_myna_file_layer(f)) for f in part_files] + + # Sort the lists by layer integers + part_files = [x for _, x in sorted(zip(layers, part_files))] + layers = sorted(layers) + + # Create a resource directory for the part's background mesh + template_dir = os.path.abspath(app.get_part_resource_template_dir(part)) + app.copy_template_to_dir(template_dir) + + # Create the background mesh based on the bounding box of the scan path + layer_data_dict = app.settings["data"]["build"]["parts"][part]["layer_data"] + all_scanpaths = [ + x["file_local"] for x in nested_find_all(layer_data_dict, "scanpath") + ] + scanpath_box = get_scanpath_bounding_box(all_scanpaths, file_format="myna") + layer_thickness = app.settings["data"]["build"]["build_data"][ + "layer_thickness" + ]["value"] + scanpath_box[0][2] = -app.args.pad_sub + scanpath_box[1][2] = 0.0 + pad = [app.args.pad_xy, app.args.pad_xy, 0.0] + _, _ = openfoam.mesh.create_cube_mesh( + template_dir, + [app.args.coarse, app.args.coarse, app.args.coarse], + 1.0e-08, + scanpath_box, + pad, + ) + + for index, (layer, myna_file) in enumerate(zip(layers, part_files)): + # Get case settings + case_dir = os.path.dirname(myna_file) + case_settings = load_input(os.path.join(case_dir, "myna_data.yaml")) + part = list(case_settings["build"]["parts"].keys())[0] + part_dict = case_settings["build"]["parts"][part] + layer_height = layer * layer_thickness + + # Copy the template case + shutil.copytree(template_dir, case_dir, dirs_exist_ok=True) + + # Update scanpath and heat source files + myna_scanfile = part_dict["layer_data"][str(layer)]["scanpath"][ + "file_local" + ] + power = case_settings["build"]["parts"][part]["laser_power"]["value"] + path_name = os.path.basename(myna_scanfile) + new_scan_path_file = os.path.join(case_dir, "constant", path_name) + convert_peregrine_scanpath(myna_scanfile, new_scan_path_file, power) + app.update_heatsource_scanfile(case_dir, path_name) + app.update_beam_spot_size(part, case_dir) + + # TODO: Update material properties from Mist data. Cannot use default + # Mist AdditiveFOAM file generation `app.update_material_properties()` + # because different properties are needed + + # Update number of processors + openfoam.update.foam_dict_set( + "numberOfSubdomains", app.args.np, f"{case_dir}/system/decomposeParDict" + ) + + # Extrude mesh + cells_to_extrude = app.args.n_cells_per_layer * layer + openfoam.update.foam_dict_set( + "nLayers", cells_to_extrude, f"{case_dir}/system/extrudeMeshDict" + ) + openfoam.update.foam_dict_set( + "linearDirectionCoeffs/thickness", + layer_height, + f"{case_dir}/system/extrudeMeshDict", + ) + openfoam.update.foam_dict_set( + "sourceCase", + f'"{case_dir}"', + f"{case_dir}/system/extrudeMeshDict", + ) + subprocess.run(["extrudeMesh", "-case", case_dir], check=True) + + # Update times and map fields between layers + if index == 0: + start_time = 0 + end_time = start_time + app.args.layer_time + openfoam.update.foam_dict_set( + "endTime", end_time, f"{case_dir}/system/controlDict" + ) + app.update_start_and_end_times(case_dir, start_time, end_time, 1) + subprocess.run(["decomposePar", "-case", case_dir], check=True) + else: + # Get directory for previous case + previous_case_dir = os.path.dirname(part_files[index - 1]) + start_time = float( + openfoam.update.foam_dict_get( + "endTime", + os.path.join(previous_case_dir, "system", "controlDict"), + ) + ) + end_time = start_time + app.args.layer_time + app.update_start_and_end_times(case_dir, start_time, end_time, 1) + subprocess.run(["decomposePar", "-case", case_dir], check=True) + cmd = [ + "mapFieldsPar", + "-case", + case_dir, + "-mapMethod", + "direct", + "-sourceTime", + str(start_time), + previous_case_dir, + ] + subprocess.run(update_parallel_cmd(app, cmd), check=True) + + # Run macroAdditiveFoam + subprocess.run( + update_parallel_cmd( + app, + [ + "transformPoints", + f"translate=(0 0 -{layer_height})", + "-case", + case_dir, + ], + ), + check=True, + ) + subprocess.run( + update_parallel_cmd(app, ["setFields", "-case", case_dir]), check=True + ) + subprocess.run( + update_parallel_cmd(app, ["macroAdditiveFoam", "-case", case_dir]), + check=True, + ) + subprocess.run( + update_parallel_cmd( + app, + [ + "transformPoints", + f"translate=(0 0 {layer_height})", + "-case", + case_dir, + ], + ), + check=True, + ) + + # Post-process + subprocess.run( + update_parallel_cmd( + app, + [ + "postProcess", + "-case", + case_dir, + "-func", + "topSurface", + "-latestTime", + ], + ), + check=True, + ) + convert_temperature_output(case_dir, myna_file) + + +if __name__ == "__main__": + main() diff --git a/src/myna/application/additivefoam/temperature_final_part/template/0/T b/src/myna/application/additivefoam/temperature_final_part/template/0/T new file mode 100644 index 00000000..a92aefb3 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/0/T @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + format binary; + class volScalarField; + location "0"; + object T; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 300; + +boundaryField +{ + bottom + { + type mixedTemperature; + h 500.0; + emissivity 0.4; + Tinf uniform 300; + value uniform 300; + } + top + { + type mixedTemperature; + h 25.0; + emissivity 0.4; + Tinf uniform 300; + value uniform 300; + } + sides + { + type zeroGradient; + value uniform 300; + } +} + + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/0/alpha.solid b/src/myna/application/additivefoam/temperature_final_part/template/0/alpha.solid new file mode 100644 index 00000000..a2dad075 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/0/alpha.solid @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + format binary; + class volScalarField; + location "0"; + object alpha.solid; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 1; + +boundaryField +{ + bottom + { + type zeroGradient; + } + top + { + type zeroGradient; + } + sides + { + type zeroGradient; + } +} diff --git a/src/myna/application/additivefoam/temperature_final_part/template/Allclean b/src/myna/application/additivefoam/temperature_final_part/template/Allclean new file mode 100644 index 00000000..98353b9b --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/Allclean @@ -0,0 +1,9 @@ +#!/bin/sh +cd ${0%/*} || exit 1 # Run from this directory + +# Source tutorial clean functions +. $WM_PROJECT_DIR/bin/tools/CleanFunctions + +cleanCase + +rm -rf layer* diff --git a/src/myna/application/additivefoam/temperature_final_part/template/constant/g b/src/myna/application/additivefoam/temperature_final_part/template/constant/g new file mode 100644 index 00000000..ee422709 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/constant/g @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2.0; + format ascii; + class uniformDimensionedVectorField; + location "constant"; + object g; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -2 0 0 0 0]; +value (0 0 -9.81); + + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/constant/heatSourceDict b/src/myna/application/additivefoam/temperature_final_part/template/constant/heatSourceDict new file mode 100644 index 00000000..181b774c --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/constant/heatSourceDict @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2; + format ascii; + class dictionary; + object heatSourceProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +sources ( beam ); + +beam +{ + pathName scanpath.txt; + absorptionModel constant; + constantCoeffs + { + eta 0.3; + } + heatSourceModel Gaussian; + GaussianCoeffs + { + dimensions ( 6.5e-05 6.5e-05 3e-05 ); + nPoints ( 2 2 2 ); + } +} + + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/constant/scanpath.txt b/src/myna/application/additivefoam/temperature_final_part/template/constant/scanpath.txt new file mode 100644 index 00000000..f2fffc1e --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/constant/scanpath.txt @@ -0,0 +1,2 @@ +Mode X(m) Y(m) Z(m) Power(W) tParam +1 0.0 0.0 0.0 0 0.0 diff --git a/src/myna/application/additivefoam/temperature_final_part/template/constant/thermoPath b/src/myna/application/additivefoam/temperature_final_part/template/constant/thermoPath new file mode 100644 index 00000000..7915d61f --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/constant/thermoPath @@ -0,0 +1,4 @@ +( +1670.0000 1.0000 +1730.0000 0.0000 +) diff --git a/src/myna/application/additivefoam/temperature_final_part/template/constant/transportProperties b/src/myna/application/additivefoam/temperature_final_part/template/constant/transportProperties new file mode 100644 index 00000000..7a95fc35 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/constant/transportProperties @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object transportProperties; +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +solid +{ + kappa (4.957 0.01571 0.0); + Cp (770.4 0.0 0.0); +} + +powder +{ + kappa (0.4957 0.001571 0.0); + Cp (42.3 0.01329 0.0); +} + +rho [1 -3 0 0 0 0 0] 7955.0; + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/blockMeshDict b/src/myna/application/additivefoam/temperature_final_part/template/system/blockMeshDict new file mode 100644 index 00000000..11d8edc4 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/blockMeshDict @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2; + format ascii; + class dictionary; + object blockMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +xmin 0.16503199000000002; +ymin 0.06701219; +zmin -1.55476e-05; + +xmax 0.17983100999999999; +ymax 0.08132541; +zmax 0.016320510000000003; + +nx 92; +ny 89; +nz 102; + +vertices +( + ( $xmin $ymin $zmin ) + ( $xmax $ymin $zmin ) + ( $xmax $ymax $zmin ) + ( $xmin $ymax $zmin ) + ( $xmin $ymin $zmax ) + ( $xmax $ymin $zmax ) + ( $xmax $ymax $zmax ) + ( $xmin $ymax $zmax ) +); + +blocks +( + hex ( 0 1 2 3 4 5 6 7 ) ( $nx $ny $nz ) simpleGrading ( 1 1 1 ) +); + +edges( ); + +boundary +( + bottom + { + type wall; + faces + ( + (0 3 2 1) + ); + } + top + { + type wall; + faces + ( + (4 5 6 7) + ); + } + sides + { + type wall; + faces + ( + (0 4 7 3) + (2 6 5 1) + (1 5 4 0) + (3 7 6 2) + ); + } +); + + +mergePatchPairs ( ); + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/controlDict b/src/myna/application/additivefoam/temperature_final_part/template/system/controlDict new file mode 100644 index 00000000..fd661736 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/controlDict @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application macroAdditiveFoam; + +startFrom startTime; + +startTime 0.0; + +stopAt endTime; + +endTime 0.0; + +deltaT 0.01; + +writeControl adjustableRunTime; + +writeInterval 0.5; + +purgeWrite 0; + +writeFormat binary; + +writePrecision 8; + +writeCompression off; + +timeFormat general; + +timePrecision 8; + +runTimeModifiable no; + +adjustTimeStep yes; + +nPathIntervals 100; +maxDi 1000; +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/decomposeParDict b/src/myna/application/additivefoam/temperature_final_part/template/system/decomposeParDict new file mode 100644 index 00000000..d0fd8b1f --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/decomposeParDict @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2; + format ascii; + class dictionary; + location "system"; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains 8; + +method scotch; + + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/extrudeMeshDict b/src/myna/application/additivefoam/temperature_final_part/template/system/extrudeMeshDict new file mode 100644 index 00000000..3fbe9691 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/extrudeMeshDict @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + + FoamFile +{ + version 2; + format ascii; + class dictionary; + location "system"; + object extrudeMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +constructFrom mesh; +sourceCase "."; +sourcePatches (top); + +flipNormals false; + +nLayers 0; +expansionRatio 1; + +extrudeModel linearDirection; + +linearDirectionCoeffs +{ + direction (0 0 1); + thickness 0; +} + +mergeFaces false; + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/fvSchemes b/src/myna/application/additivefoam/temperature_final_part/template/system/fvSchemes new file mode 100644 index 00000000..8ee17875 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/fvSchemes @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default backward; +} + +gradSchemes +{ + default Gauss linear; +} + +divSchemes +{ + default Gauss upwind; +} + +laplacianSchemes +{ + default Gauss linear corrected; + laplacian(kappa,T) Gauss harmonic corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default corrected; +} + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/fvSolution b/src/myna/application/additivefoam/temperature_final_part/template/system/fvSolution new file mode 100644 index 00000000..16a35d3b --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/fvSolution @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +solvers +{ + "T.*" + { + solver PBiCGStab; + preconditioner DIC; + tolerance 1e-10; + relTol 0; + minIter 1; + maxIter 20; + } +} + +PIMPLE +{ + momentumPredictor no; + nOuterCorrectors 0; + nCorrectors 1; + nNonOrthogonalCorrectors 0; + pRefCell 0; + pRefValue 0; +} + +relaxationFactors +{ + equations + { + ".*" 1; + } +} + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/mapFieldsDict b/src/myna/application/additivefoam/temperature_final_part/template/system/mapFieldsDict new file mode 100644 index 00000000..1ec93bb0 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/mapFieldsDict @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + + FoamFile +{ + version 2; + format ascii; + class dictionary; + location "system"; + object mapFieldsDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +patchMap (); +cuttingPatches ( bottom top sides ); + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/setFieldsDict b/src/myna/application/additivefoam/temperature_final_part/template/system/setFieldsDict new file mode 100644 index 00000000..98747ed3 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/setFieldsDict @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + + FoamFile +{ + version 2; + format ascii; + class dictionary; + location "system"; + object setFieldsDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +regions +( + boxToCell + { + box (-1 -1 -5e-5) (1 1 1); + + fieldValues + ( + volScalarFieldValue alpha.solid 0 + ); + } +); + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/temperature_final_part/template/system/topSurface b/src/myna/application/additivefoam/temperature_final_part/template/system/topSurface new file mode 100644 index 00000000..94385958 --- /dev/null +++ b/src/myna/application/additivefoam/temperature_final_part/template/system/topSurface @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------- + AdditiveFOAM template input file (compatible with 1.0, OpenFOAM 10) + + Created for simulation with Myna + ---------------------------------------------------------------------------*/ + +topSurface +{ + type surfaces; + libs ("libsampling.so"); + + surfaceFormat raw; + + fields ( T alpha.solid ); + + interpolationScheme cellPoint; + + surfaces + ( + top + { + type patch; + patches ( "top" ); + } + ); +} + +// ************************************************************************* // diff --git a/src/myna/application/additivefoam/update_parameters.py b/src/myna/application/additivefoam/update_parameters.py new file mode 100644 index 00000000..3c8c4609 --- /dev/null +++ b/src/myna/application/additivefoam/update_parameters.py @@ -0,0 +1,109 @@ +# +# Copyright (c) 2024 Oak Ridge National Laboratory. +# +# This file is part of Myna. For details, see the top-level license +# at https://github.com/ORNL-MDF/Myna/LICENSE.md. +# +# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause. +# + +import subprocess +import os +import shutil +import numpy as np + + +def set_beam_size(case_dir, spot_x, spot_y): + """Sets the beam size for an AdditiveFOAM case + + Args: + case_dir: path to the case directory to modify + spot_x: x-dimension of the heat source (radius of beam with diameter 4Dsigma) + in meters + spot_y: y-dimension of the heat source (radius of beam with diameter 4Dsigma) + in meters + """ + + # 1. Get heatSourceModel + heat_source_model = ( + subprocess.check_output( + f"foamDictionary -entry beam/heatSourceModel -value " + + f"{case_dir}/constant/heatSourceDict", + shell=True, + ) + .decode("utf-8") + .strip() + ) + + # 2. Get heatSourceModelCoeffs/dimensions + heat_source_dimensions = ( + subprocess.check_output( + f"foamDictionary -entry beam/{heat_source_model}Coeffs/dimensions -value " + + f"{case_dir}/constant/heatSourceDict", + shell=True, + ) + .decode("utf-8") + .strip() + ) + heat_source_dimensions = ( + heat_source_dimensions.replace("(", "").replace(")", "").strip() + ) + heat_source_dimensions = [float(x) for x in heat_source_dimensions.split(" ")] + + # 3. Modify X- and Y-dimensions + heat_source_dimensions[:2] = [spot_x, spot_y] + heat_source_dimensions = [round(dim, 7) for dim in heat_source_dimensions] + + # 4. Write to file + heat_source_dim_string = ( + str(heat_source_dimensions) + .replace("[", "( ") + .replace("]", " )") + .replace(",", "") + ) + os.system( + f"foamDictionary -entry beam/{heat_source_model}Coeffs/dimensions -set" + + f" {heat_source_dim_string}" + + f" {case_dir}/constant/heatSourceDict" + ) + + +def set_start_and_end_times( + case_dir, start_time, end_time, time_precision=5, num_write_times=None +): + """Updates the case to have the given start and end times + + Args: + case_dir: path to case directory to update + start_time: time to start the simulation, in seconds + end_time: time to end the simulation, in seconds + time_precision: decimal precision for time entries + num_write_times: number of time steps to write out, if not specified (None) + only the last time step will be output""" + + # Set start and end times and rename initial condition directory + start_time = np.round(start_time, time_precision) + end_time = np.round(end_time, time_precision) + os.system( + f"foamDictionary -entry startTime -set {start_time} " + + f"{case_dir}/system/controlDict" + ) + os.system( + f"foamDictionary -entry endTime -set {end_time} " + + f"{case_dir}/system/controlDict" + ) + shutil.move(os.path.join(case_dir, "0"), os.path.join(case_dir, f"{start_time}")) + + # Calculate and set write interval + if (num_write_times is None) or (num_write_times == 0): + write_interval = np.round((end_time - start_time), time_precision) + else: + write_interval = np.round( + 1 / num_write_times * (end_time - start_time), time_precision + ) + os.system( + f"foamDictionary -entry writeInterval -set" + + f" {write_interval}" + + f" {case_dir}/system/controlDict" + ) + shutil.move(os.path.join(case_dir, "0"), os.path.join(case_dir, f"{start_time}")) diff --git a/src/myna/application/openfoam/__init__.py b/src/myna/application/openfoam/__init__.py index 7f3a2f67..56e83bc5 100644 --- a/src/myna/application/openfoam/__init__.py +++ b/src/myna/application/openfoam/__init__.py @@ -13,3 +13,4 @@ """ from . import mesh +from . import update diff --git a/src/myna/application/openfoam/mesh.py b/src/myna/application/openfoam/mesh.py index 9c907675..5165c2a6 100644 --- a/src/myna/application/openfoam/mesh.py +++ b/src/myna/application/openfoam/mesh.py @@ -189,7 +189,7 @@ def create_cube_mesh(case_dir, spacing, tolerance, rve, rve_pad): origin = [a + b / 2.0 for (a, b) in zip(bb_min, span)] # update the background mesh file - blockMeshDict = os.path.join(case_dir, "system/blockMeshDict") + blockMeshDict = os.path.join(case_dir, "system", "blockMeshDict") lines = open(blockMeshDict, "r").readlines() keys = ["xmin", "ymin", "zmin", "xmax", "ymax", "zmax"] @@ -317,10 +317,10 @@ def slice(case_dir, height): def refine_RVE(case_dir, bb): os.system( - f"foamDictionary -entry box -set " + f"foamDictionary -entry ExaCA/box -set " f'"( {bb[0][0]} {bb[0][1]} {bb[0][2]} ) ' f'( {bb[1][0]} {bb[1][1]} {bb[1][2]} )" ' - f"{case_dir}/constant/foamToExaCADict" + f"{case_dir}/system/ExaCA" ) os.system( diff --git a/src/myna/application/openfoam/update.py b/src/myna/application/openfoam/update.py new file mode 100644 index 00000000..7e1c4269 --- /dev/null +++ b/src/myna/application/openfoam/update.py @@ -0,0 +1,27 @@ +# +# Copyright (c) 2024 Oak Ridge National Laboratory. +# +# This file is part of Myna. For details, see the top-level license +# at https://github.com/ORNL-MDF/Myna/LICENSE.md. +# +# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause. +# +import subprocess + + +def foam_dict_get(entry, filepath): + """Gets a value from a foamDictionary file.""" + command = ["foamDictionary", "-entry", entry, "-value", filepath] + return subprocess.run( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + check=True, + ).stdout.strip() + + +def foam_dict_set(entry, value, filepath): + """Gets a value from a foamDictionary file.""" + command = "foamDictionary", "-entry", entry, "-set", str(value), filepath + subprocess.run(command, check=True) diff --git a/src/myna/core/components/component_class_lookup.py b/src/myna/core/components/component_class_lookup.py index 39b36a73..5d142e7d 100644 --- a/src/myna/core/components/component_class_lookup.py +++ b/src/myna/core/components/component_class_lookup.py @@ -34,12 +34,13 @@ def return_step_class(step_name): "general": Component(), "solidification_part": ComponentSolidificationPart(), "solidification_build_region": ComponentSolidificationBuildRegion(), - "solidification_region_reduced": ComponentSolidificationRegion(), + "solidification_region": ComponentSolidificationRegion(), "solidification_part_solidification": ComponentSolidificationPartReduced(), "solidification_region_reduced": ComponentSolidificationRegionReduced(), "solidification_part_stl": ComponentSolidificationPartSTL(), "solidification_region_stl": ComponentSolidificationRegionSTL(), "temperature_part": ComponentTemperaturePart(), + "temperature_final_part": ComponentTemperatureFinalPart(), "temperature_final_part_stl": ComponentTemperatureFinalPartSTL(), "cluster_solidification": ComponentClusterSolidification(), "cluster_supervoxel": ComponentClusterSupervoxel(), diff --git a/src/myna/core/components/component_temperature.py b/src/myna/core/components/component_temperature.py index 098bf1c6..26119567 100644 --- a/src/myna/core/components/component_temperature.py +++ b/src/myna/core/components/component_temperature.py @@ -13,8 +13,8 @@ ComponentTemperaturePart """ -from .component import * from myna.core.files import FileTemperature, FileTemperatureFinal +from .component import Component ################## # Base Component # @@ -54,12 +54,22 @@ def __init__(self): self.types.extend(["part", "layer"]) -class ComponentTemperatureFinalPartSTL(ComponentTemperaturePart): +class ComponentTemperatureFinalPart(ComponentTemperaturePart): """Layer-wise Component that outputs the domain temperature at the end of a layer for a part in the format of the class `FileTemperatureFinal` """ def __init__(self): ComponentTemperaturePart.__init__(self) - self.data_requirements.extend(["stl"]) self.output_requirement = FileTemperatureFinal + + +class ComponentTemperatureFinalPartSTL(ComponentTemperatureFinalPart): + """Layer-wise Component that outputs the domain temperature + at the end of a layer for a part in the format of the class `FileTemperatureFinal`. + Requires an STL file as input. + """ + + def __init__(self): + ComponentTemperatureFinalPart.__init__(self) + self.data_requirements.extend(["stl"]) diff --git a/src/myna/core/utils/nested_dict_tools.py b/src/myna/core/utils/nested_dict_tools.py index 3371a379..a5968151 100644 --- a/src/myna/core/utils/nested_dict_tools.py +++ b/src/myna/core/utils/nested_dict_tools.py @@ -24,6 +24,23 @@ def nested_get(dict, keys, default_value=None): return dict[keys[-1]] +def nested_find_all(dictionary, target_key): + """Gets the values of all matching keys in the nested dictionary at any level + + Args: + dictionary: dictionary to parse + key: key to match + """ + matches = [] + if target_key in dictionary: + matches.append(dictionary[target_key]) + for key, value in dictionary.items(): + if isinstance(value, dict): + nested_matches = nested_find_all(dictionary[key], target_key) + matches.extend(nested_matches) + return matches + + def get_synonymous_key(dict_obj, synonym_list): """Returns the object at the first matching key from a dictionary-like object given a list of synonymous keys From 4a9a377c29425f70d78cf7d97cffc0e9b19b2b5a Mon Sep 17 00:00:00 2001 From: Gerry Knapp Date: Fri, 20 Dec 2024 14:57:41 -0500 Subject: [PATCH 4/4] fixup: remove unused/duplicate functions --- src/myna/application/additivefoam/__init__.py | 1 - .../additivefoam/update_parameters.py | 109 ------------------ 2 files changed, 110 deletions(-) delete mode 100644 src/myna/application/additivefoam/update_parameters.py diff --git a/src/myna/application/additivefoam/__init__.py b/src/myna/application/additivefoam/__init__.py index 3f1741a6..73f384f9 100644 --- a/src/myna/application/additivefoam/__init__.py +++ b/src/myna/application/additivefoam/__init__.py @@ -14,4 +14,3 @@ from . import path from .additivefoam import AdditiveFOAM -from .update_parameters import * diff --git a/src/myna/application/additivefoam/update_parameters.py b/src/myna/application/additivefoam/update_parameters.py deleted file mode 100644 index 3c8c4609..00000000 --- a/src/myna/application/additivefoam/update_parameters.py +++ /dev/null @@ -1,109 +0,0 @@ -# -# Copyright (c) 2024 Oak Ridge National Laboratory. -# -# This file is part of Myna. For details, see the top-level license -# at https://github.com/ORNL-MDF/Myna/LICENSE.md. -# -# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause. -# - -import subprocess -import os -import shutil -import numpy as np - - -def set_beam_size(case_dir, spot_x, spot_y): - """Sets the beam size for an AdditiveFOAM case - - Args: - case_dir: path to the case directory to modify - spot_x: x-dimension of the heat source (radius of beam with diameter 4Dsigma) - in meters - spot_y: y-dimension of the heat source (radius of beam with diameter 4Dsigma) - in meters - """ - - # 1. Get heatSourceModel - heat_source_model = ( - subprocess.check_output( - f"foamDictionary -entry beam/heatSourceModel -value " - + f"{case_dir}/constant/heatSourceDict", - shell=True, - ) - .decode("utf-8") - .strip() - ) - - # 2. Get heatSourceModelCoeffs/dimensions - heat_source_dimensions = ( - subprocess.check_output( - f"foamDictionary -entry beam/{heat_source_model}Coeffs/dimensions -value " - + f"{case_dir}/constant/heatSourceDict", - shell=True, - ) - .decode("utf-8") - .strip() - ) - heat_source_dimensions = ( - heat_source_dimensions.replace("(", "").replace(")", "").strip() - ) - heat_source_dimensions = [float(x) for x in heat_source_dimensions.split(" ")] - - # 3. Modify X- and Y-dimensions - heat_source_dimensions[:2] = [spot_x, spot_y] - heat_source_dimensions = [round(dim, 7) for dim in heat_source_dimensions] - - # 4. Write to file - heat_source_dim_string = ( - str(heat_source_dimensions) - .replace("[", "( ") - .replace("]", " )") - .replace(",", "") - ) - os.system( - f"foamDictionary -entry beam/{heat_source_model}Coeffs/dimensions -set" - + f" {heat_source_dim_string}" - + f" {case_dir}/constant/heatSourceDict" - ) - - -def set_start_and_end_times( - case_dir, start_time, end_time, time_precision=5, num_write_times=None -): - """Updates the case to have the given start and end times - - Args: - case_dir: path to case directory to update - start_time: time to start the simulation, in seconds - end_time: time to end the simulation, in seconds - time_precision: decimal precision for time entries - num_write_times: number of time steps to write out, if not specified (None) - only the last time step will be output""" - - # Set start and end times and rename initial condition directory - start_time = np.round(start_time, time_precision) - end_time = np.round(end_time, time_precision) - os.system( - f"foamDictionary -entry startTime -set {start_time} " - + f"{case_dir}/system/controlDict" - ) - os.system( - f"foamDictionary -entry endTime -set {end_time} " - + f"{case_dir}/system/controlDict" - ) - shutil.move(os.path.join(case_dir, "0"), os.path.join(case_dir, f"{start_time}")) - - # Calculate and set write interval - if (num_write_times is None) or (num_write_times == 0): - write_interval = np.round((end_time - start_time), time_precision) - else: - write_interval = np.round( - 1 / num_write_times * (end_time - start_time), time_precision - ) - os.system( - f"foamDictionary -entry writeInterval -set" - + f" {write_interval}" - + f" {case_dir}/system/controlDict" - ) - shutil.move(os.path.join(case_dir, "0"), os.path.join(case_dir, f"{start_time}"))